/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * 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$
 ****************************************************************************/

#include "CamiTKExtensionModelPresenter.h"

// Qt
#include <QTabBar>
#include <QFileInfo>
#include <QMessageBox>
#include <QPushButton>

// syntax highlighter for the JSON tab
#include "JsonSyntaxHighlighter.h"

// custom editor and widgets
#include "CppExtensionTabPresenter.h"
#include "PythonExtensionTabPresenter.h"
#include "CppActionTabPresenter.h"
#include "PythonActionTabPresenter.h"
#include "CustomInputDialog.h"

// -------------------- constructor --------------------
CamiTKExtensionModelPresenter::CamiTKExtensionModelPresenter(const QString& camitkFilePath, const QString& newExtensionName, const QString& language, const QString& cppGenerationType, QWidget* parent) : QWidget(parent) {
    // create main widget (tabs)
    tabWidget = new QTabWidget(this);
    tabWidget->setTabsClosable(true);
    connect(tabWidget, &QTabWidget::tabCloseRequested, this, &CamiTKExtensionModelPresenter::tabCloseRequest);
    jsonTextEdit = new QTextEdit();

    QVBoxLayout* layout = new QVBoxLayout(this);
    layout->addWidget(tabWidget);
    setLayout(layout);

    // Check the file
    if (camitkFilePath.isEmpty()) {
        // create empty camitk extension file
        camitkExtensionModel.clear();
    }
    else {
        camitkExtensionModel.load(QFileInfo(camitkFilePath).absoluteFilePath());
    }

    // check the model
    if (!camitkExtensionModel.getModel().isValid()) {
        // if the model is invalid, it is empty, create a sample one
        camitkExtensionModel.resetModel(newExtensionName);
    }

    // now there is at least a sample action extensions
    VariantDataModel& extensions = camitkExtensionModel.getModel();

    // if language was given this means it is a new extension file
    if (!language.isNull()) {
        // add the language
        extensions["language"] = language;
        if (!isPython()) {
            extensions["generationType"] = cppGenerationType;
        }
    }

    //-- First tab is the extension itself
    if (isPython()) {
        tabWidget->addTab(new PythonExtensionTabPresenter(this, extensions), QIcon(":/python-logo-only.svg"), extensions["name"]);
    }
    else {
        tabWidget->addTab(new CppExtensionTabPresenter(this, extensions), QIcon(":/cpp_logo.svg"), extensions["name"]);
    }

    // it is not possible to remove the action extension tab
    tabWidget->tabBar()->setTabButton(0, QTabBar::RightSide, nullptr);

    //-- one tab per action
    VariantDataModel& allActions = extensions["actions"];
    for (unsigned int i = 0; i < allActions.size(); i++) {
        ActionTabPresenter* actionTabPresenter;
        if (isPython()) {
            actionTabPresenter = new PythonActionTabPresenter(this, allActions[i], tabWidget);
        }
        else {
            actionTabPresenter = new CppActionTabPresenter(this, allActions[i], tabWidget);
        }
        tabWidget->addTab(actionTabPresenter, QIcon(":/action.svg"), allActions[i]["name"]);
    }

    //-- the last tab shows the json source
    QWidget* jsonTab = new QWidget(tabWidget);
    QVBoxLayout* boxLayout = new QVBoxLayout(jsonTab);
    jsonTextEdit->setReadOnly(true);
    jsonTextEdit->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::TextSelectableByMouse);
    JsonSyntaxHighlighter* highlighter = new JsonSyntaxHighlighter(jsonTextEdit->document());
    jsonTextEdit->setPlainText(camitkExtensionModel.toJSON());
    boxLayout->addWidget(jsonTextEdit);
    tabWidget->addTab(jsonTab, QIcon(":/JSON_logo.svg"), "JSON");
    // it is not possible to remove the JSON tab
    tabWidget->tabBar()->setTabButton(tabWidget->count() - 1, QTabBar::RightSide, nullptr);

    //-- add the + button to create new action
    QPushButton* addTabButton = new QPushButton("+");
    addTabButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
    addTabButton->setFixedSize(20, 20);
    addTabButton->setToolTip("Add a new action to the extension");
    addTabButton->setWhatsThis(addTabButton->toolTip());
    tabWidget->addTab(new QWidget(), "");
    tabWidget->setTabEnabled(tabWidget->count() - 1, false);
    tabWidget->tabBar()->setTabButton(tabWidget->count() - 1, QTabBar::RightSide, addTabButton);
    connect(addTabButton, &QPushButton::clicked, this, &CamiTKExtensionModelPresenter::plusButtonClicked);

    //-- change tool tips
    for (auto item : tabWidget->findChildren<QAbstractButton*>()) {
        if (item->inherits("CloseButton")) {
            item->setToolTip("Remove this action from the extension (cannot be undone)"); // Default "Close Tab"
            item->setWhatsThis(item->toolTip());
        }
    }
}

