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

 Copyright 2013 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 "MvQIconHolder.h"

#include <QDebug>
#include <QDropEvent>
#include <QLabel>
#include <QShortcut>
#include <QVBoxLayout>

#include "MvQContextMenu.h"
#include "MvQFolderModel.h"
#include "MvQIconMimeData.h"
#include "MvQIconProvider.h"

#include "Command.h"
#include "IconClass.h"
#include "IconFactory.h"
#include "IconObject.h"
#include "MvIconParameter.h"
#include "RequestPanelItem.h"

//#define DEBUG_MV_MVQICONHOLDER__

//====================================
//
//	MvQIconHolderModel
//
//====================================

MvQIconHolderModel::MvQIconHolderModel(const std::vector<std::string>& classes, QObject* parent) :
    QAbstractItemModel(parent),
    classes_(classes)
{
}

void MvQIconHolderModel::setIcons(const std::vector<IconObjectH>& objects)
{
    beginResetModel();

    // Cleaning
    foreach (IconObjectH actObj, icons_) {
        if (actObj && actObj->isEmbedded() &&
            std::find(objects.begin(), objects.end(), actObj) == objects.end()) {
            actObj->destroy();
        }
    }

    icons_.clear();

    for (const auto& object : objects) {
        icons_ << object;
    }

    endResetModel();
}

void MvQIconHolderModel::clearIcons(const std::vector<IconObjectH>& objects)
{
    beginResetModel();

    // Cleaning
    foreach (IconObjectH actObj, icons_) {
        if (actObj && actObj->isEmbedded() &&
            (std::find(objects.begin(), objects.end(), actObj) == objects.end())) {
            actObj->destroy();
        }
    }

    icons_.clear();

    endResetModel();
}

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


int MvQIconHolderModel::rowCount(const QModelIndex& index) const
{
    if (!index.isValid()) {
        return icons_.count();
    }
    else {
        return 0;
    }
}

QVariant MvQIconHolderModel::data(const QModelIndex& index, int role) const
{
    if (role != Qt::DisplayRole && role != Qt::DecorationRole &&
        role != Qt::ForegroundRole && role != Qt::UserRole && role != Qt::FontRole) {
        return {};
    }

    if (!index.isValid())
        return {};

    IconObject* obj = nullptr;
    if (index.row() >= 0 && index.row() < icons_.count())
        obj = icons_[index.row()];

    if (!obj)
        return {};

    switch (role) {
        case Qt::DecorationRole: {
            return MvQIconProvider::pixmap(obj, 32, true);
        }
        case Qt::DisplayRole:
            return QString::fromStdString(obj->name());
        default:
            break;
    }

    return {};
}

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


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

IconObject* MvQIconHolderModel::iconObject(const QModelIndex& index)
{
    IconObject* obj = nullptr;
    if (index.row() >= 0 && index.row() < icons_.count())
        obj = icons_[index.row()];

    return obj;
}

void MvQIconHolderModel::add(IconObjectH obj)
{
    if (icons_.contains(obj))
        return;

    beginResetModel();
    icons_ << obj;
    endResetModel();
}

void MvQIconHolderModel::remove(IconObjectH obj)
{
    if (!obj)
        return;
    beginResetModel();
    icons_.removeAll(obj);
    if (obj->isEmbedded())
        obj->toWastebasket();
    endResetModel();
}

bool MvQIconHolderModel::accept(IconObjectH obj)
{
    const IconClass& ic = obj->iconClass();
    for (auto& classe : classes_) {
        if (ic.canBecome(IconClass::find(classe)))
            return true;
    }
    return classes_.size() == 0;
}

void MvQIconHolderModel::iconObjects(std::vector<IconObjectH>& vals)
{
    for (int i = 0; i < icons_.count(); i++)
        vals.push_back(icons_[i]);
}

//====================================
//
//	MvQIconHolder
//
//====================================

MvQIconHolderView::MvQIconHolderView(MvQIconHolderModel* model, RequestPanelItem* owner, QWidget* parent) :
    QListView(parent),
    model_(model),
    owner_(owner)
{
    setViewMode(QListView::IconMode);
    setFlow(QListView::LeftToRight);
    setMovement(QListView::Snap);
    setWrapping(false);
    setResizeMode(QListView::Adjust);
    setSpacing(5);

    QFont font;
    QFontMetrics fm(font);
    setMaximumHeight(fm.size(Qt::TextExpandTabs, "A").height() + 32 + 10 + 5 + 10);

    // Double click
    connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
            this, SLOT(slotDoubleClickItem(const QModelIndex)));

    setContextMenuPolicy(Qt::CustomContextMenu);

    // Context menu
    connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
            this, SLOT(slotContextMenu(const QPoint&)));

    setMouseTracking(true);
    setAcceptDrops(true);
    setDragEnabled(true);
    setDropIndicatorShown(true);
    setDragDropMode(QAbstractItemView::DragDrop);
    // setDragDropMode(QAbstractItemView::DropOnly);

    setModel(model);

    setupShortCut();
}

