/*****************************************************************************
 * $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 "ActionTransition.h"
#include "ActionStateViewer.h"

#include <Application.h>
#include <Component.h>

#include <Log.h>

using namespace camitk;

// --------------- constructor --------------
ActionTransition::ActionTransition(QPushButton* sender, const char* signal, QState* sourceState, bool applyPreviousAction, QTextStream* logStream) : QSignalTransition(sender, signal, sourceState) {
    myButton = sender;
    this->applyPreviousAction = applyPreviousAction;
    this->logStream = logStream;
}

// --------------- onTransition --------------
void ActionTransition::onTransition(QEvent* e) {
    if (logStream != nullptr) {
        transitionTimer = QTime::currentTime();
        auto* asS = dynamic_cast<ActionState*>(sourceState());
        if (asS != nullptr) {
            QString comment;
            comment = "\t<!-- ActionTransition::onTransition source:" + asS->getName();
            auto* asD = dynamic_cast<ActionState*>(targetState());
            if (asD != nullptr) {
                comment += " dest:" + asD->getName();
            }
            else {
                comment += " dest: none";
            }
            comment += " -->";
            (*logStream) << comment << Qt::endl;
        }
        (*logStream) << "\t<transition>" << Qt::endl;
        (*logStream) << "\t\t<startTime>" << transitionTimer.toString("hh:mm:ss:zzz") << "</startTime>" << Qt::endl;
    }

    Action::ApplyStatus status(Action::TRIGGERED);

    // Apply Previous action (if necessary)
    QAbstractState* previousState = this->sourceState();
    auto* previousActionState = dynamic_cast<ActionState*>(previousState);

    // if previous is initialState, then just call "onExit()"
    if ((previousState) && (applyPreviousAction)) {
        status = previousActionState->applyAction();
    }

    // Close needed components
    if (! componentsToClose.isEmpty()) {
        if (logStream != nullptr) {
            (*logStream) << "\t\t<closing>" << Qt::endl;
        }

        ComponentList compsToClose;
        // Find the components to close (from their names and types)
        ComponentList allComps = Application::getAllComponents();
        ComponentList::const_iterator compIt;
        QMap<QString, QString>::const_iterator namesIt;

        for (namesIt = componentsToClose.constBegin(); namesIt != componentsToClose.constEnd(); ++namesIt) {
            QString compName = namesIt.key();
            QString compType = namesIt.value();

            // Look for the corresponding component into the list of all components
            compIt = allComps.constBegin();

            while ((compIt != allComps.constEnd()) &&
                    (((*compIt)->getName() != compName) ||
                     (!(*compIt)->isInstanceOf(compType)))
                  ) {
                ++compIt;
            }

            if (compIt != allComps.constEnd()) {
                compsToClose.append((*compIt));
            }
        }

        // Close them !
        for (compIt = compsToClose.constBegin(); compIt != compsToClose.constEnd(); ++compIt) {
            bool forceClose = componentsToForceClose.contains((*compIt)->getName());
            if (logStream != nullptr) {
                (*logStream) << "\t\t\t<component name='" << (*compIt)->getName();
                if (forceClose) {
                    (*logStream) << "' force='true";
                }
                (*logStream) << "'/>" << Qt::endl;
            }
            if (forceClose) {
                (*compIt)->setModified(false);
            }

            Application::close((*compIt));
        }

        if (logStream != nullptr) {
            (*logStream) << "\t\t</closing>" << Qt::endl;
        }
    }

    // Go to next State
    QAbstractState* nextState = this->targetState();
    auto* nextActionState = dynamic_cast<ActionState*>(nextState);

    if (nextActionState) {
        nextActionState->setPreviousActionStatus(status);
        dynamic_cast<ActionStateViewer*>(Application::getViewer("Action State Viewer"))->setState(nextActionState);
    }

    if (logStream != nullptr) {
        QTime endTime = QTime::currentTime();

        (*logStream) << "\t\t<endTime>" << endTime.toString("hh:mm:ss:zzz") << "</endTime>" << Qt::endl;
        (*logStream) << "\t\t<timeElapsed unit='ms'>" << transitionTimer.msecsTo(QTime::currentTime()) << "</timeElapsed>" << Qt::endl;
        (*logStream)  << "\t</transition>" << Qt::endl;
    }
}

// --------------- isNamed --------------
bool ActionTransition::isNamed(QString name) const {
    return (myButton->text() == name);
}

// --------------- addComponentToClose --------------
void ActionTransition::addComponentToClose(QString compName, QString compType, bool force) {
    this->componentsToClose.insert(compName, compType);
    if (force) {
        componentsToForceClose.append(compName);
    }
}

// --------------- autoNext --------------
void ActionTransition::autoNext() {
    // simulate the click
    myButton->clicked();
}
