/***************************** 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 "MvQPathWidget.h"

#include <QtGlobal>
#include <QDebug>
#include <QDir>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QHBoxLayout>
#include <QLabel>
#include <QMenu>
#include <QSignalMapper>
#include <QStyleOption>
#include <QPainter>

#include "MvQBookmarks.h"
#include "MvQContextMenu.h"
#include "MvQDropTarget.h"
#include "MvQFileBrowser.h"
#include "MvQFolderViewBase.h"
#include "MvQIconProvider.h"
#include "MvQMethods.h"

#include "Folder.h"
#include "IconClass.h"

QList<MvQContextItem*> MvQPathWidget::cmTbItems_;
QList<MvQContextItem*> MvQPathWidget::cmMenuItems_;

//=============================================================
//
//  MvQPathButton
//
//=============================================================

MvQPathButton::MvQPathButton(MvQPathWidgetItem* owner, QWidget* parent) :
    QToolButton(parent),
    owner_(owner)
{
    Q_ASSERT(owner_);
    setAcceptDrops(true);
}

void MvQPathButton::checkDropTarget(QDropEvent* event)
{
    if (!event->mimeData()->hasFormat("metview/icon")) {
        removeDropTarget();
    }

    QString s = text();

    if (!s.isEmpty()) {
        // Folder *f=Folder::folder(s.toStdString(),false);
        // if(f && !f->locked())
        //{
        Qt::DropAction dropAction = event->proposedAction();
        MvQDropTarget::Instance()->reset(s, (dropAction == Qt::MoveAction));

        if (window()) {
            MvQDropTarget::Instance()->move(mapToGlobal(MvQ::eventPos(event)) + QPoint(20, 20));
        }
        return;
        //}
    }

    removeDropTarget();
}

void MvQPathButton::removeDropTarget()
{
    MvQDropTarget::Instance()->hide();
}

void MvQPathButton::dragEnterEvent(QDragEnterEvent* event)
{
    if (event->source() &&
        (event->proposedAction() == Qt::CopyAction ||
         event->proposedAction() == Qt::MoveAction)) {
        event->accept();
    }
    else
        event->ignore();
}

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

void MvQPathButton::dragLeaveEvent(QDragLeaveEvent* event)
{
    removeDropTarget();
    event->accept();
}

void MvQPathButton::dropEvent(QDropEvent* event)
{
    removeDropTarget();

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

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

    emit iconDropped(event);
}

//=============================================================
//
//  MvQPathWidget
//
//=============================================================

MvQPathWidget::MvQPathWidget(QWidget* parent) :
    QWidget(parent),
    actionReload_(nullptr),
    reloadTb_(nullptr),
    bookmarkTb_(nullptr),
    emptyLabelIcon_(nullptr),
    emptyLabel_(nullptr)
{
    layout_ = new QHBoxLayout(this);
    layout_->setSpacing(0);
    layout_->setContentsMargins(6, 0, 6, 0);
    setLayout(layout_);

    setProperty("path", "1");

    smp_ = new QSignalMapper(this);

#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
    connect(smp_, SIGNAL(mappedInt(int)),
#else
    connect(smp_, SIGNAL(mapped(int)),
#endif
            this, SIGNAL(pathClicked(int)));

    connect(this, SIGNAL(pathClicked(int)),
            this, SLOT(slotChangeDir(int)));
}

void MvQPathWidget::slotContextMenu(const QPoint& pos)
{
    static MvQContextItemSet cmItems("Breadcrumbs");

    if (auto* tb = static_cast<QToolButton*>(QObject::sender())) {
        QString path = getPath(tb);

        if (!path.isEmpty()) {
            QString selection = MvQContextMenu::instance()->exec(cmItems.icon(), tb->mapToGlobal(pos), tb);
            if (!selection.isEmpty()) {
                emit commandRequested(selection, path);
            }
        }
    }
}

void MvQPathWidget::clearLayout()
{
    QLayoutItem* item = nullptr;
    while ((item = layout_->takeAt(0)) != nullptr) {
        QWidget* w = item->widget();
        if (w) {
            layout_->removeWidget(w);
            // delete w;
        }
        delete item;
    }

    if (emptyLabel_) {
        delete emptyLabel_;
        emptyLabel_ = nullptr;
    }
    if (emptyLabelIcon_) {
        delete emptyLabelIcon_;
        emptyLabelIcon_ = nullptr;
    }
}

void MvQPathWidget::setReloadAction(QAction* ac)
{
    actionReload_ = ac;
}

void MvQPathWidget::setPath(QString path)
{
    setPath(Folder::folder(path.toStdString(), false), path);
}

void MvQPathWidget::setPath(Folder* folder, QString path)
{
    // Clear the layout (widgets are not deleted in this step!)
    clearLayout();
    path_.clear();

    if (!folder) {
        Q_ASSERT(emptyLabelIcon_ == nullptr);
        emptyLabelIcon_ = new QLabel(this);
        emptyLabelIcon_->setPixmap(MvQIconProvider::warningPixmap(16));
        layout_->addWidget(emptyLabelIcon_);

        Q_ASSERT(emptyLabel_ == nullptr);
        emptyLabel_ = new QLabel("<font color='2F2F2F'>&nbsp;&nbsp;Folder <b>" + path + " </b> does not exist!</font>", this);
        layout_->addWidget(emptyLabel_);

        // Delete unused items (if there are any)
        int num = items_.count();
        for (int i = 0; i < num; i++) {
            MvQPathWidgetItem* item = items_.back();
            items_.removeLast();
            delete item;
        }
        if (reloadTb_) {
            delete reloadTb_;
            reloadTb_ = nullptr;
        }
        if (bookmarkTb_) {
            delete bookmarkTb_;
            bookmarkTb_ = nullptr;
        }
        return;
    }

    path_ = QString::fromStdString(folder->fullName());

    QList<Folder*> lst;
    lst << folder;
    Folder* f = folder;
    while (f->parent()) {
        f = f->parent();
        lst.prepend(f);
    }

    //-----------------------------------------------------------
    // Try to reuse the current items and delete the unneeded ones
    //-----------------------------------------------------------

    // Find out how many items can be reused.
    // firstIndex: the first original item that differs from the current one.
    int lastIndex = -1;
    for (int i = 0; i < lst.count() && i < items_.count(); i++) {
        if (QString::fromStdString(lst.at(i)->fullName()) == items_[i]->fullName_) {
            lastIndex = i;
        }
        else
            break;
    }

    // Delete unused items (if there are any)
    int num = items_.count();
    for (int i = lastIndex + 1; i < num; i++) {
        MvQPathWidgetItem* item = items_.back();
        items_.removeLast();
        delete item;
    }

    for (int i = lastIndex + 1; i < lst.count(); i++) {
        Folder* f = lst.at(i);

        auto* item = new MvQPathWidgetItem(QString::fromStdString(f->name()),
                                           QString::fromStdString(f->fullName()));
        items_ << item;

        MvQPathButton* tb = nullptr;

        //---------------------------------------
        // Dir name
        //---------------------------------------

        tb = new MvQPathButton(item, this);
        item->nameTb_ = tb;

        if (item->fullName_ == "/") {
            tb->setToolButtonStyle(Qt::ToolButtonIconOnly);
            tb->setIconSize(QSize(16, 16));
            tb->setIcon(MvQIconProvider::pixmap(f, 16));
            // tb->setIcon(MvQIconProvider::homePixmap(16));
            tb->setObjectName("pathIconTb");
        }
        else {
            if (f->locked()) {
                tb->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
                tb->setIconSize(QSize(12, 12));
                tb->setIcon(MvQIconProvider::lockPixmap(12));
            }
            else {
                tb->setToolButtonStyle(Qt::ToolButtonTextOnly);
            }
            tb->setText(item->name_);
            tb->setObjectName("pathNameTb");

            if (f->isLink())
                tb->setStyleSheet("QToolButton {font-style: italic;}");
        }

        // tb->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
        tb->setAutoRaise(true);

        connect(tb, SIGNAL(clicked()),
                smp_, SLOT(map()));

        // Context menu
        tb->setContextMenuPolicy(Qt::CustomContextMenu);

        connect(tb, SIGNAL(customContextMenuRequested(const QPoint&)),
                this, SLOT(slotContextMenu(const QPoint&)));

        smp_->setMapping(tb, i);

        connect(tb, SIGNAL(iconDropped(QDropEvent*)),
                this, SLOT(slotIconDropped(QDropEvent*)));

        //---------------------------------------
        // Menu with the folders in the given dir
        //---------------------------------------

        tb = new MvQPathButton(item, this);
        item->menuTb_ = tb;

        // tb->setArrowType(Qt::RightArrow);
        tb->setAutoRaise(true);
        // tb->setIconSize(QSize(10,10));
        // tb->setPopupMode(QToolButton::InstantPopup);
        tb->setObjectName("pathMenuTb");

        connect(tb, SIGNAL(clicked()),
                this, SLOT(slotShowDirMenu()));

        // QMenu* mn = createDirMenu(i, f, tb);
        // tb->addMenu(mn);
        // tb->assignMenu(mn);
    }

    // Add items to layout
    for (int i = 0; i < items_.count(); i++) {
        layout_->addWidget(items_.at(i)->nameTb_);
        layout_->addWidget(items_.at(i)->menuTb_);
    }

    // Set the text in the last item bold
    for (int i = 0; i < items_.count(); i++) {
        QString st = items_.at(i)->nameTb_->styleSheet();
        if (i == items_.count() - 1)
            st += "QToolButton {font-weight: bold;}";
        else
            st += "QToolButton {font-weight: normal;}";

        items_.at(i)->nameTb_->setStyleSheet(st);
    }

    layout_->addStretch(1);

    // Bookmarks
    if (!bookmarkTb_) {
        bookmarkTb_ = new QToolButton(this);
        bookmarkTb_->setAutoRaise(true);
        // bookmarkTb_->setIconSize(QSize(20,20));
        bookmarkTb_->setObjectName("pathIconTb");

        connect(bookmarkTb_, SIGNAL(clicked()),
                this, SLOT(slotBookmark()));
    }

    updateBookmarkStatus();

    layout_->addWidget(bookmarkTb_);

    // Reload
    if (actionReload_) {
        if (!reloadTb_) {
            reloadTb_ = new QToolButton(this);
            reloadTb_->setDefaultAction(actionReload_);
            reloadTb_->setAutoRaise(true);
            // reloadTb_->setIconSize(QSize(20,20));
            reloadTb_->setObjectName("pathIconTb");
        }
        layout_->addWidget(reloadTb_);
    }
}

void MvQPathWidget::slotShowDirMenu()
{
    if (auto* tb = dynamic_cast<MvQPathButton*>(sender())) {
        QString fullName = tb->owner_->fullName_;

        if (fullName.isEmpty())
            return;

        Folder* folder = Folder::folder(fullName.toStdString(), false);
        if (!folder)
            return;

        auto* dirMenu = new QMenu(tb);

        for (const auto& it : folder->kids()) {
            if (it.second->visible() && it.second->isFolder()) {
                dirMenu->addAction(QString::fromStdString(it.first));
            }
        }

        if (QAction* ac = dirMenu->exec(mapToGlobal(tb->pos()))) {
            QString path = fullName + "/" + ac->text();

            // we need to delete it now because the emitted signal will might
            // delete it is parent (if the path widget is recreated)
            delete dirMenu;

            emit dirChanged(path);
            return;
        }

        delete dirMenu;
    }
}

// a button representing a folder is clicked
void MvQPathWidget::slotChangeDir(int index)
{
    if (index >= 0 && index < items_.count() - 1) {
        QString path = items_.at(index)->fullName_;

        // QString path;
        // for(int i=0; i <= index; i++)
        //	path+="/"+items_[i].name_;

        // setPath(path);

        emit dirChanged(path);
        setPath(path);
    }
}

QString MvQPathWidget::getPath(QToolButton* tb)
{
    foreach (MvQPathWidgetItem* item, items_) {
        if (item->nameTb_ == tb) {
            return item->fullName_;
        }
    }

    return {};
}

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

void MvQPathWidget::slotIconDropped(QDropEvent* event)
{
    if (auto* tb = dynamic_cast<QToolButton*>(sender())) {
        QString p = getPath(tb);
        if (Folder* f = Folder::folder(p.toStdString(), false)) {
            MvQFolderViewBase::dropToFolder(f, event);
        }
    }
}

//----------------------
// Bookmarks
//----------------------

void MvQPathWidget::slotBookmark()
{
    if (MvQBookmarks::isBookmark(path_)) {
        MvQBookmarks::editItem(path_);
    }
    else {
        MvQBookmarks::addItem(path_);
    }

    updateBookmarkStatus();
}

void MvQPathWidget::updateBookmarkStatus()
{
    if (MvQBookmarks::isBookmark(path_)) {
        bookmarkTb_->setIcon(QPixmap(":/desktop/bookmark.svg"));
        bookmarkTb_->setToolTip(tr("Edit this bookmark"));
    }
    else {
        bookmarkTb_->setIcon(QPixmap(":/desktop/bookmark_empty.svg"));
        bookmarkTb_->setToolTip(tr("Bookmark current folder"));
    }
}
