// Copyright (C) 2016 Jochen Becher
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#pragma once

#include <QGraphicsItem>

#include "qmt/diagram_scene/capabilities/intersectionable.h"
#include "qmt/diagram_scene/capabilities/resizable.h"
#include "qmt/diagram_scene/capabilities/moveable.h"
#include "qmt/diagram_scene/capabilities/selectable.h"
#include "qmt/diagram_scene/capabilities/latchable.h"
#include "qmt/diagram_scene/capabilities/relationable.h"
#include "qmt/diagram_scene/capabilities/alignable.h"
#include "qmt/diagram_scene/capabilities/editable.h"

#include "qmt/stereotype/stereotypeicon.h"

#include <QCoreApplication>

QT_BEGIN_NAMESPACE
class QAction;
QT_END_NAMESPACE

namespace qmt {

class DElement;
class DObject;
class DiagramSceneModel;
class StereotypesItem;
class CustomIconItem;
class CustomRelation;
class EditableTextItem;
class RectangularSelectionItem;
class RelationStarter;
class AlignButtonsItem;
class Style;

// typical z-values for graphic items
static const int SHAPE_ZVALUE = -100; // the filled background of the shape
static const int SHAPE_DETAILS_ZVALUE = -90; // any details to the shape (e.g. extra lines in shape)
static const int SELECTION_MARKER_ZVALUE = 100;

class ObjectItem :
        public QGraphicsItem,
        public IIntersectionable,
        public IResizable,
        public IMoveable,
        public ISelectable,
        public ILatchable,
        public IRelationable,
        public IAlignable,
        public IEditable
{
protected:
    enum ResizeFlags {
        ResizeUnlocked,
        ResizeLockedSize,
        ResizeLockedWidth,
        ResizeLockedHeight,
        ResizeLockedRatio
    };

public:
    ObjectItem(const QString &elementType, DObject *object, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent = nullptr);
    ~ObjectItem() override;

    QString elementType() const { return m_elementType; }
    DObject *object() const { return m_object; }
    DiagramSceneModel *diagramSceneModel() const { return m_diagramSceneModel; }

    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;

    virtual void update() = 0;

    QPointF pos() const override;
    QRectF rect() const override;
    QSizeF minimumSize() const override = 0;
    void setPosAndRect(const QPointF &originalPos, const QRectF &originalRect,
                       const QPointF &topLeftDelta, const QPointF &bottomRightDelta) override;
    void alignItemSizeToRaster(Side adjustHorizontalSide, Side adjustVerticalSide,
                               double rasterWidth, double rasterHeight) override;

    void moveDelta(const QPointF &delta) override;
    void alignItemPositionToRaster(double rasterWidth, double rasterHeight) override;

    bool isSecondarySelected() const override;
    void setSecondarySelected(bool secondarySelected) override;
    bool isFocusSelected() const override;
    void setFocusSelected(bool focusSelected) override;
    QRectF getSecondarySelectionBoundary() override;
    void setBoundarySelected(const QRectF &boundary, bool secondary) override;

    Action horizontalLatchAction() const override;
    Action verticalLatchAction() const override;
    QList<Latch> horizontalLatches(Action action, bool grabbedItem) const override;
    QList<Latch> verticalLatches(Action action, bool grabbedItem) const override;

    QPointF relationStartPos() const override;
    void relationDrawn(const QString &id, const QPointF &toScenePos,
                       const QList<QPointF> &intermediatePoints) override;
    virtual void relationDrawn(const QString &id, ObjectItem *targetElement,
                               const QList<QPointF> &intermediatePoints);

    void align(AlignType alignType, const QString &identifier) override;

    bool isEditable() const override;
    bool isEditing() const override;
    void edit() override;
    void finishEdit() override;

protected:
    void updateStereotypeIconDisplay();
    QString stereotypeIconId() const { return m_stereotypeIconId; }
    QString shapeIconId() const { return m_shapeIconId; }
    StereotypeIcon shapeIcon() const { return m_shapeIcon; }
    StereotypeIcon::Display stereotypeIconDisplay() const { return m_stereotypeIconDisplay; }
    void updateStereotypes(const QString &stereotypeIconId,
                           StereotypeIcon::Display stereotypeDisplay, const Style *style);
    StereotypesItem *stereotypesItem() const { return m_stereotypesItem; }
    CustomIconItem *stereotypeIconItem() const { return m_stereotypeIcon; }
    CustomIconItem *customIconItem() const { return m_customIcon; }
    QSizeF customIconItemMinimumSize(const CustomIconItem* customIconItem, qreal minimumWidth,
                                     qreal minimumHeight) const;
    void correctAutoSize(const CustomIconItem *customIconItem, qreal& width, qreal& height,
                           qreal minimumWidth, qreal minimumHeight) const;
    void updateCustomIcon(const Style* style);
    bool suppressTextDisplay() const;
    void updateNameItem(const Style *style);
    EditableTextItem *nameItem() const { return m_nameItem; }
    virtual QString buildDisplayName() const;
    virtual void setFromDisplayName(const QString &displayName);
    void setObjectName(const QString &objectName);

    void updateDepth();
    void updateSelectionMarker(const CustomIconItem* customIconItem);
    void updateSelectionMarker(ResizeFlags resizeFlags);
    void updateSelectionMarkerGeometry(const QRectF &objectRect);
    void updateRelationStarter();
    RelationStarter *relationStarter() const { return m_relationStarter; }
    virtual void addRelationStarterTool(const QString &id);
    virtual void addRelationStarterTool(const CustomRelation &customRelation);
    virtual void addStandardRelationStarterTools();
    void updateRelationStarterGeometry(const QRectF &objectRect);
    void updateAlignmentButtons();
    void updateAlignmentButtonsGeometry(const QRectF &objectRect);

    IAlignable::AlignType translateLatchTypeToAlignType(ILatchable::LatchType latchType);
    const Style *adaptedStyle(const QString &stereotypeIconId);
    bool showContext() const;

    virtual bool extendContextMenu(QMenu *menu);
    virtual bool handleSelectedContextMenuAction(const QString &id);

    void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
    void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;

private:
    QSizeF minimumSize(const QSet<QGraphicsItem *> &items) const;

    QString m_elementType;
    DObject *m_object = nullptr;
    DiagramSceneModel *m_diagramSceneModel = nullptr;
    bool m_isSecondarySelected = false;
    bool m_isFocusSelected = false;
    QString m_stereotypeIconId;
    QString m_shapeIconId;
    StereotypeIcon m_shapeIcon;
    StereotypeIcon::Display m_stereotypeIconDisplay = StereotypeIcon::DisplayLabel;
    StereotypesItem *m_stereotypesItem = nullptr;
    CustomIconItem *m_stereotypeIcon = nullptr;
    CustomIconItem *m_customIcon = nullptr;
    EditableTextItem *m_nameItem = nullptr;
    RectangularSelectionItem *m_selectionMarker = nullptr;
    RelationStarter *m_relationStarter = nullptr;
    AlignButtonsItem *m_horizontalAlignButtons = nullptr;
    AlignButtonsItem *m_verticalAlignButtons = nullptr;
};

} // namespace qmt
