/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#ifndef INCLUDED_SVX_SVDHDL_HXX
#define INCLUDED_SVX_SVDHDL_HXX

#include <tools/gen.hxx>
#include <vcl/graph.hxx>

#include <svx/svdoedge.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <svx/sdr/overlay/overlayobjectlist.hxx>
#include <svx/svxdllapi.h>
#include <deque>

class SdrHdlList;
class SdrMarkView;
class SdrObject;
class SdrPageView;
class MouseEvent;

namespace sdr::contact {
    class ObjectContact;
}

// Every object must be able to create its handles. They will be fetched on
// selection, registered at the View and made visible.
// When a handle is touched by the mouse (IsHit()), from the view the matching mouse pointer
// is picked up from the handle and passed to the app (on demand) to be switched on
// Handles like e.g. rotation center or the mirror axis are generated by
// the view if the correct transformation mode was selected by the controller.
// SdrHdlKind::Move...SdrHdlKind::LowerRight should always be consecutive in the enum!

enum class SdrHdlKind
{
    Move,           // Handle to move the object
    UpperLeft,      // Upper left
    Upper,          // Upper
    UpperRight,     // Upper right
    Left,           // Left
    Right,          // Right
    LowerLeft,      // Bottom left
    Lower,          // Bottom
    LowerRight,     // Bottom right
    Poly,           // Select point in polygon or beziercurve
    BezierWeight,   // Weight of a beziercurve
    Circle,         // Angle to circle segment, corner radius to rectangle
    Ref1,           // Reference point 1, e.g. rotation center
    Ref2,           // Reference point 2, e.g. endpoint of mirror axis
    MirrorAxis,     // Mirror axis
    Glue,           // GluePoint
    Anchor,         // Anchor symbol (SD, SW)
    Transparence,   // Interactive transparence
    Gradient,       // Interactive gradient
    Color,          // Interactive color
    User,
    Anchor_TR,      // #101688# Anchor handle with (0,0) at top right for SW

    // for SJ and the CustomShapeHandles:
    CustomShape1,

    SmartTag
};


enum class BitmapColorIndex
{
    LightGreen  = 0,
    Cyan        = 1,
    LightCyan   = 2,
    Red         = 3,
    LightRed    = 4,
    Yellow      = 5,
};


enum class BitmapMarkerKind
{
    Rect_7x7,
    Rect_9x9,
    Rect_11x11,
    Rect_13x13,
    Circ_7x7,
    Circ_9x9,
    Circ_11x11,
    Elli_7x9,
    Elli_9x11,
    Elli_9x7,
    Elli_11x9,
    RectPlus_7x7,
    RectPlus_9x9,
    RectPlus_11x11,
    Crosshair,
    Glue,
    Glue_Deselected,
    Anchor,

    // #98388# add AnchorPressed to be able to animate anchor control, too.
    AnchorPressed,

    // #101688# AnchorTR for SW
    AnchorTR,
    AnchorPressedTR,

    // for SJ and the CustomShapeHandles:
    Customshape_7x7,
    Customshape_9x9,
    Customshape_11x11
};


class SVXCORE_DLLPUBLIC SdrHdl
{
    friend class                SdrMarkView; // for the access to nObjHdlNum
    friend class                SdrHdlList;

protected:
    SdrObject*                  m_pObj;      // does handle belong to an object?
    SdrPageView*                m_pPV;       // does handle belong to an object in certain pageview?
    SdrHdlList*                 m_pHdlList;  // to store the handlesize
    // OVERLAYMANAGER
    sdr::overlay::OverlayObjectList           maOverlayGroup;

    Point                       m_aPos;

    SdrHdlKind                  m_eKind;

    Degree100                   m_nRotationAngle; // turn handle or mousepointer
    sal_uInt32                  m_nObjHdlNum;     // required by MarkView
    sal_uInt32                  m_nPolyNum;       // Polygonpoint
    sal_uInt32                  m_nPPntNum;       // Point number of the polygon
    sal_uInt32                  m_nSourceHdlNum;  // still to implement

    bool                        m_bSelect : 1;    // is a polygon point selected?
    bool                        m_b1PixMore : 1;  // True=handle is shown 1 pixel larger
    bool                        m_bPlusHdl : 1;   // for Hld-Paint optimisation at MarkPoint/UnmarkPoint, and other ...

    bool                        mbMoveOutside;  // forces this handle to be moved outside of the selection rectangle

    // create marker for this kind
    virtual void CreateB2dIAObject();

    // cleanup marker if one exists
    void GetRidOfIAObject();

private:
    bool                        mbMouseOver;    // is true if the mouse is over this handle

protected:
    SAL_DLLPRIVATE std::unique_ptr<sdr::overlay::OverlayObject> CreateOverlayObject(
        const basegfx::B2DPoint& rPos,
        BitmapColorIndex eColIndex, BitmapMarkerKind eKindOfMarker,
        Point aMoveOutsideOffset = Point());
    SAL_DLLPRIVATE static BitmapMarkerKind GetNextBigger(BitmapMarkerKind eKnd);