MvQIconHolderView::~MvQIconHolderView()
{
#ifdef DEBUG_MV_MVQICONHOLDER__
    qDebug() << "deleted ---> MvQIconHolderView";
#endif
}

void MvQIconHolderView::slotDoubleClickItem(const QModelIndex& index)
{
    if (index.isValid()) {
        if (IconObjectH obj = model_->iconObject(index)) {
            QString method = QString::fromStdString(obj->iconClass().doubleClickMethod()).toLower();
            if (obj->isFolder()) {
                return;
            }
            else if (!method.isEmpty()) {
                obj->command(method.toStdString());
            }
            else {
                obj->doubleClick();
            }
        }
    }
}

MvQContextItemSet* MvQIconHolderView::cms()
{
    static MvQContextItemSet cmItems("IconHolderView");
    return &cmItems;
}

void MvQIconHolderView::slotContextMenu(const QPoint& pos)
{
    if (!cms())
        return;

    QModelIndex index = indexAt(pos);
    if (index.isValid()) {
        IconObjectH obj = model_->iconObject(index);
        QString selected = MvQContextMenu::instance()->exec(cms()->icon(), obj, mapToGlobal(pos), this);
        if (!selected.isEmpty()) {
            command(selected, obj);
        }
    }
}

void MvQIconHolderView::setupShortCut()
{
    if (!cms())
        return;

    foreach (MvQContextItem* cm, cms()->icon()) {
        if (QShortcut* sc = cm->makeShortCut(this)) {
            connect(sc, SIGNAL(activated()),
                    this, SLOT(slotIconShortCut()));
        }
    }
}

void MvQIconHolderView::slotIconShortCut()
{
    if (auto* sc = dynamic_cast<QShortcut*>(QObject::sender())) {
        QModelIndexList lst = selectedIndexes();
        if (lst.count() > 0) {
            IconObjectH obj = model_->iconObject(lst[0]);
            QString cmdName = sc->property("cmd").toString();
            command(cmdName, obj);
        }
    }
}


void MvQIconHolderView::command(QString name, IconObjectH obj)
{
    // Other actions
    if (Command::isValid(name.toStdString())) {
        obj->command(name.toStdString());
    }
    else if (name == "remove") {
        model_->remove(obj);
        emit edited();
    }
}

void MvQIconHolderView::dragEnterEvent(QDragEnterEvent* event)
{
#ifdef DEBUG_MV_MVQICONHOLDER__
    qDebug() << "dragenter" << event << event->proposedAction() << event->source();
#endif
    if ((event->proposedAction() == Qt::CopyAction ||
         event->proposedAction() == Qt::MoveAction)) {
#ifdef DEBUG_MV_MVQICONHOLDER__
        qDebug() << "dragenter ok";
#endif
        event->accept();
    }
    else {
        event->ignore();
    }
}

void MvQIconHolderView::dragMoveEvent(QDragMoveEvent* event)
{
#ifdef DEBUG_MV_MVQICONHOLDER__
    qDebug() << "dragmove" << event << event->proposedAction() << event->source();
#endif
    if ((event->proposedAction() == Qt::CopyAction ||
         event->proposedAction() == Qt::MoveAction)) {
#ifdef DEBUG_MV_MVQICONHOLDER__
        qDebug() << "dragmove ok";
#endif
        event->accept();
    }
    else {
        event->ignore();
    }
}

void MvQIconHolderView::dropEvent(QDropEvent* event)
{
#ifdef DEBUG_MV_MVQICONHOLDER__
    qDebug() << "drop" << event->mimeData()->formats();
#endif
    if (event->proposedAction() != Qt::CopyAction &&
        event->proposedAction() != Qt::MoveAction) {
        event->ignore();
        return;
    }

    if (event->mimeData()->hasFormat("metview/icon")) {
        const auto* mimeData = qobject_cast<const MvQIconMimeData*>(event->mimeData());

        if (!mimeData) {
            event->ignore();
            return;
        }

        MvQFolderModel* dropModel = mimeData->model();
        if (!dropModel && !mimeData->fromEditor()) {
            event->ignore();
            return;
        }

        bool hasObj = false;
        foreach (IconObject* dropObj, mimeData->objects()) {
            if (!dropObj || !model_->accept(dropObj)) {
                event->ignore();
                return;
            }

            IconObject* obj = dropObj;

            // If drop came from a helper
            if (dropModel && mimeData->fromHelper()) {
                std::string param = owner_->parameter().beautifiedName();
                obj = dropObj->clone(owner_->currentObject()->embeddedFolder(param));
            }

            if (obj) {
                hasObj = true;
                model_->add(obj);
            }
        }

        if (hasObj) {
            event->accept();
            emit edited();
            return;
        }
    }

    event->ignore();
}
