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

 Copyright 2015 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 "MvQNewIconWidget.h"

#include <QApplication>
#include <QButtonGroup>
#include <QComboBox>
#include <QDebug>
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QMouseEvent>
#include <QPainter>
#include <QPushButton>
#include <QSettings>
#include <QStackedWidget>
#include <QStyle>
#include <QStyleOption>
#include <QStyleOptionViewItem>
#include <QToolButton>
#include <QVBoxLayout>
#include <QDrag>

#include "MvQIconMimeData.h"
#include "MvQIconProvider.h"
#include "MvQRecentIcons.h"
#include "MvQMethods.h"

#include "Path.h"


//==============================
//
// MvQNewIconListWidget
//
//==============================

MvQNewIconListWidget::MvQNewIconListWidget(QWidget* parent) :
    QListWidget(parent)
{
    setDragEnabled(true);
    setSpacing(1);
    setContextMenuPolicy(Qt::CustomContextMenu);
}

//--------------------------------
// Drag
//--------------------------------

void MvQNewIconListWidget::mousePressEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton) {
        startPos_ = event->pos();

        /*QListWidgetItem *item=itemAt(event->pos());
        if(item)
        {
            startPos_=event->pos();
        }*/
    }

    QListView::mousePressEvent(event);
}

void MvQNewIconListWidget::mouseMoveEvent(QMouseEvent* event)
{
    if (event->buttons() & Qt::LeftButton) {
        int distance = (event->pos() - startPos_).manhattanLength();
        if (distance >= QApplication::startDragDistance()) {
            QListWidgetItem* item = itemAt(startPos_);
            if (item) {
                QString cname = item->data(Qt::UserRole).toString();
                auto* mimeData = new MvQNewIconMimeData(cname, MvQNewIconMimeData::UserDef);

                auto* drag = new QDrag(this);
                drag->setPixmap(MvQIconProvider::pixmap(IconClass::find(cname.toStdString()), 20));
                drag->setHotSpot(QPoint(20, 20));
                drag->setMimeData(mimeData);

                if (drag->exec(Qt::CopyAction) == Qt::CopyAction) {
                    emit iconCreated(cname);
                }
                return;
            }
        }
    }

    QListView::mouseMoveEvent(event);
}

//=====================================================
//
// MvQNewIconPanel
//
//=====================================================

