/***************************** 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 <QApplication>
#include <QDataStream>
#include <QDrag>
#include <QDropEvent>
#include <QDebug>
#include <QMouseEvent>
#include <QMenu>
#include <QPainter>
#include <QScrollBar>
#include <QShortcut>
#include <QTimer>

#include "MvQListFolderViewBase.h"

#include "IconClass.h"
#include "IconInfo.h"
#include "IconObject.h"
#include "Folder.h"

#include "MvQFolderViewDelegate.h"
#include "MvQContextMenu.h"
#include "MvQIconMimeData.h"
#include "MvQIconProvider.h"
#include "MvQMethods.h"

MvQListFolderViewBase::MvQListFolderViewBase(MvQFolderModel* folderModel, QWidget* parent) :
    QListView(parent),
    MvQFolderViewBase(folderModel, parent),
    allowMoveAction_(true),
    enterFolders_(true),
    canDrag_(false),
    defaultShortCut_(nullptr)
{
    setContextMenuPolicy(Qt::CustomContextMenu);

    // Drag and drop
    setDragEnabled(true);
    setAcceptDrops(true);
    setDropIndicatorShown(true);
    setDragDropMode(QAbstractItemView::DragDrop);

    // Delegate
    delegate_ = new MvQIconDelegate(this);
    setItemDelegate(delegate_);

    connect(delegate_, SIGNAL(repaintIt(const QModelIndex&)),
            this, SLOT(update(const QModelIndex&)));


    // Set the model. This will call reset on the view.
    setModel(filterModel_);

    setMouseTracking(true);

    setEditTriggers(QAbstractItemView::NoEditTriggers);
    setSelectionMode(QAbstractItemView::ExtendedSelection);

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

    // Selection in the view
    connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
            this, SLOT(slotDoubleClickItem(const QModelIndex)));

    connect(this, SIGNAL(entered(const QModelIndex&)),
            this, SLOT(slotEntered(const QModelIndex&)));

    // We let the delegate to paint the items
    delegate_->setEnablePaint(true);

    //
    setProperty("helper", "true");
}


MvQListFolderViewBase::~MvQListFolderViewBase()
{
    // delete filterModel_;
}

void MvQListFolderViewBase::doReset()
{
    reset();
}

QWidget* MvQListFolderViewBase::concreteWidget()
{
    return this;
}

//-----------------------------------------
// Event
//-----------------------------------------

bool MvQListFolderViewBase::event(QEvent* event)
{
    // We cannot call shortcut setup in the constructor beacuse it calls a pure virtual function! So we
    // call it the first time it is possible.
    if (isVisible())
        setupShortCut();

    return QListView::event(event);
}

//------------------------------------------
// Item info. See also mousemove event!
//------------------------------------------

void MvQListFolderViewBase::slotEntered(const QModelIndex& index)
{
    itemInfo_ = itemInfo(index, FilterModelIndex);
    emit itemEntered(itemInfo_);
}

void MvQListFolderViewBase::leaveEvent(QEvent* event)
{
    itemInfo_ = nullptr;
    emit itemEntered(itemInfo_);
    QWidget::leaveEvent(event);
}


void MvQListFolderViewBase::slotSelectItem(const QModelIndex& /*index*/)
{
    /*if(changeFolder(index,FilterModelIndex))
    {
        changeFolder(index,FilterModelIndex);
        emit currentFolderChanged(currentFolder());
    }*/
}

void MvQListFolderViewBase::slotDoubleClickItem(const QModelIndex& index)
{
    QModelIndexList lst = selectedIndexes();
    if (lst.count() == 1 && lst.at(0) == index)
        handleDoubleClick(index);
}

void MvQListFolderViewBase::setupShortCut()
{
    if (shortCutInit_)
        return;
    else
        shortCutInit_ = true;

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

        if (QShortcut* sc = cms->makeDefaultIconShortCut(this)) {
            defaultShortCut_ = sc;
            connect(defaultShortCut_, SIGNAL(activated()),
                    this, SLOT(slotDefaultShortCut()));
        }
    }
}


