/*****************************************************************************
 * $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$
 ****************************************************************************/

//-- MML Component
#include "MMLComponent.h"
#include "MMLDisplay.h"

//-- PML Component
#include <PMLComponent.h>

//-- MML includes
#include <MonitoringManager.h>
#include <MonitoringDialog.h>
#include <MonitoringGuiManager.h>

//-- CamiTK
#include <Application.h>
#include <AbortException.h>
#include <MainWindow.h>
#include <Action.h>
#include <Log.h>

using namespace camitk;

//-- Qt
#include <QDockWidget>

// -------------------- default constructor --------------------
MMLComponent::MMLComponent(const QString& fileName) : Component(fileName, QFileInfo(fileName).baseName()) {
    QString file = fileName;
    QFileInfo fi = QFileInfo(fileName);

    try {
        // initialize the MML export file name
        exportedMml = "";

        // create a simple MML from a Sofa scn file
        if (fi.completeSuffix() == "scn") {
            // create mml file in temp directory
            file = QDir::tempPath() + "/" + QFileInfo(fi).baseName() + ".mml";

            // check if the mml already exist
            if (QFileInfo::exists(file)) {
                CAMITK_WARNING(tr("MMLComponent::MMLComponent(..): creating MMLComponent from %1: mml file already exists in %2 (overwriting it)").arg(fileName, file))
            }

            // create an empty mmlIn file with .scn (in the same directory as the .scn)
            mml::TimeParameter dt = mml::TimeParameter(0.1, "s");
            mml::TimeParameter refresh = mml::TimeParameter(1, "s");
            mml::MonitoringIn mmlIn(dt, refresh, "sofa");
            mmlIn.simulatorFile(fileName.toStdString().c_str());

            xml_schema::namespace_infomap map;
            map[""].name = "";
            map[""].schema = "";

            exportedMml = file;

            std::ofstream ofs(exportedMml.toStdString().c_str());
            mml::monitoringIn(ofs, mmlIn, map);
            // to make sure it will be saved again
            setModified();
        }

        mmlGUI = new MonitoringGuiManager;
        mmlGUI->getDialog()->hide();
        display = new MMLDisplay(this);

        // never selected yet
        neverSelected = true;

        if (mmlGUI->loadMmlInFile(file)) {
            // in order to give the ownership of the pml pointer to the PMLComponent, we need to use takePml() not getPml()
            pmlComponent = new PMLComponent(mmlGUI->getMonitoringManager()->takePml(),  mmlGUI->getMonitoringManager()->getPmlFileName().c_str());
            // in order to give the ownership of the lml pointer to the LoadsManager of the PMManagerDC, we need to use takeLml() not getLml()
            // TODO display the LML, one way or another
            // was: pmlComponent->getLoadsManager()->setLoads(mmlGUI->getMonitoringManager()->takeLml());
            addChild(pmlComponent);
        }
        else {
            if (display) {
                delete display;
                display = nullptr;
            }

            if (mmlGUI) {
                delete mmlGUI;
                mmlGUI = nullptr;
            }
            throw AbortException("Cannot open " + myFileName.toStdString() + "\nThe given simulator is probably not supported");
        }
    }
    catch (const xml_schema::exception& e) {
        throw AbortException(tr("xml_schema exception, attempt to read from %1\n%2").arg(myFileName, QString::fromStdString(e.what())).toStdString());
    }
}

// ---------------------- destructor ----------------------------
MMLComponent::~MMLComponent() {
    if (pmlComponent) {
        delete pmlComponent;
        pmlComponent = nullptr;
    }

    if (display) {
        delete display;
        display = nullptr;
    }

    if (mmlGUI) {
        delete mmlGUI;
        mmlGUI = nullptr;
    }

}

//-------------------- setSelected -------------------
void MMLComponent::setSelected(const bool selected, const bool recursive) {
    camitk::Component::setSelected(selected, false);
    // set the default action to be the PML explorer
    if (neverSelected) {
        Action* defaultAction = Application::getAction("Simulation Dialog");
        if (defaultAction) {
            defaultAction->getQAction()->trigger();
        }
        neverSelected = false;
    }
}

// -------------------- getMonitoringGuiManager --------------------
MonitoringGuiManager* MMLComponent::getMonitoringGuiManager() {
    return mmlGUI;
}

// -------------------- getPMLComponent --------------------
PMLComponent* MMLComponent::getPMLComponent() {
    return pmlComponent;
}

// -------------------- getDisplay --------------------
MMLDisplay* MMLComponent::getDisplay() {
    return display;
}


// -------------------- connectPml --------------------
void MMLComponent::connectPml() {
    removeChild(pmlComponent);
    delete pmlComponent;
    pmlComponent = nullptr;
    deleteChildren();

    // this gives the ownership of pml pointer to the PML Component, use takePml() not getPml()
    pmlComponent = new PMLComponent(mmlGUI->getMonitoringManager()->takePml(),  mmlGUI->getMonitoringManager()->getPmlFileName().c_str());
    addChild(pmlComponent);
    refresh();

    // This is forced refresh (bad, breaking rules)
    // TODO: refresh should be called in the MMLDisplay connectPml() method instead)
    pmlComponent->refresh();
    display->updateDisplay();
}

// -------------------- getModified --------------------
bool MMLComponent::getModified() const {
    return (camitk::Component::getModified()
            || pmlComponent->getModified());
}

// -------------------- saveMML --------------------
void MMLComponent::saveMML() {
    // save mml
    getMonitoringGuiManager()->saveMmlInFile(getFileName());
    // TODO
    // The next blocked is commented because it is too dangerous!
    // use case: the user changed a property in the phyisical model
    // and ran a simulation to check it was correct
    // => the physical model is modified (property AND position !)
    // => it is therefore saved... property AND position (deformed!)
    // The physical model properties should be changed directly in the PMManagerDC property tab
    /*
    // save pml if needed
    if (getMonitoringGuiManager()->getMonitoringManager()->getPml()->isModified()) {
        std::ofstream outputFile(getMonitoringGuiManager()->getMonitoringManager()->getPmlFileName().c_str());
        getMonitoringGuiManager()->getMonitoringManager()->getPml()->xmlPrint(outputFile);
        outputFile.close();
    }
    */
}
