/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "UBGraphicsAristo.h"
#include "board/UBBoardController.h"
#include "board/UBDrawingController.h"
#include "core/UBApplication.h"
#include "domain/UBGraphicsScene.h"

#include <QColor>
#include <QFont>
#include <QFontMetricsF>
#include <QGraphicsItem>
#include <QLineF>
#include <QPolygonF>
#include <QRadialGradient>
#include <QString>

#include "core/memcheck.h"

const QRectF UBGraphicsAristo::sDefaultRect =  QRectF(0, 0, 800, 500);
const UBGraphicsAristo::Orientation UBGraphicsAristo::sDefaultOrientation = UBGraphicsAristo::Bottom;

UBGraphicsAristo::UBGraphicsAristo()
    : UBAbstractDrawRuler()
    , QGraphicsPathItem()
    , mMarking(false)
    , mResizing(false)
    , mRotating(false)
    , mOrientation(Undefined)
    , mRotatedAngle(0)
    , mMarkerAngle(0)
    , mStartAngle(0)
    , mSpan(180)
    , mHFlipSvgItem(0)
    , mMarkerSvgItem(0)
    , mResizeSvgItem(0)
    , mRotateSvgItem(0)
{
    mHFlipSvgItem = new QGraphicsSvgItem(":/images/vflipTool.svg", this);
    mHFlipSvgItem->setVisible(false);
    mHFlipSvgItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Control));

    mResizeSvgItem = new QGraphicsSvgItem(":/images/resizeTool.svg", this);
    mResizeSvgItem->setVisible(false);
    mResizeSvgItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Control));

    mRotateSvgItem = new QGraphicsSvgItem(":/images/rotateTool.svg", this);
    mRotateSvgItem->setVisible(false);
    mRotateSvgItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Control));

    mMarkerSvgItem = new QGraphicsSvgItem(":/images/angleMarker.svg", this);
    mMarkerSvgItem->setVisible(false);
    mMarkerSvgItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));
    mMarkerSvgItem->setVisible(true);

    create(*this);
    setBoundingRect(sDefaultRect);
    setOrientation(sDefaultOrientation);
}

UBGraphicsAristo::~UBGraphicsAristo()
{
    /* NOOP */
}

/*
 * setOrientation() modify the tool orientation.
 * makeGeometryChange() is called so points are recomputed, control items are positionnated and shape is determined according to this modification.
 */
void UBGraphicsAristo::setOrientation(Orientation orientation)
{
    mOrientation = orientation;
    makeGeometryChange();
}

/* calculatePoints() is used to calculate polygon's apexes coordinates.
 * This function handles orientation changes too.
 */
void UBGraphicsAristo::calculatePoints()
{
    switch (mOrientation) {
    case Bottom:
        C.setX(boundingRect().center().x());
        C.setY(boundingRect().bottom());

        A.setX(boundingRect().left());
        A.setY(boundingRect().bottom() - boundingRect().width() / 2);

        B.setX(boundingRect().right());
        B.setY(boundingRect().bottom() - boundingRect().width() / 2);
        break;
    case Top:
        C.setX(boundingRect().center().x());
        C.setY(boundingRect().top());

        A.setX(boundingRect().left());
        A.setY(boundingRect().top() + boundingRect().width() / 2);

        B.setX(boundingRect().right());
        B.setY(boundingRect().top() + boundingRect().width() / 2);
        break;
    default:
        break;
    }
}

/*
 * setItemsPos() places control items according to A, B and C positions.
 * Call this function after A, B or C position modification, mostly after calling calculatePoints().
 * These positions has to be set when calling setPath() to allow hover events on items which are not into the main polygon.
 */
void UBGraphicsAristo::setItemsPos()
{
    mCloseSvgItem->setPos(closeButtonRect().topLeft() + rotationCenter());
    mHFlipSvgItem->setPos(hFlipRect().topLeft() + rotationCenter());
    mRotateSvgItem->setPos(rotateRect().topLeft() + rotationCenter());
    mResizeSvgItem->setPos(resizeButtonRect().topLeft() + rotationCenter()); 
    mMarkerSvgItem->setPos(markerButtonRect().topLeft() + rotationCenter());
}