    // Helper to support inserting a new OverlayObject. It will do all
    // necessary stuff involved with that:
    // - add GridOffset for non-linear ViewToDevice transformation (calc)
    // - add to OverlayManager
    // - add to local OverlayObjectList - ownership change (!)
    // It is centralized here (and protected) to avoid that new usages/
    // implementations forget one of these needed steps.
    void insertNewlyCreatedOverlayObjectForSdrHdl(
        std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject,
        const sdr::contact::ObjectContact& rObjectContact,
        sdr::overlay::OverlayManager& rOverlayManager);

public:
    SAL_DLLPRIVATE SdrHdl();
    explicit SdrHdl(const Point& rPnt, SdrHdlKind eNewKind);
    virtual ~SdrHdl();

    const sdr::overlay::OverlayObjectList& getOverlayObjectList() const { return maOverlayGroup; }

    SAL_DLLPRIVATE void SetHdlList(SdrHdlList* pList);
    SdrHdlKind GetKind() const { return m_eKind; }
    void Touch();

    const Point& GetPos() const { return m_aPos; }
    void SetPos(const Point& rPnt);

    SdrPageView* GetPageView() const { return m_pPV; }
    void SetPageView(SdrPageView* pNewPV) { m_pPV=pNewPV; }

    SdrObject* GetObj() const { return m_pObj;  }
    void SetObj(SdrObject* pNewObj);

    bool IsSelected() const { return m_bSelect; }
    void SetSelected(bool bJa=true);

    SAL_DLLPRIVATE void Set1PixMore(bool bJa=true);
    SAL_DLLPRIVATE void SetRotationAngle(Degree100 n);

    bool IsCornerHdl() const { return m_eKind==SdrHdlKind::UpperLeft || m_eKind==SdrHdlKind::UpperRight || m_eKind==SdrHdlKind::LowerLeft || m_eKind==SdrHdlKind::LowerRight; }
    bool IsVertexHdl() const { return m_eKind==SdrHdlKind::Upper || m_eKind==SdrHdlKind::Lower || m_eKind==SdrHdlKind::Left  || m_eKind==SdrHdlKind::Right; }

    void SetObjHdlNum(sal_uInt32 nNum) { m_nObjHdlNum=nNum; }
    sal_uInt32 GetObjHdlNum() const { return m_nObjHdlNum; }

    void SetPolyNum(sal_uInt32 nNum) { m_nPolyNum=nNum; }
    sal_uInt32 GetPolyNum() const { return m_nPolyNum; }

    void SetPointNum(sal_uInt32 nNum) { m_nPPntNum=nNum; }
    sal_uInt32 GetPointNum() const { return m_nPPntNum; }

    void SetPlusHdl(bool bOn) { m_bPlusHdl=bOn; }
    bool IsPlusHdl() const { return m_bPlusHdl; }

    void SetSourceHdlNum(sal_uInt32 nNum) { m_nSourceHdlNum=nNum; }
    sal_uInt32 GetSourceHdlNum() const { return m_nSourceHdlNum; }

    virtual PointerStyle GetPointer() const;
    SAL_DLLPRIVATE bool IsHdlHit(const Point& rPnt) const;

    virtual bool IsFocusHdl() const;

    SAL_DLLPRIVATE void SetMoveOutside( bool bMoveOutside );

    /** is called when the mouse enters the area of this handle. If the handle changes his
        visualisation during mouse over it must override this method and call Touch(). */
    virtual void onMouseEnter(const MouseEvent& rMEvt);

    /** is called when help is requested for the area of this handle */
    virtual void onHelpRequest();

    /** is called when the mouse leaves the area of this handle. If the handle changes his
        visualisation during mouse over it must override this method and call Touch(). */
    virtual void onMouseLeave();

    SAL_DLLPRIVATE static Bitmap createGluePointBitmap();
};


#define SDR_HANDLE_COLOR_SIZE_NORMAL            Size(13, 13)
#define SDR_HANDLE_COLOR_SIZE_SELECTED          Size(17, 17)

class SVXCORE_DLLPUBLIC SdrHdlColor final : public SdrHdl
{
    // size of colr markers
    Size                        m_aMarkerSize;

    // color
    Color                       m_aMarkerColor;

    // callback link when value changed
    Link<SdrHdlColor*,void>     m_aColorChangeHdl;

    // use luminance values only
    bool                        m_bUseLuminance : 1;

    // create marker for this kind
    SVX_DLLPRIVATE virtual void CreateB2dIAObject() override;

