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

 Copyright 2021 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 <QAbstractItemModel>
#include <QDialog>
#include <QDir>
#include <QJsonDocument>
#include <QMap>
#include <QPen>
#include <QBrush>
#include <QLineEdit>
#include <QPixmap>
#include <QSettings>
#include <QSortFilterProxyModel>
#include <QString>
#include <QTreeView>
#include <QVector>

#include <memory>
#include <vector>

#include "MvQFeatureFwd.h"

class MvRequest;
class WmoWeatherSymbol;
class MvQFeatureItem;
class WsType;

class FeatureStoreItem;
using FeatureStoreItemPtr = std::shared_ptr<FeatureStoreItem>;

class FeatureStoreItem
{
    friend class FeatureStore;
public:
    static FeatureStoreItemPtr make(QString fileName, QString dirPath, bool local);
    static FeatureStoreItemPtr make(QString fileName, MvQFeatureItemPtr fItem, QString dirPath, bool local);
    virtual ~FeatureStoreItem();

    WsType* type() const { return type_; }
    QString fileName() const { return fileName_; }
    QString toolTip() const { return toolTip_; }
    QPixmap pixmap() const { return pix_; }
    void setPixmap(QPixmap p);
    bool isGeoLocked() const { return geoLocked_; }
    void setGeoLocked(bool);
    bool isLocal() const {return local_;}
    void addToSceneAtGeoCoord();
    void addToSceneAtClick();
    void saveToDisk();
    void removeFromDisk();
    void update(QString fileName, QString toolTip, bool geoLocked);

protected:
    FeatureStoreItem(QString fileName, QString dirPath, bool local);
    FeatureStoreItem(QString fileName, MvQFeatureItemPtr fItem, QString dirPath, bool local);
    QString jsonFilePath() const;
    QString pixFilePath() const;
    void loadJson();
    void clearJson();

    WsType* type_{nullptr};
    QString fileName_;
    QString label_;
    QString toolTip_;
    QString dirPath_;
    QJsonDocument doc_;
    bool saved_{false};
    QPixmap pix_;
    bool geoLocked_{false};
    static const QSize pixSize_;
    bool readOnly_{false};
    bool local_{true};
    static const QString pixSuffix_;
};


class FeatureStoreObserver
{
public:
    virtual void notifyStoreChangeBegin() = 0;
    virtual void notifyStoreChangeEnd()   = 0;
    virtual void notifyStoreInitEnd()     = 0;
};

class FeatureStore
{
public:
    static FeatureStore* Instance();

    void load();
    const QVector<FeatureStoreItemPtr>& items() const { return items_; }
    void add(MvQFeatureItemPtr);
    void add(QString name, MvQFeatureItemPtr item);
    FeatureStoreItemPtr find(QString, bool local) const;
    void edit(FeatureStoreItemPtr item);
    void remove(FeatureStoreItemPtr);

    void addObserver(FeatureStoreObserver*);
    void removeObserver(FeatureStoreObserver*);

    enum NameErrorState
    {
        NameNoError,
        NameEmptyError,
        NameStartError,
        NameInvalidCharError,
        NameDuplicateError
    };
    NameErrorState checkName(QString name, QString& err, bool local) const;

protected:
    FeatureStore();
    void load(QString dirPath, bool local);
    using ObsFunc = void (FeatureStoreObserver::*)();
    void broadcast(ObsFunc);
    void broadcastChangeBegin();
    void broadcastChangeEnd();
    void broadcastInitEnd();

    static FeatureStore* instance_;
    QVector<FeatureStoreItemPtr> items_;
    QString localDir_;
    QString extraDir_;
    std::vector<FeatureStoreObserver*> observers_;
};

class FeatureStoreModel : public QAbstractItemModel, FeatureStoreObserver
{
    Q_OBJECT
public:
    FeatureStoreModel(QObject* parent);
    ~FeatureStoreModel() override = default;

    int columnCount(const QModelIndex&) const override;
    int rowCount(const QModelIndex& parent = QModelIndex()) const override;
    QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const override;
    QVariant headerData(int section, Qt::Orientation ori, int role) const override;
    QModelIndex index(int, int, const QModelIndex& parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex&) const override;

    void notifyStoreChangeBegin() override;
    void notifyStoreChangeEnd() override;
    void notifyStoreInitEnd() override;
    FeatureStoreItemPtr indexToItem(const QModelIndex&) const;

signals:
    void initFinished();

protected:
    QString group_;
    QPixmap lockPix_;
    QPixmap emptyLockPix_;
    QColor typeColour_;
};

class FeatureStoreFilterModel : public QSortFilterProxyModel
{
public:
    using QSortFilterProxyModel::QSortFilterProxyModel;
    ~FeatureStoreFilterModel() override = default;
    void setFilterStr(QString);

protected:
    bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;

    QString filterStr_;
};

namespace Ui
{
class FeatureStoreWidget;
class FeatureStoreAddDialog;
class FeatureStoreEditDialog;
}  // namespace Ui

class FeatureStoreView : public QTreeView
{
    Q_OBJECT
public:
    using QTreeView::QTreeView;

signals:
    void contextMenuRequested(const QModelIndex&);

protected:
    void contextMenuEvent(QContextMenuEvent *event) override;
};

class FeatureStoreWidget : public QWidget
{
    Q_OBJECT
public:
    explicit FeatureStoreWidget(QWidget* parent = nullptr);

    void writeSettings(QSettings& settings);
    void readSettings(QSettings& settings);

protected slots:
    void slotClicked(const QModelIndex& idx);
    void slotDoubleClicked(const QModelIndex& idx);
    void slotFilter(QString t);
    void addToSceneAtGeo();
    void addToSceneAtClick();
    void editItem();
    void removeItem();
    void viewContextMenuRequested(const QModelIndex& idx);
    void selectionChanged(const QModelIndex& current, const QModelIndex& previous);

protected:
    FeatureStoreItemPtr currentItem() const;
    void checkActionState(FeatureStoreItemPtr item);

    Ui::FeatureStoreWidget* ui_{nullptr};
    FeatureStoreModel* model_{nullptr};
    FeatureStoreFilterModel* filterModel_{nullptr};
    bool columnWidthInitialised_{false};
};


class FeatureStoreAddDialog : public QDialog
{
    Q_OBJECT
public:
    explicit FeatureStoreAddDialog(MvQFeatureItemPtr item, QWidget* parent = nullptr);

public slots:
    void accept() override;

protected:
    Ui::FeatureStoreAddDialog* ui_{nullptr};
    MvQFeatureItemPtr item_{nullptr};
};

class FeatureStoreEditDialog : public QDialog
{
    Q_OBJECT
public:
    explicit FeatureStoreEditDialog(FeatureStoreItemPtr item, QWidget* parent = nullptr);

public slots:
    void accept() override;

protected:
    Ui::FeatureStoreEditDialog* ui_{nullptr};
    FeatureStoreItemPtr item_{nullptr};
};