MvQNewIconPanel::MvQNewIconPanel(SelectionMode mode, QWidget* parent) :
    QWidget(parent),
    selectionMode_(mode)
{
    auto* vb = new QVBoxLayout;
    vb->setSpacing(2);
    vb->setContentsMargins(2, 2, 2, 2);
    setLayout(vb);

    QLabel* label;

    // In panel mode we need a close button
    if (selectionMode_ == PanelMode) {
        // Top row
        auto* hb = new QHBoxLayout;
        hb->setSpacing(0);
        hb->setContentsMargins(0, 0, 0, 0);

        vb->addLayout(hb);

        hb->addSpacing(4);
        label = new QLabel("<b>" + tr("Create new icon") + "</b>", this);
        hb->addWidget(label, 1);

        auto* closeTb = new QToolButton(this);
        closeTb->setAutoRaise(true);
        closeTb->setIcon(QPixmap(":/desktop/remove.svg"));
        closeTb->setToolTip(tr("Close sidebar"));
        hb->addWidget(closeTb);

        connect(closeTb, SIGNAL(clicked(bool)),
                this, SIGNAL(closePanel(bool)));
    }

    // Button row
    auto* hbButton = new QHBoxLayout;
    vb->addLayout(hbButton);
    hbButton->setSpacing(0);

    QSize icSize(16, 16);

    auto* recentTb = new QToolButton(this);
    recentTb->setIconSize(icSize);
    recentTb->setToolTip(tr("Most recent"));
    recentTb->setCheckable(true);
    recentTb->setChecked(false);
    recentTb->setText(tr("Recent"));
    recentTb->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    recentTb->setFocusPolicy(Qt::NoFocus);
    hbButton->addWidget(recentTb);

    auto* groupTb = new QToolButton(this);
    groupTb->setIconSize(icSize);
    groupTb->setToolTip(tr("Types"));
    groupTb->setCheckable(true);
    groupTb->setChecked(false);
    groupTb->setText(tr("Types"));
    groupTb->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    groupTb->setFocusPolicy(Qt::NoFocus);
    hbButton->addWidget(groupTb);

    auto* searchTb = new QToolButton(this);
    searchTb->setIconSize(icSize);
    searchTb->setToolTip(tr("Filter"));
    searchTb->setCheckable(true);
    searchTb->setChecked(false);
    searchTb->setText(tr("Filter"));
    searchTb->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    searchTb->setFocusPolicy(Qt::NoFocus);
    hbButton->addWidget(searchTb);

    hbButton->addStretch(1);

    vb->addSpacing(3);

    // Button group for mode selection
    modeBg_ = new QButtonGroup(this);
    modeBg_->setExclusive(true);

    modeBg_->addButton(recentTb, 0);
    modeBg_->addButton(groupTb, 1);
    modeBg_->addButton(searchTb, 2);

    connect(modeBg_, SIGNAL(buttonClicked(QAbstractButton*)),
            this, SLOT(slotChangeMode(QAbstractButton*)));

    //----------------------------------------------------
    // Stacked widget for all the icon selection widgets
    //----------------------------------------------------

    mainPanel_ = new QStackedWidget(this);
    vb->addWidget(mainPanel_);

    //---------------------------
    // Recently used
    //---------------------------

    recentList_ = new MvQNewIconListWidget(this);
    mainPanel_->addWidget(recentList_);

    connect(recentList_, SIGNAL(itemActivated(QListWidgetItem*)),
            this, SLOT(slotIconSelected(QListWidgetItem*)));

    connect(recentList_, SIGNAL(iconCreated(QString)),
            this, SLOT(slotAddToRecentIcons(QString)));

    //---------------------------
    // Type widget
    //---------------------------

    auto* typeW = new QWidget(this);
    auto* typeVb = new QVBoxLayout(typeW);
    // typeVb->setSpacing(0);
    typeVb->setContentsMargins(1, 1, 1, 1);

    // Type
    auto* typeHb = new QHBoxLayout;
    auto* typeLabel = new QLabel(tr("Type: "));
    typeHb->addWidget(typeLabel);
    typeCb_ = new QComboBox(this);
    typeHb->addWidget(typeCb_, 1);
    typeVb->addLayout(typeHb);

    // Icon
    iconPanel_ = new QStackedWidget(this);
    typeVb->addWidget(iconPanel_);

    // Scan for icons that can be created
    IconClass::scan(*this);

    foreach (QString key, iconLists_.keys()) {
        typeCb_->addItem(key, key);
        iconPanel_->addWidget(iconLists_[key]);
        iconLists_[key]->sortItems();

        connect(iconLists_[key], SIGNAL(iconCreated(QString)),
                this, SLOT(slotAddToRecentIcons(QString)));

        // This is just a selection,. It will not trigger the itemactivated signal.
        if (iconLists_[key]->count() > 0)
            iconLists_[key]->setCurrentRow(0);
    }

    mainPanel_->addWidget(typeW);

    connect(typeCb_, SIGNAL(currentIndexChanged(int)),
            this, SLOT(slotTypeChanged(int)));

    //------------------
    // Search widget
    //------------------

    auto* searchW = new QWidget(this);
    auto* searchVb = new QVBoxLayout(searchW);
    searchVb->setSpacing(0);
    searchVb->setContentsMargins(1, 1, 1, 1);

    auto* searchHb = new QHBoxLayout;
    label = new QLabel(tr("Filter:"));
    searchLine_ = new QLineEdit(searchW);
    searchHb->addWidget(label);
    searchHb->addSpacing(2);
    searchHb->addWidget(searchLine_, 1);
    searchVb->addLayout(searchHb);
    searchVb->addSpacing(4);

    searchList_ = new MvQNewIconListWidget(searchW);
    searchVb->addWidget(searchList_);

    connect(searchList_, SIGNAL(itemActivated(QListWidgetItem*)),
            this, SLOT(slotIconSelected(QListWidgetItem*)));

    connect(searchList_, SIGNAL(iconCreated(QString)),
            this, SLOT(slotAddToRecentIcons(QString)));


    mainPanel_->addWidget(searchW);

    connect(searchLine_, SIGNAL(textChanged(QString)),
            this, SLOT(slotSearch(QString)));

    //----------------
    // Init
    //----------------

    slotSearch("");
    updateRecentList();

    modeBg_->button(2)->setChecked(true);
    mainPanel_->setCurrentIndex(2);

    MvQRecentIcons::addObserver(this);
}

