/***************************** 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 <string>

#include <QAbstractItemModel>
#include <QList>
#include <QSortFilterProxyModel>
#include <QStyledItemDelegate>
#include <QStyleOptionViewItem>
#include <QTreeView>

#include "Layer.h"
#include "MvQDragDrop.h"

class MgQLayerItem;
class MgQPlotScene;
class MgQSceneItem;
class MvQLayerContentsIcon;

class MvQLayerDelegate : public QStyledItemDelegate
{
public:
    MvQLayerDelegate(QWidget* parent = nullptr);
    void paint(QPainter* painter, const QStyleOptionViewItem& option,
               const QModelIndex& index) const override;
    QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;

private:
    int itemHeight_;
};


class MvQLayerModel : public QAbstractItemModel
{
    Q_OBJECT

public:
    MvQLayerModel(MgQPlotScene*);
    ~MvQLayerModel() override;

    enum CustomItemRole
    {
        VisibleRole = Qt::UserRole + 1,
        LevelRole = Qt::UserRole + 2
    };

    int columnCount(const QModelIndex& parent = QModelIndex()) const override;
    int rowCount(const QModelIndex& parent = QModelIndex()) const override;

    QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const override;
    bool setData(const QModelIndex&, const QVariant&, int role = Qt::EditRole) override;
    // QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const;

    QModelIndex index(int, int, const QModelIndex& parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex&) const override;

    // Layers functions
    QList<MgQLayerItem*>& layers() { return layers_; }
    MgQLayerItem* layer(const QModelIndex&) const;
    MgQLayerItem* layerFromAnyLevel(const QModelIndex&) const;

    std::string layerId(const QModelIndex&);
    QString layerName(const QModelIndex&);
    void layerMetaData(const QModelIndex&, MetaDataCollector&);
    QString layerMetaData(const QModelIndex&, QString);

    void layersAreAboutToChange();
    void resetLayers(MgQSceneItem*);

    void moveUp(const QModelIndex& index);
    void moveDown(const QModelIndex& index);
    void moveTop(const QModelIndex& index);
    void moveBottom(const QModelIndex& index);

    Qt::ItemFlags flags(const QModelIndex&) const override;

    Qt::DropActions supportedDropActions() const override;
    QStringList mimeTypes() const override;
    QMimeData* mimeData(const QModelIndexList&) const override;
    bool dropMimeData(const QMimeData* data, Qt::DropAction action,
                      int row, int column, const QModelIndex& parent) override;

    void setTransparency(const QModelIndex&, int);
    int transparency(const QModelIndex&);
    MgQLayerItem* rowToLayer(int) const;
    int layerToRow(MgQLayerItem*) const;
    int indexToLevel(const QModelIndex&) const;
    MvQLayerContentsIcon* indexToIcon(const QModelIndex&) const;

    int idToParentRow(int) const;

    // Set/unset highlighting layers for drag&drop operations
    void setHighlightedLayer(const QModelIndex&, int);

    int presentableId()
    {
        return presentableId_;
    }

    // Check if icon is to be shown
    bool checkIconVisibility(MvQLayerContentsIcon*, int& dataId) const;
    bool showIcon(MvQLayerContentsIcon*) const;

    // Check if layer is to be shown
    bool checkLayerVisibility(MgQLayerItem*) const;

signals:
    void layerVisibilityChanged(QString, bool);
    void layerTransparencyChanged(QString, int);
    void layerStackingOrderChanged(QList<QPair<QString, int> >);
    void layerUpdate();
    void layerDragFinished();

protected:
    void clearIcons();
    MvQLayerContentsIcon* layerIcon(MgQLayerItem*, int) const;
    int rowToStackLevel(int row);
    QString label(MgQLayerItem*, const int, const int) const;
    int idToLevel(int) const;
    void moveLayer(int, int);

    MgQPlotScene* scene_;
    MgQSceneItem* sceneItem_;
    QList<MgQLayerItem*> layers_;
    int higlightedId_;
    Qt::CheckState state_;
    mutable QHash<MgQLayerItem*, QList<MvQLayerContentsIcon*> > icons_;

    int presentableId_;  // current presentable/scene id
};


//----------------------------------------------------------------------------

// Filter icons to be shown
class MvQLayerFilterModel : public QSortFilterProxyModel
{
public:
    MvQLayerFilterModel(QObject* parent = nullptr);

    bool filterAcceptsRow(int, const QModelIndex&) const override;
};


//----------------------------------------------------------------------------

// Handle external drag&drop
class MvQLayerView : public QTreeView
{
    Q_OBJECT

public:
    explicit MvQLayerView(QWidget* parent = nullptr);

    void setLayerModel(MvQLayerModel*, MvQLayerFilterModel*);

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

    // Handle MvQDropTarget facility
    void checkDropTarget(QDropEvent*);
    void removeDropTarget();

    MvQLayerModel* model_;
    MvQLayerFilterModel* filterModel_;
    int layerId_;

signals:
    void iconDropped(const MvQDrop&, const QModelIndex&);
};