    // help functions
    SVX_DLLPRIVATE Bitmap CreateColorDropper(Color aCol);
    SVX_DLLPRIVATE static Color GetLuminance(const Color& rCol);

public:
    SAL_DLLPRIVATE explicit SdrHdlColor(const Point& rRef, Color aCol, const Size& rSize, bool bLuminance);
    SAL_DLLPRIVATE virtual ~SdrHdlColor() override;

    bool IsUseLuminance() const { return m_bUseLuminance; }

    const Color& GetColor() const { return m_aMarkerColor; }
    void SetColor(Color aNew, bool bCallLink = false);

    void SetSize(const Size& rNew);

    void SetColorChangeHdl(const Link<SdrHdlColor*,void>& rLink) { m_aColorChangeHdl = rLink; }
};


class SdrHdlGradient final : public SdrHdl
{
private:
    // pointer to used color handles
    SdrHdlColor*                m_pColHdl1;
    SdrHdlColor*                m_pColHdl2;

    // 2nd position
    Point                       m_a2ndPos;

    // is this a gradient or a transparence
    bool                        m_bGradient : 1;

    // select which handle to move
    bool                        m_bMoveSingleHandle : 1;
    bool                        m_bMoveFirstHandle : 1;

    // create marker for this kind
    virtual void CreateB2dIAObject() override;

public:
    SdrHdlGradient(const Point& rRef1, const Point& rRef2, bool bGrad);
    virtual ~SdrHdlGradient() override;

    bool IsGradient() const { return m_bGradient; }

    // set the associated color handles
    void SetColorHandles(SdrHdlColor* pL1, SdrHdlColor* pL2) { m_pColHdl1 = pL1; m_pColHdl2 = pL2; }
    SdrHdlColor* GetColorHdl1() const { return m_pColHdl1; }
    SdrHdlColor* GetColorHdl2() const { return m_pColHdl2; }

    const Point& Get2ndPos() const { return m_a2ndPos; }
    void Set2ndPos(const Point& rPnt);

    // the link called by the color handles
    DECL_LINK(ColorChangeHdl, SdrHdlColor*, void);

    // transformation call, create gradient from this handle
    void FromIAOToItem(SdrObject* pObj, bool bSetItemOnObject, bool bUndo);

    // selection flags for interaction
    bool IsMoveSingleHandle() const { return m_bMoveSingleHandle; }
    void SetMoveSingleHandle(bool bNew) { m_bMoveSingleHandle = bNew; }
    bool IsMoveFirstHandle() const { return m_bMoveFirstHandle; }
    void SetMoveFirstHandle(bool bNew) { m_bMoveFirstHandle = bNew; }
};


// Mirror axis
class SdrHdlLine final : public SdrHdl
{
    // create marker for this kind
    virtual void CreateB2dIAObject() override;

    SdrHdl*                     m_pHdl1;
    SdrHdl*                     m_pHdl2;

public:
    SdrHdlLine(SdrHdl& rHdl1, SdrHdl& rHdl2, SdrHdlKind eNewKind) { m_eKind=eNewKind; m_pHdl1=&rHdl1; m_pHdl2=&rHdl2; }
    virtual ~SdrHdlLine() override;

    virtual PointerStyle GetPointer() const override;
};

// a SdrHdlBezWgt knows about its "base handle". Its draw method
// draws additionally a line from its position to the position
// of the base handle
class SdrHdlBezWgt final : public SdrHdl
{
public:
    // this is not a Copy-Ctor!!!
    SdrHdlBezWgt(const SdrHdl* pRefHdl1, SdrHdlKind eNewKind=SdrHdlKind::BezierWeight) { m_eKind=eNewKind; m_pHdl1=pRefHdl1; }
    virtual ~SdrHdlBezWgt() override;

private:
    // create marker for this kind
    virtual void CreateB2dIAObject() override;

    const SdrHdl* m_pHdl1;
};


class E3dVolumeMarker final : public SdrHdl
{
    basegfx::B2DPolyPolygon             m_aWireframePoly;

    // create marker for this kind
    virtual void CreateB2dIAObject() override;

public:
    explicit E3dVolumeMarker(const basegfx::B2DPolyPolygon& rWireframePoly);
};


class ImpEdgeHdl final : public SdrHdl
{
    SdrEdgeLineCode m_eLineCode;

    // create marker for this kind
    virtual void CreateB2dIAObject() override;

public:
    ImpEdgeHdl(const Point& rPnt, SdrHdlKind eNewKind): SdrHdl(rPnt,eNewKind),m_eLineCode(SdrEdgeLineCode::MiddleLine) {}
    virtual ~ImpEdgeHdl() override;

    void SetLineCode(SdrEdgeLineCode eCode);
    SdrEdgeLineCode GetLineCode() const     { return m_eLineCode; }
    bool IsHorzDrag() const;
    virtual PointerStyle GetPointer() const override;
};


