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

 Copyright 2019 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 "FunctionListWidget.h"
#include "LanguageHelper.h"
#include "IconClassHelp.h"
#include "VerbFunctionHelp.h"
#include "MvQMethods.h"
#include "MvQTheme.h"

#include <QtGlobal>
#include <QFile>
#include <QPainter>
#include <QItemSelectionModel>
#include <QSortFilterProxyModel>
#include <QTextStream>
#include <QTreeWidgetItem>

#include "ui_FunctionListWidget.h"

//========================================================
//
// FunctionListDelegate
//
//========================================================

FunctionListDelegate::FunctionListDelegate(QWidget* parent) :
    QStyledItemDelegate(parent)
{
    borderPen_ = MvQTheme::pen("treeview", "border_pen");
}

void FunctionListDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
                                 const QModelIndex& index) const
{
    QStyledItemDelegate::paint(painter, option, index);

    painter->save();

    // 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 bgRect = option.rect;
    painter->setPen(borderPen_);
    if (index.column() != 0)
        painter->drawLine(bgRect.topLeft(), bgRect.topRight());
    else
        painter->drawLine(QPoint(0, bgRect.y()), bgRect.topRight());

    painter->restore();
}

QSize FunctionListDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    QSize size = QStyledItemDelegate::sizeHint(option, index);
    size += QSize(0, 2);
    return size;
}

//================================================
//
// FunctionListModel
//
//================================================

FunctionListModel::FunctionListModel(QObject* parent) :
    QAbstractItemModel(parent),
    data_(nullptr)
{
}

bool FunctionListModel::hasData() const
{
    return (data_ != nullptr);
}

int FunctionListModel::columnCount(const QModelIndex& /*parent*/) const
{
    return 3;
}

int FunctionListModel::rowCount(const QModelIndex& parent) const
{
    if (!hasData() || parent.isValid())
        return 0;

    return data_->totalFunctionNum();
}

QVariant FunctionListModel::data(const QModelIndex& index, int role) const
{
    if (!hasData() || !index.isValid()) {
        return {};
    }

    QPair<VerbFunctionHelpFunction*, int> item = data_->itemAtInTotal(index.row());

    // VerbFunctionHelpFunction *item = data_->itemAt(index.row());
    if (!item.first)
        return {};

    if (index.column() == 0) {
        if (role == Qt::DecorationRole) {
            if (IconClassHelp* ich = item.first->iconClass()) {
                return QIcon(MvQTheme::icIconFile(ich->pixmap()));
            }
        }
    }
    else if (index.column() == 1) {
        if (role == Qt::DisplayRole)
            return item.first->name();
        else if (role == Qt::ToolTipRole)
            return item.first->descriptionAsRt(item.second);
    }
    else if (index.column() == 2) {
        if (role == Qt::DisplayRole)
            return item.first->descriptionAsStr(item.second);
        else if (role == Qt::ToolTipRole)
            return item.first->descriptionAsRt(item.second);
        else if (role == Qt::ForegroundRole)
            return MvQTheme::subText();
    }

    return {};
}

QVariant FunctionListModel::headerData(const int section, const Qt::Orientation orient,
                                       const int role) const
{
    if (orient == Qt::Vertical)
        return {};

    if (role == Qt::DisplayRole) {
        if (section == 0)
            return tr("I");
        else if (section == 1)
            return tr("Function");
        else if (section == 2)
            return tr("Description");
    }
    else if (role == Qt::ToolTipRole) {
        if (section == 0)
            return tr("Icon representation of icon functions");
        else if (section == 1)
            return tr("Name of the built-in function");
        else if (section == 2)
            return tr("Description of the built-in function");
    }

    return {};
}

Qt::ItemFlags FunctionListModel::flags(const QModelIndex& index) const
{
    if (!index.isValid())
        return QAbstractItemModel::flags(index) | Qt::ItemIsDropEnabled;

    return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}

QModelIndex FunctionListModel::index(int row, int column, const QModelIndex&) const
{
    if (!hasData() || row < 0 || column < 0) {
        return {};
    }

    return createIndex(row, column, static_cast<void*>(nullptr));
}


QModelIndex FunctionListModel::parent(const QModelIndex&) const
{
    return {};
}

void FunctionListModel::reload(VerbFunctionHelp* data)
{
    beginResetModel();
    data_ = data;
    endResetModel();
}

