/***************************** 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 "MvQPaletteHelp.h"

#include <QtGlobal>
#include <QApplication>
#include <QComboBox>
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPainter>
#include <QSortFilterProxyModel>
#include <QToolButton>
#include <QTreeView>
#include <QVBoxLayout>

#include "MvQPaletteDb.h"
#include "MvQPaletteLine.h"
#include "MvMiscellaneous.h"

#include "HelpFactory.h"

//=====================================================
//
//  MvQPaletteModel
//
//=====================================================

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

MvQPaletteModel::~MvQPaletteModel() = default;

int MvQPaletteModel::columnCount(const QModelIndex& /*parent */) const
{
    return 2;
}

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

    return 0;
}

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

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

    return {};
}

QVariant MvQPaletteModel::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);

    if (role == Qt::DisplayRole) {
        switch (section) {
            case 0:
                return "Palette";
            case 1:
                return "Name";
            default:
                return {};
        }
    }

    return {};
}

QModelIndex MvQPaletteModel::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 MvQPaletteModel::parent(const QModelIndex& /*child*/) const
{
    return {};
}

void MvQPaletteModel::setOriginFilter(QString s)
{
    originFilter_ = s;
}

void MvQPaletteModel::setColourFilter(QString s)
{
    colourFilter_ = s;
}

void MvQPaletteModel::setNumFilter(QString s)
{
    numFilter_ = s;
}

void MvQPaletteModel::setParamFilter(QString s)
{
    paramFilter_ = s;
}

void MvQPaletteModel::setNameFilter(QString s)
{
    nameFilter_ = s;
}

void MvQPaletteModel::clearFilter()
{
    nameFilter_.clear();
    originFilter_.clear();
    colourFilter_.clear();
    numFilter_.clear();
    paramFilter_.clear();
}

bool MvQPaletteModel::isFiltered(MvQPaletteDbItem* item) const
{
    if (!nameFilter_.isEmpty()) {
        if (!item->name().contains(nameFilter_, Qt::CaseInsensitive))
            return false;
    }

    if (!originFilter_.isEmpty())
        if (item->origin() != originFilter_)
            return false;

    if (!colourFilter_.isEmpty())
        if (!item->colours().contains(colourFilter_))
            return false;

    if (!numFilter_.isEmpty())
        if (item->nLevels() != numFilter_.toInt())
            return false;

    if (!paramFilter_.isEmpty())
        if (!item->parameters().contains(paramFilter_))
            return false;

    return true;
}

//==============================
//
// MvQPaletteDelegate
//
//==============================

MvQPaletteDelegate::MvQPaletteDelegate(QWidget* parent) :
    QStyledItemDelegate(parent),
    borderCol_(QColor(210, 210, 210))
{
}

void MvQPaletteDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
                               const QModelIndex& index) const
{
    painter->save();

    if (index.column() == 0) {
        // Background
        QStyleOptionViewItem vopt(option);
        initStyleOption(&vopt, index);

        const QStyle* style = vopt.widget ? vopt.widget->style() : QApplication::style();
        const QWidget* widget = vopt.widget;

        // painter->fillRect(option.rect,QColor(238,238,238));

        // We render everything with the default method
        style->drawControl(QStyle::CE_ItemViewItem, &vopt, painter, widget);

        int row = index.data(Qt::DisplayRole).toInt();
        if (row < 0)
            return;

        QRect rect = option.rect.adjusted(2, 2, -4, -2);

        MvQPaletteDbItem* item = MvQPaletteDb::instance()->items()[row];
        Q_ASSERT(item);
        if (item)
            item->paint(painter, rect);
    }
    else {
        QStyledItemDelegate::paint(painter, option, index);
    }

    // Render the horizontal border for rows. We only render the top border line.
    // With this technique we miss the bottom border line of the last row!!!
    // QRect fullRect=QRect(0,option.rect.y(),painter->device()->width(),option.rect.height());
    QRect bgRect = option.rect;
    painter->setPen(borderCol_);
    painter->drawLine(bgRect.topLeft(), bgRect.topRight());

    painter->restore();
}

