text: #pragma once
  
  #ifndef SELECTIONTOOL_INCLUDED
  #define SELECTIONTOOL_INCLUDED
  
  #include "tproperty.h"
  #include "toonzqt/selection.h"
  #include "tools/toolutils.h"
  #include "toonz/strokegenerator.h"
  
  // For Qt translation support
  #include <QCoreApplication>
  #include <QSet>
  
  class SelectionTool;
  
  //=============================================================================
  // Constants / Defines
  //-----------------------------------------------------------------------------
  
  enum SelectionType {
    RECT_SELECTION_IDX,
    FREEHAND_SELECTION_IDX,
    POLYLINE_SELECTION_IDX,
  };
  
  #define RECT_SELECTION L"Rectangular"
  #define FREEHAND_SELECTION L"Freehand"
  #define POLYLINE_SELECTION L"Polyline"
  
  //=============================================================================
  // FreeDeformer
  //-----------------------------------------------------------------------------
  
  class FreeDeformer {
  protected:
    TPointD m_originalP00;
    TPointD m_originalP11;
  
    std::vector<TPointD> m_newPoints;
  
  public:
    FreeDeformer() {}
    virtual ~FreeDeformer() {}
  
    /*! Set \b index point to \b p, with index from 0 to 3. */
    virtual void setPoint(int index, const TPointD &p) = 0;
    /*! Helper function. */
    virtual void setPoints(const TPointD &p0, const TPointD &p1,
                           const TPointD &p2, const TPointD &p3) = 0;
    virtual void deformImage()                                   = 0;
  };
  
  //=============================================================================
  // DragSelectionTool
  //-----------------------------------------------------------------------------
  namespace DragSelectionTool {
  //-----------------------------------------------------------------------------
  
  //=============================================================================
  // FourPoints
  //-----------------------------------------------------------------------------
  
  class FourPoints {
    TPointD m_p00, m_p01, m_p10, m_p11;
  
  public:
    FourPoints(TPointD p00, TPointD p01, TPointD p10, TPointD p11)
        : m_p00(p00), m_p01(p01), m_p10(p10), m_p11(p11) {}
    FourPoints()
        : m_p00(TPointD())
        , m_p01(TPointD())
        , m_p10(TPointD())
        , m_p11(TPointD()) {}
  
    void setP00(TPointD p) { m_p00 = p; }
    void setP01(TPointD p) { m_p01 = p; }
    void setP10(TPointD p) { m_p10 = p; }
    void setP11(TPointD p) { m_p11 = p; }
  
    TPointD getP00() const { return m_p00; }
    TPointD getP01() const { return m_p01; }
    TPointD getP10() const { return m_p10; }
    TPointD getP11() const { return m_p11; }
  
    /*! Order four point from BottomLeft to TopRight. */
    FourPoints orderedPoints() const;
    TPointD getBottomLeft() const { return orderedPoints().getP00(); }
    TPointD getBottomRight() const { return orderedPoints().getP10(); }
    TPointD getTopRight() const { return orderedPoints().getP11(); }
    TPointD getTopLeft() const { return orderedPoints().getP01(); }
  
    /*! Point = index: P00 =  0; P10 = 1; P11 = 2; P01 = 3; P0M = 4; P1M = 5; PM1
     * = 6; P0M = 7. */
    TPointD getPoint(int index) const;
    /*! Point = index: P00 =  0; P10 = 1; P11 = 2; P01 = 3. */
    void setPoint(int index, const TPointD &p);
  
    FourPoints enlarge(double d);
    bool isEmpty();
    void empty();
    bool contains(TPointD p);
    TRectD getBox() const;
    FourPoints &operator=(const TRectD &r);
    bool operator==(const FourPoints &p) const;
    FourPoints operator*(const TAffine &aff) const;
  };
  
  //=============================================================================
  void drawFourPoints(const FourPoints &rect, const TPixel32 &color,
                      unsigned short stipple, bool doContrast);
  //-----------------------------------------------------------------------------
  
  //=============================================================================
  // DeformValues
  //-----------------------------------------------------------------------------
  
  struct DeformValues {
    double m_rotationAngle, m_maxSelectionThickness;
    TPointD m_scaleValue, m_moveValue;
    bool m_isSelectionModified;
  
    DeformValues(double rotationAngle = 0, double maxSelectionThickness = 0,
                 TPointD scaleValue = TPointD(1, 1),
                 TPointD moveValue = TPointD(), bool isSelectionModified = false)
        : m_rotationAngle(rotationAngle)
        , m_maxSelectionThickness(maxSelectionThickness)
        , m_scaleValue(scaleValue)
        , m_moveValue(moveValue)
        , m_isSelectionModified(isSelectionModified) {}
  
    void reset() {
      m_rotationAngle         = 0;
      m_maxSelectionThickness = 0;
      m_scaleValue            = TPointD(1, 1);
      m_moveValue             = TPointD();
      m_isSelectionModified   = false;
    }
  };
  
  //=============================================================================
  // DragTool
  //-----------------------------------------------------------------------------
  
  class DragTool {
  protected:
    SelectionTool *m_tool;
  
  public:
    DragTool(SelectionTool *tool) : m_tool(tool) {}
    virtual ~DragTool() {}
  
    SelectionTool *getTool() const { return m_tool; }
  
    virtual void transform(TAffine aff, double angle) {}
    virtual void transform(TAffine aff) {}
    virtual TPointD transform(int index, TPointD newPos,
                              bool onFastDragging = false) {
      return TPointD();
    }
    virtual void addTransformUndo() {}
  
    virtual void leftButtonDown(const TPointD &pos, const TMouseEvent &) = 0;
    virtual void leftButtonDrag(const TPointD &pos, const TMouseEvent &) = 0;
    virtual void leftButtonUp(const TPointD &pos, const TMouseEvent &)   = 0;
    virtual void draw()                                                  = 0;
  };
  
  //=============================================================================
  // DeformTool
  //-----------------------------------------------------------------------------
  
  class DeformTool : public DragTool {
  protected:
    TPointD m_curPos;
    bool m_isDragging;
    TPointD m_startScaleValue;
    TPointD m_startPos;
  
  public:
    DeformTool(SelectionTool *tool);
  
    virtual void applyTransform(FourPoints bbox, bool onFastDragging = false) = 0;
    virtual void applyTransform(TAffine aff){};
  
    void addTransformUndo() override = 0;
  
    int getSymmetricPointIndex(int index) const;
    /*! Return before point \b index between possible point index
     * {0,4,1,5,2,6,3,7}, include middle point. */
    int getBeforePointIndex(int index) const;
    /*! Return next point \b index between possible point index {0,4,1,5,2,6,3,7},
     * include middle point. */
    int getNextPointIndex(int index) const;
    /*! Return before vertex \b index between possible point vertex index
     * {0,1,2,3}*/
    int getBeforeVertexIndex(int index) const;
    /*! Return next vertex \b index between possible point vertex index
     * {0,1,2,3}*/
    int getNextVertexIndex(int index) const;
  
    TPointD getStartPos() const { return m_startPos; }
    void setStartPos(const TPointD &pos) { m_startPos = pos; }
    TPointD getCurPos() const { return m_curPos; }
    void setCurPos(const TPointD &pos) { m_curPos = pos; }
    bool isDragging() const { return m_isDragging; }
    TPointD getStartScaleValue() const { return m_startScaleValue; }
  
    void leftButtonDown(const TPointD &pos, const TMouseEvent &e) override;
    void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) override = 0;
    void leftButtonUp(const TPointD &pos, const TMouseEvent &e) override;
    void draw() override = 0;
  };
  
  //=============================================================================
  // Rotation
  //-----------------------------------------------------------------------------
  
  class Rotation {
    double m_curAng, m_dstAng;
    DeformTool *m_deformTool;
  
  public:
    Rotation(DeformTool *deformTool);
    TPointD getStartCenter() const;
    void leftButtonDrag(const TPointD &pos, const TMouseEvent &e);
    void draw();
  };
  
  //=============================================================================
  // FreeDeform
  //-----------------------------------------------------------------------------
  
  class FreeDeform {
    DeformTool *m_deformTool;
  
  public:
    FreeDeform(DeformTool *deformTool);
    void leftButtonDrag(const TPointD &pos, const TMouseEvent &e);
    void leftButtonUp();
  };
  
  //=============================================================================
  // MoveSelection
  //-----------------------------------------------------------------------------
  
  class MoveSelection {
    DeformTool *m_deformTool;
    TPointD m_lastDelta, m_firstPos;
  
  public:
    MoveSelection(DeformTool *deformTool);
    void leftButtonDown(const TPointD &pos, const TMouseEvent &e);
    void leftButtonDrag(const TPointD &pos, const TMouseEvent &e);
  };
  
  //=============================================================================
  // Scale
  //-----------------------------------------------------------------------------
  
  enum class ScaleType { GLOBAL = 0, HORIZONTAL, VERTICAL };
  
  class Scale {
    TPointD m_startCenter;
    bool m_isShiftPressed;
    bool m_isAltPressed;
    bool m_scaleInCenter;
    std::vector<FourPoints> m_startBboxs;
  
    DeformTool *m_deformTool;
  
  public:
    ScaleType m_type;
    Scale(DeformTool *deformTool, ScaleType type);
  
    /*! Return intersection between straight line in \b point0, \b point1 and
  straight line for
  \b p parallel to straight line in \b point2, \b point3. */
    TPointD getIntersectionPoint(const TPointD &point0, const TPointD &point1,
                                 const TPointD &point2, const TPointD &point3,
                                 const TPointD &p) const;
    /*! Scale \b index point of \b bbox in \b pos and return scaled bbox. */
    FourPoints bboxScale(int index, const FourPoints &oldBbox,
                         const TPointD &pos);
    /*! Compute new scale value take care of new position of \b movedIndex point
     * in \b bbox. */
    TPointD computeScaleValue(int movedIndex, const FourPoints newBbox);
    /*! Return \b index point scaled in \b center of \b scaleValue. */
    TPointD getScaledPoint(int index, const FourPoints &oldBbox,
                           const TPointD scaleValue, const TPointD center);
    /*! Compute new center after scale of \b bbox \b index point. */
    TPointD getNewCenter(int index, const FourPoints bbox,
                         const TPointD scaleValue);
    /*! Scale \b bbox \b index point in pos and if \b m_scaleInCenter is true
  scale in \b center \b bbox symmetric point;
  compute scaleValue. */
    FourPoints bboxScaleInCenter(int index, const FourPoints &oldBbox,
                                 const TPointD newPos, TPointD &scaleValue,
                                 const TPointD center, bool recomputeScaleValue);
  
    void leftButtonDown(const TPointD &pos, const TMouseEvent &e);
    void leftButtonDrag(const TPointD &pos, const TMouseEvent &e);
    void leftButtonUp();
  
    std::vector<FourPoints> getStartBboxs() const { return m_startBboxs; }
    TPointD getStartCenter() const { return m_startCenter; }
    bool scaleInCenter() const { return m_scaleInCenter; }
  };
  };  // namespace DragSelectionTool
  
  //=============================================================================
  // Utility
  //-----------------------------------------------------------------------------
  
  DragSelectionTool::DragTool *createNewMoveSelectionTool(SelectionTool *st);
  DragSelectionTool::DragTool *createNewRotationTool(SelectionTool *st);
  DragSelectionTool::DragTool *createNewFreeDeformTool(SelectionTool *st);
  DragSelectionTool::DragTool *createNewScaleTool(
      SelectionTool *st, DragSelectionTool::ScaleType type);
  
  //=============================================================================
  // SelectionTool
  //-----------------------------------------------------------------------------
  
  class SelectionTool : public QObject, public TTool, public TSelection::View {
    Q_OBJECT
  
  protected:
    bool m_firstTime;
    DragSelectionTool::DragTool *m_dragTool;
  
    StrokeGenerator m_track;
    std::vector<TPointD> m_polyline;
    TPointD m_mousePosition;
    TStroke *m_stroke;
  
    // To modify selection
    TPointD m_curPos;
    TPointD m_firstPos;
  
    bool m_selecting;
    bool m_justSelected;
    bool m_shiftPressed;
  
    enum {
      Outside,
      Inside,
      DEFORM,
      ROTATION,
      MOVE_CENTER,
      SCALE,
      SCALE_X,
      SCALE_Y,
      GLOBAL_THICKNESS,
      ADD_SELECTION
    } m_what;  // RV
  
    enum {
      P00 = 0,
      P10 = 1,
      P11 = 2,
      P01 = 3,
      PM0 = 4,
      P1M = 5,
      PM1 = 6,
      P0M = 7,
      NONE
    } m_selectedPoint;  // RV
  
    int m_cursorId;
  
    bool m_leftButtonMousePressed;
  
    DragSelectionTool::FourPoints m_selectingRect;
  
    std::vector<DragSelectionTool::FourPoints> m_bboxs;
    std::vector<TPointD> m_centers;
    std::vector<FreeDeformer *> m_freeDeformers;
  
    TEnumProperty m_strokeSelectionType;
    TPropertyGroup m_prop;
  
    virtual void updateAction(TPointD pos, const TMouseEvent &e);
  
    virtual void modifySelectionOnClick(TImageP image, const TPointD &pos,
                                        const TMouseEvent &e) = 0;
  
    virtual void doOnActivate()   = 0;
    virtual void doOnDeactivate() = 0;
  
    // Metodi per disegnare la linea della selezione Freehand
    void startFreehand(const TPointD &pos);
    void freehandDrag(const TPointD &pos);
    void closeFreehand(const TPointD &pos);
  
    // Metodi per disegnare la linea della selezione Polyline
    void addPointPolyline(const TPointD &pos);
    void closePolyline(const TPointD &pos);
  
    void updateTranslation() override;
  
    void drawPolylineSelection();
    void drawRectSelection(const TImage *image);
    void drawFreehandSelection();
    void drawCommandHandle(const TImage *image);
  
  public:
    DragSelectionTool::DeformValues m_deformValues;
  
    SelectionTool(int targetType);
    ~SelectionTool() override;
  
    ToolType getToolType() const override { return TTool::LevelWriteTool; }
  
    TPointD getCenter(int index = 0) const;
    void setCenter(const TPointD &center, int index = 0);
  
    int getBBoxsCount() const;
    DragSelectionTool::FourPoints getBBox(int index = 0) const;
    virtual void setBBox(const DragSelectionTool::FourPoints &points,
                         int index = 0);
  
    FreeDeformer *getFreeDeformer(int index = 0) const;
    virtual void setNewFreeDeformer() = 0;
    void clearDeformers();
  
    int getSelectedPoint() const { return m_selectedPoint; }
  
    virtual bool isConstantThickness() const { return true; }
    virtual bool isLevelType() const { return false; }
    virtual bool isSelectedFramesType() const { return false; }
    virtual bool isSameStyleType() const { return false; }
    virtual bool isModifiableSelectionType() const {
      return !(isLevelType() || isSelectedFramesType());
    }
    virtual bool isFloating() const { return false; }
  
    virtual QSet<int> getSelectedStyles() const { return QSet<int>(); }
  
    void leftButtonDown(const TPointD &pos, const TMouseEvent &) override;
    void leftButtonDrag(const TPointD &pos, const TMouseEvent &) override = 0;
    void leftButtonUp(const TPointD &pos, const TMouseEvent &) override   = 0;
    void mouseMove(const TPointD &pos, const TMouseEvent &e) override;
    void leftButtonDoubleClick(const TPointD &,
                               const TMouseEvent &e) override = 0;
    bool keyDown(QKeyEvent *) override;
  
    int getCursorId() const override;
  
    void draw() override = 0;
  
    TSelection *getSelection() override = 0;
    virtual bool isSelectionEmpty()     = 0;
  
    virtual void computeBBox() = 0;
  
    void onActivate() override;
    void onDeactivate() override;
    void onImageChanged() override = 0;
    void onSelectionChanged() override;
  
    TPropertyGroup *getProperties(int targetType) override { return &m_prop; }
  
    bool onPropertyChanged(std::string propertyName) override;
  
    // returns true if the pressed key is recognized and processed.
    bool isEventAcceptable(QEvent *e) override;
  
    virtual bool isSelectionEditable() { return true; }
  
  signals:
    void clickFlipHorizontal();
    void clickFlipVertical();
    void clickRotateLeft();
    void clickRotateRight();
  };
  
  #endif  // SELECTIONTOOL_INCLUDED
  