/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#pragma once

#include <QGraphicsItem>
#include <QGraphicsView>
#include <QUndoCommand>

#include "IconObject.h"

class QGraphicsScene;
class QUndoStack;

class MvQPageItem;
class MvQSuperPageItem;
class MvQPageView;
class MvRequest;
class MvQPageHandlerItem;

namespace MvQ
{
const int PageItemType = QGraphicsItem::UserType + 1;
}

class MvQUndoCommand : public QUndoCommand
{
public:
    MvQUndoCommand(MvQPageView*, QUndoCommand* parent = nullptr);
    void undo() override;
    void redo() override;

protected:
    MvQPageView* view_;
};

class MvQPageAddCommand : public MvQUndoCommand
{
public:
    MvQPageAddCommand(MvQPageView*, float, float, float, float, QUndoCommand* parent = nullptr);
    MvQPageAddCommand(MvQPageView*, MvQPageItem*, QUndoCommand* parent = nullptr);
    void undo() override;
    void redo() override;
    MvQPageItem* page() const { return page_; }

protected:
    QRectF oriRect_;
    MvQPageItem* page_;
};

class MvQPageDeleteCommand : public MvQUndoCommand
{
public:
    MvQPageDeleteCommand(MvQPageView*, MvQPageItem*, QUndoCommand* parent = nullptr);
    void undo() override;
    void redo() override;

protected:
    MvQPageItem* page_;
};

class MvQPageResizeCommand : public MvQUndoCommand
{
public:
    MvQPageResizeCommand(MvQPageView*, MvQPageItem*, const QRectF&, const QRectF&, QUndoCommand* parent = nullptr);
    void undo() override;
    void redo() override;

protected:
    MvQPageItem* page_;
    QRectF fromRect_;
    QRectF toRect_;
};


class MvQPageMoveCommand : public MvQUndoCommand
{
public:
    enum
    {
        Id = 1
    };

    MvQPageMoveCommand(MvQPageView*, MvQPageItem*, const QPointF&, QUndoCommand* parent = nullptr);
    void undo() override;
    void redo() override;
    int id() const override { return Id; }

protected:
    MvQPageItem* page_;
    QPointF fromPos_;
    QPointF toPos_;
};


class MvQPageExpandCommand : public MvQUndoCommand
{
public:
    MvQPageExpandCommand(MvQPageView*, MvQPageItem*, QRectF, QUndoCommand* parent = nullptr);
    void undo() override;
    void redo() override;
    MvQPageItem* page() const { return page_; }

protected:
    MvQPageItem* page_;
    QRectF fromRect_;
    QRectF toRect_;
};

class MvQPageJoinCommand : public MvQUndoCommand
{
public:
    MvQPageJoinCommand(MvQPageView*, QList<MvQPageItem*>, QUndoCommand* parent = nullptr);
    void undo() override;
    void redo() override;

protected:
    QList<MvQPageItem*> pages_;
    QGraphicsItem* parentItem_;
    QRectF firstOriRect_;
};

class MvQPageSplitCommand : public MvQUndoCommand
{
public:
    MvQPageSplitCommand(MvQPageView*, MvQPageItem*, int, int, QUndoCommand* parent = nullptr);
    void undo() override;
    void redo() override;

protected:
    MvQPageItem* page_;
    QList<MvQPageItem*> splitPages_;
    int row_;
    int column_;
};

class MvQPageConnectCommand : public MvQUndoCommand
{
public:
    MvQPageConnectCommand(MvQPageView*, QList<MvQPageItem*>, QUndoCommand* parent = nullptr);
    void undo() override;
    void redo() override;

protected:
    QList<MvQPageItem*> pages_;
    QRectF firstOriRect_;
};

class MvQPageDisconnectCommand : public MvQUndoCommand
{
public:
    MvQPageDisconnectCommand(MvQPageView*, MvQPageItem*, QUndoCommand* parent = nullptr);
    void undo() override;
    void redo() override;

protected:
    MvQPageItem* page_;
    QList<MvQPageItem*> pages_;
    QRectF firstOriRect_;
};