QSize MvQPaletteDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    QSize s = QStyledItemDelegate::sizeHint(option, index);
    if (index.column() == 0)
        return {180, s.height() + 2};

    return {s.width(), s.height() + 2};
}


//=====================================================
//
// MvQPaletteSelectionWidget
//
//=====================================================

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

    auto* topHb = new QHBoxLayout(this);
    vb->addLayout(topHb);

    topHb->addStretch(1);

    showFilterTb_ = new QToolButton(this);
    showFilterTb_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    showFilterTb_->setText(tr("Show all filters"));
    showFilterTb_->setIcon(QPixmap(":/desktop/filter.svg"));
    showFilterTb_->setCheckable(true);
    showFilterTb_->setChecked(false);
    topHb->addWidget(showFilterTb_);

    resetTb_ = new QToolButton(this);
    resetTb_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    resetTb_->setText(tr("Clear all filters"));
    resetTb_->setIcon(QPixmap(":/desktop/undo.svg"));
    topHb->addWidget(resetTb_);

    filterGrid_ = new QGridLayout();
    vb->addLayout(filterGrid_);

    int cbWidth = 42;  // number of characters!

    // name
    int row = 0;
    filterGrid_->addWidget(new QLabel(tr("Name:"), this), row, 0);
    filterGrid_->setContentsMargins(1, 1, 1, 1);
    filterGrid_->setVerticalSpacing(2);

    nameLe_ = new QLineEdit(this);
    // nameLe_->setPlaceholderText("");
#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
    nameLe_->setClearButtonEnabled(true);