/*
 * determinePath() modify the shape according to apexes coordinates and control item positions.
 * This is useful when orientation is modified.
 * Returns the painter path corresponding to object parameters.
 */
QPainterPath UBGraphicsAristo::determinePath()
{
    QPainterPath path;

    QPolygonF polygon;
    polygon << A << B << C;
    path.addPolygon(polygon);

    path.addPath(mResizeSvgItem->shape().translated(mResizeSvgItem->pos()));
    path.addPath(mMarkerSvgItem->shape().translated(mMarkerSvgItem->pos()));

    return path;
}

/*
 * setBoundingRect() is a helper to set the given rectangle as the new shape to limit apexes coordinates.
 * This is useful when instanciating or resizing the object.
 * makeGeometryChange() is called so points are recomputed, control items are positionnated and shape is determined according to this modification. 
 * Setting bounds' width less than 300 is not allowed.
 */
void UBGraphicsAristo::setBoundingRect(QRectF boundingRect)
{
    if (boundingRect.width() < 300)
        return;

    QPainterPath path;
    path.addRect(boundingRect);
    setPath(path);
    if (mOrientation != Undefined)
        makeGeometryChange();
}

void UBGraphicsAristo::makeGeometryChange()
{
    calculatePoints();
    setItemsPos();
    setPath(determinePath());
}


UBItem* UBGraphicsAristo::deepCopy(void) const
{
    UBGraphicsAristo* copy = new UBGraphicsAristo();
    copyItemParameters(copy);
    return copy;
}

void UBGraphicsAristo::copyItemParameters(UBItem *copy) const
{
    UBGraphicsAristo* cp = dynamic_cast<UBGraphicsAristo*>(copy);
    if (cp)
    {   
        /* TODO: copy all members */
        cp->setPos(this->pos());
        cp->setTransform(this->transform());
        cp->setBoundingRect(boundingRect());
        cp->setOrientation(mOrientation);
        cp->mRotatedAngle = mRotatedAngle;
        cp->mMarkerAngle = mMarkerAngle;
    }
}


void UBGraphicsAristo::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    QPolygonF polygon;

    painter->setBrush(fillBrush());
    painter->setPen(drawColor());

    polygon << A << B << C;
    painter->drawPolygon(polygon);
    polygon.clear();

    paintGraduations(painter);
    paintMarker(painter);    
}

QBrush UBGraphicsAristo::fillBrush() const
{
    QColor fillColor = edgeFillColor();// scene()->isDarkBackground() ? sDarkBackgroundFillColor : sFillColor;
    QColor fillColorCenter = middleFillColor();//scene()->isDarkBackground() ? sDarkBackgroundFillColorCenter : sFillColorCenter;
    QColor transparentWhite = Qt::white;
    transparentWhite.setAlpha(scene()->isDarkBackground() ? sDrawTransparency : sFillTransparency);
    QRadialGradient radialGradient(boundingRect().center(), radius(), boundingRect().center());
    radialGradient.setColorAt(0, fillColorCenter);
    radialGradient.setColorAt(1, fillColor);
    return radialGradient;
}

/* paintGraduations() paints graduations on the ruler side (length graduations) and the two other sides (angle graduation) */
void UBGraphicsAristo::paintGraduations(QPainter *painter)
{
    paintRulerGraduations(painter);
    paintProtractorGraduations(painter);
}