class MvQPageSelectionItem : public QGraphicsItem
{
public:
    MvQPageSelectionItem(MvQPageView*, MvQSuperPageItem*);
    QRectF boundingRect() const override;
    void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override;
    void createHandlers();
    void handlerPressed(MvQPageHandlerItem*);
    void handlerMoved(MvQPageHandlerItem*, QRectF);
    void handlerReleased(MvQPageHandlerItem*);
    QList<MvQPageItem*> pages() const { return pages_; }
    bool hasPages() const { return pages_.count() > 0; }
    void selectionChanged();
    void pagePressed(MvQPageItem*, QGraphicsSceneMouseEvent*);

protected:
    void dropEvent(QGraphicsSceneDragDropEvent* event) override;
    void mousePressEvent(QGraphicsSceneMouseEvent*) override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent*) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override;
    void adjust(QRectF);

    MvQPageView* view_;
    MvQSuperPageItem* superPage_;
    QList<MvQPageItem*> pages_;
    QList<QRectF> oriPageRect_;
    QList<MvQPageHandlerItem*> handlers_;
    QRectF bRect_;
    QRectF oriRect_;
    QPointF oriPos_;
    QPointF dragPos_;
    QPen pen_;
    QBrush brush_;
    bool handlerUsed_;
};

class MvQPageHandlerItem : public QGraphicsItem
{
public:
    enum HandlerType
    {
        TopLeft,
        TopMid,
        TopRight,
        RightMid,
        BottomRight,
        BottomMid,
        BottomLeft,
        LeftMid
    };
    MvQPageHandlerItem(MvQPageSelectionItem*, HandlerType);
    QRectF boundingRect() const override;
    void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override;
    void updatePosition();

protected:
    void setupPixmap();
    QVariant itemChange(GraphicsItemChange, const QVariant&) override;
    void mousePressEvent(QGraphicsSceneMouseEvent*) override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent*) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override;
    void hoverEnterEvent(QGraphicsSceneHoverEvent*) override;
    void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override;

    MvQPageSelectionItem* page_;
    HandlerType type_;
    QRectF bRect_;
    bool hover_;
    QBrush brush_;
    QBrush hoverBrush_;
    static QMap<HandlerType, QPixmap> pix_;
    static QMap<HandlerType, QPixmap> pixHover_;
};


class MvQSubPageItem : public QGraphicsItem
{
public:
    MvQSubPageItem(MvQPageItem*);

protected:
    QRectF rect_;
    MvQPageItem* page_;
};

class MvQPageItem : public QGraphicsItem
{
public:
    enum
    {
        Type = MvQ::PageItemType
    };

    MvQPageItem(MvQPageView*, QRectF, MvQSuperPageItem*);
    QRectF boundingRect() const override;
    void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override;
    IconObjectH viewIcon() const { return viewIcon_; }
    void setViewIcon(IconObjectH);
    void adjustSize();
    QRectF paperRect() { return paperRect_; }
    void setPaperRect(QRectF);
    void paperRect(double&, double&, double&, double&);
    void addSubPage(MvQPageItem*);
    void addSubPageInPageCoords(double, double, double, double);
    void clearSubPaperRects();
    void addSubPaperRect(MvQPageItem*, QRectF);
    void addSubPaperRectInPageCoords(double, double, double, double);
    QList<QRectF> subPaperRects() const;
    MvQPageItem* subPage(int) const;
    int type() const override { return Type; }
    QRectF rectToSelector(QRectF, QRectF);
    void adjustSizeToSelector(QRectF, QRectF);
    void resize(QRectF);
    void moveBy(QPointF);
    bool hasSubPaperRect() const { return sub_.count() > 0; }
    QPointF itemToPaper(QPointF);
    QPointF paperToItem(QPointF);

protected:
    void keyPressEvent(QKeyEvent* event) override;
    void mousePressEvent(QGraphicsSceneMouseEvent*) override;
    void dropEvent(QGraphicsSceneDragDropEvent* event) override;
    void adjustPaperRect();
    QVariant itemChange(GraphicsItemChange, const QVariant&) override;