void FunctionListModel::clear()
{
    beginResetModel();
    data_ = nullptr;
    endResetModel();
}

VerbFunctionHelpFunction* FunctionListModel::functionObj(const QModelIndex& idx)
{
    if (data_ && idx.isValid()) {
        return data_->itemAtInTotal(idx.row()).first;
    }
    return nullptr;
}

int FunctionListModel::functionDescriptionIndex(const QModelIndex& idx)
{
    if (data_ && idx.isValid()) {
        return data_->itemAtInTotal(idx.row()).second;
    }
    return -1;
}

QModelIndex FunctionListModel::functionToIndex(VerbFunctionHelpFunction* fn, int descIndex)
{
    if (data_) {
        int row = data_->totalIndexOf(fn, descIndex);
        if (row >= 0) {
            return createIndex(row, 0);
        }
    }
    return {};
}

QString FunctionListModel::scopePrefix() const
{
    return (data_) ? (data_->scopePrefix()) : QString();
}

//================================================
//
// FunctionListWidget
//
//================================================

FunctionListWidget::FunctionListWidget(QWidget* parent) :
    QWidget(parent),
    ui_(new Ui::FunctionListWidget)
{
    ui_->setupUi(this);

    MvQ::addShortCutToToolTip(this);
    MvQ::showShortcutInContextMenu(this);

    ui_->tree->setSortingEnabled(true);
    ui_->tree->setAlternatingRowColors(true);
    ui_->tree->setRootIsDecorated(false);
    ui_->tree->setItemDelegate(new FunctionListDelegate(this));

    model_ = new FunctionListModel(this);
    sortModel_ = new QSortFilterProxyModel(this);
    sortModel_->setDynamicSortFilter(true);
    sortModel_->setFilterKeyColumn(1);
    sortModel_->setSourceModel(model_);

    ui_->tree->setModel(sortModel_);
    ui_->filterLe->setPlaceholderText(tr("Filter"));
    ui_->iconOnlyTb->hide();

#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
    ui_->filterLe->setClearButtonEnabled(true);
#endif

    ui_->fontSizeUpTb->setDefaultAction(ui_->actionFontSizeUp);
    ui_->fontSizeDownTb->setDefaultAction(ui_->actionFontSizeDown);

    // context menu
    ui_->tree->addAction(ui_->actionInsert);
    ui_->tree->addAction(ui_->actionCopy);
    auto* sep = new QAction(this);
    sep->setSeparator(true);
    ui_->tree->addAction(sep);
    ui_->tree->addAction(ui_->actionIconParam);
    sep = new QAction(this);
    sep->setSeparator(true);
    ui_->tree->addAction(sep);
    ui_->tree->addAction(ui_->actionWeb);


    ui_->insertTb->setDefaultAction(ui_->actionInsert);
    ui_->copyTb->setDefaultAction(ui_->actionCopy);

    // make default action bold
    QFont f = ui_->actionIconParam->font();
    f.setBold(true);
    ui_->actionIconParam->setFont(f);
    ui_->iconParamTb->setDefaultAction(ui_->actionIconParam);

    ui_->webTb->setDefaultAction(ui_->actionWeb);

    connect(ui_->filterLe, SIGNAL(textChanged(QString)),
            this, SLOT(slotFilter(QString)));

    connect(ui_->actionFontSizeDown, SIGNAL(triggered()),
            this, SLOT(slotFontSizeDown()));

    connect(ui_->actionFontSizeUp, SIGNAL(triggered()),
            this, SLOT(slotFontSizeUp()));

    connect(ui_->tree->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)),
            this, SLOT(slotCurrentChanged(QModelIndex, QModelIndex)));

    connect(ui_->tree, SIGNAL(doubleClicked(QModelIndex)),
            this, SLOT(slotDoubleClicked(QModelIndex)));

    connect(ui_->actionInsert, SIGNAL(triggered()),
            this, SLOT(slotInsert()));

    connect(ui_->actionCopy, SIGNAL(triggered()),
            this, SLOT(slotCopy()));

    connect(ui_->actionWeb, SIGNAL(triggered()),
            this, SLOT(slotWebDoc()));

    connect(ui_->actionIconParam, SIGNAL(triggered()),
            this, SLOT(slotIconParam()));

    checkButtonState();
}

void FunctionListWidget::clear()
{
    model_->clear();
    checkButtonState();
}