void UBGraphicsAristo::paintRulerGraduations(QPainter *painter)
{
    /* defining useful constants */
    const int centimeterGraduationHeight = 15;
    const int halfCentimeterGraduationHeight = 10;
    const int millimeterGraduationHeight = 5;
    const int millimetersPerCentimeter = 10;
    const int millimetersPerHalfCentimeter = 5;

    painter->save();
    painter->setFont(font());
    QFontMetricsF fontMetrics(painter->font());

    /* Browsing milliters in half width of ruler side */
    for (int millimeters = 0; millimeters < (boundingRect().width() / 2 - sLeftEdgeMargin - sRoundingRadius) / sPixelsPerMillimeter; millimeters++)
    {
        /* defining graduationHeight ; values are different to draw bigger lines if millimiter considered is a centimeter or a half centimeter */
        int graduationHeight = (0 == millimeters % millimetersPerCentimeter) ?
            centimeterGraduationHeight :
            ((0 == millimeters % millimetersPerHalfCentimeter) ?
                halfCentimeterGraduationHeight : millimeterGraduationHeight);

        /* correcting graduationHeight: draw the line in the other direction in case ruler is top-oriented, to stay inside the tool and inside the rect */
        graduationHeight = mOrientation == Bottom ? graduationHeight : - graduationHeight;
        
        /* drawing graduation to the left and to the right of origin, which is the center of graduated side */
        painter->drawLine(QLine(rotationCenter().x() + sPixelsPerMillimeter * millimeters, rotationCenter().y(), rotationCenter().x() + sPixelsPerMillimeter * millimeters, rotationCenter().y() + graduationHeight));
        if (millimeters != 0)
            painter->drawLine(QLine(rotationCenter().x() - sPixelsPerMillimeter * millimeters, rotationCenter().y(), rotationCenter().x() - sPixelsPerMillimeter * millimeters, rotationCenter().y() + graduationHeight));

        /* drawing associated value if considered graduation is a centimeter */
        if (0 == millimeters % millimetersPerCentimeter)
        {
            /* defining graduation value */
            QString text = QString("%1").arg((int)(millimeters / millimetersPerCentimeter));
            
            /* staying inside polygon */
            if (rotationCenter().x() + sPixelsPerMillimeter * millimeters + fontMetrics.width(text) / 2 < boundingRect().right())
            {
                qreal textWidth = fontMetrics.width(text);
                qreal textHeight = fontMetrics.tightBoundingRect(text).height() + 5;
                
                /* text y-coordinate is different according to tool's orientation */
                qreal textY = mOrientation == Bottom ? A.y() + 5 + centimeterGraduationHeight : A.y() - 5 - centimeterGraduationHeight + graduationHeight;
                
                /* if text's rect is not out of polygon's bounds, drawing value below or above graduation */
                QPointF intersectionPoint;
                
                bool paint = false;

                if (mOrientation == Bottom && QLineF(QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters - textWidth / 2, rotationCenter().y()), QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters - textWidth / 2, textY + textHeight)).intersect(QLineF(A, C), &intersectionPoint) != QLineF::BoundedIntersection && QLineF(QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters + textWidth / 2, rotationCenter().y()), QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters + textWidth / 2, textY + textHeight)).intersect(QLineF(A, C), &intersectionPoint) != QLineF::BoundedIntersection) {
                    paint = true;
                }
                else if (mOrientation == Top && QLineF(QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters - textWidth / 2, rotationCenter().y()), QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters - textWidth / 2, textY - textHeight)).intersect(QLineF(A, C), &intersectionPoint) != QLineF::BoundedIntersection && QLineF(QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters + textWidth / 2, rotationCenter().y()), QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters + textWidth / 2, textY - textHeight)).intersect(QLineF(A, C), &intersectionPoint) != QLineF::BoundedIntersection) {
                    paint = true;
                }

                if (paint) {
                    painter->drawText(
                        QRectF(rotationCenter().x() + sPixelsPerMillimeter * millimeters - textWidth / 2, textY, textWidth, textHeight),
                        Qt::AlignVCenter, text);
                    if (millimeters != 0)
                        painter->drawText(
                        QRectF(rotationCenter().x() - sPixelsPerMillimeter * millimeters - textWidth / 2, textY, textWidth, textHeight),
                        Qt::AlignVCenter, text);
                }
            }
        }
    }
    painter->restore();
}