#endif
    filterGrid_->addWidget(nameLe_, row, 1, 1, 2);

    //    nameResetTb_ = new QToolButton(this);
    //    nameResetTb_->setIcon(QPixmap(":/desktop/undo.svg"));
    //    nameResetTb_->setAutoRaise(true);
    //    nameResetTb_->setToolTip(tr("Clear filter"));
    //    filterGrid_->addWidget(nameResetTb_, row, 2);
    row++;

    // origin
    filterGrid_->addWidget(new QLabel(tr("Origin:"), this), row, 0);
    filterGrid_->setContentsMargins(1, 1, 1, 1);
    filterGrid_->setVerticalSpacing(2);

    originCb_ = new QComboBox(this);
    originCb_->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
    originCb_->setMinimumContentsLength(cbWidth);
    originCb_->addItem("ANY");
    originCb_->addItems(MvQPaletteDb::instance()->origins());
    filterGrid_->addWidget(originCb_, row, 1);

    originResetTb_ = new QToolButton(this);
    originResetTb_->setIcon(QPixmap(":/desktop/undo.svg"));
    originResetTb_->setAutoRaise(true);
    originResetTb_->setToolTip(tr("Clear filter"));
    filterGrid_->addWidget(originResetTb_, row, 2);
    row++;

    // colour
    filterGrid_->addWidget(new QLabel(tr("Colour:"), this), row, 0);

    colourCb_ = new QComboBox(this);
    colourCb_->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
    colourCb_->setMinimumContentsLength(cbWidth);
    colourCb_->addItem("ANY");
    colourCb_->addItems(MvQPaletteDb::instance()->colours());
    filterGrid_->addWidget(colourCb_, row, 1);

    colourResetTb_ = new QToolButton(this);
    colourResetTb_->setIcon(QPixmap(":/desktop/undo.svg"));
    colourResetTb_->setAutoRaise(true);
    colourResetTb_->setToolTip(tr("Clear filter"));
    filterGrid_->addWidget(colourResetTb_, row, 2);
    row++;

    // Num of colours in palette
    filterGrid_->addWidget(new QLabel(tr("Count:"), this), row, 0);

    numCb_ = new QComboBox(this);
    numCb_->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
    numCb_->setMinimumContentsLength(cbWidth);
    numCb_->addItem("ANY");

    QStringList numLst;
    for (int i = 0; i < MvQPaletteDb::instance()->nLevels().count(); i++)
        numLst << QString::number(MvQPaletteDb::instance()->nLevels()[i]);
    numCb_->addItems(numLst);

    filterGrid_->addWidget(numCb_, row, 1);

    numResetTb_ = new QToolButton(this);
    numResetTb_->setIcon(QPixmap(":/desktop/undo.svg"));
    numResetTb_->setAutoRaise(true);
    numResetTb_->setToolTip(tr("Clear filter"));
    filterGrid_->addWidget(numResetTb_, row, 2);

    row++;

    // params
    filterGrid_->addWidget(new QLabel(tr("Parameter:"), this), row, 0);

    paramCb_ = new QComboBox(this);
    paramCb_->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
    paramCb_->setMinimumContentsLength(cbWidth);
    paramCb_->addItem("ANY");
    paramCb_->addItems(MvQPaletteDb::instance()->parameters());
    filterGrid_->addWidget(paramCb_, row, 1);

    paramResetTb_ = new QToolButton(this);
    paramResetTb_->setIcon(QPixmap(":/desktop/undo.svg"));
    paramResetTb_->setAutoRaise(true);
    paramResetTb_->setToolTip(tr("Clear filter"));
    filterGrid_->addWidget(paramResetTb_, row, 2);

    row++;

    filterGrid_->setColumnStretch(1, 1);

    // tree view and model

    tree_ = new QTreeView(this);
    tree_->setRootIsDecorated(false);
    tree_->setUniformRowHeights(true);
    tree_->setMinimumHeight(270);
    vb->addWidget(tree_, 1);

    model_ = new MvQPaletteModel(this);
    tree_->setItemDelegate(new MvQPaletteDelegate(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(MvQPaletteModel::SortRole);
    sortModel_->setSourceModel(model_);

    tree_->setSortingEnabled(true);
    tree_->sortByColumn(1, Qt::AscendingOrder);
    tree_->setModel(sortModel_);
    tree_->resizeColumnToContents(0);

    connect(showFilterTb_, SIGNAL(clicked(bool)),
            this, SLOT(slotShowFilters(bool)));

    connect(resetTb_, SIGNAL(clicked()),
            this, SLOT(slotClearFilter()));

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

    //    connect(nameResetTb_, SIGNAL(clicked()),
    //            nameLe_, SLOT(clear()));

    connect(originCb_, SIGNAL(currentIndexChanged(int)),
            this, SLOT(slotOriginFilter(int)));

    connect(originResetTb_, SIGNAL(clicked()),
            this, SLOT(slotResetOriginFilter()));

    connect(colourCb_, SIGNAL(currentIndexChanged(int)),
            this, SLOT(slotColourFilter(int)));

    connect(colourResetTb_, SIGNAL(clicked()),
            this, SLOT(slotResetColourFilter()));

    connect(numCb_, SIGNAL(currentIndexChanged(int)),
            this, SLOT(slotNumFilter(int)));

    connect(numResetTb_, SIGNAL(clicked()),
            this, SLOT(slotResetNumFilter()));

    connect(paramCb_, SIGNAL(currentIndexChanged(int)),
            this, SLOT(slotParamFilter(int)));

    connect(paramResetTb_, SIGNAL(clicked()),
            this, SLOT(slotResetParamFilter()));

    connect(tree_->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)),
            this, SLOT(slotItemSelected(QModelIndex, QModelIndex)));

    // Init the button states!!
    checkButtonState();

    // init filters visibility
    slotShowFilters(showFilterTb_->isChecked());
}

