/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2016 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO)
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/

// -- Core stuff
#include "SettingsDialog.h"
#include "Application.h"
#include "Core.h"
#include "ExtensionManager.h"
#include "ComponentExtension.h"
#include "ActionExtension.h"
#include "Action.h"
#include "ObjectController.h"
#include "ui_Settings.h"

// -- QT stuff
#include <QApplication>
#include <QMessageBox>
#include <QFileDialog>
#include <QHeaderView>
#include <QMetaObject>
#include <QMetaMethod>
#include <QSettings>

// -- stl stuff
#include <iostream>

namespace camitk {
// ------------------------------ constructor -------------------------------
SettingsDialog::SettingsDialog() : QDialog(qApp->activeWindow()) {
    myUI = new Ui::ui_Settings;
    myUI->setupUi(this);

    // -- update plugin viewers
    readUserExtensions();
    updateComponentExtensionList();
    updateActionExtensionList();

    // -- setup the general tab
    myUI->generalTab->setVisible(false);
    objectController = new ObjectController(myUI->propEditorFrame, ObjectController::BUTTON);
    myUI->propEditorFrame->layout()->addWidget(objectController);

    // and its buttons
    QFrame *buttonFrame = new QFrame(myUI->propEditorFrame);
    QHBoxLayout *buttonLayout = new QHBoxLayout;
    QPushButton *applyButton = new QPushButton("Apply");
    buttonLayout->addWidget(applyButton);

    QPushButton *revertButton = new QPushButton("Revert");
    buttonLayout->addWidget(revertButton);

    buttonFrame->setLayout(buttonLayout);
    myUI->propEditorFrame->layout()->addWidget(buttonFrame);

    // connect the buttons
    QObject::connect(applyButton, SIGNAL(clicked()), objectController, SLOT(apply()));
    QObject::connect(revertButton, SIGNAL(clicked()), objectController, SLOT(revert()));

    // -- show settings
    myUI->tabWidget->setCurrentWidget(myUI->componentExtensionsTab);
    adjustSize();
}

// ------------------------------ destructor -------------------------------
SettingsDialog::~SettingsDialog() {
    delete myUI;
    myUI = NULL;
    delete objectController;
    objectController = NULL;
}

// ------------------------------ readUserExtensions -------------------------------
void SettingsDialog::readUserExtensions() {
    QSettings & settings = Application::getSettings();
    settings.beginGroup ( "UserExtensions" );
    userComponents = settings.value("components", QVariant(QStringList())).toStringList();
    userActions = settings.value("actions", QVariant(QStringList())).toStringList();
    settings.endGroup();
}

// ------------------------------ writeUserExtensions -------------------------------
void SettingsDialog::writeUserExtensions() {
    QSettings & settings = Application::getSettings();
    settings.beginGroup ( "UserExtensions" );
    settings.setValue("components", userComponents );
    settings.setValue("actions", userActions );
    settings.endGroup();
}

// ------------------------------ editSettings -------------------------------
void SettingsDialog::editSettings(QObject *qObj) {
    editedObjectMap.insert(qObj->objectName(), qObj);

    //-- add the object to the property editor and the objectList
    objectController->setObject(NULL); // force update
    objectController->setObject(qObj);
    objectController->adjustSize();
    objectController->show();

    QListWidgetItem *qObjNameWidget = new QListWidgetItem;
    qObjNameWidget->setText(qObj->objectName());
    myUI->objectList->addItem(qObjNameWidget);
    myUI->objectList->setCurrentItem(qObjNameWidget);

    // focus on it
    myUI->generalTab->setVisible(true);
    myUI->tabWidget->setCurrentWidget(myUI->generalTab);
}

// ------------------------------ updateComponentExtensionList -------------------------------
void SettingsDialog::updateComponentExtensionList() {
    const QList<ComponentExtension *> & ceList =  ExtensionManager::getComponentExtensions().values().toSet().toList();
    const QList<ComponentExtension *> & ceddList =  ExtensionManager::getDataDirectoryComponents().values().toSet().toList();

    // remove all the items
    myUI->componentExtensionList->clearContents();
    myUI->componentExtensionList->setRowCount(ceList.size() + ceddList.size());
    myUI->componentExtensionList->verticalHeader()->setVisible(false);
    myUI->componentExtensionList->setSortingEnabled(false); // temporarily for insertion!

    // loop over all ManagerExtensions
    // set the different columns text and pixmaps
    // columns: name, state (i.e loaded or not), extension, location
    int i;
    for (i = 0; i < ceList.size(); i++) {
        ComponentExtension *ce = ceList.at(i);
        // set the icon depending if this is a system-wide component (camitk icon) or user settings (user icon)
        myUI->componentExtensionList->setItem(i, 0, new QTableWidgetItem((userComponents.contains(ce->getLocation()) ? QPixmap(":/user") : QPixmap(":/camiTKIcon")), ce->getName()));
        myUI->componentExtensionList->setItem(i, 1, new QTableWidgetItem(ce->getFileExtensions().join(", ")));
        myUI->componentExtensionList->setItem(i, 2, new QTableWidgetItem(ce->getLocation()));
    }

    // list all data directory plugin
    for (; (i - ceList.size()) < ceddList.size(); i++) {
        ComponentExtension *ce = ceddList.at(i - ceList.size());
        myUI->componentExtensionList->setItem(i, 0, new QTableWidgetItem((userComponents.contains(ce->getLocation()) ? QPixmap(":/user") : QPixmap(":/camiTKIcon")), ce->getName()));
        myUI->componentExtensionList->setItem(i, 1, new QTableWidgetItem("[directory]"));
        myUI->componentExtensionList->setItem(i, 2, new QTableWidgetItem(ce->getLocation()));
    }

    myUI->componentExtensionList->resizeColumnsToContents();
    myUI->componentExtensionList->resizeRowsToContents();
    myUI->componentExtensionList->setSortingEnabled(true);
    myUI->componentExtensionList->sortByColumn(0, Qt::AscendingOrder);
}


// ------------------------------ updateComponentExtensionList -------------------------------
void SettingsDialog::updateActionExtensionList() {
    const QMap<QString, ActionExtension *> & aeMap = ExtensionManager::getActionExtensions();

    // remove all the items
    myUI->actionExtensionList->clearContents();
    myUI->actionExtensionList->setRowCount(Application::getActions().size());
    myUI->actionExtensionList->verticalHeader()->setVisible(false);
    myUI->actionExtensionList->setSortingEnabled(false); // temporarily for insertion!

    // loop over all ActionExtensions
    unsigned int i = 0;
    QMapIterator<QString, ActionExtension *> it(aeMap);
    while (it.hasNext()) {
        it.next();
        // set the different columns text and pixmaps
        // columns: name, state (i.e loaded or not), extension, location
        foreach (Action *action, it.value()->getActions()) {
            myUI->actionExtensionList->setItem(i, 0, new QTableWidgetItem((userActions.contains(it.key()) ? QPixmap(":/user") : QPixmap(":/camiTKIcon")), action->getName()));
            myUI->actionExtensionList->setItem(i, 1, new QTableWidgetItem(action->getComponent()));
            myUI->actionExtensionList->setItem(i++, 2, new QTableWidgetItem(it.key()));
        }
    }

    myUI->actionExtensionList->resizeColumnsToContents();
    myUI->actionExtensionList->setSortingEnabled(true);
    myUI->actionExtensionList->sortByColumn(0, Qt::AscendingOrder);
}

// ------------------------------ accept -------------------------------
void SettingsDialog::accept() {
    if (objectController->isModified()) {
        if (QMessageBox::question(this, "Unsaved changes", tr("There are unsaved changes in current item.\nIf you click \"Discard\" now, they will be discarded,\nif you click \"Apply\" they will be applied."), QMessageBox::Discard, QMessageBox::Apply) == QMessageBox::Apply)
            objectController->apply();
    }

    QDialog::accept();
}

// ------------------------------ on_objectList_itemSelectionChanged -------------------------------
void SettingsDialog::on_objectList_itemSelectionChanged() {
    if (myUI->objectList->selectedItems().size() > 0) {
        if (objectController->isModified()) {
            if (QMessageBox::question(this, "Unsaved changes", tr("There are unsaved changes in current item.\nIf you click \"Discard\" now, they will be discarded,\nif you click \"Apply\" they will be applied."), QMessageBox::Discard, QMessageBox::Apply) == QMessageBox::Apply)
                objectController->apply();
        }
        QListWidgetItem * selected = myUI->objectList->selectedItems()[0];
        objectController->setObject(editedObjectMap.value(selected->text()));
    } else {
        objectController->setObject(NULL);
    }
}

// ------------------------------ on_resetConfigurationButton_released -------------------------------
void SettingsDialog::on_resetConfigurationButton_released() {
    QMessageBox warnMsg(QMessageBox::Warning,
                        tr("Reset All Preferences?"),
                        tr("This action will erase all your modified settings."
                           "All user preferences will be deleted."
                           "The default configuration will be loaded instead"),
                        QMessageBox::Ok | QMessageBox::Cancel,
                        this);
    warnMsg.setDefaultButton(QMessageBox::Cancel);
    warnMsg.setButtonText(QMessageBox::Ok, tr("Reset Configuration"));

    if (QMessageBox::Ok == warnMsg.exec()) {

        QSettings & settings = Application::getSettings();
        settings.clear();

        QMessageBox restartMsg(QMessageBox::Warning,
                               tr("Please, restart the application"),
                               tr("All default configuration and user preference have been deleted. "
                                  "Please exit the application and restart for this action to take effect!"),
                               QMessageBox::Ok,
                               this);
        restartMsg.exec();

        QDialog::reject();
    }
}


// ------------------------------ on_addComponentExtensionButton_released -------------------------------
void SettingsDialog::on_addComponentExtensionButton_released() {
    QStringList files = QFileDialog::getOpenFileNames(0, "Add a ComponentExtension (Component) Plugin", Core::getComponentDirectories().first(), "Component Plugin (*.so.* *.dll *.dylib)");

    QStringList pluginFile = files;
    QStringList::Iterator it = pluginFile.begin();

    while (it != pluginFile.end()) {
        if (!ExtensionManager::loadExtension(ExtensionManager::COMPONENT, *it)) {
            QMessageBox::warning(this, "ComponentExtension Error", tr("Can't load plugin from file") + (*it) + tr("!\nTry recompile the plugin\n(plugin might be outdated)"));
        } else {
            Application::showStatusBarMessage("Plugin " + (*it) + " loaded.");
            // Add the filename in the application settings: this is a manually loaded plugin, it should therefore
            // be kept in a list for the next run
            userComponents += (*it);
            writeUserExtensions();
        }

        ++it; //next one!
    }

    // update the display
    updateComponentExtensionList();
}

// ----------------- on_removeComponentExtensionButton_released ----------------------------
void SettingsDialog::on_removeComponentExtensionButton_released() {
    // remove the plugin
    QString pluginExt = myUI->componentExtensionList->item(myUI->componentExtensionList->currentRow(), 1)->text();
    ExtensionManager::unloadComponentExtension(pluginExt);

    // remove from settings
    QString location = myUI->componentExtensionList->item(myUI->componentExtensionList->currentRow(), 2)->text();
    userComponents.removeAll(location);
    writeUserExtensions();

    // update the display
    updateComponentExtensionList();

    // removing is allowed by selecting a new component extension
    myUI->removeComponentExtensionButton->setEnabled(false);
}

// ------------------------------ on_componentExtensionList_cellClicked -------------------------------
void SettingsDialog::on_componentExtensionList_cellClicked(int row, int column) {
    const ComponentExtension *ce = ExtensionManager::getComponentExtension(myUI->componentExtensionList->item(myUI->componentExtensionList->currentRow(), 0)->text());
    myUI->removeComponentExtensionButton->setEnabled(userComponents.contains(ce->getLocation()));
    myUI->componentPluginDescription->setText(ce->getDescription());
}

// ------------------------------ on_addActionExtensionButton_released -------------------------------
void SettingsDialog::on_addActionExtensionButton_released() {
    QStringList files = QFileDialog::getOpenFileNames(0, "Add an Action Extension", Core::getActionDirectories().first(), "Action Extension (*.so.* *.dll *.dylib)");

    QStringList pluginFile = files;
    QStringList::Iterator it = pluginFile.begin();

    while (it != pluginFile.end()) {
        if (!ExtensionManager::loadExtension(ExtensionManager::ACTION, *it)) {
            QMessageBox::warning(this, "ActionExtension Error", tr("Can't load plugin from file") + (*it) + tr("!\nTry recompile the plugin\n(plugin might be outdated)"));
        } else {
            Application::showStatusBarMessage("Plugin " + (*it) + " loaded.");
            // Add the filename in the application settings: this is a manually loaded plugin, it should therefore
            // be kept in a list for the next run
            userActions += (*it);
            writeUserExtensions();
        }

        ++it; //next one!
    }

    // update the display
    updateActionExtensionList();
}


// ----------------- on_removeActionExtensionButton_released ----------------------------
void SettingsDialog::on_removeActionExtensionButton_released() {
    // remove the plugin
    QString actionFileName = myUI->actionExtensionList->item(myUI->actionExtensionList->currentRow(), 2)->text();
    ActionList actions = ExtensionManager::getActionExtensions().value(actionFileName)->getActions();
    bool reallyDelete = true;
    if (actions.size() > 1) {
        QString actionName = myUI->actionExtensionList->item(myUI->actionExtensionList->currentRow(), 0)->text();
        QStringList actionNames;
        foreach(Action *a, actions) {
            if (a->getName() != actionName)
                actionNames << a->getName();
        }
        reallyDelete = (QMessageBox::warning(this, "Removing multiple actions", tr("Removing action \"") + actionName + tr(" \" will also remove the following actions:<ul><li>") + actionNames.join("</li><li>") + tr("</li></ul>Are you sure you want to delete all these actions?"), QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Yes);
    }
    if (reallyDelete) {
        ExtensionManager::unloadActionExtension(actionFileName);
        // remove from settings
        userActions.removeAll(actionFileName);
        writeUserExtensions();

        // update the display
        updateActionExtensionList();

        // removing is allowed by selecting a new component extension
        myUI->removeActionExtensionButton->setEnabled(false);
    }

}

// ------------------------------ on_actionExtensionList_cellClicked -------------------------------
void SettingsDialog::on_actionExtensionList_cellClicked(int row, int column) {
    myUI->removeActionExtensionButton->setEnabled(userActions.contains(myUI->actionExtensionList->item(myUI->actionExtensionList->currentRow(), 2)->text()));
    myUI->actionExtensionDescription->setText(Application::getAction
            (myUI->actionExtensionList->item(myUI->actionExtensionList->currentRow(), 0)->text())->getDescription());
}

}