void UBGraphicsAristo::paintProtractorGraduations(QPainter* painter)
{
    /* defining useful constants */
    const int  tenDegreeGraduationLength = 15;
    const int fiveDegreeGraduationLength = 10;
    const int  oneDegreeGraduationLength = 5;

    painter->save();

    QFont font1 = painter->font();
#ifdef Q_WS_MAC
    font1.setPointSizeF(font1.pointSizeF() - 3);
#endif
    QFontMetricsF fm1(font1);

    //Font for internal arc
    QFont font2 = painter->font();
    font2.setPointSizeF(font1.pointSizeF()/1.5);
    QFontMetricsF fm2(font2);

    /* defining virtual arc diameter */
    qreal rad = radius();

    QPointF center = rotationCenter();

    /* browsing angles */
    for (int angle = 1; angle < mSpan; angle++)
    {
        int graduationLength = (0 == angle % 10) ? tenDegreeGraduationLength : ((0 == angle % 5) ? fiveDegreeGraduationLength : oneDegreeGraduationLength);
        
        qreal co = cos(((qreal)angle + mStartAngle) * PI/180);
        qreal si = sin(((qreal)angle + mStartAngle) * PI/180);

        /* inverse sinus according to the orientation, to draw graduations on the polygon */
        si = mOrientation == Bottom ? -si : si;

        /* drawing the graduation around the virtual arc */
        if (angle >= sArcAngleMargin && angle <= mSpan - sArcAngleMargin)
            painter->drawLine(QLineF(QPointF(center.x()+ rad/2*co, center.y() - rad/2*si),
                                    QPointF(center.x()+ (rad/2 + graduationLength)*co,
                                            center.y() - (rad/2 + graduationLength)*si)));


        QPointF intersectionPoint;
        QLineF referenceLine;
        if (angle < 90)
            referenceLine.setP1(B);
        else
            referenceLine.setP1(A);
        referenceLine.setP2(C);

        /* if angle is 10-multiple, drawing it's value, rotated to be easily red */
        if (0 == angle % 10) {
            QString grad = QString("%1").arg((int)(angle));
            QString grad2 = QString("%1").arg((int)mSpan - angle);

            painter->setFont(font2);

            painter->save();
            painter->translate(center.x() + (rad/2 + graduationLength*1.5)*co, center.y() - (rad/2 + graduationLength*1.5)*si);
            int degrees = mOrientation == Bottom ? angle : -angle;
            painter->rotate(-90 + degrees);
            painter->drawText(- fm2.width(grad)/2, - fm2.height()/2, fm2.width(grad), fm2.height(), Qt::AlignCenter, grad);
            painter->restore();

            painter->setFont(font1);

            
            /* drawing the graduation near tool's side */
            if (QLineF(QPointF(center.x()+ rad/2*co, center.y() - rad/2*si),
                                     QPointF(center.x()+ (rad/2 + graduationLength)*co,
                                             center.y() - (rad/2 + graduationLength)*si)).intersect(referenceLine, &intersectionPoint) == QLineF::UnboundedIntersection)

                painter->drawLine(QLineF(QPointF(center.x() + (rad/2 + graduationLength*1.5 + fm2.width(grad)/2)*co,
                                                 center.y() - (rad/2 + graduationLength*1.5 + fm2.height()/2)*si),
                                        intersectionPoint));

        }

        /* drawing the graduation near tool's side */
        else
            if (QLineF(QPointF(center.x()+ rad/2*co, center.y() - rad/2*si),
                                     QPointF(center.x()+ (rad/2 + graduationLength)*co,
                                             center.y() - (rad/2 + graduationLength)*si)).intersect(referenceLine, &intersectionPoint) == QLineF::UnboundedIntersection)

                painter->drawLine(QLineF(QPointF(intersectionPoint.x() - (graduationLength*1.5)*co,
                                                 intersectionPoint.y() + (graduationLength*1.5)*si),
                                        intersectionPoint));
    }

    painter->restore();
}

