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

 Copyright 2012 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 "MvQFeatureWidget.h"

#include <string>
#include <vector>

#include <QDebug>
#include <QApplication>
#include <QButtonGroup>
#include <QComboBox>
#include <QFontMetrics>
#include <QHBoxLayout>
#include <QLabel>
#include <QListWidget>
#include <QListWidgetItem>
#include <QFontMetrics>
#include <QFontMetrics>
#include <QScrollBar>
#include <QStackedWidget>
#include <QTabWidget>
#include <QTimer>
#include <QToolButton>
#include <QTreeView>
#include <QVBoxLayout>

#include "WsType.h"
#include "MvQFeatureCommand.h"
#include "MvQFeatureContextMenu.h"
#include "FeatureStore.h"
#include "MvQMethods.h"
#include "MvQPanel.h"
#include "MvMiscellaneous.h"
#include "WmoWeatherSymbol.h"
#include "MvLog.h"
#include "MvQStreamOper.h"

//==================================================================
//
// MvQFeaterManagerProxyModel
//
//==================================================================

MvQFeatureListProxyModel::MvQFeatureListProxyModel(QString group, QObject* parent) :
    QSortFilterProxyModel(parent),
    group_(group)
{
}

bool MvQFeatureListProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
{
    auto idx = sourceModel()->index(source_row, 0, source_parent);
    if (!subGroup_.isEmpty()) {
        auto sgr = sourceModel()->data(idx, MvQFeatureListModel::SubGroupRole).toString();
        if (sgr != subGroup_) {
            return false;
        }
    }

    auto gr = sourceModel()->data(idx, Qt::UserRole).toString();
    return gr == group_;
}

void MvQFeatureListProxyModel::setSubGroup(QString subGroup)
{
    subGroup_ = subGroup;
    invalidate();
}

//==================================================================
//
// MvQFeatureManagerModel
//
//==================================================================

MvQFeatureListModel::MvQFeatureListModel(QString group, QObject* parent) :
    QAbstractItemModel(parent),
    group_(group)
{
}

int MvQFeatureListModel::columnCount(const QModelIndex& parent) const
{
    return parent.isValid() ? 0 : 1;
}

int MvQFeatureListModel::rowCount(const QModelIndex& index) const
{
    if (!index.isValid())
        return static_cast<int>(WsType::items().size());
    else
        return 0;
}

QVariant MvQFeatureListModel::data(const QModelIndex& index, int role) const
{
    if (index.row() < 0 || index.row() >= static_cast<int>(WsType::items().size()))
        return {};

    WsType* item = indexToItem(index);
    Q_ASSERT(item);

    switch (role) {
        case Qt::DecorationRole:
            return item->defaultPixmap();
            break;
            //      case Qt::DisplayRole:
            //         return item->label();
        case Qt::ToolTipRole:
            return "<span>" + item->description() + "</span>";
        case Qt::UserRole:
            return item->group();
        case SubGroupRole:
            return item->subGroup();
        default:
            break;
    }

    return {};
}

QModelIndex MvQFeatureListModel::index(int row, int column, const QModelIndex& /*parent */) const
{
    return createIndex(row, column);
}

QModelIndex MvQFeatureListModel::parent(const QModelIndex& /*index*/) const
{
    return QModelIndex();
}

WsType* MvQFeatureListModel::indexToItem(const QModelIndex& index) const
{
    if (!index.isValid() || index.column() < 0 || index.column() > static_cast<int>(WsType::items().size()))
        return nullptr;

    return WsType::items()[index.row()];
}

//=================================================================
//
// MvQFeatureListView
//
//=================================================================

MvQFeatureListView::MvQFeatureListView(MvQFeatureListModel* model, MvQFeatureListProxyModel* proxyModel, bool autoAdjustHeight, QWidget* parent) :
    QListView(parent),
    model_(model),
    proxyModel_(proxyModel),
    autoAdjustHeight_(autoAdjustHeight)
{
    // Set behaviour
    setViewMode(QListView::IconMode);
    setFlow(QListView::LeftToRight);
    setMovement(QListView::Snap);
    setWrapping(true);
    setResizeMode(QListView::Adjust);
    setSelectionMode(QAbstractItemView::SingleSelection);
    setProperty("feature", "1");
    setSpacing(3);
    setMouseTracking(true);
    setAcceptDrops(false);
    setModel(proxyModel_);

    connect(this, SIGNAL(clicked(QModelIndex)),
            this, SLOT(slotClicked(QModelIndex)));
}

