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

 Copyright 2018 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 *************************************/

#include "MvQStyleHelp.h"

#include <QtGlobal>
#include <QAction>
#include <QApplication>
#include <QBuffer>
#include <QClipboard>
#include <QComboBox>
#include <QCompleter>
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPainter>
#include <QTextEdit>
#include <QSortFilterProxyModel>
#include <QSplitter>
#include <QToolButton>
#include <QTreeView>
#include <QVBoxLayout>

#include "MvQMethods.h"
#include "MvQStyleDb.h"
#include "MvQStyleLine.h"
#include "MvQStyleTreeWidget.h"

#include "MvMiscellaneous.h"

#include "HelpFactory.h"

//=====================================================
//
//  MvQStyleModel
//
//=====================================================

MvQStyleModel::MvQStyleModel(QObject* parent) :
    QAbstractItemModel(parent)
{
}

MvQStyleModel::~MvQStyleModel() = default;

int MvQStyleModel::columnCount(const QModelIndex& /*parent */) const
{
    return 1;
}

int MvQStyleModel::rowCount(const QModelIndex& parent) const
{
    // Parent is the root:
    if (!parent.isValid()) {
        return MvQStyleDb::instance()->items().count();
    }

    return 0;
}

QVariant MvQStyleModel::data(const QModelIndex& index, int role) const
{
    int row = index.row();
    if (row < 0 || row >= MvQStyleDb::instance()->items().count())
        return {};

    if (role == Qt::DisplayRole) {
        if (index.column() == 0)
            return row;
    }
    else if (role == Qt::UserRole) {
        return (isFiltered(MvQStyleDb::instance()->items()[row]) == true) ? "1" : "0";
    }
    else if (role == SortRole) {
        if (index.column() == 0)
            return MvQStyleDb::instance()->items()[row]->name();
    }

    return {};
}

QVariant MvQStyleModel::headerData(const int section, const Qt::Orientation orient, const int role) const
{
    if (orient != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::ToolTipRole))
        return QAbstractItemModel::headerData(section, orient, role);

    return role == Qt::DisplayRole && section == 0 ? " Matching styles" : QVariant();
}

QModelIndex MvQStyleModel::index(int row, int column, const QModelIndex& parent) const
{
    if (row < 0 || column < 0) {
        return {};
    }

    // When parent is the root this index refers to a node or server
    if (!parent.isValid()) {
        return createIndex(row, column);
    }

    return {};
}

QModelIndex MvQStyleModel::parent(const QModelIndex& /*child*/) const
{
    return {};
}

void MvQStyleModel::setFilter(QString s)
{
    filter_ = s;
}

void MvQStyleModel::clearFilter()
{
    filter_.clear();
}

bool MvQStyleModel::isFiltered(MvQStyleDbItem* item) const
{
    if (!filter_.isEmpty()) {
        if (item->name().contains(filter_, Qt::CaseInsensitive))
            return true;

        if (item->layerMatch(filter_, Qt::CaseInsensitive))
            return true;

        if (item->keywordMatch(filter_, Qt::CaseInsensitive))
            return true;

        if (item->colourMatch(filter_, Qt::CaseInsensitive))
            return true;

        return false;
    }

    return true;
}

//=====================================================
//
// MvQStyleSelectionWidget
//
//=====================================================

