/*****************************************************************************
 * $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$
 ****************************************************************************/
// monitoring includes
#include <monitoring/Monitor.h>
#include <monitoring/StoppingCriterion.h>
#include <monitoring/Criterion.h>
#include <monitoring/InteractiveMonitoringManager.h>

// Monitor includes
#include "MonitoringDialog.h"
#include "MonitoringDriver.h"
#include "MonitoringGuiManager.h"

#include "ui_MonitoringDialog.h"

// qt
#include <QtGui>
#include <QFileDialog>
#include <QMessageBox>

// ----------------------- constructor ---------------------------
MonitoringDialog::MonitoringDialog(MonitoringGuiManager* guiManager, QWidget* parent): QDialog(parent) {
    ui = new Ui::MonitoringDialog;
    ui->setupUi(this);

    this->guiManager = guiManager;

    QObject* qobj = guiManager->getDriver();
    connect(qobj, SIGNAL(doOneStep()), guiManager, SLOT(doOneStep()));

    connect(ui->rewindPushButton, SIGNAL(clicked()), this, SLOT(rewind()));
    connect(ui->pausePushButton, SIGNAL(clicked()), this, SLOT(pause()));
    connect(ui->playOneStepPushButton, SIGNAL(clicked()), this, SLOT(simulateOneStep()));
    connect(ui->playPushButton, SIGNAL(clicked()), this, SLOT(simulate()));

    connect(ui->mmlInBrowsePushButton, SIGNAL(clicked()), this, SLOT(browseMmlIn()));
    connect(ui->mmlOutSavePushButton, SIGNAL(clicked()), this, SLOT(saveMmlOut()));
    connect(ui->csvSavePushButton, SIGNAL(clicked()), this, SLOT(saveCsv()));
    connect(ui->mmlInSavePushButton, SIGNAL(clicked()), this, SLOT(saveMmlIn()));
    connect(ui->pmlBrowsePushButton, SIGNAL(clicked()), this, SLOT(browsePml()));
    connect(ui->lmlBrowsePushButton, SIGNAL(clicked()), this, SLOT(browseLml()));

    connect(ui->dtLineEdit, SIGNAL(editingFinished()), this, SLOT(dtModified()));
    connect(ui->refreshLineEdit, SIGNAL(editingFinished()), this, SLOT(refreshModified()));

    ui->rewindPushButton->setFocusPolicy(Qt::NoFocus);
    ui->pausePushButton->setFocusPolicy(Qt::NoFocus);
    ui->playOneStepPushButton->setFocusPolicy(Qt::NoFocus);
    ui->playPushButton->setFocusPolicy(Qt::NoFocus);
    ui->mmlInBrowsePushButton->setFocusPolicy(Qt::NoFocus);
    ui->mmlOutSavePushButton->setFocusPolicy(Qt::NoFocus);
    ui->mmlInSavePushButton->setFocusPolicy(Qt::NoFocus);
    ui->pmlBrowsePushButton->setFocusPolicy(Qt::NoFocus);
    ui->lmlBrowsePushButton->setFocusPolicy(Qt::NoFocus);

    ui->currentFpsLineEdit->setStyleSheet("background: lightgreen");
    ui->averageFpsLineEdit->setStyleSheet("background: lightgreen");
    ui->timeLineEdit->setStyleSheet("background: lightgreen");

}

//--------------- Destructor ---------------------------------
MonitoringDialog::~MonitoringDialog() {
    // unselect and clear the monitor tab's content.
    // this allow not to encounter crash when closing the image
    this->ui->monitorsTable->clearContents();
    QWidget* simulatorWidget = ui->monitoringTabs->widget(3);
    if (simulatorWidget != nullptr) {
        ui->monitoringTabs->removeTab(ui->monitoringTabs->indexOf(simulatorWidget));
        delete simulatorWidget;
    }
    delete ui;
    ui = nullptr;
}

//--------------- init ---------------------------------
void MonitoringDialog::init() {
    updateAll();
}

//--------------- enableButtons -----------------------
void MonitoringDialog::enableButtons(bool enable) {
    ui->dtLineEdit->setEnabled(enable);
    ui->refreshLineEdit->setEnabled(enable);
    ui->pmlBrowsePushButton->setEnabled(enable);
    ui->lmlBrowsePushButton->setEnabled(enable);
    ui->lmlLineEdit->setEnabled(enable);
    ui->pmlLineEdit->setEnabled(enable);
    ui->mmlOutSavePushButton->setEnabled(enable);
    ui->mmlInSavePushButton->setEnabled(enable);
    ui->rewindPushButton->setEnabled(enable);
    ui->playPushButton->setEnabled(enable);
    ui->playOneStepPushButton->setEnabled(enable);
    ui->pausePushButton->setEnabled(enable);
    ui->currentFpsLineEdit->setEnabled(enable);
    ui->averageFpsLineEdit->setEnabled(enable);
    ui->timeLineEdit->setEnabled(enable);

    if (guiManager->getMonitoringManager()) {
        //an mmlIn file is loaded check if lml and pml is present
        if (!guiManager->getMonitoringManager()->isLmlPresent()) {
            ui->lmlBrowsePushButton->setEnabled(false);
            ui->lmlLineEdit->setEnabled(false);
        }
        if (!guiManager->getMonitoringManager()->isPmlPresent()) {
            ui->pmlBrowsePushButton->setEnabled(false);
            ui->pmlLineEdit->setEnabled(false);
            ui->lmlBrowsePushButton->setEnabled(false);
            ui->lmlLineEdit->setEnabled(false);
        }
    }
}