void MvQFeatureListView::slotClicked(const QModelIndex& current)
{
    auto item = model_->indexToItem(proxyModel_->mapToSource(current));
    if (item) {
        MvQFeatureHandler::Instance()->registerToAddAtClick(item);
    }
}

void MvQFeatureListView::resizeEvent(QResizeEvent* e)
{
//    MvLog().dbg() << MV_FN_INFO << "group=" << model()->index(0,0).data(Qt::UserRole).toString() << " cnt=" << model()->rowCount();
    if (autoAdjustHeight_ && model()->rowCount() >= 1) {
        QTimer::singleShot(0, this, SLOT(delayedAdjustHeight()));
    }
    QListView::resizeEvent(e);
}

void MvQFeatureListView::delayedAdjustHeight()
{
    if (autoAdjustHeight_ && model()->rowCount() >= 1) {
//        MvLog().dbg() << MV_FN_INFO << "group=" << model()->index(0,0).data(Qt::UserRole).toString();
        int maxY = 0;
        for (int i = 0; i < model()->rowCount(); i++) {
            auto vr = visualRect(model()->index(i, 0));
            if (vr.y() + vr.height() > maxY) {
                maxY = vr.y() + vr.height();
            }
        }
        auto r     = visualRect(model()->index(0, 0));
        auto itemH = r.height();
        int space = 4;
        if (model()->rowCount() > 1) {
            space = visualRect(model()->index(1, 0)).x() - (r.x() + r.width());
        }

        auto h   = height();
        auto scH = 0;
        if (horizontalScrollBar()) {
            if (horizontalScrollBar()->isVisible()) {
                scH = horizontalScrollBar()->height();
            }
        }

        h -= scH;

        if (h - maxY > space + itemH / 2 || maxY - h > itemH * 2 / 3) {
            h = maxY + space + scH;
            setFixedHeight(h);
        }
    }
}

//=================================================================
//
// MvQFeatureWidget
//
//=================================================================

MvQFeatureWidget::MvQFeatureWidget(QWidget* parent) :
    QWidget(parent)
{
    auto vb = new QVBoxLayout(this);
    vb->setContentsMargins(0, 0, 0, 0);

    auto controlLayout = new QHBoxLayout;
    controlLayout->setContentsMargins(2, 2, 0, 2);
    vb->addLayout(controlLayout);

    auto typeTb = new QToolButton(this);
    typeTb->setCheckable(true);
    typeTb->setText(tr("Types"));

    auto userTb = new QToolButton(this);
    userTb->setCheckable(true);
    userTb->setText(tr("User library"));

    controlTbGroup_ = new QButtonGroup(this);
    controlTbGroup_->addButton(typeTb, 0);
    controlTbGroup_->addButton(userTb, 1);

    controlLayout->addWidget(typeTb);
    controlLayout->addWidget(userTb);
    controlLayout->addStretch(1);

    // Stacked
    stackedW_ = new QStackedWidget(this);
    vb->addWidget(stackedW_);

    // Types
    auto w = new QWidget(this);
    typeLayout_ = new QVBoxLayout(w);
    typeLayout_->setContentsMargins(2,2,2,2);
    typeLayout_->setAlignment(Qt::AlignTop);
    stackedW_->addWidget(w);

    // User lib
    storeW_ = new FeatureStoreWidget(this);
    stackedW_->addWidget(storeW_);

    //TODO: do init in the right place
    WsType::init(WsType::PlotWindowMode);
    MvQFeatureMenuItem::init();
    FeatureStore::Instance()->load();

    for (auto gr : WsGroup::items()) {
        buildGroup(gr->name().toStdString(), gr->label().toStdString());
    }

    connect(controlTbGroup_, SIGNAL(buttonClicked(QAbstractButton*)),
            this, SLOT(slotControlTbClicked(QAbstractButton*)));

    // select type widget!
    typeTb->click();
}