void MvQListFolderViewBase::slotIconShortCut()
{
    auto* sc = dynamic_cast<QShortcut*>(QObject::sender());
    if (sc) {
        QModelIndexList lst = selectedIndexes();
        if (lst.count() > 0)
            handleIconShortCut(sc, lst);
    }
}

void MvQListFolderViewBase::slotDefaultShortCut()
{
    if (defaultShortCut_) {
        QModelIndexList lst = selectedIndexes();
        if (lst.count() == 1)
            handleDoubleClick(lst.at(0));
    }
}

void MvQListFolderViewBase::slotContextMenu(const QPoint& position)
{
    QModelIndexList lst = selectedIndexes();
    QPoint scrollOffset(horizontalScrollBar()->value(), verticalScrollBar()->value());
    handleContextMenu(indexAt(position), lst, mapToGlobal(position), position + scrollOffset, this);

    /*if(lst.count() == 1)
    {
        QModelIndex index=indexAt(position);
        QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value());

        handleContextMenu(index,mapToGlobal(position),position+scrollOffset,this);
    }
    else if(lst.count() > 1)
    {
        handleContextMenu(lst,mapToGlobal(position),position+scrollOffset,this);
    } */
}

void MvQListFolderViewBase::folderChanged()
{
    emit currentFolderChanged(currentFolder());
}

void MvQListFolderViewBase::iconCommand(QString name, IconObjectH obj)
{
    emit iconCommandRequested(name, obj);
}

void MvQListFolderViewBase::desktopCommand(QString name, QPoint pos)
{
    emit desktopCommandRequested(name, pos);
}

void MvQListFolderViewBase::keyPressEvent(QKeyEvent* event)
{
    setupShortCut();
    QAbstractItemView::keyPressEvent(event);
}

void MvQListFolderViewBase::reset()
{
    QListView::reset();
    if (filterModel_)
        filterModel_->sort(0);
}

//=================================================
//
// Icon positions
//
//==================================================

QRect MvQListFolderViewBase::itemRect(QList<IconObject*> objLst)
{
    QRect bbox;
    foreach (IconObject* obj, objLst) {
        bbox = bbox.united(visualRect(filterModel_->mapFromSource(folderModel_->indexFromObject(obj))));
    }

    return bbox;
}

QRect MvQListFolderViewBase::itemRect(IconObject* obj)
{
    return visualRect(filterModel_->mapFromSource(folderModel_->indexFromObject(obj)));
}

QRect MvQListFolderViewBase::pixmapRect(QList<IconObject*> objLst)
{
    QRect bbox;
    foreach (IconObject* obj, objLst) {
        bbox = bbox.united(pixmapRect(obj));
    }

    return bbox;
}


QRect MvQListFolderViewBase::pixmapRect(IconObject* obj)
{
    QRect r = visualRect(filterModel_->mapFromSource(folderModel_->indexFromObject(obj)));
    return {r.center().x() - getIconSize() / 2, r.y(), getIconSize(), getIconSize()};
}


//=================================================
//
//  Drag and drop. We need this cutom implementation
//  because the solution offered by the model-view
//  framework was not satisfactory.
//
//=================================================

//===========================
// Drag
//===========================

void MvQListFolderViewBase::mousePressEvent(QMouseEvent* event)
{
    canDrag_ = false;

    if (event->button() == Qt::LeftButton || event->button() == Qt::MiddleButton) {
        startPos_ = event->pos();

        QModelIndex index = indexAt(event->pos());
        if (index.isValid()) {
            QModelIndexList lst = selectedIndexes();

            if (lst.count() <= 1 || !lst.contains(index)) {
                setCurrentIndex(index);
                QRect rect = visualRect(index);
                if (event->pos().y() > rect.top() + getIconSize()) {
                    edit(index);
                    return;
                }
            }

            canDrag_ = true;

            // This prevents rubberband selection
            setState(NoState);
            // return;
        }
    }

    QListView::mousePressEvent(event);
}