/* paintMarker() adjust marker button according to the current angle, draw the line allowing user to set precisely the angle, and draw the current angle's value. */
void UBGraphicsAristo::paintMarker(QPainter *painter)
{
    /* adjusting marker button */
    mMarkerSvgItem->resetTransform();
    mMarkerSvgItem->translate(-markerButtonRect().left(), -markerButtonRect().top());
    mMarkerSvgItem->rotate(mMarkerAngle);
    mMarkerSvgItem->translate(markerButtonRect().left(), markerButtonRect().top());

    
    qreal co = cos((mMarkerAngle) * PI/180);
    qreal si = sin((mMarkerAngle) * PI/180);

    /* Setting point composing the line (from point C) which intersects the line we want to draw. */
    QPointF referencePoint;
    if (mOrientation == Bottom) {
        if ((int)mMarkerAngle % 360 < 90)
            referencePoint = B;
        else
            referencePoint = A;
    }
    else if (mOrientation == Top) {
        if ((int)mMarkerAngle % 360 < 270 && (int)mMarkerAngle % 360 > 0)
            referencePoint = A;
        else
            referencePoint = B;
    }
    
    /* getting intersection point to draw the wanted line */
    QLineF intersectedLine(rotationCenter(), QPointF(rotationCenter().x()+co, rotationCenter().y()+si)); 
    QPointF intersectionPoint;
    if (intersectedLine.intersect(QLineF(referencePoint, C), &intersectionPoint))
        painter->drawLine(QLineF(intersectionPoint, rotationCenter()));

    /* drawing angle value */
    qreal rightAngle = mOrientation == Bottom ? mMarkerAngle : 360 - mMarkerAngle;


    QString angleText = QString("%1°").arg(rightAngle, 0, 'f', 1);

    QFont font1 = painter->font();
#ifdef Q_WS_MAC
    font1.setPointSizeF(font1.pointSizeF() - 3);
#endif
    QFontMetricsF fm1(font1);

    if (mOrientation == Bottom)
        painter->drawText(rotationCenter().x() - fm1.width(angleText)/2 - radius()/8, rotationCenter().y() + radius()/8 - fm1.height()/2, fm1.width(angleText), fm1.height(), Qt::AlignCenter, angleText);
    else
        painter->drawText(rotationCenter().x() - fm1.width(angleText)/2 - radius()/8, rotationCenter().y() - radius()/8 - fm1.height()/2, fm1.width(angleText), fm1.height(), Qt::AlignCenter, angleText);
}


void UBGraphicsAristo::rotateAroundCenter(qreal angle)
{
    qreal oldAngle = mRotatedAngle;
    mRotatedAngle = angle;
    QTransform transform;
    rotateAroundCenter(transform, rotationCenter());
    setTransform(transform, true);
    mRotatedAngle = oldAngle + angle; // We have to store absolute value for FLIP case
}

void UBGraphicsAristo::rotateAroundCenter(QTransform& transform, QPointF center)
{
    transform.translate(center.x(), center.y());
    transform.rotate(mRotatedAngle);
    transform.translate(- center.x(), - center.y());
}

void UBGraphicsAristo::resize(qreal factor)
{
    prepareGeometryChange();
    translate(rotationCenter().x(), rotationCenter().y());
    scale(factor, factor);
    translate(-rotationCenter().x(), -rotationCenter().y());
}


QPointF UBGraphicsAristo::rotationCenter() const
{
    return QPointF((A.x() + B.x()) / 2, (A.y() + B.y()) / 2);
}

QRectF UBGraphicsAristo::closeButtonRect() const
{
    qreal y = radius() / 4 + hFlipRect().height() + 3 + rotateRect().height() + 3;
    if (mOrientation == Top)
        y = -y;
    return QRectF(- mCloseSvgItem->boundingRect().width() / 2, y, mCloseSvgItem->boundingRect().width(), mCloseSvgItem->boundingRect().height());
}

QRectF UBGraphicsAristo::hFlipRect() const
{
    qreal y = radius() / 4;
    if (mOrientation == Top)
        y = -y;

     return QRectF(- mHFlipSvgItem->boundingRect().width() / 2, y, mHFlipSvgItem->boundingRect().width(), mHFlipSvgItem->boundingRect().height());
}

QRectF UBGraphicsAristo::markerButtonRect() const
{
    return QRectF (radius()/2 - mMarkerSvgItem->boundingRect().width(), - mMarkerSvgItem->boundingRect().height()/2, mMarkerSvgItem->boundingRect().width(), mMarkerSvgItem->boundingRect().height());
}

QRectF  UBGraphicsAristo::resizeButtonRect() const
{
    return QRectF((B - rotationCenter()).x() - 100 - mResizeSvgItem->boundingRect().width()/2, - mResizeSvgItem->boundingRect().height()/2, mResizeSvgItem->boundingRect().width(), mResizeSvgItem->boundingRect().height());   
}

QRectF UBGraphicsAristo::rotateRect() const
{
    qreal y = radius() / 4 + hFlipRect().height() + 3;
    if (mOrientation == Top)
        y = -y;
    return QRectF(- mRotateSvgItem->boundingRect().width() / 2, y, mRotateSvgItem->boundingRect().width(), mRotateSvgItem->boundingRect().height());

}

QCursor UBGraphicsAristo::flipCursor() const
{
    return Qt::ArrowCursor;
}