//--------------- browseMmlIn ---------------------------------
void MonitoringDialog::browseMmlIn() {
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open MMLIn"), ".", tr("MML files (*.mml)"));
    if (!fileName.isEmpty()) {
        ui->mmlInLineEdit->setText(fileName);
        guiManager->loadMmlInFile(fileName);
    }
}

//--------------- saveMmlOut ---------------------------------
void MonitoringDialog::saveMmlOut() {
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save MMLOut"), ".", tr("MML files (*.mml)"));
    if (!fileName.isEmpty()) {
        guiManager->saveMmlOutFile(fileName);
    }
}

//--------------- saveCsv ---------------------------------
void MonitoringDialog::saveCsv() {
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save as Csv"), ".", tr("CSV files (*.csv)"));
    if (!fileName.isEmpty()) {
        guiManager->saveCsvFile(fileName);
    }
}

//--------------- saveMmlIn ---------------------------------
void MonitoringDialog::saveMmlIn() {
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save MMLIn"), ".", tr("MML files (*.mml)"));
    if (!fileName.isEmpty()) {
        guiManager->saveMmlInFile(fileName);
    }
}


//--------------- browseLml ---------------------------------
void MonitoringDialog::browseLml() {
    // CCC Exception: Use a QMessageBox::warning instead of CAMITK_WARNING as the user interaction is required
    int r = QMessageBox::warning(this, tr("Changing Lml"), tr("Changing lml will reload simulation.\n Not saved data will be lost."), QMessageBox::Ok, QMessageBox::Cancel);
    if (r == QMessageBox::Ok) {
        QString fileName = QFileDialog::getOpenFileName(this, tr("Open Lml"), ".", tr("LML files (*.lml)"));
        if (!fileName.isEmpty()) {
            ui->lmlLineEdit->setText(fileName);
            guiManager->updateLml(fileName);
            guiManager->reload();
        }
    }
}

//--------------- browsePml ---------------------------------
void MonitoringDialog::browsePml() {
    // CCC Exception: Use a QMessageBox::warning instead of CAMITK_WARNING as the user interaction is required
    int r = QMessageBox::warning(this, tr("Changing Pml"), tr("Changing pml will reload simulation.\n Not saved data will be lost."), QMessageBox::Ok, QMessageBox::Cancel);
    if (r == QMessageBox::Ok) {
        QString fileName = QFileDialog::getOpenFileName(this, tr("Open Pml"), ".", tr("PML files (*.pml)"));
        if (!fileName.isEmpty()) {
            ui->pmlLineEdit->setText(fileName);
            guiManager->updatePml(fileName);
            guiManager->reload();
        }
    }
}

//--------------- simulateOneStep ---------------------------------
void MonitoringDialog::simulateOneStep() {
    guiManager->simulateOneStep();
}

//--------------- pause ---------------------------------
void MonitoringDialog::pause() {
    guiManager->pause();
}

//--------------- rewind ---------------------------------
void MonitoringDialog::rewind() {
    // CCC Exception: Use a QMessageBox::warning instead of CAMITK_WARNING as the user interaction is required
    int r = QMessageBox::warning(this, tr("Rewind Simulation"), tr("Rewind simulation?\n Not saved data will be lost."), QMessageBox::Ok, QMessageBox::Cancel);
    if (r == QMessageBox::Ok) {
        guiManager->rewind();
    }
}

//--------------- simulate() ---------------------------------
void MonitoringDialog::simulate() {
    guiManager->simulate();
}



//--------------- updateAll ---------------------------------
void MonitoringDialog::updateAll() {
    if (guiManager->getMonitoringManager()) {

        enableButtons(true);

        ui->monitorsTab->setEnabled(true);

        updateSimulation();
        updateMonitors();
        updateSimulatorWidget();
        // enable stopping criteria tab only if monitoring manager is interactive
        if (dynamic_cast<InteractiveMonitoringManager*>(guiManager->getMonitoringManager())) {
            ui->sCriteriaTab->setEnabled(true);
            updateStoppingCriteria();
        }
    }
    else {   // no mml loaded

        enableButtons(false);

        ui->monitorsTab->setEnabled(false);
        ui->sCriteriaTab->setEnabled(false);
        // deleting simulator widget if exists
        QWidget* simulatorWidget = ui->monitoringTabs->widget(3);
        if (simulatorWidget != nullptr) {
            ui->monitoringTabs->removeTab(ui->monitoringTabs->indexOf(simulatorWidget));
            delete simulatorWidget;
        }
    }
}