void FunctionListWidget::reload(VerbFunctionHelp* data)
{
    model_->reload(data);
    sortModel_->sort(1, Qt::AscendingOrder);
    ui_->tree->resizeColumnToContents(0);
    QFont f;
    QFontMetrics fm(f);
    ui_->tree->setColumnWidth(1, MvQ::textWidth(fm, "fill_missing_values_ew "));
    checkButtonState();
}

void FunctionListWidget::slotCurrentChanged(const QModelIndex&, const QModelIndex&)
{
    checkButtonState();
}

void FunctionListWidget::showIconParams(const QModelIndex& idx)
{
    checkButtonState();
    if (idx.isValid()) {
        if (VerbFunctionHelpFunction* fn = model_->functionObj(sortModel_->mapToSource(idx)))
            emit helperRequested(fn,
                                 model_->functionDescriptionIndex(sortModel_->mapToSource(idx)));
    }
}

void FunctionListWidget::showWebDoc(const QModelIndex& idx)
{
    checkButtonState();
    if (idx.isValid()) {
        if (VerbFunctionHelpFunction* fn = model_->functionObj(sortModel_->mapToSource(idx)))
            emit webDocRequested(fn,
                                 model_->functionDescriptionIndex(sortModel_->mapToSource(idx)));
    }
}

void FunctionListWidget::slotDoubleClicked(const QModelIndex& idx)
{
    showIconParams(idx);
}

void FunctionListWidget::slotWebDoc()
{
    QModelIndex idx = ui_->tree->currentIndex();
    showWebDoc(idx);
}

void FunctionListWidget::slotIconParam()
{
    QModelIndex idx = ui_->tree->currentIndex();
    showIconParams(idx);
}

void FunctionListWidget::slotInsert()
{
    QModelIndex idx = sortModel_->mapToSource(ui_->tree->currentIndex());
    if (VerbFunctionHelpFunction* fn = model_->functionObj(idx)) {
        QString v = fn->name();
        if (!v.isEmpty())
            emit insertRequested(model_->scopePrefix() + v + "()");
    }
}

void FunctionListWidget::slotFilter(QString txt)
{
    sortModel_->setFilterFixedString(txt);
}

void FunctionListWidget::slotCopy()
{
    QModelIndex idx = sortModel_->mapToSource(ui_->tree->currentIndex());
    if (VerbFunctionHelpFunction* fn = model_->functionObj(idx)) {
        QString v = fn->name();
        if (!v.isEmpty())
            MvQ::toClipboard(model_->scopePrefix() + v + "()");
    }
}

void FunctionListWidget::slotFontSizeDown()
{
    MvQ::changeFontSize(ui_->tree, -1);
}

void FunctionListWidget::slotFontSizeUp()
{
    MvQ::changeFontSize(ui_->tree, 1);
}

void FunctionListWidget::selectFunction(VerbFunctionHelpFunction* fn, int descIndex)
{
    QModelIndex idx = model_->functionToIndex(fn, descIndex);
    if (idx.isValid()) {
        ui_->tree->setCurrentIndex(sortModel_->mapFromSource(idx));
    }
}

void FunctionListWidget::checkButtonState()
{
    bool st = ui_->tree->currentIndex().isValid();
    ui_->actionInsert->setEnabled(st);
    ui_->actionCopy->setEnabled(st);
    ui_->actionWeb->setEnabled(st);
    ui_->actionIconParam->setEnabled(st);

#if 0
    st = false;
    if (VerbFunctionHelpFunction *fn = model_->functionObj(sortModel_->mapToSource( ui_->tree->currentIndex()))) {
        st = (fn->iconClass() != nullptr);
    }
    ui_->actionIconParam->setEnabled(st);
#endif
}

void FunctionListWidget::readSettins(QSettings& settings)
{
    settings.beginGroup("FunctionListWidget");
    MvQ::initTreeColumnWidth(settings, "treeWidth", ui_->tree);
    MvQ::setFontSize(ui_->tree, settings.value("fontSize").toInt());
    settings.endGroup();
}

void FunctionListWidget::writeSettings(QSettings& settings)
{
    settings.beginGroup("FunctionListWidget");
    MvQ::saveTreeColumnWidth(settings, "treeWidth", ui_->tree);
    settings.setValue("fontSize", ui_->tree->font().pointSize());
    settings.endGroup();
}