QCursor UBGraphicsAristo::markerCursor() const
{
    return Qt::ArrowCursor;
}

QCursor UBGraphicsAristo::resizeCursor() const
{
    return Qt::ArrowCursor;
}


void UBGraphicsAristo::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    switch (toolFromPos(event->pos())) {
    case Rotate:
        mRotating = true;
        event->accept();
        break;
    case Resize:
        mResizing = true;
        event->accept();
        break;
    case MoveMarker:
        mMarking = true;
        event->accept();
        break;
    default:
        QGraphicsItem::mousePressEvent(event);
        break;
    }

    mShowButtons = false;
    mHFlipSvgItem->setVisible(false);
    mCloseSvgItem->setVisible(false);
    mRotateSvgItem->setVisible(mRotating);
    mResizeSvgItem->setVisible(mResizing);
    update();
}

void UBGraphicsAristo::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{

    if (!mResizing && !mRotating && !mMarking)
    {
        QGraphicsItem::mouseMoveEvent(event);
    }
    else
    {
        QLineF currentLine(rotationCenter(), event->pos());
        QLineF lastLine(rotationCenter(), event->lastPos());

        if (mRotating) {
            
            rotateAroundCenter(currentLine.angleTo(lastLine));
        }
        else if (mResizing) {
            QPointF delta = event->pos() - event->lastPos();
            setBoundingRect(QRectF(boundingRect().topLeft(), QSizeF(boundingRect().width() + delta.x(), boundingRect().height() + delta.x())));
        }
        else if(mMarking) {
            qreal angle = currentLine.angleTo(lastLine);

            mMarkerAngle += angle;
            mMarkerAngle -= (int)(mMarkerAngle/360)*360;

            if (mOrientation == Bottom) {
                if (mMarkerAngle >= 270)
                    mMarkerAngle = 0;
                else if (mMarkerAngle > 180)
                    mMarkerAngle = 180;
            }
            else if (mOrientation == Top) {
                if (mMarkerAngle < 90)
                    mMarkerAngle = 360;
                else if (mMarkerAngle < 180)
                    mMarkerAngle = 180;
            }
            update();
        }

        event->accept();
    }
}

void UBGraphicsAristo::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (mResizing || mRotating || mMarking)
    {
        mResizing = false;
        mRotating = false;
        mMarking = false;
        event->accept();
    }
    else
    {
        switch (toolFromPos(event->pos())) {
        case Close :
            hide();
            emit hidden();
            break;
        case HorizontalFlip:
            /* substracting difference to zero [2pi] twice, to obtain the desired angle */
            mMarkerAngle -= 2 * (mMarkerAngle - (int)(mMarkerAngle/360)*360) - 360;
            /* setting new orientation */
            switch(mOrientation) {
            case Bottom:
                setOrientation(Top);
                break;
            case Top:
                setOrientation(Bottom);
                break;
            default:
                break;
            }
        default:
            QGraphicsItem::mouseReleaseEvent(event);
            break;
        }
    }

    mShowButtons = true;
    update();
    if (scene())
        scene()->setModified(true);
}

void UBGraphicsAristo::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    UBStylusTool::Enum currentTool = (UBStylusTool::Enum)UBDrawingController::drawingController ()->stylusTool ();

    if (currentTool == UBStylusTool::Selector)  {
        mShowButtons = true;
        mHFlipSvgItem->setVisible(true);
        mRotateSvgItem->setVisible(true);
        mResizeSvgItem->setVisible(true);
        mCloseSvgItem->setVisible(true);

        switch (toolFromPos(event->pos())) {
        case HorizontalFlip:
            setCursor(flipCursor());
            break;
        case Rotate:
            setCursor(rotateCursor());
            break;
        case Resize:
            setCursor(resizeCursor());
            break;
        case MoveMarker:
            setCursor(markerCursor());
            break;
        case Close:
            setCursor(closeCursor());
            break;
        default:
            setCursor(moveCursor());
            break;
        }

        event->accept();
        update();

    } else if (UBDrawingController::drawingController()->isDrawingTool())  {
            setCursor(drawRulerLineCursor());
            UBDrawingController::drawingController()->mActiveRuler = this;
            event->accept();
    }
}

