/***************************** 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 <QComboBox>
#include <QDebug>
#include <QHBoxLayout>
#include <QInputDialog>
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
#include <QSortFilterProxyModel>
#include <QToolButton>
#include <QVBoxLayout>

#include "MvKeyProfile.h"

#include "MvQKeyDialog.h"
#include "MvQKeyProfileTree.h"
#include "MvQKeyManager.h"
#include "MvQStepModel.h"
#include "MvQStepWidget.h"
#include "MvQTreeView.h"
#include "MvQTheme.h"
#include "MvVariant.h"

#include "MgQPlotScene.h"
#include "MgQSceneItem.h"
#include "MgQStepMetaData.h"

class MgQPlotScene;

MvQStepWidget::MvQStepWidget(QWidget* parent) :
    QWidget(parent)
{
    activeScene_ = 0;

    auto* layout = new QVBoxLayout;
    setLayout(layout);

    // Create model
    model_ = new MvQStepModel(this);

    sortModel_ = new MvQKeyProfileSortFilterModel(this);
    sortModel_->setSourceModel(model_);
    sortModel_->setDynamicSortFilter(true);

    // Create view
    view_ = new MvQKeyProfileTree(this);
    view_->setModels(model_, sortModel_);

    connect(view_, SIGNAL(selectionChanged(QModelIndex)),
            this, SIGNAL(selectionChanged(QModelIndex)));

    // The profile was altered via the treeview headings
    connect(view_, SIGNAL(profileChanged(bool, int)),
            this, SLOT(adjustProfile(bool, int)));

    layout->addWidget(view_);

    // Buttons at the bottom

    auto* hb = new QHBoxLayout;

    auto* tbEdit = new QToolButton(this);
    tbEdit->setIcon(QIcon(QPixmap(QString::fromUtf8(":/keyDialog/profile_manage.svg"))));
    tbEdit->setToolTip(tr("Manage key profiles"));
    tbEdit->setAutoRaise(true);
    hb->addWidget(tbEdit);
    hb->addStretch(2);

    connect(tbEdit, SIGNAL(clicked()),
            this, SLOT(slotManageKeyProfiles()));

    // Combo box
    // We will add items + signal/slots later in "init"!
    auto* keyLabel = new QLabel(tr("Key profile:"));
    keyCombo_ = new QComboBox;
    keyLabel->setBuddy(keyCombo_);

    hb->addWidget(keyLabel);
    hb->addWidget(keyCombo_);

    layout->addLayout(hb);

    keyManager_ = new MvQKeyManager(MvQKeyManager::FrameType);
    keyManager_->loadProfiles();
}

MvQStepWidget::~MvQStepWidget()
{
    saveKeyProfiles(true);
}

void MvQStepWidget::init(MgQPlotScene* scene, QString keyProfileName)
{
    scene_ = scene;

    model_->setStepData(scene_->currentSceneItem());  // Just to have something!!!

    //----------------------------------
    // Initilaize the key profile list
    //---------------------------------

    // disconnect(keyCombo_,SIGNAL(currentIndexChanged(int)),this,SLOT(loadKeyProfile(int)));

    int currentProfileIndex = 0;
    for (unsigned int i = 0; i < keyManager_->data().size(); i++) {
        QString str(keyManager_->data().at(i)->name().c_str());
        keyCombo_->addItem(str);
        if (keyProfileName == str) {
            currentProfileIndex = i;
        }
        if (keyManager_->data().at(i)->systemProfile()) {
            keyCombo_->setItemIcon(i, MvQTheme::logo());
        }
    }

    keyCombo_->setCurrentIndex(-1);

    connect(keyCombo_, SIGNAL(currentIndexChanged(int)),
            this, SLOT(slotLoadKeyProfile(int)));

    // Select the first element
    keyCombo_->setCurrentIndex(currentProfileIndex);

    // Get all keys
    std::vector<MvKeyProfile*> allKeyProf;
    keyManager_->loadAllKeys(allKeyProf);
    for (auto& it : allKeyProf) {
        allKeys_ << it;
    }
}

void MvQStepWidget::init(QString keyProfileName)
{
    //----------------------------------
    // Initilaize the key profile list
    //---------------------------------

    // disconnect(keyCombo_,SIGNAL(currentIndexChanged(int)),this,SLOT(loadKeyProfile(int)));

    int currentProfileIndex = 0;
    for (unsigned int i = 0; i < keyManager_->data().size(); i++) {
        QString str(keyManager_->data().at(i)->name().c_str());
        keyCombo_->addItem(str);
        if (keyProfileName == str) {
            currentProfileIndex = i;
        }
        if (keyManager_->data().at(i)->systemProfile()) {
            keyCombo_->setItemIcon(i, MvQTheme::logo());
        }
    }

    keyCombo_->setCurrentIndex(-1);

    connect(keyCombo_, SIGNAL(currentIndexChanged(int)),
            this, SLOT(slotLoadKeyProfile(int)));

    // Select the first element
    keyCombo_->setCurrentIndex(currentProfileIndex);

    // Get all keys
    std::vector<MvKeyProfile*> allKeyProf;
    keyManager_->loadAllKeys(allKeyProf);
    for (auto& it : allKeyProf) {
        allKeys_ << it;
    }
}

QString MvQStepWidget::currentKeyProfile()
{
    return keyCombo_->currentText();
}

void MvQStepWidget::setCurrentKeyProfile(QString keyProfileName)
{
    if (keyCombo_->count() == 0)
        return;

    for (int i = 0; i < keyCombo_->count(); i++) {
        if (keyCombo_->itemText(i) == keyProfileName) {
            keyCombo_->setCurrentIndex(i);
            return;
        }
    }
}

void MvQStepWidget::reloadKeyProfile()
{
    // set the keyboard focus to the view!
    view_->setFocus();

    if (MvKeyProfile* profDef = model_->keyProfile()) {
        // Clone the profile
        MvKeyProfile* prof = profDef->clone();

        // Load data into the profile
        collectKeyProfileData(prof);

        // Update message tree. The model takes ownership of the profile.
        model_->setKeyProfile(prof);
    }
}

// It is called when a new request sent to magics is finsihed
void MvQStepWidget::reset(MgQSceneItem* sceneItem)
{
    activeScene_ = sceneItem;

    model_->setStepData(activeScene_);

    reloadKeyProfile();
}

// Collect the metadata into the profile.
void MvQStepWidget::collectKeyProfileData(MvKeyProfile* prof)
{
    if (!prof)
        return;

    // At this point the profile is not yet added to the model.
    Q_ASSERT(model_->keyProfile() != prof);

    QStringList keys;
    QList<QPointF> posL;
    QList<int> posIndex;

    for (int i = 0; i < static_cast<int>(prof->size()); i++) {
        QString s = QString::fromStdString(prof->at(i)->name());

        if (s.startsWith("MV_Value")) {
            float x = QString::fromStdString(prof->at(i)->metaData("x_coord")).toFloat();
            float y = QString::fromStdString(prof->at(i)->metaData("y_coord")).toFloat();
            posL << QPointF(x, y);
            posIndex << i;
            keys << "MV_Value";
        }
        else {
            keys << s;
        }
    }

    prof->clearKeyData();

    // Get metadata
    auto* metaData = new MgQStepMetaData(keys);

    if (activeScene_)
        activeScene_->stepMetaData(metaData);

    for (int i = 0; i < keys.count(); i++) {
        MvKey* kd = prof->at(i);
        int step = 1;
        foreach (QString str, metaData->stepData(i)) {
            if (str.endsWith('\n')) {
                str.remove(str.size() - 1, 1);
            }

            if (keys[i] == "MV_Frame" || keys[i] == "MV_Index") {
                if (kd->valueType() == MvKey::IntType) {
                    kd->addValue(step);
                }
                else {
                    kd->addValue(MvVariant(QString::number(step).toStdString()));
                }
            }
            else if (keys[i] == "MV_Value") {
                kd->addValue(std::string());
            }
            else {
                kd->addValue(str.toStdString());
            }

            step++;
        }
    }

    delete metaData;

#ifdef METVIEW_EXPERIMENTAL

#if 0
	if(scene_->stepNum() <= 0)
		return;
	//Get layerdata -> data values for given positions
	QMap<int,QList<QStringList> > layerData;
	for(int i=0; i < scene_->stepNum(); i++)
	{
		foreach(QPointF pp,posL)
		{
			layerData[i] << QStringList();
		}
	}

	scene_->collectLayerData(posL,layerData,false);
	
	for(int i=0; i< posIndex.count(); i++)
	{
		MvKey* kd=prof->at(posIndex[i]);	

		for(int step=0; step < scene_->stepNum(); step++)
		{
			QString t;
			foreach(QString str,layerData[step][i])
			{
				if(str.endsWith('\n'))
				{
					str.remove(str.size()-1,1);
				}
				t+=str + '\n';			
			}
			if(t.endsWith('\n'))
				t.remove(t.size()-1,1);

			kd->setValue(step,t.toStdString());
		}
	}

#endif

#endif
}

MvKeyProfile* MvQStepWidget::loadedKeyProfile() const
{
    Q_ASSERT(model_);
    return model_->keyProfile();
}

void MvQStepWidget::slotLoadKeyProfile(int index)
{
    saveKeyProfiles(false);

    if (index != -1 && keyManager_->isEmpty() == false) {
        loadKeyProfile(keyManager_->data().at(index));
    }
}

void MvQStepWidget::loadKeyProfile(MvKeyProfile* profDef)
{
    if (!profDef)
        return;

    // Clone the profile
    MvKeyProfile* prof = profDef->clone();

    // Load data into the profile
    collectKeyProfileData(prof);

    // Update message tree. The model takes ownership of the profile.
    model_->setKeyProfile(prof);


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

    view_->setCurrentIndex(sortModel_->index(0, 0));

    if (prof->systemProfile()) {
        view_->setEditable(false);
    }
    else {
        view_->setEditable(true);
    }

    emit keyProfileChanged(model_->keyProfile());
}

// The profile was modified via the treeview headings
void MvQStepWidget::adjustProfile(bool reload, int selectedRow)
{
    // emit statusMessage(tr("Reload key profile ..."));

    if (reload) {
        if (selectedRow == -1)
            selectedRow = sortModel_->mapToSource(view_->currentIndex()).row();

        reloadKeyProfile();

        if (selectedRow >= model_->rowCount())
            selectedRow = 0;

        view_->setCurrentIndex(sortModel_->mapFromSource(model_->index(selectedRow, 0)));
    }

    emit keyProfileChanged(model_->keyProfile());
}

//===============================================
// Steps
//===============================================

int MvQStepWidget::stepNum()
{
    if (!activeScene_)
        return -1;

    return activeScene_->stepNum();
}

void MvQStepWidget::updateStepForScenes(const QList<MgQSceneItem*>& scenesToStep)
{
    if (!activeScene_)
        return;

    int step = activeScene_->currentStep();

    foreach (MgQSceneItem* item, scenesToStep) {
        if (item != activeScene_ && item->stepNum() > 0) {
            int current = step;
            if (current > item->stepNum() - 1)
                current = current % item->stepNum();

            // We just load it and do not render it into the pixmap cache!!!
            item->setCurrentStep(current, false);
        }
    }
}

int MvQStepWidget::setCurrentStep(int sortedStep, const QList<MgQSceneItem*>& scenesToStep)
{
    if (!activeScene_ || sortedStep < 0 || sortedStep >= activeScene_->stepNum()) {
        return -1;
    }

    // Get the original (unsorted) model index for the step num
    QModelIndex index = sortModel_->mapToSource(sortModel_->index(sortedStep, 0));

    int step = index.row();

    bool oriCacheState = activeScene_->stepCached(step);
    activeScene_->setCurrentStep(step);
    if (oriCacheState != activeScene_->stepCached(step)) {
        emit stepCacheStateChanged();
    }

    foreach (MgQSceneItem* item, scenesToStep) {
        if (item != activeScene_ && item->stepNum() > 0) {
            int current = step;
            if (current > item->stepNum() - 1)
                current = current % item->stepNum();

            item->setCurrentStep(current);
        }
    }

    return sortedStep;  //!!!!
}

int MvQStepWidget::currentStep()
{
    if (!activeScene_)
        return -1;

    int currentStep = activeScene_->currentStep();
    QModelIndex index = sortModel_->mapFromSource(model_->index(currentStep, 0));
    int sortedStep = index.row();
    return sortedStep;
}

void MvQStepWidget::setViewToCurrentStep()
{
    if (!activeScene_)
        return;

    int currentStep = activeScene_->currentStep();

    //	QModelIndex src=model_->index(currentStep,0);
    QModelIndex index = sortModel_->mapFromSource(model_->index(currentStep, 0));
    QModelIndex current = view_->currentIndex();

    // qDebug() << model_->currentStep() << src << index;
    if (index.row() != current.row()) {
        view_->setCurrentIndex(index);
    }
}

void MvQStepWidget::stepDataIsAboutToChange()
{
    model_->stepDataIsAboutToChange();
}

//==============================
// Key manager
//==============================

void MvQStepWidget::slotManageKeyProfiles()
{
    saveKeyProfiles(false);

    QString dialogTitle = "Metview - Frame Key Profile Manager";

    MvQKeyManager::KeyType type = MvQKeyManager::FrameType;

    // Create a tmp copy of the whole manager (no data
    // values, just key descriptions)
    MvQKeyManager* tmpManager = keyManager_->clone();

    MvQKeyDialog profDialog(dialogTitle, type, tmpManager, keyCombo_->currentIndex(), allKeys_);

    if (profDialog.exec() == QDialog::Accepted) {
        // Save the current profile name
        std::string currentProfName = keyCombo_->currentText().toStdString();
#if 0
        messageModel_->keyProfileIsAboutToChange();
        messageModel_->setKeyProfile(0);
#endif
        keyManager_->update(tmpManager);
        keyManager_->saveProfiles();

        disconnect(keyCombo_, SIGNAL(currentIndexChanged(int)), nullptr, nullptr);

        // Clear the profile list
        keyCombo_->clear();

        // resetMessageHeader();

        int i = 0;
        int index = -1;
        for (auto it = keyManager_->data().begin(); it != keyManager_->data().end(); it++) {
            keyCombo_->addItem(QString((*it)->name().c_str()));
            if ((*it)->name() == currentProfName) {
                index = i;
            }
            if (keyManager_->data().at(i)->systemProfile()) {
                keyCombo_->setItemIcon(i, MvQTheme::logo());
            }

            i++;
        }

        if (index != -1) {
            keyCombo_->setCurrentIndex(index);
        }
        else {
            keyCombo_->setCurrentIndex(0);
        }

        Q_ASSERT(keyCombo_->currentIndex() >= 0 &&
                 keyCombo_->currentIndex() < static_cast<int>(keyManager_->data().size()));

        // We load the profile directly
        loadKeyProfile(keyManager_->data().at(keyCombo_->currentIndex()));

        connect(keyCombo_, SIGNAL(currentIndexChanged(int)),
                this, SLOT(slotLoadKeyProfile(int)));
    }

    delete tmpManager;
}

void MvQStepWidget::saveKeyProfiles(bool saveToDisk)
{
    bool diff = false;
    if (MvKeyProfile* prof = loadedKeyProfile()) {
        if (MvKeyProfile* ap = keyManager_->findProfile(prof->name())) {
            if (*ap != *prof) {
                keyManager_->changeProfile(prof->name(), prof);
                diff = true;
            }
        }
    }

    if (saveToDisk || diff)
        keyManager_->saveProfiles();
}