void MvQPaletteSelectionWidget::slotOriginFilter(int)
{
    if (ignoreFilterChanged_)
        return;

    QString s = originCb_->currentText();
    if (s == "ANY")
        s.clear();
    model_->setOriginFilter(s);
    sortModel_->invalidate();
    checkButtonState();
}

void MvQPaletteSelectionWidget::slotColourFilter(int)
{
    if (ignoreFilterChanged_)
        return;

    QString s = colourCb_->currentText();
    if (s == "ANY")
        s.clear();
    model_->setColourFilter(s);
    sortModel_->invalidate();
    checkButtonState();
}

void MvQPaletteSelectionWidget::slotNumFilter(int)
{
    if (ignoreFilterChanged_)
        return;

    QString s = numCb_->currentText();
    if (s == "ANY")
        s.clear();
    model_->setNumFilter(s);
    sortModel_->invalidate();
    checkButtonState();
}

void MvQPaletteSelectionWidget::slotParamFilter(int)
{
    if (ignoreFilterChanged_)
        return;

    QString s = paramCb_->currentText();
    if (s == "ANY")
        s.clear();
    model_->setParamFilter(s);
    sortModel_->invalidate();
    checkButtonState();
}

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

    if (s == "ANY")
        s.clear();
    model_->setNameFilter(s);
    sortModel_->invalidate();
    checkButtonState();
}

void MvQPaletteSelectionWidget::slotResetOriginFilter()
{
    originCb_->setCurrentIndex(0);
}

void MvQPaletteSelectionWidget::slotResetColourFilter()
{
    colourCb_->setCurrentIndex(0);
}

void MvQPaletteSelectionWidget::slotResetNumFilter()
{
    numCb_->setCurrentIndex(0);
}

void MvQPaletteSelectionWidget::slotResetParamFilter()
{
    paramCb_->setCurrentIndex(0);
}

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

void MvQPaletteSelectionWidget::showFilters(bool visible)
{
    for (int i = 1; i < filterGrid_->rowCount(); i++) {
        for (int j = 0; j < 3; j++) {
            if (QLayoutItem* item = filterGrid_->itemAtPosition(i, j)) {
                if (QWidget* w = item->widget()) {
                    w->setVisible(visible);
                }
            }
        }
    }
}

void MvQPaletteSelectionWidget::slotShowFilters(bool visible)
{
    showFilters(visible);
    slotClearFilter();
}

void MvQPaletteSelectionWidget::slotClearFilter()
{
    ignoreFilterChanged_ = true;
    nameLe_->clear();
    originCb_->setCurrentIndex(0);
    colourCb_->setCurrentIndex(0);
    paramCb_->setCurrentIndex(0);
    numCb_->setCurrentIndex(0);
    ignoreFilterChanged_ = false;

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

void MvQPaletteSelectionWidget::checkButtonState()
{
    // nameResetTb_->setEnabled(!nameLe_->text().isEmpty());
    originResetTb_->setEnabled(originCb_->currentText() != "ANY");
    colourResetTb_->setEnabled(colourCb_->currentText() != "ANY");
    numResetTb_->setEnabled(numCb_->currentText() != "ANY");
    paramResetTb_->setEnabled(paramCb_->currentText() != "ANY");

    resetTb_->setEnabled(originResetTb_->isEnabled() ||
                         !nameLe_->text().isEmpty() || colourResetTb_->isEnabled() ||
                         numResetTb_->isEnabled() || paramResetTb_->isEnabled());
}

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


    slotClearFilter();
    int row = MvQPaletteDb::instance()->indexOf(name);
    if (row >= 0) {
        QModelIndex idxSrc = model_->index(row, 0);
        if (idxSrc.isValid()) {
            tree_->setCurrentIndex(sortModel_->mapFromSource(idxSrc));
        }
    }
}

//==================================================
//
//  MvQPaletteHelp
//
//==================================================

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

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

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

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

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

static HelpMaker<MvQPaletteHelp> maker2("help_palette");