void MvQFeatureWidget::buildGroup(const std::string& grId, const std::string& grLabel)
{
    auto labelPanel = MvQ::makeLabelPanel(QString::fromStdString(grLabel), this, -1);
    typeLayout_->addWidget(labelPanel);

    if (grId != "wmo") {
        auto model      = new MvQFeatureListModel(QString::fromStdString(grId), this);
        auto proxyModel = new MvQFeatureListProxyModel(QString::fromStdString(grId), this);
        proxyModel->setSourceModel(model);
        proxyModel->setDynamicSortFilter(true);
        auto view = new MvQFeatureListView(model, proxyModel, true, this);
        //        view->adjustHeight((grId=="surf" || grId=="geo")?2:1);
        typeLayout_->addWidget(view);
        if (proxyModel->rowCount() == 0) {
            labelPanel->hide();
            view->hide();
        }
    }
    else {
        Q_ASSERT(wmoW_ == nullptr);
        wmoW_ = new MvQWmoFeatureWidget("wmo", this);
        typeLayout_->addWidget(wmoW_);
    }
}

void MvQFeatureWidget::slotControlTbClicked(QAbstractButton*)
{
    if (controlTbGroup_->checkedId() != -1) {
        stackedW_->setCurrentIndex(controlTbGroup_->checkedId());
    }
}

void MvQFeatureWidget::writeSettings(QSettings& settings)
{
    settings.beginGroup("MvQFeatureWidget");

    if (wmoW_) {
        wmoW_->writeSettings(settings);
    }
    storeW_->writeSettings(settings);

    settings.endGroup();
}

void MvQFeatureWidget::readSettings(QSettings& settings)
{
    settings.beginGroup("MvQFeatureWidget");

    if (wmoW_) {
        wmoW_->readSettings(settings);
    }
    storeW_->readSettings(settings);
    settings.endGroup();
}

//=================================================================
//
// MvQWmoFeatureWidget
//
//=================================================================

MvQWmoFeatureWidget::MvQWmoFeatureWidget(QString groupId, QWidget* parent) :
    QWidget(parent)
{
    auto layout = new QVBoxLayout;
    layout->setContentsMargins(2, 2, 2, 2);
    layout->setAlignment(Qt::AlignTop);
    setLayout(layout);

    selectorW_ = new QComboBox(this);
    layout->addWidget(selectorW_);
    QFont f;
    f.setPointSize(f.pointSize() - 1);
    selectorW_->setFont(f);
    for (const auto& gr : WmoWeatherSymbol::groups()) {
        auto desc = QString::fromStdString(gr->desc_);
        auto name = QString::fromStdString(gr->name_);
        selectorW_->addItem(desc, name);
    }
    if (selectorW_->model()) {
        selectorW_->model()->sort(0, Qt::AscendingOrder);
    }
    connect(selectorW_, SIGNAL(currentIndexChanged(int)),
            this, SLOT(subGroupSelected(int)));


    auto model  = new MvQFeatureListModel(groupId, this);
    proxyModel_ = new MvQFeatureListProxyModel(groupId, this);
    proxyModel_->setSourceModel(model);
    proxyModel_->setDynamicSortFilter(true);
    view_ = new MvQFeatureListView(model, proxyModel_, false, this);
    layout->addWidget(view_);

    bool hasww = false;
    for (int i = 0; i < selectorW_->count(); i++) {
        if (selectorW_->itemData(i).toString() == "ww") {
            selectorW_->setCurrentIndex(i);
            hasww = true;
            break;
        }
    }
    if (!hasww) {
        selectorW_->setCurrentIndex(0);
    }
}

void MvQWmoFeatureWidget::subGroupSelected(int idx)
{
    if (idx >= 0) {
        proxyModel_->setSubGroup(selectorW_->itemData(idx).toString());
    }
}

void MvQWmoFeatureWidget::writeSettings(QSettings& settings)
{
    if (selectorW_->currentIndex() != -1)
        settings.setValue("wmoSelector", selectorW_->itemData(selectorW_->currentIndex()).toString());
}

void MvQWmoFeatureWidget::readSettings(QSettings& settings)
{
    MvQ::initComboBoxByData(settings, "wmoSelector", selectorW_);
}
