#ifndef MAPEDITOR_H
#define MAPEDITOR_H

#include <QGraphicsView>
#include <QItemSelectionModel>
#include <QLineEdit>
#include <QPropertyAnimation>

#include "tmp-parent-container.h"
#include "settings.h"
#include "vymmodel.h"

class XLink;
class XLinkItem;
class Winter;

/*! \brief Main widget in vym to display and edit a map */

class MapEditor : public QGraphicsView {
    Q_OBJECT

  public:
    enum EditorState {
        Neutral,
        EditingHeading,
        CreatingXLink,
        EditingXLink,
        MovingObject,
        MovingObjectTmpLinked,
        MovingObjectWithoutLinking,
        PanningView,
        PickingColor
    };

    MapEditor(VymModel *vm);
    ~MapEditor();
    VymModel *getModel();
    QGraphicsScene *getScene();

    // Animation of scrollbars
    Q_PROPERTY(QPointF scrollBarPos READ getScrollBarPos WRITE setScrollBarPos)

  protected:
    QPointF scrollBarPos;
    QPointF scrollBarPosTarget;
    QPropertyAnimation scrollBarPosAnimation;
    QTimer *panningTimer;
    QPointF vPan;      //! Direction of panning during moving of object

  private slots:
    void panView();

  public:
    void ensureAreaVisibleAnimated(
            const QRectF &area, 
            bool scaled = false, 
            bool rotated = false,
            qreal new_angle = 0) ;
    void ensureSelectionVisibleAnimated(bool scaled = false, bool rotated = false);
    void setScrollBarPosTarget(QRectF rect); //!  ensureVisible of rect
    QPointF getScrollBarPosTarget();
    void setScrollBarPos(const QPointF &p);
    QPointF getScrollBarPos();
    void animateScrollBars();

    // Animation of containers
  private:
    QTimer *animationTimer;
    bool animationUse;
    uint animationTicks;
    uint animationInterval;
    int timerId;                 // animation timer
    QList<Container*> animatedContainers;

  private slots:
    void animate(); //!< Called by timer to animate stuff
  public:
    void startAnimation(Container *c, const QPointF &v);
    void startAnimation(Container *c, const QPointF &start, const QPointF &dest);
    void stopContainerAnimation(Container *c);
    void stopContainerAnimations();
    void stopViewAnimations();

    // Animation of zoom
    Q_PROPERTY(qreal zoomFactorInt READ zoomFactor WRITE setZoomFactor)

  protected:
    qreal zoomDelta;
    qreal zoomFactorInt;
    qreal zoomFactorTargetInt;
    QPropertyAnimation zoomAnimation;

  public:
    void zoomIn();
    void zoomOut();
    void setZoomFactorTarget(const qreal &zf);
    qreal zoomFactorTarget();
    void setZoomFactor(const qreal &zf);
    qreal zoomFactor();

    // Animation of rotation
    Q_PROPERTY(qreal rotationInt READ rotation WRITE setRotation)

  protected:
    qreal rotationInt;
    qreal rotationTargetInt;
    QPropertyAnimation rotationAnimation;

    bool useTransformationOrigin;
    QPointF transformationOrigin;
    QPointF vp_center;   // Calculated before transformation to center on later

  public:
    void setRotationTarget(const qreal &a);
    qreal rotationTarget();
    void setRotation(const qreal &a);
    qreal rotation();

    // Animation of viewCenter
    Q_PROPERTY(QPointF viewCenter READ getViewCenter WRITE setViewCenter)

  protected:
    QPointF viewCenter;
    QPointF viewCenterTarget;

  public:
    void setViewCenterTarget(
        const QPointF &p, const qreal &zft, const qreal &at,
        const int duration = 2000,
        const QEasingCurve &easingCurve = QEasingCurve::OutQuint);
    void
    setViewCenterTarget(); //! Convenience function, center on selected item
    QPointF getViewCenterTarget();
    void setViewCenter(const QPointF &p);
    QPointF getViewCenter();
    QPropertyAnimation viewCenterAnimation;

    void updateMatrix(); //! Sets transformation matrix with current rotation
                         //! and zoom values
    void minimizeView();

    // xmas egg
  protected:
    Winter *winter;

  public:
    void print();                     //!< Print the map
    QRectF getTotalBBox();            //!< Bounding box of all items in map
    QImage getImage(QPointF &offset); //!< Get a pixmap of the map
    void setAntiAlias(bool);          //!< Set or unset antialiasing
    void setSmoothPixmap(bool);       //!< Set or unset smoothing of pixmaps
  public slots:
    void autoLayout();    //!< Auto layout of map by using collision detection
    void testFunction1(); //! just testing new stuff
    void testFunction2(); //! just testing new stuff

