/***************************** 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 <QAction>
#include <QDebug>
#include <QHeaderView>
#include <QInputDialog>
#include <QMenu>
#include <QMessageBox>

#include "MvQKeyDialog.h"
#include "MvQKeyProfileTree.h"

#include "MvKeyProfile.h"

#include "MvQKeyProfileModel.h"


MvQKeyProfileTree::MvQKeyProfileTree(QWidget* parent) :
    MvQTreeView(parent, true)
{
    setObjectName("messageTree");
    setSortingEnabled(true);
    sortByColumn(0, Qt::AscendingOrder);
    setAlternatingRowColors(true);
    setAllColumnsShowFocus(true);
    // setActvatedByKeyNavigation(true);
    setAcceptDrops(true);
    setDragDropMode(QAbstractItemView::DropOnly);
    setRootIsDecorated(false);

    setStyleId(0);

    // Set header ContextMenuPolicy
    header()->setContextMenuPolicy(Qt::CustomContextMenu);

    connect(header(), SIGNAL(customContextMenuRequested(const QPoint&)),
            this, SLOT(slotMessageTreeContextMenu(const QPoint&)));

    connect(header(), SIGNAL(sectionMoved(int, int, int)),
            this, SLOT(slotMessageTreeColumnMoved(int, int, int)));
}

void MvQKeyProfileTree::setModels(MvQKeyProfileModel* messageModel,
                                  MvQKeyProfileSortFilterModel* sortModel)
{
    Q_ASSERT(messageModel_ == nullptr);
    Q_ASSERT(messageSortModel_ == nullptr);

    messageModel_ = messageModel;
    messageSortModel_ = sortModel;

    Q_ASSERT(messageModel_);
    Q_ASSERT(messageSortModel_);

    setModel(messageSortModel_);
    messageSortModel_->setSortRole(MvQKeyProfileModel::SortRole);
    connect(messageModel_, SIGNAL(keysInserted(QList<MvKey*>, int)),
            this, SLOT(insertKeys(QList<MvKey*>, int)));
}

void MvQKeyProfileTree::setEditable(bool b)
{
    editable_ = b;

    if (editable_) {
        setAcceptDrops(true);
        setDragDropMode(QAbstractItemView::DropOnly);
        header()->setSectionsMovable(true);
    }
    else {
        setAcceptDrops(false);
        setDragDropMode(QAbstractItemView::NoDragDrop);
        header()->setSectionsMovable(false);
    }
}

void MvQKeyProfileTree::slotMessageTreeContextMenu(const QPoint& position)
{
    if (!editable_)
        return;

    int section = header()->logicalIndexAt(position);

    if (section < 0 || section >= header()->count())
        return;

    MvKeyProfile* prof = messageModel_->keyProfile();
    if (prof == nullptr || section >= static_cast<int>(prof->size()))
        return;


    QList<QAction*> actions;

    // Inser new key
    auto* actionInsertBefore = new QAction(this);
    actionInsertBefore->setObjectName(QString::fromUtf8("actionInsertBefore"));
    actionInsertBefore->setText(tr("Insert key before"));
    actions.append(actionInsertBefore);

    // Check if users can define keys
    if (predefinedKeysOnly_) {
        actionInsertBefore->setEnabled(false);
    }

    auto* actionInsertAfter = new QAction(this);
    actionInsertAfter->setObjectName(QString::fromUtf8("actionInsertAfter"));
    actionInsertAfter->setText(tr("Insert key after"));
    actions.append(actionInsertAfter);

    // Check if users can define keys
    if (predefinedKeysOnly_) {
        actionInsertAfter->setEnabled(false);
    }

    // Edit

#ifdef METVIEW_EXPERIMENTAL
    bool keyEditable = prof->at(section)->editable();
    QAction* actionEdit;
    if (keyEditable) {
        actionEdit = new QAction(this);
        actionEdit->setObjectName(QString::fromUtf8("actionEdit"));
        actionEdit->setText(tr("Edit key"));
        // actionRename->setShortcut(tr("Ctrl+L"));
        actions.append(actionEdit);
    }
#endif

    auto* actionDelete = new QAction(this);
    actionDelete->setObjectName(QString::fromUtf8("actionDelete"));
    actionDelete->setText(tr("Delete key"));
    // actionDelete->setShortcut(tr("Ctrl+D"));
    actions.append(actionDelete);

    // Rename
    auto* actionRename = new QAction(this);
    actionRename->setObjectName(QString::fromUtf8("actionRename"));
    actionRename->setText(tr("Rename header"));
    // actionRename->setShortcut(tr("Ctrl+L"));
    actions.append(actionRename);

    if (actions.count() > 0) {
        QAction* action = QMenu::exec(actions, header()->mapToGlobal(position));

        if (action == actionInsertBefore) {
            insertMessageTreeColumn(section, true);
        }
        else if (action == actionInsertAfter) {
            insertMessageTreeColumn(section, false);
        }
        else if (action == actionRename) {
            renameMessageTreeHeader(section);
        }
        else if (action == actionDelete) {
            deleteMessageTreeColumn(section);
        }
#ifdef METVIEW_EXPERIMENTAL
        else if (keyEditable &&
                 action == actionEdit) {
            editMessageTreeHeader(section);
        }
#endif
    }

    qDeleteAll(actions);
}


void MvQKeyProfileTree::insertMessageTreeColumn(int column, bool before)
{
    auto* key = new MvKey;
    MvQKeyEditDialog keyDialog(key);

    int oriRow = messageSortModel_->mapToSource(currentIndex()).row();

    if (keyDialog.exec() == QDialog::Accepted) {
        // Get the current profile from the model.
        MvKeyProfile* prof = messageModel_->keyProfile();

        if (column >= 0 && column < static_cast<int>(prof->size())) {
            messageModel_->keyProfileIsAboutToChange();

            // insertKey always inserts before
            if (before)
                prof->insertKey(column, key);
            else
                prof->insertKey(column + 1, key);

            prof->clearKeyData();

            messageModel_->keyProfileChangeFinished();

            emit profileChanged(true, oriRow);
        }
    }
}

void MvQKeyProfileTree::insertKeys(QList<MvKey*> keyLst, int column)
{
    if (keyLst.isEmpty())
        return;

    int oriRow = messageSortModel_->mapToSource(currentIndex()).row();

    messageModel_->keyProfileIsAboutToChange();

    // Get the current profile from the model.
    MvKeyProfile* prof = messageModel_->keyProfile();

    foreach (MvKey* k, keyLst) {
        MvKey* key = k->clone();
        prof->insertKey(column, key);
        prof->clearKeyData();
    }

    messageModel_->keyProfileChangeFinished();

    emit profileChanged(true, oriRow);
}

void MvQKeyProfileTree::editMessageTreeHeader(int column)
{
    if (!editable_)
        return;

    MvKeyProfile* prof = messageModel_->keyProfile();
    if (prof == nullptr || column < 0 || column >= static_cast<int>(prof->size()))
        return;

    MvKeyProfile* nProf = prof->clone();
    std::string name = prof->at(column)->name();

    MvQKeyEditDialog edit(nProf->at(column));
    if (edit.exec() == QDialog::Accepted) {
        int oriRow = messageSortModel_->mapToSource(currentIndex()).row();

        messageModel_->setKeyProfile(nProf);

        if (name == nProf->at(column)->name())
            emit profileChanged(false, oriRow);
        else
            emit profileChanged(true, oriRow);
    }
    else {
        delete nProf;
    }
}

void MvQKeyProfileTree::renameMessageTreeHeader(int column)
{
    if (!editable_)
        return;

    MvKeyProfile* prof = messageModel_->keyProfile();
    if (column < 0 || column >= static_cast<int>(prof->size()))
        return;

    bool ok = false;
    QString text = QInputDialog::getText(nullptr,
                                         tr("Rename header"),
                                         tr("New name:"), QLineEdit::Normal,
                                         prof->at(column)->shortName().c_str(), &ok);

    if (ok && !text.isEmpty()) {
        int oriRow = messageSortModel_->mapToSource(currentIndex()).row();
        messageModel_->keyProfileIsAboutToChange();
        prof->at(column)->setShortName(text.toStdString());
        messageModel_->keyProfileChangeFinished();
        emit profileChanged(false, oriRow);
    }
}

void MvQKeyProfileTree::deleteMessageTreeColumn(int column)
{
    if (!editable_)
        return;

    MvKeyProfile* prof = messageModel_->keyProfile();
    if (column < 0 || column >= static_cast<int>(prof->size()))
        return;

    QMessageBox msgBox;

    QString str = tr("Are you sure that you want to delete key <b>");
    str += QString::fromStdString(prof->at(column)->name());
    str += "</b>?";

    msgBox.setText(str);
    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
    msgBox.setDefaultButton(QMessageBox::Cancel);
    msgBox.setWindowTitle(tr("Delete key"));

    int ret = msgBox.exec();

    switch (ret) {
        case QMessageBox::Yes: {
            int oriRow = messageSortModel_->mapToSource(currentIndex()).row();
            messageModel_->keyProfileIsAboutToChange();
            prof->deleteKey(column);
            messageModel_->keyProfileChangeFinished();
            emit profileChanged(false, oriRow);
        } break;
        default:
            break;
    }
}

void MvQKeyProfileTree::slotMessageTreeColumnMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
{
    if (logicalIndex != oldVisualIndex) {
        qDebug() << "MvQGribExaminerBase::slotMessageTreeColumnMoved> problem with column indices!";
    }

    MvKeyProfile* prof = messageModel_->keyProfile();
    if (oldVisualIndex >= 0 && oldVisualIndex < static_cast<int>(prof->size()) &&
        newVisualIndex >= 0 && newVisualIndex < static_cast<int>(prof->size()) &&
        oldVisualIndex != newVisualIndex) {
        // for(int i=0; i < messageTree_->header()->count(); i++)
        //	qDebug() << "header: (" << i << ") " << messageTree_->header()->logicalIndex(i);

        prof->reposition(oldVisualIndex, newVisualIndex);

        // We need it because
        //	- QHeaderView::reset() does not initialize the
        //          the logicalindeces
        //       - But if the column count changes the
        //          logicalIndeces are reinitialized

        int oriRow = messageSortModel_->mapToSource(currentIndex()).row();

        messageModel_->reloadKeyProfile();


        // messageModel_->keyProfileIsAboutToChange();
        // messageModel_->setKeyProfile(0);

        // messageModel_->keyProfileIsAboutToChange();
        // messageModel_->setKeyProfile(prof);

        for (int i = 0; i < messageModel_->columnCount() - 1; i++) {
            resizeColumnToContents(i);
        }

        emit profileChanged(false, oriRow);
    }
}

void MvQKeyProfileTree::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
    QModelIndexList lst = selectedIndexes();
    if (lst.count() > 0) {
        Q_EMIT selectionChanged(lst.back());
    }
    QTreeView::selectionChanged(selected, deselected);
}
