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

// -- Qt dependencies
#include <QFileDialog>
#include <QTextStream>
#include <QDate>

// -- Qt State Machine stuff
#include <QFinalState>

// -- CamiTK Core stuff
#include <MainWindow.h>
#include <Application.h>
#include <Core.h>
#include <ExtensionManager.h>
#include <AbortException.h>
#include <Log.h>

using namespace camitk;
// -- CamiTK Application Local stuff
#include "ActionStateMachine.h"
#include "ActionStateViewer.h"
#include "ActionStateViewerExtension.h"
#include "ActionTransition.h"
#include "ActionStateWizard.h"

// ---------------------- constructor ----------------------------
ActionStateMachine::ActionStateMachine(int& argc, char** argv, QString inputFileName, QString outputDirectory) :
    Application("camitk-actionstatemachine", argc, argv) {

    setProperty("Log to File", true);
    setProperty("Logger Level", InterfaceLogger::TRACE);

    // Read parameters or ask for scxml filename and output directory
    QString filename;
    if (inputFileName.isEmpty() || outputDirectory.isEmpty()) {
        // open dialog wizard for parameters input
        ActionStateWizard asmwiz(inputFileName, outputDirectory);

        // if OK is pressed we continue starting action state machine
        // otherwise we exit
        if (asmwiz.exec()) {
            filename = asmwiz.getSCXMLFilename();
            saveDirectory = QDir(asmwiz.getLogDirectory());
            asmwiz.done(QDialog::Accepted);
        }
        else {
            asmwiz.done(QDialog::Rejected);
            throw AbortException("Operation aborted by user.");
        }
    }
    else {
        filename = inputFileName;
        saveDirectory = QDir(outputDirectory);
    }

    // load the specific viewer extension
    ActionStateViewerExtension* ext = new ActionStateViewerExtension();
    ext->initResources();
    ext->init();
    registerAllViewers(ext);

    // Atributes initialization
    mainWindow = nullptr;
    name = QString("Action State Machine");

    // CamiTK Application main window
    initMainWindow();

    // check the given file
    this->checkSCXMLFile(filename);

    QDateTime now = QDateTime::currentDateTime();
    // NOTE on Windows ":" (colon) is reserved by the system for separating drive names, therefore hh:mm:ss cannot be used
    QString newDir = now.toString("yyyy-MM-dd") + "T" + now.toString("hh-mm-ss");

    if (!saveDirectory.mkdir(newDir))  {
        saveDirectory.setPath(QFileDialog::getExistingDirectory(this->mainWindow, tr("Please select a directory where to save log and component files")));
        saveDirectory.mkdir(newDir);
    }
    saveDirectory.cd(newDir);
    CAMITK_TRACE(tr("Logging in %1").arg(saveDirectory.absolutePath()));

    // parse XML file and store actions and transitions in states
    name = this->parseSCXMLTree();
    mainWindow->setWindowSubtitle(name);

    //-- Starting the machine

    // create the main timer
    stateMachineTimer = QTime::currentTime();

    (*logStream) << "<?xml version='1.0' encoding='UTF-8' ?>" << Qt::endl;
    (*logStream) << "<?xml-stylesheet type='text/xsl' href='#stylesheet'?>" << Qt::endl;
    (*logStream) << "<!DOCTYPE log [" << Qt::endl;
    (*logStream) << "<!ATTLIST xsl:stylesheet" << Qt::endl;
    (*logStream) << "id ID #REQUIRED>" << Qt::endl;
    (*logStream) << "]>" << Qt::endl;
    (*logStream) << "<log>" << Qt::endl;
    // embed the XSL stylesheet directly in the XML

    (*logStream) << "<!--" << Qt::endl;
    (*logStream) << "/*****************************************************************************" << Qt::endl;
    (*logStream) << " * $CAMITK_LICENCE_BEGIN$" << Qt::endl;
    (*logStream) << " *" << Qt::endl;
    (*logStream) << " * CamiTK - Computer Assisted Medical Intervention ToolKit" << Qt::endl;
    (*logStream) << " * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France" << Qt::endl;
    (*logStream) << " *" << Qt::endl;
    (*logStream) << " * Visit http://camitk.imag.fr for more information" << Qt::endl;
    (*logStream) << " *" << Qt::endl;
    (*logStream) << " * This file is part of CamiTK." << Qt::endl;
    (*logStream) << " *" << Qt::endl;
    (*logStream) << " * CamiTK is free software: you can redistribute it and/or modify" << Qt::endl;
    (*logStream) << " * it under the terms of the GNU Lesser General Public License version 3" << Qt::endl;
    (*logStream) << " * only, as published by the Free Software Foundation." << Qt::endl;
    (*logStream) << " *" << Qt::endl;
    (*logStream) << " * CamiTK is distributed in the hope that it will be useful," << Qt::endl;
    (*logStream) << " * but WITHOUT ANY WARRANTY; without even the implied warranty of" << Qt::endl;
    (*logStream) << " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the" << Qt::endl;
    (*logStream) << " * GNU Lesser General Public License version 3 for more details." << Qt::endl;
    (*logStream) << " *" << Qt::endl;
    (*logStream) << " * You should have received a copy of the GNU Lesser General Public License" << Qt::endl;
    (*logStream) << " * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>." << Qt::endl;
    (*logStream) << " *" << Qt::endl;
    (*logStream) << " * $CAMITK_LICENCE_END$" << Qt::endl;
    (*logStream) << " ****************************************************************************/" << Qt::endl;
    (*logStream) << " " << Qt::endl;
    (*logStream) << "  XSL stylesheet to display an ActionStateMachine application log in HTML." << Qt::endl;
    (*logStream) << "  Insert this code in the XML for a client-side rendering." << Qt::endl;
    (*logStream) << " " << Qt::endl;
    (*logStream) << "-->" << Qt::endl;
    (*logStream) << "" << Qt::endl;
    (*logStream) << "<xsl:stylesheet id='stylesheet'" << Qt::endl;
    (*logStream) << "                xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'" << Qt::endl;
    (*logStream) << "                xmlns:log='http://ujf-grenoble.fr/camitk/logApplication'>" << Qt::endl;
    (*logStream) << "    <xsl:output method='html'/>" << Qt::endl;
    (*logStream) << "" << Qt::endl;
    (*logStream) << "    <xsl:template match='xsl:stylesheet' />" << Qt::endl;
    (*logStream) << "" << Qt::endl;
    (*logStream) << "    <xsl:template match='/log'>" << Qt::endl;
    (*logStream) << "        <html>" << Qt::endl;
    (*logStream) << "            <head>" << Qt::endl;
    (*logStream) << "                <title>Log of Application <xsl:value-of select='log:application/log:name'/></title>" << Qt::endl;
    (*logStream) << "                <style type='text/css'>" << Qt::endl;
    (*logStream) << "                    body {" << Qt::endl;
    (*logStream) << "                        background: #FFFFFF;" << Qt::endl;
    (*logStream) << "                        font-family: arial, sans-serif;" << Qt::endl;
    (*logStream) << "                        font-size: 12pt" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                    table {" << Qt::endl;
    (*logStream) << "                        border-width: 1px;" << Qt::endl;
    (*logStream) << "                         border-style: solid;" << Qt::endl;
    (*logStream) << "                         border-color: #eee;" << Qt::endl;
    (*logStream) << "                         width: 90%;" << Qt::endl;
    (*logStream) << "                         margin-left: 5%;" << Qt::endl;
    (*logStream) << "                         margin-right: 5%;" << Qt::endl;
    (*logStream) << "                        border: none;" << Qt::endl;
    (*logStream) << "                        border-top: 1px solid #EEEEEE;" << Qt::endl;
    (*logStream) << "                        font-family: arial, sans-serif;" << Qt::endl;
    (*logStream) << "                        border-collapse: collapse" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                    td," << Qt::endl;
    (*logStream) << "                    th {" << Qt::endl;
    (*logStream) << "                        border: none; /* 0px solid #EEEEEE;*/" << Qt::endl;
    (*logStream) << "                        border-top: none;" << Qt::endl;
    (*logStream) << "                        text-align: left;" << Qt::endl;
    (*logStream) << "                        padding: 8px;" << Qt::endl;
    (*logStream) << "                        color: #363D41;" << Qt::endl;
    (*logStream) << "                        font-size: 14px" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                    tr {" << Qt::endl;
    (*logStream) << "                        background-color: #fff;" << Qt::endl;
    (*logStream) << "                        border-width: 1px;" << Qt::endl;
    (*logStream) << "                        border-style: solid;" << Qt::endl;
    (*logStream) << "                        border-color: #ccc;" << Qt::endl;
    (*logStream) << "                        cursor: pointer;" << Qt::endl;
    (*logStream) << "                        display: grid;" << Qt::endl;
    (*logStream) << "                        grid-template-columns: repeat(3, 1fr);" << Qt::endl;
    (*logStream) << "                        justify-content: flex-start" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                    tr: first-child: hover {" << Qt::endl;
    (*logStream) << "                        cursor: default;" << Qt::endl;
    (*logStream) << "                        background-color: #fff" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                    tr: hover {" << Qt::endl;
    (*logStream) << "                        background-color: #EEF4FD" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                    .expanded-row-content {" << Qt::endl;
    (*logStream) << "                        border-top: none;" << Qt::endl;
    (*logStream) << "                        display: grid;" << Qt::endl;
    (*logStream) << "                        grid-column: 1/-1;" << Qt::endl;
    (*logStream) << "                        justify-content: flex-start;" << Qt::endl;
    (*logStream) << "                        color: #AEB1B3;" << Qt::endl;
    (*logStream) << "                        font-size: 13px;" << Qt::endl;
    (*logStream) << "                        background-color: #fff" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                    .hide-row {" << Qt::endl;
    (*logStream) << "                        display: none" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                    .mainFrame {" << Qt::endl;
    (*logStream) << "                        padding: 0px;" << Qt::endl;
    (*logStream) << "                        border: 3px solid gray;" << Qt::endl;
    (*logStream) << "                        background: #FFFFFF;" << Qt::endl;
    (*logStream) << "                        -moz-border-radius: 20px;" << Qt::endl;
    (*logStream) << "                        -webkit-border-radius: 20px;" << Qt::endl;
    (*logStream) << "                        -khtml-border-radius: 20px;" << Qt::endl;
    (*logStream) << "                           border-radius: 20px;" << Qt::endl;
    (*logStream) << "                        margin-top: 50px;" << Qt::endl;
    (*logStream) << "                        margin-bottom: 50px;" << Qt::endl;
    (*logStream) << "                        margin-right: 50px;" << Qt::endl;
    (*logStream) << "                        margin-left: 50px" << Qt::endl;
    (*logStream) << "                        " << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                    .stateFrame {" << Qt::endl;
    (*logStream) << "                         margin-top: 10px;" << Qt::endl;
    (*logStream) << "                         margin-bottom: 10px;" << Qt::endl;
    (*logStream) << "                         margin-right: 10px;" << Qt::endl;
    (*logStream) << "                        margin-left: 10px;" << Qt::endl;
    (*logStream) << "                        padding: 30px;" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                         /* make rounded borders */" << Qt::endl;
    (*logStream) << "                         border: 3px solid #ccc;" << Qt::endl;
    (*logStream) << "                         -moz-border-radius: 10px;" << Qt::endl;
    (*logStream) << "                         -webkit-border-radius: 10px;" << Qt::endl;
    (*logStream) << "                         -khtml-border-radius: 10px;" << Qt::endl;
    (*logStream) << "                         border-radius: 10px;" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                         /* make a nice linear graidant */" << Qt::endl;
    (*logStream) << "                         background: #eef; /* Old browsers */" << Qt::endl;
    (*logStream) << "                         background: -moz-linear-gradient(top, #eec; 0%, #ccc 100%); /* FF3.6+ */" << Qt::endl;
    (*logStream) << "                         background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#eee), color-stop(100%,#ccc)); /* Chrome,Safari4+ */" << Qt::endl;
    (*logStream) << "                         background: -webkit-linear-gradient(top, #eee 0%,#ccc 100%); /* Chrome10+,Safari5.1+ */" << Qt::endl;
    (*logStream) << "                         background: -o-linear-gradient(top, #eee 0%,#ccc 100%); /* Opera 11.10+ */" << Qt::endl;
    (*logStream) << "                         background: -ms-linear-gradient(top, #eee 0%,#ccc 100%); /* IE10+ */" << Qt::endl;
    (*logStream) << "                         background: linear-gradient(to bottom, #eee 0%,#ccc 100%); /* W3C */" << Qt::endl;
    (*logStream) << "                         filter: progid: DXImageTransform.Microsoft.gradient( startColorstr='#eee', endColorstr='#ccc',GradientType=0 ); /* IE6-9 */                    " << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                    h1 {" << Qt::endl;
    (*logStream) << "                         font-weight: 700;" << Qt::endl;
    (*logStream) << "                         font-size: 25px;" << Qt::endl;
    (*logStream) << "                         text-align: center" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                    h2 {" << Qt::endl;
    (*logStream) << "                         font-size: 20px;" << Qt::endl;
    (*logStream) << "                         text-align: center;" << Qt::endl;
    (*logStream) << "                         font-style: italic" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    h3 {" << Qt::endl;
    (*logStream) << "                         font-size: 20px;" << Qt::endl;
    (*logStream) << "                         text-align: center" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                    .enhanced {" << Qt::endl;
    (*logStream) << "                         font-size: 20px;" << Qt::endl;
    (*logStream) << "                         font-style: italic;" << Qt::endl;
    (*logStream) << "                         font-weight: 700" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    .italic {" << Qt::endl;
    (*logStream) << "                         font-style: italic" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    table.simple {" << Qt::endl;
    (*logStream) << "                         border-width: 0px" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    td.simple {" << Qt::endl;
    (*logStream) << "                         border-width: 0px" << Qt::endl;
    (*logStream) << "                    }                    " << Qt::endl;
    (*logStream) << "                    .stateColor {" << Qt::endl;
    (*logStream) << "                        color: #00008B;" << Qt::endl;
    (*logStream) << "                        font-size: 12pt;" << Qt::endl;
    (*logStream) << "                        margin-bottom: 0.5em;" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    .ioColor {" << Qt::endl;
    (*logStream) << "                        color: #00008B;" << Qt::endl;
    (*logStream) << "                        font-size: 12pt;" << Qt::endl;
    (*logStream) << "                    }                    " << Qt::endl;
    (*logStream) << "                    .actionColor {" << Qt::endl;
    (*logStream) << "                        color: #8B0000;" << Qt::endl;
    (*logStream) << "                        font-size: 12pt;" << Qt::endl;
    (*logStream) << "                        margin-bottom: 0.5em;" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                    " << Qt::endl;
    (*logStream) << "                    kbd {" << Qt::endl;
    (*logStream) << "                        border-radius: 5px;" << Qt::endl;
    (*logStream) << "                        moz-border-radius: 5px;" << Qt::endl;
    (*logStream) << "                         -webkit-border-radius: 5px;" << Qt::endl;
    (*logStream) << "                         -khtml-border-radius: 5px;" << Qt::endl;
    (*logStream) << "                        color: white;" << Qt::endl;
    (*logStream) << "                        padding: 2px;" << Qt::endl;
    (*logStream) << "                        border: 2px solid white;" << Qt::endl;
    (*logStream) << "                        background-color: #333;" << Qt::endl;
    (*logStream) << "                        font-size: 80%;" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "            </style>" << Qt::endl;
    (*logStream) << "            </head>" << Qt::endl;
    (*logStream) << "            <body>" << Qt::endl;
    (*logStream) << "                <div class='mainFrame'>                    " << Qt::endl;
    (*logStream) << "                    <div class='stateFrame'>" << Qt::endl;
    (*logStream) << "                        <h1><xsl:value-of select='log:application/log:name'/></h1>" << Qt::endl;
    (*logStream) << "                        <h2>Log file</h2>" << Qt::endl;
    (*logStream) << "                        <h3>Date: <xsl:value-of select='log:application/log:startDate'/></h3>" << Qt::endl;
    (*logStream) << "                        <h3>Started: <xsl:value-of select='log:application/log:startTime'/> - Finished: <xsl:value-of select='log:application/log:endTime'/> </h3>" << Qt::endl;
    (*logStream) << "                        <h2>Duration: <xsl:call-template name='formatTime'>" << Qt::endl;
    (*logStream) << "                                          <xsl:with-param name='timeElapsed' select='log:application/log:timeElapsed'/>" << Qt::endl;
    (*logStream) << "                                      </xsl:call-template>" << Qt::endl;
    (*logStream) << "                        </h2>" << Qt::endl;
    (*logStream) << "                        <h3>Input xml file name: <tt><xsl:value-of select='log:application/log:inputXmlFile'/></tt></h3>" << Qt::endl;
    (*logStream) << "                        <p></p>" << Qt::endl;
    (*logStream) << "                    </div>" << Qt::endl;
    (*logStream) << "" << Qt::endl;
    (*logStream) << "                    <div class='stateFrame'>" << Qt::endl;
    (*logStream) << "                        <xsl:variable name='timeStateTotal'  select='sum(log:application/log:state/log:timeElapsed)'/>" << Qt::endl;
    (*logStream) << "                        <xsl:variable name='timeActionTotal' select='sum(log:application/log:transition/log:timeElapsed)'/>" << Qt::endl;
    (*logStream) << "" << Qt::endl;
    (*logStream) << "                        <h3>Total time spend within states: " << Qt::endl;
    (*logStream) << "                            <span class='stateColor'>" << Qt::endl;
    (*logStream) << "                                <xsl:call-template name='formatTime'>" << Qt::endl;
    (*logStream) << "                                    <xsl:with-param name='timeElapsed' select='sum(log:application/log:state/log:timeElapsed)'/>" << Qt::endl;
    (*logStream) << "                                </xsl:call-template>" << Qt::endl;
    (*logStream) << "                            (<xsl:value-of select='sum(log:application/log:state/log:timeElapsed)'/> ms)</span>" << Qt::endl;
    (*logStream) << "                        </h3>" << Qt::endl;
    (*logStream) << "                       " << Qt::endl;
    (*logStream) << "                        <h3>Total time spend on action transitions: " << Qt::endl;
    (*logStream) << "                            <span class='actionColor'>" << Qt::endl;
    (*logStream) << "                                <xsl:call-template name='formatTime'>" << Qt::endl;
    (*logStream) << "                                    <xsl:with-param name='timeElapsed' select='sum(log:application/log:transition/log:timeElapsed)'/>" << Qt::endl;
    (*logStream) << "                                </xsl:call-template>" << Qt::endl;
    (*logStream) << "                                (<xsl:value-of select='sum(log:application/log:transition/log:timeElapsed)'/> ms)</span>" << Qt::endl;
    (*logStream) << "                        </h3>" << Qt::endl;
    (*logStream) << "                        <table>" << Qt::endl;
    (*logStream) << "                            <tr>" << Qt::endl;
    (*logStream) << "                                <th><span class='stateColor'><div class='enhanced'>State</div></span> (click on a state to show its execution details)</th>" << Qt::endl;
    (*logStream) << "                               </tr>" << Qt::endl;
    (*logStream) << "                            <xsl:apply-templates select='log:application/log:state'/>" << Qt::endl;
    (*logStream) << "                        </table>" << Qt::endl;
    (*logStream) << "                    </div>" << Qt::endl;
    (*logStream) << "                </div>" << Qt::endl;
    (*logStream) << "                <script>" << Qt::endl;
    (*logStream) << "                    const toggleRow = (element) => {" << Qt::endl;
    (*logStream) << "                      element.getElementsByClassName('expanded-row-content')[0].classList.toggle('hide-row');" << Qt::endl;
    (*logStream) << "                    }" << Qt::endl;
    (*logStream) << "                  </script>" << Qt::endl;
    (*logStream) << "            </body>" << Qt::endl;
    (*logStream) << "        </html>" << Qt::endl;
    (*logStream) << "    </xsl:template>" << Qt::endl;
    (*logStream) << "" << Qt::endl;
    (*logStream) << "    <xsl:template match='log:state'>" << Qt::endl;
    (*logStream) << "        <xsl:variable name='count'>" << Qt::endl;
    (*logStream) << "            <xsl:number/>" << Qt::endl;
    (*logStream) << "        </xsl:variable>" << Qt::endl;
    (*logStream) << "        <tr onClick='toggleRow(this)'>" << Qt::endl;
    (*logStream) << "            <td>" << Qt::endl;
    (*logStream) << "                <span class='stateColor'><div class='enhanced'>" << Qt::endl;
    (*logStream) << "                <xsl:value-of select='$count'/>. <xsl:value-of select='log:name'/>" << Qt::endl;
    (*logStream) << "                </div></span>" << Qt::endl;
    (*logStream) << "            </td>" << Qt::endl;
    (*logStream) << "            <td class='expanded-row-content hide-row'>" << Qt::endl;
    (*logStream) << "                <xsl:if test='count(log:actionName)>0'>" << Qt::endl;
    (*logStream) << "                    <span class='actionColor'>CamiTK action's name: <kbd><xsl:value-of select='log:actionName'/></kbd></span>" << Qt::endl;
    (*logStream) << "                </xsl:if>" << Qt::endl;
    (*logStream) << "                <span class='stateColor'>State duration: " << Qt::endl;
    (*logStream) << "                    <xsl:call-template name='formatTime'>" << Qt::endl;
    (*logStream) << "                        <xsl:with-param name='timeElapsed' select='log:timeElapsed'/>" << Qt::endl;
    (*logStream) << "                    </xsl:call-template>" << Qt::endl;
    (*logStream) << "                </span>" << Qt::endl;
    (*logStream) << "                <xsl:apply-templates select='following-sibling::log:transition[1]'/>" << Qt::endl;
    (*logStream) << "            </td>" << Qt::endl;
    (*logStream) << "         </tr>" << Qt::endl;
    (*logStream) << "    </xsl:template>" << Qt::endl;
    (*logStream) << "    " << Qt::endl;
    (*logStream) << "    <xsl:template match='log:transition'>" << Qt::endl;
    (*logStream) << "        <xsl:apply-templates select='log:applyAction | log:closing'/>" << Qt::endl;
    (*logStream) << "    </xsl:template>" << Qt::endl;
    (*logStream) << "    " << Qt::endl;
    (*logStream) << "    <xsl:template match='log:applyAction'>" << Qt::endl;
    (*logStream) << "        <xsl:variable name='count'>" << Qt::endl;
    (*logStream) << "            <xsl:number/>" << Qt::endl;
    (*logStream) << "        </xsl:variable>" << Qt::endl;
    (*logStream) << "        <!-- the preceding <state> element (which corresponds to the state of the current transition) -->" << Qt::endl;
    (*logStream) << "        <xsl:variable name='state' select='../preceding-sibling::log:state[1]'/>" << Qt::endl;
    (*logStream) << "" << Qt::endl;
    (*logStream) << "        <span class='stateColor'>Transition duration: " << Qt::endl;
    (*logStream) << "            <xsl:call-template name='formatTime'>" << Qt::endl;
    (*logStream) << "                <xsl:with-param name='timeElapsed' select='../log:timeElapsed'/>" << Qt::endl;
    (*logStream) << "            </xsl:call-template>" << Qt::endl;
    (*logStream) << "        </span>" << Qt::endl;
    (*logStream) << "        <span class='stateColor'>Action <tt></tt> applied with status: <xsl:value-of select='log:status'/></span>" << Qt::endl;
    (*logStream) << "" << Qt::endl;
    (*logStream) << "        <!-- for some action, the parameters and inputs are logged inside the <applyAction> element" << Qt::endl;
    (*logStream) << "                but for most of the action they are logged inside the <state> element." << Qt::endl;
    (*logStream) << "                The XPaths expression below are selecting the <parameters> and <inputs> elements" << Qt::endl;
    (*logStream) << "                - inside the current <applyAction> element" << Qt::endl;
    (*logStream) << "                - as well as inside the preceding <state> element (which corresponds to the state current of the current transition)" << Qt::endl;
    (*logStream) << "        -->" << Qt::endl;
    (*logStream) << "        <xsl:if test='count(log:parameters|$state/log:parameters)>0'>" << Qt::endl;
    (*logStream) << "            <span class='ioColor'>Parameters:</span>                " << Qt::endl;
    (*logStream) << "            <xsl:apply-templates select='log:parameters|$state/log:parameters'/>" << Qt::endl;
    (*logStream) << "        </xsl:if>" << Qt::endl;
    (*logStream) << "        <xsl:if test='count(log:inputs|$state/log:inputs)>0'>" << Qt::endl;
    (*logStream) << "            <span class='ioColor'>Input:</span>" << Qt::endl;
    (*logStream) << "            <xsl:apply-templates select='log:inputs|$state/log:inputs'/>" << Qt::endl;
    (*logStream) << "        </xsl:if>" << Qt::endl;
    (*logStream) << "        <xsl:if test='count(log:outputs)>0'>" << Qt::endl;
    (*logStream) << "            <span class='ioColor'>Output:</span>" << Qt::endl;
    (*logStream) << "            <xsl:apply-templates select='log:outputs'/>                " << Qt::endl;
    (*logStream) << "        </xsl:if>        " << Qt::endl;
    (*logStream) << "" << Qt::endl;
    (*logStream) << "    </xsl:template>" << Qt::endl;
    (*logStream) << "    " << Qt::endl;
    (*logStream) << "    <xsl:template match='log:closing'>" << Qt::endl;
    (*logStream) << "        <span class='stateColor'>Closing:</span>" << Qt::endl;
    (*logStream) << "        <xsl:apply-templates select='log:component'/>" << Qt::endl;
    (*logStream) << "        " << Qt::endl;
    (*logStream) << "    </xsl:template>" << Qt::endl;
    (*logStream) << "    " << Qt::endl;
    (*logStream) << "    <xsl:template match='log:parameters'>" << Qt::endl;
    (*logStream) << "        <ul style='margin:0px;'>" << Qt::endl;
    (*logStream) << "            <xsl:apply-templates select='log:parameter'/>" << Qt::endl;
    (*logStream) << "        </ul>" << Qt::endl;
    (*logStream) << "    </xsl:template>" << Qt::endl;
    (*logStream) << "" << Qt::endl;
    (*logStream) << "    <xsl:template match='log:parameter'>" << Qt::endl;
    (*logStream) << "        <li><span class='stateColor'><xsl:value-of select='@name'/>: <kbd><xsl:value-of select='@value'/></kbd></span></li>" << Qt::endl;
    (*logStream) << "    </xsl:template>" << Qt::endl;
    (*logStream) << "" << Qt::endl;
    (*logStream) << "    <xsl:template match='log:outputs'>" << Qt::endl;
    (*logStream) << "        <ul style='margin:0px;'>" << Qt::endl;
    (*logStream) << "            <xsl:apply-templates select='log:component'/>" << Qt::endl;
    (*logStream) << "        </ul>" << Qt::endl;
    (*logStream) << "    </xsl:template>" << Qt::endl;
    (*logStream) << "" << Qt::endl;
    (*logStream) << "    <xsl:template match='log:inputs'>" << Qt::endl;
    (*logStream) << "        <ul style='margin:0px;'>" << Qt::endl;
    (*logStream) << "            <xsl:apply-templates select='log:component'/>" << Qt::endl;
    (*logStream) << "        </ul>" << Qt::endl;
    (*logStream) << "    </xsl:template>" << Qt::endl;
    (*logStream) << "" << Qt::endl;
    (*logStream) << "    <xsl:template match='log:component'>" << Qt::endl;
    (*logStream) << "        <li><span class='stateColor'><xsl:value-of select='@type'/>: <kbd><xsl:value-of select='@name'/></kbd></span></li>" << Qt::endl;
    (*logStream) << "    </xsl:template>" << Qt::endl;
    (*logStream) << "    " << Qt::endl;
    (*logStream) << "    <xsl:template name='formatTime'>" << Qt::endl;
    (*logStream) << "        <xsl:param name='timeElapsed' select='/log/log:application/log:timeElapsed'/>" << Qt::endl;
    (*logStream) << "        <!--" << Qt::endl;
    (*logStream) << "            int milisec = (startTime->elapsed());" << Qt::endl;
    (*logStream) << "            int mili    = milisec % 1000;" << Qt::endl;
    (*logStream) << "            int seconds = ((milisec % (3600000)) % (60000)) / 1000;" << Qt::endl;
    (*logStream) << "            int minutes = (milisec % (3600000)) / (60000);" << Qt::endl;
    (*logStream) << "            int hours   = milisec / (3600000);" << Qt::endl;
    (*logStream) << "        -->" << Qt::endl;
    (*logStream) << "        <xsl:variable name='hours'    select='floor($timeElapsed div 3600000)'/>" << Qt::endl;
    (*logStream) << "        <xsl:variable name='minutes'  select='floor(($timeElapsed - $hours * 3600000) div 60000)'/>" << Qt::endl;
    (*logStream) << "        <xsl:variable name='secondes' select='floor(($timeElapsed - $hours * 3600000 - $minutes * 60000) div 1000)'/>" << Qt::endl;
    (*logStream) << "        <xsl:variable name='msec'     select='floor($timeElapsed - $hours * 3600000 - $minutes * 60000 -$secondes * 1000)'/>        <xsl:if test='$hours > 0'><xsl:value-of select='$hours'/> h </xsl:if>" << Qt::endl;
    (*logStream) << "        <xsl:if test='$minutes > 0'><xsl:value-of select='$minutes'/> min </xsl:if>" << Qt::endl;
    (*logStream) << "        <xsl:if test='$secondes > 0'><xsl:value-of select='$secondes'/> s </xsl:if>" << Qt::endl;
    (*logStream) << "        <xsl:if test='$msec > 0'><xsl:value-of select='$msec'/> ms</xsl:if>" << Qt::endl;
    (*logStream) << "        <xsl:if test='$timeElapsed = 0'>&lt;1 ms</xsl:if>" << Qt::endl;
    (*logStream) << "    </xsl:template>" << Qt::endl;
    (*logStream) << "</xsl:stylesheet>    " << Qt::endl;
    (*logStream) << "<application xmlns='http://ujf-grenoble.fr/camitk/logApplication'>" << Qt::endl;
    (*logStream) << "\t<name>" << getName() << "</name>" << Qt::endl;
    (*logStream) << "\t<inputXmlFile>" << filename << "</inputXmlFile>" << Qt::endl;
    (*logStream) << "\t<startDate>" << now.toString("yyyy-MM-dd") << "</startDate>" << Qt::endl;
    (*logStream) << "\t<startTime>" << stateMachineTimer.toString("hh:mm:ss:zzz") << "</startTime>" << Qt::endl;

    machine.start();
}

// ---------------------- autoNext ----------------------------
void ActionStateMachine::autoNext() {
    // if autonext is called, then this is an automatic test, there won't be any user to close the dialog
    setProperty("Message Box Level", InterfaceLogger::NONE);

    // machine.configuration() returns all the current state, the first on is the active state
    ActionState* currentState = qobject_cast<ActionState*>(machine.configuration().values().first());

    // loop until no more state is available
    while (currentState != nullptr && currentState->transitions().size() > 0) {
        // look for the "Next" or "Quit" transition
        // (if there is a "Next" and a "Quit", "Next" is prioritized)
        auto it = currentState->transitions().constBegin();
        bool foundNextState = false;
        while (it != currentState->transitions().constEnd() && !foundNextState) {
            // all transition in this state machine are ActionTransition instances...
            ActionTransition* currentActionTransition = qobject_cast<ActionTransition*>(*it);

            //... in which we can look for the "Next" transition first
            if (currentActionTransition->isNamed("Next")) {
                // activate the transition (simulate a click on the button)
                currentActionTransition->autoNext();  // activating the "Quit" transition enter the final state, which make the state machine to emit the finished() signal
                foundNextState = true;
            }

            ++it;
        }
        // if the next state is not found, look for the "Quit" state
        if (!foundNextState) {
            it = currentState->transitions().constBegin();
            while (it != currentState->transitions().constEnd() && !foundNextState) {
                // all transition in this state machine are ActionTransition instances...
                ActionTransition* currentActionTransition = qobject_cast<ActionTransition*>(*it);

                //... in which we can look for the "Quit" transition
                if (currentActionTransition->isNamed("Quit")) {
                    // activate the transition (simulate a click on the button)
                    currentActionTransition->autoNext();  // activating the "Quit" transition enter the final state, which make the state machine to emit the finished() signal
                    foundNextState = true;
                }

                ++it;
            }
        }
        // get the new current active state
        currentState = dynamic_cast<ActionState*>(machine.configuration().values().first());
    }

    while (currentState != nullptr);

}

// ---------------------- initMainWindow ----------------------------
void ActionStateMachine::initMainWindow() {

    mainWindow = new MainWindow(name);
    Viewer* medicalImageViewer = Application::getViewer("Medical Image Viewer");
    if (medicalImageViewer != nullptr) {
        mainWindow->setCentralViewer(medicalImageViewer);
    }
    else {
        CAMITK_ERROR("Cannot find \"Medical Image Viewer\". This viewer is mandatory for the action state machine application.")
        return;
    }
    mainWindow->addDockViewer(Qt::RightDockWidgetArea, Application::getViewer("Action State Viewer"));
    Viewer* explorer = Application::getViewer("Component Explorer");
    if (explorer != nullptr) {
        mainWindow->addDockViewer(Qt::LeftDockWidgetArea, explorer);
    }
    else {
        CAMITK_ERROR("Cannot find \"Explorer\" viewer. This viewer is mandatory for the action state machine application.")
        return;
    }

    // never show the toolbar
    medicalImageViewer->setToolBarVisibility(false);

    mainWindow->showStatusBar(true);
    this->setMainWindow(mainWindow);

    // force no redirection to console (to see error message if they occur)
    mainWindow->redirectToConsole(false);

    // set the specific actions state machine icon
    mainWindow->setWindowIcon(QPixmap(":/applicationIcon"));
}

// ---------------------- checkSCXMLFile ----------------------------
void ActionStateMachine::checkSCXMLFile(QString filename) {
    QString msg;
    QDomDocument doc;

    //read the file
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly)) {
        msg = tr("File not found: the file %1 was not found").arg(filename);
        CAMITK_ERROR(msg)
        throw AbortException(msg.toStdString());
    }
    if (!doc.setContent(&file)) {
        file.close();
        msg = tr("File %1 have no valid root (not a valid XML file format)").arg(filename);
        CAMITK_ERROR(msg)
        throw AbortException(msg.toStdString());
    }

    QString rootName = doc.documentElement().nodeName();
    if (rootName != QString("scxml")) {
        file.close();
        msg = tr("File %1 is not a SCXML file (%2: root node not equals to \"scxml\")").arg(filename, getDomNodeLocation(doc.documentElement()));
        CAMITK_ERROR(msg)
        throw AbortException(msg.toStdString());
    }

    // Ok, after all this checking, the file seems to be good looking,
    // set it as the right xml doc...
    this->scxmlDoc = doc;

    file.close();
}

// ---------------------- parseSCXMLTree ----------------------------
QString ActionStateMachine::parseSCXMLTree() {

    // Get the name of the application
    QString applicationName = "";
    QDomElement docElem = scxmlDoc.documentElement();
    applicationName = docElem.attribute("applicationName");

    QString logFileName = this->saveDirectory.absolutePath() + "/log.xml";
    logFile = new QFile(logFileName);
    logFile->open(QIODevice::WriteOnly | QIODevice::Text);
    logStream = new QTextStream(logFile);

    // Get the states node
    QDomNodeList theStates = scxmlDoc.elementsByTagName("state");

    // Create ActionStates
    createAllActionStates(theStates);

    // Now that all the states are created and stored in the map,
    // create the transitions in between.
    createTransitions(theStates);

    // Set the initial state (specified in the scxml element with initial attribute).
    QString initialStateName = scxmlDoc.documentElement().attribute("initial");
    ActionState* initialState = statesMap.find(initialStateName).value();
    machine.setInitialState(initialState);
    dynamic_cast<ActionStateViewer*>(Application::getViewer("Action State Viewer"))->setState(initialState);

    // Connect the end of the machine with the end of the application
    QObject::connect(&machine, SIGNAL(finished()), QApplication::instance(), SLOT(finished()));

    return applicationName;
}

// ---------------------- finished ----------------------------
void ActionStateMachine::finished() {
    QTime endTime = QTime::currentTime();

    CAMITK_TRACE("State machine finished")

    (*logStream) << "\t<endTime>" << endTime.toString("hh:mm:ss:zzz") << "</endTime>" << Qt::endl;
    (*logStream) << "\t<timeElapsed unit='ms'>" << stateMachineTimer.msecsTo(QTime::currentTime()) << "</timeElapsed>" << Qt::endl;
    (*logStream) << "</application>" << Qt::endl;
    (*logStream) << "</log>" << Qt::endl;
    logFile->close();

    // now we can quit
    exit();
}

// ---------------------- createAllActionStates ----------------------------
void ActionStateMachine::createAllActionStates(QDomNodeList nodeList) {
    int nbStates = nodeList.size();
    if (nbStates < 1) {
        QString msg = tr("No state and no camitk-action!");
        CAMITK_ERROR(msg)
        throw AbortException(msg.toStdString());
    }

    for (int nb = 0; nb < nbStates; nb++) {
        QDomElement element = nodeList.item(nb).toElement();
        if (! element.isNull()) {
            QString elementName = element.nodeName();
            if (elementName == QString("state")) {
                QString stateId = element.attribute("id");
                QString stateDescription = element.elementsByTagName("camitk:description").item(0).toElement().text();
                ActionState* state = nullptr;
                if (element.elementsByTagName("camitk:action").size() > 0) {
                    QDomElement actionElement = element.elementsByTagName("camitk:action").item(0).toElement();
                    // Find the name of the action
                    QString camiTKActionName = actionElement.elementsByTagName("camitk:name").item(0).toElement().text();
                    if (camiTKActionName == "Save") {
                        state = new SaveActionState(&machine, stateId, stateDescription, saveDirectory.absolutePath(), logStream);
                        setCamiTKSaveAction((SaveActionState*) state, actionElement);
                    }
                    else {
                        state = new ActionState(&machine, stateId, stateDescription, logStream);
                        setCamiTKAction(state, actionElement);
                    }
                }
                else {
                    state = new ActionState(&machine, stateId, stateDescription, logStream);
                }
                QDomNodeList alternativeDescriptions = element.elementsByTagName("camitk:altDescription");
                for (int i = 0; i < alternativeDescriptions.size(); i++) {
                    QDomElement altDescElmt = alternativeDescriptions.item(i).toElement();
                    QString altDescText = altDescElmt.text();
                    QString altDescCond =  altDescElmt.attribute("previousStatus", "");
                    QVector<Action::ApplyStatus> statusList = stringToStatus(altDescCond);

                    state->setAlternativeDesc(altDescText, statusList);

                }
                statesMap.insert(stateId, state);
            }
        }
    }
    // Do not forget the final state
    finalState = new QFinalState();
    machine.addState(finalState);

    //-- check that all input component exists as output components
    // 1. build the map
    QHash <ActionState*, QString> inputNames;
    QHash <ActionState*, QString> outputNames;
    QMap <QString, ActionState* >::const_iterator it = statesMap.constBegin();
    while (it != statesMap.constEnd()) {
        for (QString inputCompName : it.value()->getInputComponents().keys()) {
            if (inputNames.values().contains(inputCompName)) {
                CAMITK_WARNING(tr("Warning: same input \"%1\" for more than one state. It might be wrong.\n").arg(inputCompName))
            }
            else {
                inputNames.insert(it.value(), inputCompName);
            }
        }
        for (QString outputCompName : it.value()->getOutputComponents().keys()) {
            if (outputNames.values().contains(outputCompName)) {
                QString msg = tr("Error: same output name \"%1\" for more than one state. Please rename the output of \"%2\"").arg(outputCompName, it.value()->getName());
                CAMITK_ERROR(msg)
                throw AbortException(msg.toStdString());
            }
            else {
                outputNames.insert(it.value(), outputCompName);
            }
        }
        ++it;
    }
    // 2. check
    auto itAS = inputNames.constBegin();
    while (itAS != inputNames.constEnd()) {
        if (!outputNames.values().contains(itAS.value())) {
            QString msg = tr("Error: Cannot find output name \"%1\" for input of \"%2\".").arg(itAS.value(), itAS.key()->getName());
            CAMITK_ERROR(msg)
            throw AbortException(msg.toStdString());
        }
        ++itAS;
    }

}

// ---------------------- createTransitions ----------------------------
void ActionStateMachine::createTransitions(QDomNodeList nodeList)  {
    int nbStates = nodeList.size();
    for (int stateNumber = 0; stateNumber < nbStates; stateNumber++) {
        // For each state ...
        QDomElement element = nodeList.item(stateNumber).toElement();

        // Find its name (id attribute)...
        QString stateId = element.attribute("id");
        // ... and get the corresponding created state in the map:
        ActionState* state = (statesMap.find(stateId)).value();
        // Check if this state is final
        bool isFinal = (element.attribute("final", "false").toLower() == QString("true"));

        // Find its transitions (transition children nodes)
        QDomNodeList transitionsList = element.elementsByTagName("transition");
        for (int transNb = 0; transNb < transitionsList.size(); transNb++) {
            QDomElement transElement = transitionsList.item(transNb).toElement();
            if (! transElement.isNull()) {
                QString eventName  = transElement.attribute("event");
                QString targetName = transElement.attribute("target");

                // Apply the previous action during this transition ?
                QString applyActionStr = transElement.attribute("applyAction", "true");
                bool applyAction = (applyActionStr.toLower() == "true");

                // Find disable conditions
                QString disableConds = transElement.attribute("disable", "");
                QVector<Action::ApplyStatus> disableConditions = stringToStatus(disableConds);

                // Find the corresponding target state
                QMap <QString, ActionState* >::iterator it = statesMap.find(targetName);
                if (it == statesMap.end()) {
                    QString msg = tr(R"(XML Parsing: %1 in state "%2": cannot find target "%3" for event "%4")")
                                  .arg(getDomNodeLocation(transitionsList.item(transNb)),
                                       state->getName(),
                                       targetName,
                                       eventName);

                    CAMITK_ERROR(msg)
                    throw AbortException(msg.toStdString());
                }
                ActionState* target = it.value();

                // Create the actual transition:
                ActionTransition* trans = state->addActionTransition(eventName, target, applyAction, disableConditions);

                // Check if the transition should close some components...
                QDomNodeList toClose = transElement.elementsByTagName("camitk:close");
                if (toClose.size() > 0) {
                    QDomNodeList compsToClose = toClose.item(0).toElement().elementsByTagName("camitk:component");
                    for (int idx = 0; idx < compsToClose.size(); idx++) {
                        QDomElement oneComp = compsToClose.item(idx).toElement();
                        QString forceOn = oneComp.attribute("force", "false");
                        QString compName = oneComp.attribute("name");
                        QString compType = oneComp.attribute("type");
                        trans->addComponentToClose(compName, compType, QVariant(forceOn).toBool());
                    }
                }
            }
        }

        if (isFinal) {
            state->addActionTransition("Quit", finalState, false);
        }
    }

}

// ---------------------- stringToStatus ----------------------------
QVector<Action::ApplyStatus> ActionStateMachine::stringToStatus(QString listOfStatus) {
    QVector<Action::ApplyStatus> statusList;
    statusList.clear();

    QStringList statusStringList = listOfStatus.split(" ");
    for (QStringList::iterator it = statusStringList.begin(); it != statusStringList.end(); ++it) {
        if ((*it) == "SUCCESS") {
            statusList.append(Action::SUCCESS);
        }
        else if ((*it) == "ERROR") {
            statusList.append(Action::ERROR);
        }
        else if ((*it) == "WARNING") {
            statusList.append(Action::WARNING);
        }
        else if ((*it) == "ABORTED") {
            statusList.append(Action::ABORTED);
        }
        else if ((*it) == "TRIGGERED") {
            statusList.append(Action::TRIGGERED);
        }
    }

    return statusList;
}

// ---------------------- setCamiTKAction ----------------------------
void ActionStateMachine::setCamiTKAction(ActionState* actionState, QDomElement actionElement) {
    QString msg;
    if ((actionState == nullptr) || (actionElement.isNull())) {
        msg = tr("No Action or no state...");
        CAMITK_ERROR(msg)
        throw AbortException(msg.toStdString());
    }

    QDomNode actionNameNode = actionElement.elementsByTagName("camitk:name").item(0);
    QString actionName = actionNameNode.toElement().text();
    Action* action = Application::getAction(actionName);
    if (action == nullptr) {
        msg = tr("XML parsing error: in %1, action \"%2\" is not a proper CamiTK action.").arg(getDomNodeLocation(actionNameNode), actionName);
        CAMITK_ERROR(msg)
        throw AbortException(msg.toStdString());
    }

    // Parameters
    QMap<QString, QVariant> parameters;
    parameters.clear();
    QDomNodeList domParameters = actionElement.elementsByTagName("camitk:parameters");
    if (! domParameters.isEmpty()) {
        QDomNodeList parameterList = domParameters.item(0).toElement().elementsByTagName("camitk:parameter");
        for (int i = 0; i < parameterList.size(); i++) {
            QDomElement param  = parameterList.item(i).toElement();
            QString paramName  = param.attribute("name");
            QString paramType  = param.attribute("type");
            QString paramValue = param.attribute("value");
            QVariant val;

            QStringList possibleTypes;
            possibleTypes << "int" << "bool" << "double" << "QString";


            switch (possibleTypes.indexOf(paramType)) {
                case 0: // int
                    val = QVariant(paramValue.toInt());
                    break;
                case 1: // bool
                    val = QVariant(paramValue);
                    break;
                case 2: // double
                    val = QVariant(paramValue.toDouble());
                    break;
                case 3: // QString
                    val = QVariant(paramValue);
                    break;
                default:
                    val = QVariant(paramValue);
                    break;
            }
            parameters.insert(paramName, val);
        }
    }

    // Input Component Names
    QMap<QString, QString> inputComponentNames;
    inputComponentNames.clear();
    QDomNodeList inputList = actionElement.elementsByTagName("camitk:inputs");
    if (! inputList.isEmpty()) {
        QDomNodeList inputComps = inputList.item(0).toElement().elementsByTagName("camitk:component");
        for (int i = 0; i < inputComps.size(); i++) {
            QDomElement inputComp = inputComps.item(i).toElement();
            QString compName = inputComp.attribute("name");
            QString compType = inputComp.attribute("type");
            inputComponentNames.insert(compName, compType);
        }
    }

    // Output Component Names
    QMap<QString, QString> outputComponentNames;
    outputComponentNames.clear();
    QDomNodeList outputList = actionElement.elementsByTagName("camitk:outputs");
    if (! outputList.isEmpty()) {
        QDomNodeList outputComps = outputList.item(0).toElement().elementsByTagName("camitk:component");
        for (int i = 0; i < outputComps.size(); i++) {
            QDomElement outputComp = outputComps.item(i).toElement();
            QString compName = outputComp.attribute("name");
            QString compType = outputComp.attribute("type");
            outputComponentNames.insert(compName, compType);
        }
    }

    actionState->setAction(action, parameters, inputComponentNames, outputComponentNames);
}

// ---------------------- setCamiTKSaveAction ----------------------------
void ActionStateMachine::setCamiTKSaveAction(SaveActionState* actionState, QDomElement actionElement) {

    QString msg;
    if ((actionState == nullptr) || (actionElement.isNull())) {
        msg = tr("No Action or no state...");
        CAMITK_ERROR(msg)
        throw AbortException(msg.toStdString());
    }


    // Input Component Names, Types, Extensions and Directories....
    QVector<SaveActionState::saveComponentsInfo> inputInfos;
    QDomNodeList inputList = actionElement.elementsByTagName("camitk:inputs");
    if (! inputList.isEmpty()) {
        QDomNodeList inputComps = inputList.item(0).toElement().elementsByTagName("camitk:component");
        for (int i = 0; i < inputComps.size(); i++) {
            QDomElement inputComp = inputComps.item(i).toElement();
            SaveActionState::saveComponentsInfo compInfos;
            compInfos.name = inputComp.attribute("name");
            compInfos.type = inputComp.attribute("type");
            compInfos.extension = inputComp.attribute("suffix");
            compInfos.directory = inputComp.attribute("saveDirectory");

            inputInfos.append(compInfos);
        }
    }

    actionState->setInput(inputInfos);
}

// ---------------------- getSaveDirectory ----------------------------
QString ActionStateMachine::getSaveDirectory() {
    return this->saveDirectory.absolutePath();
}

// ---------------------- getDomNodeLocation ----------------------------
QString ActionStateMachine::getDomNodeLocation(QDomNode inputNode) {
    QString msg;
    int whereInXML = inputNode.lineNumber();
    if (whereInXML > 0) {
        msg += "line " + QString::number(whereInXML);
    }
    whereInXML = inputNode.columnNumber();
    if (whereInXML > 0) {
        msg += QString(", column ") + QString::number(whereInXML) + QString(": ");
    }

    return msg;
}
