/*****************************************************************************
 * $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 "PropertyExplorer.h"

// -- Core stuff
#include <Application.h>
#include <Component.h>

// -- QT stuff
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QTabWidget>
#include <QSettings>

using namespace camitk;

// ---------------- constructor ----------------
PropertyExplorer::PropertyExplorer(QString name) : Viewer(name, Viewer::DOCKED) {
    theController = nullptr;
    tabWidget = nullptr;
    revertButton = nullptr;
    applyButton = nullptr;
    currentComponent = nullptr;

    setIcon(QPixmap(":/properties"));
    setDescription("The property explorer shows the properties of the currently selected component");
    createProperties();
}

// ---------------- destructor ----------------
PropertyExplorer::~PropertyExplorer() {
    clear();

    if (propertyObject != nullptr) {
        delete propertyObject; // this will also delete viewModeProperty
    }
}

// ---------------- refresh ----------------
void PropertyExplorer::refresh(Viewer* whoIsAsking) {
    if (tabWidget == nullptr)  {
        // no widget → no refresh needed
        return;
    }

    // refresh the explorer if there is one selected Component
    if (!Application::getSelectedComponents().isEmpty()) {
        // last selected is different from currently selected
        if (currentComponent != Application::getSelectedComponents().last()) {
            //-- clear
            clear();
            //-- update currentComponent
            currentComponent = Application::getSelectedComponents().last();
            currentComponent->setVisibility(this->getName(), true);
            //-- get the property widgets
            QWidget* customWidget;

            for (unsigned int i = 0; i < currentComponent->getNumberOfPropertyWidget(); i++) {
                customWidget = currentComponent->getPropertyWidgetAt(i);

                if (customWidget) {
                    tabWidget->addTab(customWidget, customWidget->objectName());
                }
            }
        }

        //--  update the property editor
        // first clear all (otherwise if a new dynamic properties was added, it won't show)
        theController->setObject(nullptr);
        // rebuild all GUI
        theController->setObject(currentComponent->getPropertyObject());

        //-- select the desired tab in the PropertyExplorer
        this->selectIndex(currentComponent->getIndexOfPropertyExplorerTab());
    }
    else {
        // clean everything only if there is no selection and cleaning was not already done
        if (currentComponent != nullptr) {
            // clear tabs
            clear();
            theController->setObject(nullptr);
        }
    }

    //-- update button states
    revertButton->setEnabled(currentComponent != nullptr && currentComponent->getPropertyObject() != nullptr);
    applyButton->setEnabled(currentComponent != nullptr && currentComponent->getPropertyObject() != nullptr);
}

// ---------------- getWidget ----------------
QWidget* PropertyExplorer::getWidget() {
    if (tabWidget == nullptr) {
        tabWidget = new QTabWidget();
        tabWidget->setWindowTitle(tr("Property Tabs"));

        // create the property editor tab itself
        auto* propertyFrame = new QFrame();
        auto* propertyTabLayout = new QVBoxLayout;
        propertyTabLayout->setSpacing(2);
        propertyTabLayout->setContentsMargins(2, 2, 2, 2);

        // get user pref for view mode
        QSettings& settings = Application::getSettings();
        settings.beginGroup("PropertyExplorer");
        ObjectController::ViewMode mode = (ObjectController::ViewMode) propertyObject->property(viewModeProperty->getName().toStdString().c_str()).toInt();
        settings.endGroup();

        // first the property editor
        theController = new ObjectController(propertyFrame, mode);
        propertyTabLayout->addWidget(theController);

        // then the buttons
        auto* buttonFrame = new QFrame();
        auto* buttonLayout = new QHBoxLayout();

        applyButton = new QPushButton("Apply");
        buttonLayout->addWidget(applyButton);
        applyButton->show();
        applyButton->setEnabled(false);

        revertButton = new QPushButton("Revert");
        buttonLayout->addWidget(revertButton);
        revertButton->show();
        revertButton->setEnabled(false);

        buttonFrame->setLayout(buttonLayout);
        propertyTabLayout->addWidget(buttonFrame);

        // connect the buttons
        QObject::connect(applyButton, &QPushButton::clicked, this, [this]() {
            theController->apply();
            if (currentComponent != nullptr) {
                currentComponent->setModified(true);
                currentComponent->setNodeModified(true);
            }
            Application::refresh();
        });
        QObject::connect(revertButton, &QPushButton::clicked, theController, &ObjectController::revert);

        propertyFrame->setLayout(propertyTabLayout);

        tabWidget->addTab(propertyFrame, tr("P&roperties"));

        // connect the tab widget index change signal
        QObject::connect(this->tabWidget, &QTabWidget::currentChanged, this, &PropertyExplorer::updateTabIndexToDisplay);
    }

    return tabWidget;
}

// ---------------- getPropertyObject ----------------
PropertyObject* PropertyExplorer::getPropertyObject() {
    return propertyObject;
}

// ---------------- clear ----------------
void PropertyExplorer::clear() {
    if (tabWidget != nullptr) {
        // page #0 is the property widget (not to be counted)
        for (int i = tabWidget->count() - 1; i > 0; i--) {
            tabWidget->removeTab(i);
        }
    }

    if (currentComponent != nullptr && Application::isAlive(currentComponent)) {
        currentComponent->setVisibility(this->getName(), false);
    }

    currentComponent = nullptr;
}

// ---------------- selectWidget ----------------
void PropertyExplorer::selectWidget(QWidget* widget) {
    // get the last component tab widgets
    QWidget* customWidget;

    for (int i = 0; i < this->tabWidget->count(); i++) {
        customWidget = this->tabWidget->widget(i);

        if (customWidget && (customWidget == widget)) {
            this->tabWidget->setCurrentWidget(widget);
            return;
        }
    }
}

// ---------------- selectIndex ----------------
void PropertyExplorer::selectIndex(unsigned int index) {
    if ((index >= 0) && ((int)index < this->tabWidget->count())) {
        this->tabWidget->setCurrentIndex(index);
    }
}

// ---------------- updateTabIndexToDisplay ----------------
void PropertyExplorer::updateTabIndexToDisplay(int index) {
    if (!Application::getSelectedComponents().isEmpty()) {
        currentComponent = Application::getSelectedComponents().last();
        currentComponent->setIndexOfPropertyExplorerTab((unsigned int)index);
    }
}

// ---------------- eventFilter ----------------
bool PropertyExplorer::eventFilter(QObject* object, QEvent* event) {
    // watch propertyObject instance for dynamic property changes
    if (event->type() == QEvent::DynamicPropertyChange) {
        // objectcontroller view mode update
        ObjectController::ViewMode mode = (ObjectController::ViewMode) propertyObject->property(viewModeProperty->getName().toStdString().c_str()).toInt();
        theController->setViewMode(mode);
        QSettings& settings = Application::getSettings();
        settings.beginGroup("PropertyExplorer");
        settings.setValue("viewMode", mode);
        settings.endGroup();
        return true;

    }
    else {
        // pass the event on to the parent class
        return Viewer::eventFilter(object, event);
    }

}

// ---------------- createProperties ----------------
void PropertyExplorer::createProperties() {
    // viewer properties
    propertyObject = new PropertyObject("Property Explorer");

    // view mode property
    // get the value store in the application settings
    QSettings& settings = Application::getSettings();
    settings.beginGroup("PropertyExplorer");
    ObjectController::ViewMode mode = (ObjectController::ViewMode) settings.value("viewMode", ObjectController::BUTTON).toInt();
    settings.endGroup();

    viewModeProperty = new Property("View mode", mode, "The display type of the PropertyExplorer", "");
    viewModeProperty->setEnumTypeName("camitk::ObjectController::ViewMode");
    QStringList viewModeNames;
    viewModeNames << "Tree" << "Groupbox" << "Button";
    viewModeProperty->setAttribute("enumNames", viewModeNames);
    propertyObject->addProperty(viewModeProperty);

    // propertyObject is monitored by the PropertyExplorer instance (this) when its properties change
    propertyObject->installEventFilter(this);
}