//--------------- updateMonitors ---------------------------------
void MonitoringDialog::updateMonitors() {
    ui->monitorsTable->clearContents();
    ui->monitorsTable->setSortingEnabled(false);
    ui->monitorsTable->setRowCount(guiManager->getMonitoringManager()->numberOfMonitor());

    for (unsigned int i = 0; i < guiManager->getMonitoringManager()->numberOfMonitor(); i++) {
        // get current monitor
        Monitor* currentMonitor = guiManager->getMonitoringManager()->getMonitor(i);

        // get type of current monitor
        ui->monitorsTable->setItem(i, 0, new QTableWidgetItem(currentMonitor->getTypeName().c_str()));

        // get target of current monitor
        ui->monitorsTable->setItem(i, 1, new QTableWidgetItem((currentMonitor->getTargetName().c_str())));

        // get startAt of current monitor
        QString s;
        ui->monitorsTable->setItem(i, 2, new QTableWidgetItem(s.setNum(currentMonitor->getStartAt())));

        // get stopAt of current monitor
        QString s2;
        ui->monitorsTable->setItem(i, 3, new QTableWidgetItem(s2.setNum(currentMonitor->getStopAt())));

        // get reference of current monitor
        ui->monitorsTable->setItem(i, 4, new QTableWidgetItem(currentMonitor->getReferenceName().c_str()));

    }
    emit monitorsChanged();
}

//--------------- updateSimulation ---------------------------------
void MonitoringDialog::updateSimulation() {
    QString s;
    ui->dtLineEdit->setText(s.setNum(guiManager->getMonitoringManager()->getDt()));
    ui->refreshLineEdit->setText(s.setNum(guiManager->getMonitoringManager()->getRefresh()));
    ui->pmlLineEdit->setText(tr(guiManager->getMonitoringManager()->getPmlFileName().c_str()));
    ui->lmlLineEdit->setText(tr(guiManager->getMonitoringManager()->getLmlFileName().c_str()));
    ui->mmlInLineEdit->setText(tr(guiManager->getMonitoringManager()->getMmlFileName().c_str()));
    ui->timeLineEdit->setText(s.setNum(guiManager->getMonitoringManager()->getCurrentTime()));
    ui->averageFpsLineEdit->setText(s.setNum(guiManager->getMonitoringManager()->getCurrentStep() / guiManager->getMonitoringManager()->getComputingTime()));
    ui->currentFpsLineEdit->setText(s.setNum(1 / guiManager->getMonitoringManager()->getStepComputingTime()));
}

//--------------- updateStoppingCriteria ---------------------------------
void MonitoringDialog::updateStoppingCriteria() {
    ui->sCriteriaTreeWidget->clear();

    InteractiveMonitoringManager* imm = (InteractiveMonitoringManager*)(guiManager->getMonitoringManager());
    if (imm->getStoppingCriterion()) {
        QTreeWidgetItem* topItem = createItem(imm->getStoppingCriterion(), nullptr);
        ui->sCriteriaTreeWidget->addTopLevelItem(topItem);
    }


}

//--------------- updateSimulatorWidget ---------------------------------
void MonitoringDialog::updateSimulatorWidget() {
    // deleting simulator widget if exists
    QWidget* simulatorWidget = ui->monitoringTabs->widget(3);
    if (simulatorWidget != nullptr) {
        ui->monitoringTabs->removeTab(ui->monitoringTabs->indexOf(simulatorWidget));
        delete simulatorWidget;
    }

#ifdef MML_GENERATE_GUI
    simulatorWidget = guiManager->getMonitoringManager()->getSimulator()->getWidget();
    if (simulatorWidget != nullptr) {
        connect(simulatorWidget, SIGNAL(reload()), guiManager, SLOT(reload()));
        ui->monitoringTabs->addTab(simulatorWidget, tr("Simulator"));
    }
#endif
}


//--------------- createItem ---------------------------------
QTreeWidgetItem* MonitoringDialog::createItem(StoppingCriterion* sc, QTreeWidgetItem* parent) {
    QTreeWidgetItem* item = new QTreeWidgetItem(parent);
    item->setText(0, tr(sc->getName().c_str()));
    if (sc->getNumberOfChildren() < 0) {
        item->setText(1, tr(((Criterion*)sc)->getMethodString().c_str()));
        item->setText(2, tr(((Criterion*)sc)->scopeToString().c_str()));
        item->setText(2, tr(((Criterion*)sc)->scopeToString().c_str()));
    }
    else {

    }
    for (int i = 0; i < sc->getNumberOfChildren(); i++) {
        item->addChild(createItem(sc->getChild(i), item));
    }
    return item;
}

//--------------- getMmlInFileName ---------------------------------
QString MonitoringDialog::getMmlInFileName() {
    return ui->mmlInLineEdit->text();
}

//--------------- dtModified ---------------------------------
void MonitoringDialog::dtModified() {
    guiManager->updateDt(ui->dtLineEdit->text().toDouble());
}

//--------------- refreshModified ---------------------------------
void MonitoringDialog::refreshModified() {
    guiManager->updateRefresh(ui->refreshLineEdit->text().toDouble());
}

QTableWidget* MonitoringDialog::getMonitorsTableWidget() {
    return ui->monitorsTable;
}