void UBGraphicsAristo::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
    mShowButtons = false;
    setCursor(Qt::ArrowCursor);
    mHFlipSvgItem->setVisible(false);
    mRotateSvgItem->setVisible(false);
    mResizeSvgItem->setVisible(false);
    mCloseSvgItem->setVisible(false);
    UBDrawingController::drawingController()->mActiveRuler = NULL;
    event->accept();
    update();
}

void UBGraphicsAristo::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
    UBStylusTool::Enum currentTool = (UBStylusTool::Enum)UBDrawingController::drawingController ()->stylusTool ();

    if (currentTool == UBStylusTool::Selector)
    {
        mShowButtons = true;
        mHFlipSvgItem->setVisible(true);
        mRotateSvgItem->setVisible(true);
        mResizeSvgItem->setVisible(true);
        mCloseSvgItem->setVisible(true);

        switch (toolFromPos(event->pos())) {
        case HorizontalFlip:
            setCursor(flipCursor());
            break;
        case Rotate:
            setCursor(rotateCursor());
            break;
        case Resize:
            setCursor(resizeCursor());
            break;
        case MoveMarker:
            setCursor(markerCursor());
            break;
        case Close:
            setCursor(closeCursor());
            break;
        default:
            setCursor(moveCursor());
            break;
        }

        event->accept();
    }
    else if (UBDrawingController::drawingController()->isDrawingTool())
        event->accept();
}

/*
 * toolfromPos() returns the item type corresponding to the given position.
 * This method is used to reduce the amount of code in each event function and improve class' maintainability.
 * pos: event's position ; a rotation is done to counter elements rotation, like the marker button.
 */ 
UBGraphicsAristo::Tool UBGraphicsAristo::toolFromPos(QPointF pos)
{
    pos = pos - rotationCenter();

    qreal rotationAngle = mOrientation == Bottom ? - mMarkerAngle : Top ? 360 * (int)(mMarkerAngle / 360 + 1) - mMarkerAngle : 0;

    QTransform t;
    t.rotate(rotationAngle);
    QPointF p2 = t.map(pos);

    if (resizeButtonRect().contains(pos))
        return Resize;
    else if (closeButtonRect().contains(pos))
        return Close;
    else if (rotateRect().contains(pos))
        return Rotate;
    else if (markerButtonRect().contains(p2))
        return MoveMarker;
    else if (hFlipRect().contains(pos))
        return HorizontalFlip;
    else if (shape().contains(pos))
        return Move;
    else
        return None;
}


void UBGraphicsAristo::StartLine(const QPointF &scenePos, qreal width)
{
    QPointF itemPos = mapFromScene(scenePos);

    qreal y;

    y = rotationCenter().y();

    if (itemPos.x() < boundingRect().x() + sLeftEdgeMargin)
            itemPos.setX(boundingRect().x() + sLeftEdgeMargin);
    if (itemPos.x() > boundingRect().x() + boundingRect().width() - sLeftEdgeMargin)
            itemPos.setX(boundingRect().x() + boundingRect().width() - sLeftEdgeMargin);

    itemPos.setY(y);
    itemPos = mapToScene(itemPos);

    scene()->moveTo(itemPos);
    scene()->drawLineTo(itemPos, width, true);
}

void UBGraphicsAristo::DrawLine(const QPointF &scenePos, qreal width)
{
    QPointF itemPos = mapFromScene(scenePos);

    qreal y;

    y = rotationCenter().y();

    if (itemPos.x() < boundingRect().x() + sLeftEdgeMargin)
            itemPos.setX(boundingRect().x() + sLeftEdgeMargin);
    if (itemPos.x() > boundingRect().x() + boundingRect().width() - sLeftEdgeMargin)
            itemPos.setX(boundingRect().x() + boundingRect().width() - sLeftEdgeMargin);

    itemPos.setY(y);
    itemPos = mapToScene(itemPos);

    // We have to use "pointed" line for marker tool
    scene()->drawLineTo(itemPos, width,
            UBDrawingController::drawingController()->stylusTool() != UBStylusTool::Marker);
}

void UBGraphicsAristo::EndLine()
{
    /* NOOP */
}


UBGraphicsScene* UBGraphicsAristo::scene() const
{
    return static_cast<UBGraphicsScene*>(QGraphicsPathItem::scene());
}