    MvQPageView* view_;
    QRectF paperRect_;
    // QList<MvQPageItem*> subPages_;
    QList<QPair<MvQPageItem*, QRectF> > sub_;
    QRectF bRect_;
    MvQSuperPageItem* superPage_;
    QString classType_;
    IconObjectH viewIcon_;
    QPixmap viewPix_;
    QPixmap viewPixHalf_;
    QPen pen_;
    QPen selectionPen_;
    QBrush brush_;
    QPen subPen_;
    bool mouseMove_;
    QPointF mouseMoveStart_;
};

class MvQSuperPageItem : public QGraphicsItem
{
    friend class MvQPageItem;

public:
    MvQSuperPageItem(MvQPageView*, double, double, bool, float, float, bool);
    QRectF boundingRect() const override;
    void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override;

    QRectF paperRect() const { return paperRect_; }
    MvQPageItem* addPage(double x, double y, double w, double h);
    void resize(double, double);
    void adjustSize(QSize);
    void connectSelection();
    void disconnectSelection();
    bool adjustItemMove(QGraphicsItem*, QPointF&);
    bool adjustItemResize(QGraphicsItem*, QRectF&);
    void enableSnap(bool);
    void setSnap(float, float);
    void enableGrid(bool);
    QList<MvQPageItem*> pages() const;
    QRectF itemToPercentage(QRectF);

protected:
    QPointF itemToPercentage(QPointF);
    QPointF percentageToItem(QPointF);
    QPointF itemToPaper(QPointF);
    QPointF paperToItem(QPointF);
    QPointF snapToGrid(QPointF);
    QRectF snapToGrid(QRectF);

    MvQPageView* view_;
    QRectF paperRect_;
    QRectF bRect_;
    float scaling_;
    bool snap_;
    float snapX_;
    float snapY_;
    QPen gridPen_;
    bool grid_;
};

class MvQRulerItem : public QGraphicsItem
{
public:
    MvQRulerItem(MvQPageView*, Qt::Orientation);
    QRectF boundingRect() const override;
    void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override;
    void adjustSize();

protected:
    MvQPageView* view_;
    Qt::Orientation orientation_;
    QRectF bRect_;
    QBrush bg_;
    QPen pen_;
    QPen subPen_;
    int halfLen_;
    int subLen_;
    QFont font_;
};

class MvQPageView : public QGraphicsView
{
    Q_OBJECT

public:
    MvQPageView(const std::vector<std::string>&, QUndoStack*, QWidget* parent = nullptr);

    void clear();
    void clearPages();
    static bool matchClass(const std::string&);

    MvQPageItem* addPage(double x, double y, double w, double h);
    QList<MvQPageItem*> pages() const;
    void setSuperPage(double, double, bool, float, float, bool);
    void duplicateSelection();
    void deleteSelection();
    void joinSelection();
    void splitSelection(int, int);
    void expandSelection();
    void connectSelection();
    void disconnectSelection();
    void selectAll();
    const std::vector<std::string>& classes() { return classes_; }
    void enableSnap(bool);
    void setSnap(float, float);
    void enableGrid(bool);

    void selection();
    void pageSelected(MvQPageItem*);
    void pagePressed(MvQPageItem*, QGraphicsSceneMouseEvent*);
    void pagesResized(QList<MvQPageItem*>, QList<QRectF>);
    void pagesMoved(QList<MvQPageItem*>, const QPointF&);

    MvQSuperPageItem* superPage() { return superPage_; }

signals:
    void changed();
    void selectionChanged(int);

protected:
    void resizeEvent(QResizeEvent*) override;
    void adjustSize();

    QUndoStack* undoStack_;
    QGraphicsScene* scene_;
    MvQSuperPageItem* superPage_;
    MvQPageSelectionItem* selector_;
    MvQRulerItem* rulerH_;
    MvQRulerItem* rulerV_;
    static std::vector<std::string> classes_;
};
