/*
 * Copyright 2009- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation
 * nor does it submit to any jurisdiction.
 */

#ifndef ecflow_viewer_VariableModel_HPP
#define ecflow_viewer_VariableModel_HPP

#include <vector>

#include <QAbstractItemModel>
#include <QSortFilterProxyModel>

#include "NodeObserver.hpp"
#include "VInfo.hpp"

class Node;
class VariableModelData;
class VariableModelDataHandler;
class VariableSortModel;

class VariableModel : public QAbstractItemModel {
    Q_OBJECT

    friend class VariableSortModel;

public:
    explicit VariableModel(VariableModelDataHandler* data, QObject* parent = nullptr);

    enum CustomItemRole {
        ReadOnlyRole = Qt::UserRole + 1,
        GenVarRole   = Qt::UserRole + 2,
        ShadowRole   = Qt::UserRole + 3,
        MirrorRole   = Qt::UserRole + 4
    };

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

    Qt::ItemFlags flags(const QModelIndex& index) const override;
    QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const override;
    QVariant headerData(int, Qt::Orientation, int role = Qt::DisplayRole) const override;

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

    bool variable(const QModelIndex& index, QString& name, QString& value, bool& genVar) const;

    VariableModelData* indexToData(const QModelIndex& invarViewdex) const;
    VariableModelData* indexToData(const QModelIndex& index, int& block) const;
    VInfo_ptr indexToInfo(const QModelIndex& index) const;
    QModelIndex infoToIndex(VInfo_ptr info) const;

    bool isVariable(const QModelIndex& index) const;

public Q_SLOTS:
    void slotReloadBegin();
    void slotReloadEnd();
    void slotClearBegin(int block, int num);
    void slotClearEnd(int block, int num);
    void slotLoadBegin(int block, int num);
    void slotLoadEnd(int block, int num);
    void slotDataChanged(int);

Q_SIGNALS:
    void filterChanged();
    void rerunFilter();

protected:
    bool hasData() const;

    int indexToLevel(const QModelIndex&) const;
    void identify(const QModelIndex& index, int& parent, int& row) const;

    VariableModelDataHandler* data_;
    static QColor varCol_;
    static QColor genVarCol_;
    static QColor shadowCol_;
    static QColor mirrorCol_;
    static QColor blockBgCol_;
    static QColor blockFgCol_;
};

// Filter and sorts the variables

class VariableSortModel : public QSortFilterProxyModel {
    Q_OBJECT

public:
    enum MatchMode { FilterMode, SearchMode };

    explicit VariableSortModel(VariableModel*, QObject* parent = nullptr);
    ~VariableSortModel() override = default;

    MatchMode matchMode() const { return matchMode_; }
    void setMatchMode(MatchMode mode);
    void setMatchText(QString text);

    bool lessThan(const QModelIndex& left, const QModelIndex& right) const override;
    bool filterAcceptsRow(int, const QModelIndex&) const override;

    // From QSortFilterProxyModel:
    // we set the source model in the constructor. So this function should not do anything.
    void setSourceModel(QAbstractItemModel*) override {}
    QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const override;

    QModelIndexList match(const QModelIndex& start,
                          int role,
                          const QVariant& value,
                          int hits             = 1,
                          Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const override;

#if 0
    void test();
    void test(const QModelIndex& p);
    void testSource(const QModelIndex& p);
#endif

public Q_SLOTS:
    void slotShowShadowed(bool);

protected Q_SLOTS:
    void slotFilterChanged();
    void slotRerunFilter();

protected:
    void match(QString text);
    void print(const QModelIndex idx);

    VariableModel* varModel_;
    bool showShadowed_;

    MatchMode matchMode_;
    mutable QString matchText_;
    mutable QModelIndexList matchLst_;

    QMap<QString, int> nameCnt_;
    bool ignoreDuplicateNames_; // Ignore duplicate names across ancestors
};

#endif /* ecflow_viewer_VariableModel_HPP */