MvQNewIconPanel::~MvQNewIconPanel()
{
    MvQRecentIcons::removeObserver(this);
}

void MvQNewIconPanel::next(const IconClass& c)
{
    if (c.canBeCreated()) {
        QString type = QString::fromStdString(c.iconBox());
        if (type.isEmpty())
            type = "Other";

        MvQNewIconListWidget* w;
        QMap<QString, MvQNewIconListWidget*>::iterator it = iconLists_.find(type);
        if (it == iconLists_.end()) {
            w = new MvQNewIconListWidget(this);
            connect(w, SIGNAL(itemActivated(QListWidgetItem*)),
                    this, SLOT(slotIconSelected(QListWidgetItem*)));

            iconLists_[type] = w;
        }
        else {
            w = it.value();
        }

        auto* typeItem = new QListWidgetItem(MvQIconProvider::pixmap(c, 20),
                                             QString::fromStdString(c.defaultName()));

        typeItem->setData(Qt::UserRole, QString::fromStdString(c.name()));

        w->addItem(typeItem);

        iconClasses_.push_back(&c);
    }
}

void MvQNewIconPanel::slotTypeChanged(int index)
{
    QString type = typeCb_->itemData(index).toString();
    QMap<QString, MvQNewIconListWidget*>::iterator it = iconLists_.find(type);
    if (it != iconLists_.end()) {
        iconPanel_->setCurrentWidget(it.value());
    }
}

void MvQNewIconPanel::slotIconSelected(QListWidgetItem* item)
{
    if (selectionMode_ == DialogMode) {
        QString name = item->data(Qt::UserRole).toString();
        emit iconSelected(name);
        slotAddToRecentIcons(name);
    }
}

void MvQNewIconPanel::slotSearch(QString str)
{
    std::vector<const IconClass*> res;

    if (str.simplified().isEmpty()) {
        if (searchList_->count() != static_cast<int>(iconClasses_.size())) {
            res = iconClasses_;
            searchList_->clear();
        }
        else
            return;
    }
    else {
        IconClass::find(str.toStdString(), res);
        searchList_->clear();
    }

    for (auto& re : res) {
        auto* item = new QListWidgetItem(MvQIconProvider::pixmap(*re, 32),
                                         QString::fromStdString(re->defaultName()));

        item->setData(Qt::UserRole, QString::fromStdString(re->name()));
        searchList_->addItem(item);
    }

    searchList_->sortItems();

    // This is just a selection,. It will not trigger the itemactivated signal.
    if (searchList_->count() > 0)
        searchList_->setCurrentRow(0);
}

int MvQNewIconPanel::currentMode() const
{
    return mainPanel_->currentIndex();
}

void MvQNewIconPanel::setCurrentMode(int index)
{
    if (index >= 0 && index < modeBg_->buttons().count()) {
        modeBg_->button(index)->setChecked(true);
        slotChangeMode(modeBg_->checkedButton());
        // mainPanel_->setCurrentIndex(index);
    }
}

void MvQNewIconPanel::slotChangeMode(QAbstractButton*)
{
    if (modeBg_->checkedId() != -1) {
        mainPanel_->setCurrentIndex(modeBg_->checkedId());
    }
}

void MvQNewIconPanel::slotAddToRecentIcons(QString name)
{
    MvQRecentIcons::add(name);
}

void MvQNewIconPanel::updateRecentList()
{
    recentList_->clear();
    QStringList lst = MvQRecentIcons::items();

    for (int i = lst.count() - 1; i >= 0; i--) {
        const IconClass& kind = IconClass::find(lst[i].toStdString());

        auto* item = new QListWidgetItem(MvQIconProvider::pixmap(kind, 32),
                                         QString::fromStdString(kind.defaultName()));

        item->setData(Qt::UserRole, QString::fromStdString(kind.name()));
        recentList_->addItem(item);
    }

    // This is just a selection,. It will not trigger the itemactivated signal.
    if (recentList_->count() > 0)
        recentList_->setCurrentRow(0);
}

