/*****************************************************************************
 * $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 "ActionWidget.h"
#include "Action.h"
#include "ObjectController.h"
#include "Component.h"
#include "Application.h"
#include "Property.h"

#include <QLabel>
#include <QTextEdit>
#include <QTextBrowser>
#include <QTabWidget>
#include <QDialog>
#include <QJsonObject>
#include <QStyle>

#include "ActionExtension.h"

namespace camitk {

// -------------------- constructor --------------------
ActionWidget::ActionWidget(Action* action): QFrame() {
    myAction = action;

    // create the property editor tab itself
    setObjectName(myAction->getName());

    // The main layout
    auto* widgetLayout = new QVBoxLayout;

    //-- Action's name and icon
    auto* nameLayout = new QHBoxLayout;
    QLabel* iconPicture = nullptr;
    if (!myAction->getIcon().isNull()) {
        iconPicture = new QLabel();
        iconPicture->setPixmap(myAction->getIcon());
        nameLayout->addWidget(iconPicture);
    }
    QLabel* actionNameLabel = new QLabel("<b>" + myAction->getName() + "</b>");
    QSpacerItem* spacer = new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Fixed);
    QPushButton* infoButton = new QPushButton(actionNameLabel->style()->standardIcon(QStyle::SP_MessageBoxInformation), "");
    infoButton->setFixedHeight(infoButton->fontMetrics().height());
    infoButton->setToolTip(QString("Help on action \"%1\"").arg(myAction->getName()));
    infoButton->setWhatsThis(QString("Help on action \"%1\"").arg(myAction->getName()));
    infoButton->setStyleSheet(R"(
        QPushButton {
            background-color: transparent;
            border: none;
            padding: 2px;
        }
        QPushButton:hover {
            background-color: palette(button);
            border: 1px solid palette(highlight);
        }
    )");
    connect(infoButton, &QPushButton::clicked, this, &ActionWidget::showInfo);

    nameLayout->addWidget(actionNameLabel);
    nameLayout->addItem(spacer);
    nameLayout->addWidget(infoButton);
    widgetLayout->addLayout(nameLayout);

    // get the proper text height
    actionNameLabel->adjustSize();
    if (iconPicture != nullptr) {
        // fix the icon height
        iconPicture->setFixedSize(actionNameLabel->height(), actionNameLabel->height());
        iconPicture->setScaledContents(true);
    }

    //-- property editor
    objectController = new ObjectController(this, ObjectController::BUTTON);
    widgetLayout->addWidget(objectController);

    //-- button frame
    buttonFrame = new QFrame();
    auto* buttonLayout = new QHBoxLayout;
    applyButton = new QPushButton(tr("Apply"));
    buttonLayout->addWidget(applyButton);
    QPushButton* revertButton = new QPushButton(tr("Revert"));
    buttonLayout->addWidget(revertButton);
    buttonFrame->setLayout(buttonLayout);

    widgetLayout->addWidget(buttonFrame);

    // connect the buttons
    QObject::connect(applyButton, SIGNAL(clicked()), objectController, SLOT(apply()));
    // from http://doc.trolltech.com/4.7/signalsandslots.html
    // "If several slots are connected to one signal, the slots will be executed one after the other,
    // in the order they have been connected, when the signal is emitted."
    // it means that here when the user click on apply, it will first update the properties
    // and then call the apply() method
    QObject::connect(applyButton, SIGNAL(clicked()), myAction, SLOT(applyAndRegister()));
    QObject::connect(revertButton, SIGNAL(clicked()), objectController, SLOT(revert()));

    // Now tell the ObjectController that this Action itself is the one to manage
    objectController->setObject(myAction);

    setLayout(widgetLayout);
}

// -------------------- destructor --------------------
ActionWidget::~ActionWidget() {
    // TODO delete
}

// -------------------- setButtonVisibility --------------------
void ActionWidget::setButtonVisibility(bool visible) {
    buttonFrame->setVisible(visible);
}

// -------------------- setButtonVisibility --------------------
void ActionWidget::setApplyButtonText(QString text) {
    applyButton->setText(text);
}

// -------------------- showInfo --------------------
void ActionWidget::showInfo() {
    QDialog dialog;
    dialog.setWindowTitle(QString("Help on action \"%1\"").arg(myAction->getName()));
    dialog.resize(400, 300);

    //-- palette with the "normal" neutral background
    QPalette readOnlyPalette = dialog.palette();
    QColor windowBackgroundColor = readOnlyPalette.color(QPalette::Window);
    readOnlyPalette.setColor(QPalette::Base, windowBackgroundColor);

    //-- title with the name
    auto* nameLayout = new QHBoxLayout;
    QLabel* iconPicture = new QLabel;
    iconPicture->setPixmap(myAction->getIcon());
    nameLayout->addWidget(iconPicture);
    QLabel* actionNameLabel = new QLabel("<b>" + myAction->getName() + "</b>");
    nameLayout->addWidget(actionNameLabel);
    // get the proper text height
    actionNameLabel->adjustSize();
    // fix the icon height
    iconPicture->setFixedSize(actionNameLabel->height(), actionNameLabel->height());
    iconPicture->setScaledContents(true);

    //-- main widget with 3 tabs
    QTabWidget* widgetTabs = new QTabWidget;

    //-- 1. general tab
    QString description = "";
    description += "<table border='1' align='center' width='90%' cellspacing='0' cellpadding='4'>";
    description += "<tr><td><b>Family</b></td><td>" + myAction->getFamily() + "</td></tr>";
    //TODO uncomment when moved to Action description += "<tr><td><b>Extension</b></td><td>" + myAction->getExtensionName() + "</td></tr>";
    description += "<tr><td><b>Tags</b></td><td>" + myAction->getTag().join(", ") + "</td></tr>";
    description += "<tr><td><b>Input</b></td><td>" + (myAction->getComponentClassName().isEmpty() ? "None" : myAction->getComponentClassName()) + "</td></tr>";
    description += "</table>";
    QTextEdit* generalInfoTextEdit = new QTextEdit(description);
    generalInfoTextEdit->setFrameShape(QFrame::NoFrame);
    generalInfoTextEdit->setFrameShadow(QFrame::Plain);
    generalInfoTextEdit->setReadOnly(true);
    generalInfoTextEdit->setPalette(readOnlyPalette);

    //-- 2. description tab
    QTextBrowser* descriptionTextEdit = new  QTextBrowser();
    descriptionTextEdit->setHtml(myAction->getDescription());
    descriptionTextEdit->setOpenExternalLinks(true);
    descriptionTextEdit->setFrameShape(QFrame::NoFrame);
    descriptionTextEdit->setFrameShadow(QFrame::Plain);
    descriptionTextEdit->setPalette(readOnlyPalette);

    //-- 3. parameters' tab
    description = "";
    // introspect all parameters
    QByteArrayList dynamicPropertyNames = myAction->dynamicPropertyNames();
    for (int idx = 0; idx < dynamicPropertyNames.size(); ++idx) {
        QJsonObject parameterInfo = Property::getPropertyInformation(myAction, QString(dynamicPropertyNames.at(idx)));
        description += "<h4>" + parameterInfo["name"].toString();
        if (parameterInfo["readOnly"].toBool()) {
            description += " (read only)";
        }
        description += "</h4>";
        description += "<table border='1' align='center' width='90%' cellspacing='0' cellpadding='4'>";
        description += "<tr><td><b>Type</b></td><td>" + parameterInfo["type"].toString() + "</td></tr>";

        description += "<tr><td><b>Description</b></td><td>" + parameterInfo["description"].toString() + "</td></tr>";
        if (!parameterInfo["unit"].toString().isEmpty()) {
            description += "<tr><td><b>Unit</b></td><td>" + parameterInfo["unit"].toString() + "</td></tr>";
        }
        if (parameterInfo["type"].toString() == "enum") {
            description += "<tr><td><b>Possible values</b></td><td>" + parameterInfo["enumValues"].toVariant().toStringList().join(", ");
        }
        if (parameterInfo["type"].toString() == "QString" && parameterInfo.contains("regExp")) {
            description += "<tr><td><b>Constrained to regular expression</b></td><td>" + parameterInfo["regExp"].toString() + "</td></tr>";
        }
        if (parameterInfo.contains("minimum")) {
            description += "<tr><td><b>Minimal value</b></td><td>" + QString::number(parameterInfo["minimum"].toDouble()) + "</td></tr>";
        }
        if (parameterInfo.contains("maximum")) {
            description += "<tr><td><b>Maximal value</b></td><td>" + QString::number(parameterInfo["maximum"].toDouble()) + "</td></tr>";
        }
        description += "</table>";
    }

    QTextBrowser* parametersTextEdit = new QTextBrowser();
    parametersTextEdit->setHtml(description);
    parametersTextEdit->setOpenExternalLinks(true);
    parametersTextEdit->setFrameShape(QFrame::NoFrame);
    parametersTextEdit->setFrameShadow(QFrame::Plain);
    parametersTextEdit->setPalette(readOnlyPalette);

    //-- 4. Targets' tab
    QTextEdit* targetTextEdit = new QTextEdit(getTargetLabel());
    targetTextEdit->setFrameShape(QFrame::NoFrame);
    targetTextEdit->setFrameShadow(QFrame::Plain);
    targetTextEdit->setReadOnly(true);
    targetTextEdit->setPalette(readOnlyPalette);

    //-- populate widget tabs
    widgetTabs->addTab(generalInfoTextEdit, tr("General"));
    widgetTabs->addTab(descriptionTextEdit, tr("Description"));
    widgetTabs->addTab(parametersTextEdit, tr("Parameters"));
    widgetTabs->addTab(targetTextEdit, tr("Inputs"));

    //-- finally the close button
    QPushButton* closeButton = new QPushButton("Close", &dialog);
    QObject::connect(closeButton, &QPushButton::clicked, &dialog, &QDialog::accept);

    QVBoxLayout* mainLayout = new QVBoxLayout(&dialog);
    mainLayout->addLayout(nameLayout);
    mainLayout->addWidget(widgetTabs);
    mainLayout->addWidget(closeButton);

    dialog.setLayout(mainLayout);
    dialog.exec();
}

// -------------------- update --------------------
void ActionWidget::update() {
    // force update
    objectController->setObject(nullptr);
    objectController->setObject(myAction);
}

// -------------------- getTargetLabel --------------------
QString ActionWidget::getTargetLabel() {
    QString targetNames = "<p>Currently Selected Targets :</p><ul>";

    for (Component* comp : myAction->getTargets()) {
        if (Application::isAlive(comp)) {
            targetNames += "<li> " + comp->getName() + "<i> (" + comp->metaObject()->className() + ") </i>" + "</li>";
        }
    }

    targetNames += "</ul>";
    return targetNames;
}

// -------------------- setAutoUpdateProperty --------------------
void ActionWidget::setAutoUpdateProperty(bool autoUpdate) {
    objectController->setAutoUpdateProperty(autoUpdate);
}

}