MvQStyleSelectionWidget::MvQStyleSelectionWidget(QWidget* parent) :
    QWidget(parent),
    ignoreFilterChanged_(false)
{
    auto* vb = new QVBoxLayout(this);

    nameLe_ = new QLineEdit(this);
    nameLe_->setPlaceholderText("Filter");
#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
    nameLe_->setClearButtonEnabled(true);
#endif

    QStringList allNames = MvQStyleDb::instance()->names();
    allNames << MvQStyleDb::instance()->layers() << MvQStyleDb::instance()->keywords() << MvQStyleDb::instance()->colours();

    auto* nameCompleter = new QCompleter(allNames, this);
    nameCompleter->setCaseSensitivity(Qt::CaseInsensitive);
#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
    nameCompleter->setFilterMode(Qt::MatchContains);
#endif
    nameLe_->setCompleter(nameCompleter);

    vb->addWidget(nameLe_);

    // Layout to hold tree and sidebar

    browser_ = new MvQStyleTreeWidget(this);
    vb->addWidget(browser_, 1);

    model_ = new MvQStyleModel(this);

    sortModel_ = new QSortFilterProxyModel(this);
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
    sortModel_->setFilterRegularExpression("1");
#else
    sortModel_->setFilterRegExp("1");
#endif
    sortModel_->setFilterRole(Qt::UserRole);
    sortModel_->setSortRole(MvQStyleModel::SortRole);
    sortModel_->setSourceModel(model_);

    browser_->tree()->setModel(sortModel_);

    // Signals and slots

    connect(nameLe_, SIGNAL(textChanged(QString)),
            this, SLOT(slotNameFilter(QString)));

    connect(browser_->tree()->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)),
            this, SLOT(slotItemSelected(QModelIndex, QModelIndex)));
}

void MvQStyleSelectionWidget::slotNameFilter(QString s)
{
    if (ignoreFilterChanged_)
        return;

    if (s == "Filter")
        s.clear();
    model_->setFilter(s);
    sortModel_->invalidate();
}

void MvQStyleSelectionWidget::clearFilter()
{
    ignoreFilterChanged_ = true;
    nameLe_->clear();
    ignoreFilterChanged_ = false;

    model_->clearFilter();
    sortModel_->invalidate();
}

void MvQStyleSelectionWidget::slotItemSelected(const QModelIndex& current, const QModelIndex& /*prev*/)
{
    QModelIndex idxSrc = sortModel_->mapToSource(current);
    if (idxSrc.isValid()) {
        int row = idxSrc.row();
        emit itemSelected(row);

        if (row >= 0 && row < MvQStyleDb::instance()->items().count()) {
            MvQStyleDbItem* item = MvQStyleDb::instance()->items()[row];
            Q_ASSERT(item);
            browser_->setStyle(item);
        }
    }
}

void MvQStyleSelectionWidget::setCurrent(const std::string& name)
{
    QModelIndex idxSrc = sortModel_->mapToSource(browser_->tree()->currentIndex());
    if (idxSrc.isValid()) {
        int row = idxSrc.row();
        if (row >= 0 && row < MvQStyleDb::instance()->items().size() &&
            MvQStyleDb::instance()->items()[row]->name().toStdString() == name)
            return;
    }

    clearFilter();
    int row = MvQStyleDb::instance()->indexOf(name);
    if (row >= 0) {
        QModelIndex idxSrc = model_->index(row, 0);
        if (idxSrc.isValid()) {
            browser_->tree()->setCurrentIndex(sortModel_->mapFromSource(idxSrc));

            int row = idxSrc.row();
            emit itemSelected(row);

            if (row >= 0 && row < MvQStyleDb::instance()->items().count()) {
                MvQStyleDbItem* item = MvQStyleDb::instance()->items()[row];
                Q_ASSERT(item);
                browser_->setStyle(item);
            }
        }
    }
}

//==================================================
//
//  MvQStyleHelp
//
//==================================================

MvQStyleHelp::MvQStyleHelp(RequestPanel& owner, const MvIconParameter& param) :
    MvQRequestPanelHelp(owner, param)
{
    selector_ = new MvQStyleSelectionWidget(parentWidget_);

    connect(selector_, SIGNAL(itemSelected(int)),
            this, SLOT(slotSelected(int)));
}

void MvQStyleHelp::slotSelected(int idx)
{
    if (idx >= 0 && idx < MvQStyleDb::instance()->items().count()) {
        if (MvQStyleDb::instance()->items()[idx]->name() != oriName_) {
            std::vector<std::string> vs;
            vs.push_back(std::to_string(idx));
            emit edited(vs);
            oriName_.clear();
        }
    }
}

void MvQStyleHelp::refresh(const std::vector<std::string>& values)
{
    if (values.size() == 0)
        return;

    oriName_ = QString::fromStdString(values[0]);
    selector_->setCurrent(values[0]);
}

static HelpMaker<MvQStyleHelp> maker2("help_style");