void MvQNewIconPanel::notifyRecentChanged()
{
    updateRecentList();
}

void MvQNewIconPanel::paintEvent(QPaintEvent*)
{
    QStyleOption opt;
    opt.initFrom(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}

// Returns the active mode's current selection
QString MvQNewIconPanel::currentSelection() const
{
    QString s;

    switch (mainPanel_->currentIndex()) {
        case 0:
            if (QListWidgetItem* item = searchList_->currentItem()) {
                s = item->data(Qt::UserRole).toString();
            }
            break;
        case 1: {
            QString type = typeCb_->currentText();
            QMap<QString, MvQNewIconListWidget*>::const_iterator it = iconLists_.find(type);
            if (it != iconLists_.end()) {
                if (QListWidgetItem* item = it.value()->currentItem()) {
                    s = item->data(Qt::UserRole).toString();
                }
            }
        } break;
        case 2:
            if (QListWidgetItem* item = searchList_->currentItem()) {
                s = item->data(Qt::UserRole).toString();
            }
            break;
        default:
            break;
    }

    return s;
}

void MvQNewIconPanel::keyPressEvent(QKeyEvent* event)
{
    // The search list gets the focus when arrow down or up is pressed
    if (event->key() == Qt::Key_Down || event->key() == Qt::Key_Up) {
        if (mainPanel_->currentIndex() == 2) {
            if (!searchList_->hasFocus()) {
                searchList_->setFocus();
            }
        }
    }

    QWidget::keyPressEvent(event);
}

void MvQNewIconPanel::writeSettings(QSettings& settings)
{
    settings.setValue("currentMode", currentMode());
    settings.setValue("currentTypeGroup", typeCb_->currentText());
}

void MvQNewIconPanel::readSettings(QSettings& settings)
{
    if (settings.contains("currentMode")) {
        setCurrentMode(settings.value("currentMode").toInt());
    }

    MvQ::initComboBox(settings, "currentTypeGroup", typeCb_);
}

//==============================
//
// MvQNewIconDialog
//
//==============================

MvQNewIconDialog::MvQNewIconDialog(QWidget* parent) :
    QDialog(parent)
{
    setWindowTitle(tr("Create new icon"));

    auto* vb = new QVBoxLayout;
    vb->setSpacing(0);
    vb->setContentsMargins(1, 1, 1, 1);
    setLayout(vb);

    iconWidget_ = new MvQNewIconPanel(MvQNewIconPanel::DialogMode, this);
    vb->addWidget(iconWidget_, 1);

    auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok |
                                           QDialogButtonBox::Close);

    connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));

    vb->addWidget(buttonBox);

    connect(iconWidget_, SIGNAL(iconSelected(QString)),
            this, SLOT(slotIconSelected(QString)));

    // Read settings
    readSettings();
}

MvQNewIconDialog::~MvQNewIconDialog()
{
    writeSettings();
}

// It is called when return is pressed on the
// dialog. We pick the current selection in the active selection panel.
void MvQNewIconDialog::accept()
{
    selected_ = iconWidget_->currentSelection();
    if (!selected_.isEmpty())
        QDialog::accept();
    else
        QDialog::reject();
}

// When an item is clicked or double-clicked in one of the icon lists
void MvQNewIconDialog::slotIconSelected(QString name)
{
    selected_ = name;
    QDialog::accept();
}

const IconClass& MvQNewIconDialog::selected()
{
    return IconClass::find(selected_.toStdString());
}

void MvQNewIconDialog::writeSettings()
{
    QSettings settings(MVQ_QSETTINGS_DEFAULT_ARGS, "MV4-Desktop-NewIconDialog");

    // We have to clear it not to remember all the previous windows
    settings.clear();

    settings.beginGroup("main");
    settings.setValue("size", size());
    iconWidget_->writeSettings(settings);
    settings.endGroup();
}

void MvQNewIconDialog::readSettings()
{
    QSettings settings(MVQ_QSETTINGS_DEFAULT_ARGS, "MV4-Desktop-NewIconDialog");

    settings.beginGroup("main");
    if (settings.contains("size")) {
        resize(settings.value("size").toSize());
    }
    else {
        resize(QSize(350, 500));
    }

    iconWidget_->readSettings(settings);
    settings.endGroup();
}