void MvQListFolderViewBase::mouseMoveEvent(QMouseEvent* event)
{
    if (itemInfo_ && !indexAt(event->pos()).isValid()) {
        itemInfo_ = nullptr;
        emit itemEntered(itemInfo_);
    }

    if (canDrag_ && event->buttons() & (Qt::LeftButton | Qt::MiddleButton)) {
        // qDebug() << "should start drag" << event->pos() << startPos_;

        int distance = (event->pos() - startPos_).manhattanLength();
        if (distance >= QApplication::startDragDistance()) {
            canDrag_ = false;
            setState(DraggingState);

            if (allowMoveAction_) {
                if (event->buttons() & Qt::LeftButton)
                    performDrag(Qt::MoveAction, startPos_);
                else if (event->buttons() & Qt::MiddleButton)
                    performDrag(Qt::CopyAction, startPos_);
            }
            else {
                performDrag(Qt::CopyAction, startPos_);
            }

            setState(NoState);
        }

        return;
    }

    QListView::mouseMoveEvent(event);
}

void MvQListFolderViewBase::performDrag(Qt::DropAction dropAction, QPoint pos)
{
    QModelIndex viewIndex = indexAt(pos);
    QModelIndex index = filterModel_->mapToSource(viewIndex);

    // The object that was dragged
    IconObject* dragObj = folderModel_->objectFromIndex(index);

    // List of objects to drag
    QList<IconObject*> objLst;
    foreach (QModelIndex idx, selectedIndexes()) {
        objLst << folderModel_->objectFromIndex(filterModel_->mapToSource(idx));
    }

    // Need a solution!!!!!!!!!!!!!!!!!1
    bool fromHelper = (!allowMoveAction_);

    QDrag* drag = buildDrag(dragObj, objLst, fromHelper, this);
    if (drag) {
        drag->exec(dropAction, dropAction);
    }
}

//===========================
// Drop
//===========================

void MvQListFolderViewBase::dragEnterEvent(QDragEnterEvent* event)
{
    // qDebug() << event->mimeData()->formats();
    // qDebug() << event->mimeData()->text();
    // qDebug() << event->proposedAction();

    if (event->source() &&
        (event->proposedAction() == Qt::CopyAction ||
         event->proposedAction() == Qt::MoveAction)) {
        // event->setDropAction(Qt::CopyAction);
        event->accept();
    }
    else
        event->ignore();
}

void MvQListFolderViewBase::dragMoveEvent(QDragMoveEvent* event)
{
    if (event->source() &&
        (event->proposedAction() == Qt::CopyAction ||
         event->proposedAction() == Qt::MoveAction)) {
        event->accept();
    }
    else
        event->ignore();
}

void MvQListFolderViewBase::dropEvent(QDropEvent* event)
{
    // qDebug() << "dropEvent" << folderModel_->fullName();

    if (folderModel_->folder()->locked()) {
        event->ignore();
        return;
    }

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

    if (event->proposedAction() != Qt::CopyAction &&
        event->proposedAction() != Qt::MoveAction) {
        event->ignore();
        return;
    }

    QPoint scrollOffset(horizontalScrollBar()->value(), verticalScrollBar()->value());
    QPoint pos = MvQ::eventPos(event) + scrollOffset;

    //--------------------------------------
    // Drag and drop from another folder
    //--------------------------------------

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

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

        IconObject* dragObj = mimeData->dragObject();
        if (!dragObj) {
            event->ignore();
            return;
        }

        MvQFolderModel* model = mimeData->model();

        bool fromSameView = false;
        if (model) {
            fromSameView = (model == folderModel_);
        }

        // when we drop from an editor we pretend the icon comes from another view.
        // as a result a copy could be created of the icon!
        if (mimeData->fromEditor()) {
            fromSameView = false;
        }

        performDrop(event->proposedAction(), mimeData, pos, fromSameView);
        event->accept();
        return;
    }

    event->ignore();
}

void MvQListFolderViewBase::rename(IconObject* obj)
{
    if (!obj)
        return;
    QModelIndex index = filterModel_->mapFromSource(folderModel_->indexFromObject(obj));
    edit(index);
}

void MvQListFolderViewBase::blink(const QModelIndex& index)
{
    scrollTo(index);
    delegate_->blink(index);
}