// -------------------- Destructor --------------------
CamiTKExtensionModelPresenter::~CamiTKExtensionModelPresenter() {
//    delete tabWidget;
}

// -------------------- emitShowMessage --------------------
void CamiTKExtensionModelPresenter::emitShowMessage(const QString& message, int durationMilliseconds, bool warning) {
    emit showMessage(message, durationMilliseconds, warning);
}

// -------------------- tabCloseRequest --------------------
void CamiTKExtensionModelPresenter::tabCloseRequest(int index) {
    QWidget* closingTab = tabWidget->widget(index);
    ExtensionTypeTabPresenter* extensionTypeTabPresenter = dynamic_cast<ExtensionTypeTabPresenter*>(closingTab);

    if (extensionTypeTabPresenter != nullptr) {
        QString type = extensionTypeTabPresenter->getExtensionType();
        VariantDataModel& extensionToDelete = extensionTypeTabPresenter->getDataModel();

        if (extensionToDelete.isValid()) {
            //-- ask for confirmation
            QString extensionName = extensionToDelete["name"];
            QMessageBox::StandardButton reply = QMessageBox::warning(tabWidget, "Remove " + type,
                                                tr("Warning: removing CamiTK %1 '%2' cannot be undone. Are you sure you want to proceed?").arg(type).arg(extensionName),
                                                QMessageBox::Yes | QMessageBox::No);

            if (reply == QMessageBox::Yes) {
                // remove the UI
                tabWidget->removeTab(index);

                // remove the model (set value to invalid QVariant)
                extensionToDelete.remove();

                modelUpdated();
                emit showMessage(type + " " + extensionName + " deleted.");
            }
        }
    }
}

// -------------------- plusButtonClicked --------------------
void CamiTKExtensionModelPresenter::plusButtonClicked() {
    VariantDataModel& allActions = camitkExtensionModel.getModel()["actions"];

    // ask for the new action name
    CustomInputDialog dialog("New Action Name", "Please enter the new action name:", "<p>Please choose wisely. This name will be used to generate the action source code.</p><p>While renaming an action is always possible, it should be done very carefully as there are many consequences. Therefore once entered here the new action name will be presented as a <i>read only field</i>. If a rename is needed in the future, changing the action name can be done by directly modifying the .camitk file.</p>", "New Action " + QString::number(allActions.size() + 1), tabWidget);

    if (dialog.exec() == QDialog::Accepted) {
        QString inputName = dialog.getInput();
        if (!inputName.isEmpty()) {
            VariantDataModel newAction = VariantDataModel(QVariantMap());
            newAction["name"] = inputName;
            allActions.append(newAction);

            ActionTabPresenter* actionTabPresenter;
            if (isPython()) {
                actionTabPresenter = new PythonActionTabPresenter(this, allActions.last(), tabWidget);
            }
            else {
                actionTabPresenter = new CppActionTabPresenter(this, allActions.last(), tabWidget);
            }
            int index = tabWidget->insertTab(tabWidget->count() - 2, actionTabPresenter, QIcon(":/action.svg"), newAction["name"]);
            tabWidget->setCurrentIndex(index);

            // update tooltips every time a tab is added
            for (auto item : tabWidget->findChildren<QAbstractButton*>()) {
                if (item->inherits("CloseButton")) {
                    item->setToolTip("Remove this action from the extension"); // Default "Close Tab"
                    item->setWhatsThis(item->toolTip());
                }
            }

            modelUpdated();
            emit showMessage("New action added.");
        }
    }
}

// -------------------- modelUpdated --------------------
void CamiTKExtensionModelPresenter::modelUpdated() {
    jsonTextEdit->setPlainText(camitkExtensionModel.toJSON());

    emit dataModelUpdated();
}

// -------------------- isModified --------------------
bool CamiTKExtensionModelPresenter::isModified() {
    return camitkExtensionModel.isModified();
}

// -------------------- isHotPlug --------------------
bool CamiTKExtensionModelPresenter::isHotPlug() {
    return camitkExtensionModel.getModel().isValid() && camitkExtensionModel.getModel()["generationType"].toString() == "HotPlug";
}

// -------------------- isPython --------------------
bool CamiTKExtensionModelPresenter::isPython() {
    if (camitkExtensionModel.getModel().isValid()) {
        // it's python only if there is a language key and its value is python
        const VariantDataModel& language = camitkExtensionModel.getModel()["language"];
        return (language.isValid() && language.toString() == "Python");
    }
    else {
        return false;
    }
}

// -------------------- saveExtensionFile --------------------
void CamiTKExtensionModelPresenter::saveExtensionFile(const QFile& file) {
    camitkExtensionModel.save(QFileInfo(file).absoluteFilePath());
}