class ImpMeasureHdl final : public SdrHdl
{
    // create marker for this kind
    virtual void CreateB2dIAObject() override;

public:
    ImpMeasureHdl(const Point& rPnt, SdrHdlKind eNewKind): SdrHdl(rPnt,eNewKind) {}
    virtual ~ImpMeasureHdl() override;

    virtual PointerStyle GetPointer() const override;
};


class ImpTextframeHdl final : public SdrHdl
{
    const tools::Rectangle maRect;

    // create marker for this kind
    virtual void CreateB2dIAObject() override;

public:
    explicit ImpTextframeHdl(const tools::Rectangle& rRect);
};


class SVXCORE_DLLPUBLIC SdrHdlList
{
    size_t                      mnFocusIndex;
    SdrMarkView*                m_pView;
    std::deque<std::unique_ptr<SdrHdl>> maList;
    sal_uInt16                  m_nHdlSize;

    bool                        m_bRotateShear : 1;
    bool                        m_bDistortShear : 1;
    bool                        m_bMoveOutside : 1;      // move handles outwards (for TextEdit)

    SVX_DLLPRIVATE SdrHdlList(const SdrHdlList&) = delete;
    SVX_DLLPRIVATE void operator=(const SdrHdlList&) = delete;

public:
    explicit SdrHdlList(SdrMarkView* pV);
    ~SdrHdlList();
    void Clear();

    void TravelFocusHdl(bool bForward);
    SdrHdl* GetFocusHdl() const;
    void SetFocusHdl(SdrHdl* pNew);
    void ResetFocusHdl();

    // Access to View
    SdrMarkView* GetView() const { return m_pView;}

    // Sorting: 1.Level first reference point handle, then normal handles, next Glue, then User then Plushandles
    //          2.Level PageView (Pointer)
    //          3.Level Position (x+y)
    SAL_DLLPRIVATE void     Sort();
    size_t   GetHdlCount() const { return maList.size(); }
    SdrHdl*  GetHdl(size_t nNum) const { return nNum < maList.size() ? maList[nNum].get() : nullptr; }
    size_t   GetHdlNum(const SdrHdl* pHdl) const;
    SAL_DLLPRIVATE void     SetHdlSize(sal_uInt16 nSiz);
    sal_uInt16   GetHdlSize() const                        { return m_nHdlSize; }
    SAL_DLLPRIVATE void     SetMoveOutside(bool bOn);
    bool IsMoveOutside() const                     { return m_bMoveOutside; }
    SAL_DLLPRIVATE void     SetRotateShear(bool bOn);
    bool IsRotateShear() const                     { return m_bRotateShear; }
    SAL_DLLPRIVATE void     SetDistortShear(bool bOn);
    bool IsDistortShear() const                    { return m_bDistortShear; }

    // AddHdl takes ownership of the handle. It should be on the Heap
    // as Clear() deletes it.
    void    AddHdl(std::unique_ptr<SdrHdl> pHdl);
    SAL_DLLPRIVATE std::unique_ptr<SdrHdl> RemoveHdl(size_t nNum);
    void RemoveAllByKind(SdrHdlKind eKind);

    // move the ownership of all the SdrHdl to rOther
    void MoveTo(SdrHdlList& rOther);

    // Last inserted handles are likely hit (if the handles are above each other)
    SAL_DLLPRIVATE SdrHdl* IsHdlListHit(const Point& rPnt) const;
    SdrHdl* GetHdl(SdrHdlKind eKind1) const;
};


class SVXCORE_DLLPUBLIC SdrCropHdl final : public SdrHdl
{
public:
    SdrCropHdl(
        const Point& rPnt,
        SdrHdlKind eNewKind,
        double fShearX,
        double fRotation);

private:
    // create marker for this kind
    virtual void CreateB2dIAObject() override;

    Bitmap GetBitmapForHandle( const Bitmap& rBitmap, int nSize );

    // evtl. shear and rotation, equal to the object's one to allow adaptation of
    // the visualization handles
    double          mfShearX;
    double          mfRotation;
};


class SdrCropViewHdl final : public SdrHdl
{
private:
    basegfx::B2DHomMatrix       maObjectTransform;
    Graphic                     maGraphic;
    double                      mfCropLeft;
    double                      mfCropTop;
    double                      mfCropRight;
    double                      mfCropBottom;

public:
    SdrCropViewHdl(
        basegfx::B2DHomMatrix aObjectTransform,
        Graphic aGraphic,
        double fCropLeft,
        double fCropTop,
        double fCropRight,
        double fCropBottom);

private:
    // create marker for this kind
    virtual void CreateB2dIAObject() override;
};


#endif // INCLUDED_SVX_SVDHDL_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