  public:
    TreeItem *findMapItem(
            QPointF p, 
            const QList <TreeItem*> &excludedItems = QList<TreeItem*>(),
            bool findNearCenter = false);  //! find item in map at position
                                           //! p. Ignore item exclude
    BranchItem *findMapBranchItem(
            QPointF p,
            const QList <TreeItem*> &excludedItems = QList<TreeItem*>(),
            bool findNearCenter = false); //! only return BranchItem
    void toggleWinter();

    enum RadarDirection {
        UpDirection,
        DownDirection,
        LeftDirection,
        RightDirection
    };

    bool isContainerCloserInDirection(Container *c1, Container *c2, const qreal &d_min, const QPoint &v, RadarDirection radarDir);
    TreeItem* getItemInDirection(TreeItem *ti, RadarDirection);
    TreeItem* getItemFromGeometry(TreeItem *ti, RadarDirection);
    TreeItem* getItemFromOrgChart(TreeItem *ti, RadarDirection);
    TreeItem* getItemFromHirarchy(TreeItem *ti, RadarDirection);

    TreeItem* getItemDirectAbove(TreeItem *ti);
    TreeItem* getItemDirectBelow( TreeItem *ti);

  private:
    // Toggle objects by moving the cursor up/down with shift modifier
    // (needs to consider the current direction of movement)
    enum ToggleDirection {toggleUndefined, toggleUp, toggleDown};
    ToggleDirection lastToggleDirection;

  public slots:
    void cursorUp();
    void cursorUpToggleSelection();
    void cursorDown();
    void cursorDownToggleSelection();
    void cursorLeft();
    void cursorRight();
    void editHeading(BranchItem *selbi = nullptr);
    void editHeadingCanceled();
    void editHeadingFinished();

  private:
    void hideLineEdit();
    QLineEdit *lineEdit;
    QAction *editHeadingCancelAction;
    QGraphicsProxyWidget *proxyWidget;

  private:
    void contextMenuEvent(QContextMenuEvent *e);
    void keyPressEvent(QKeyEvent *);
    void keyReleaseEvent(QKeyEvent *);
    void startPanningView(QMouseEvent *);
    void mousePressEvent(QMouseEvent *);
    void mouseMoveEvent(QMouseEvent *);
    void moveObject(QMouseEvent *, const QPointF &p_event);    // Called from mouseMoveEvent
    void mouseReleaseEvent(QMouseEvent *);
    void mouseDoubleClickEvent(QMouseEvent *);
    void wheelEvent(QWheelEvent *);
    void focusInEvent(QFocusEvent *);
    void focusOutEvent(QFocusEvent *);
    void resizeEvent(QResizeEvent *);

    void dragEnterEvent(QDragEnterEvent *);
    void dragMoveEvent(QDragMoveEvent *);
    void dragLeaveEvent(QDragLeaveEvent *);
    void dropEvent(QDropEvent *);

    void updateCursor();
  private:
    QGraphicsScene *mapScene;
    VymModel *model; //!< Vym Map, includding several mapCenters

    bool adjustCanvasRequested; // collect requests until end of user event
    BranchObj *editingBO;       // entering Text into BO

    QCursor HandOpenCursor;  // cursor while moving canvas view
    QCursor PickColorCursor; // cursor while picking color
    QCursor CopyCursor;      // cursor while picking color
    QCursor XLinkCursor;     // cursor while picking color

    // Various states of the MapEditor
  public:
    MapEditor::EditorState state();

  private:
    EditorState editorState;

    void setState(EditorState);
    bool objectMoved; // true if object was not clicked, but moved with mouse

    // Temporary used for linkx
    XLink *tmpXLink;

    // Temporary used for panning view
    QPoint panning_initialPointerPos;           // initial pos in pointer coordinates
    QPoint panning_initialScrollBarValues;      // inital values of scrollbars

    // Moving containers
    QList <TreeItem*> movingItems;              // selected items which are currently moved
    QPointF movingObj_initialScenePos;          // coord when button was pressed
    QPointF movingObj_initialContainerOffset;   // offset from above coordinates to object
    TmpParentContainer *tmpParentContainer;

    QPointF contextMenuPos; // position where context event was triggered

    bool printFrame;  // Print frame around map
    bool printFooter; // Print footer below map

    QPoint exportOffset; // set before export, used in save

    //////////// Selection related
  public:
    enum SelectionMode {
        AutoSelection,
        HirarchicalSelection,   // legacy selection type (default)
        OrgChartSelection,
        GeometricSelection
    };

  SelectionMode currentSelectionMode(TreeItem *);

  private:
    SelectionMode selectionMode;

  signals:
    void selectionChanged(const QItemSelection &, const QItemSelection &);

  public slots:
    void updateData(const QModelIndex &);                 // update data
    void togglePresentationMode();
};
#endif
