//  Copyright (2010-2013) Cédric Coussinet (cedric.coussinet@nomoseed.net)
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published
//  by the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program 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 General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program. If not, see <http://www.gnu.org/licenses/>

#include <QtWebKit/QWebFrame>
#include <QtGui/QMessageBox>
#include <QtWebKit/QWebHistory>
#include <QDir>
#include <QTimer>
#include <QTextStream>

#if WIN32
   #include <windows.h>
   #include <Tlhelp32.h>
#endif

#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>

#include "mainwindow.h"
#include "benchmark.h"
#include "worldsquareui.h"
#include "pointsscatterui.h"
#include "nomointerpreter.h"

void killProcessus (const QString nameProcess) {
#if WIN32
    HANDLE hSnapShot;
    PROCESSENTRY32 uProcess;
    BOOL r;
    short PID = 0;
    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL,0);
    uProcess.dwSize = (DWORD) sizeof(PROCESSENTRY32);
    r = Process32First(hSnapShot, &uProcess);
    do
    {
        if ( QString::fromWCharArray(uProcess.szExeFile) == nameProcess + ".exe")
            PID = (short) uProcess.th32ProcessID;
        r = Process32Next(hSnapShot, &uProcess);
    }
    while ( r );
    CloseHandle(hSnapShot);
    if (PID != 0){
        HANDLE hTemp;
        hTemp = OpenProcess(PROCESS_ALL_ACCESS, false, (DWORD) PID);
        PostThreadMessage(PID, WM_QUIT, 0, 0);
        WaitForSingleObject( hTemp, 2000 );
        DWORD dwExitCode = 0;
        GetExitCodeProcess(hTemp, &dwExitCode);
        if(dwExitCode == STILL_ACTIVE)
            TerminateProcess(hTemp, 0);
        CloseHandle(hTemp);
    }
#else
    QProcess p;
    p.start("pkill " + nameProcess);
    p.waitForFinished();
#endif
}

Benchmark::Benchmark(QMainWindow * mainWindow, QWebView * webView){
    this->mainWindow = mainWindow;
    this->webView = webView;
    QWebFrame *webFrame = webView->page()->mainFrame();
    webFrame->setScrollBarPolicy (Qt::Horizontal, Qt::ScrollBarAlwaysOff);
    QByteArray xhtmlPath(QDir::currentPath().toUtf8());
    xhtmlPath.append("/web/benchmark/benchmark.xhtml");
    xhtmlUrl = QUrl::fromLocalFile (xhtmlPath);
    xhtmlFile.setFileName("web/benchmark/benchmark.xhtml");
    webFrame->setScrollBarPolicy (Qt::Vertical, Qt::ScrollBarAlwaysOff);
    connect (webView->page()->mainFrame(), SIGNAL (javaScriptWindowObjectCleared()),
            this, SLOT (populateJavaScriptWindowObject()));
    worldsquare = new QProcess(this);
    connect (worldsquare, SIGNAL (readyReadStandardOutput()), this, SLOT(actionWorldsquare()));
    connect (worldsquare, SIGNAL (finished(int, QProcess::ExitStatus)), this, SLOT(logFinished(int, QProcess::ExitStatus)));
    pointsscatter = new QProcess(this);
    connect (pointsscatter, SIGNAL (readyReadStandardOutput ()), this, SLOT(actionPointsscatter()));
    connect (pointsscatter, SIGNAL (finished(int, QProcess::ExitStatus)), this, SLOT(logFinished(int, QProcess::ExitStatus)));
    xslXHTML.importStylesheet(QByteArray("web/benchmark/benchmark.xsl"));
    initWorldSquareXML ();
    hueSlab = "100";
    spheresNbrAgent = "0";
    this->webView->history()->setMaximumItemCount(0);
    unitNameText = "";
    logFlagValueText = "4";
    logPositionFlagValueText = "1";
    fileFlagValueText = QString::number(NOMO_ANY_NEW_FILE);
    animationValueText = "1";
    dataNameText = "";
    startStateText = "start";
    progressionText = "101";
    stepsNumberText = "1";
    finalValueText = "3";
    agentsNbrUnitText = "10000";
    xslXHTML.setNewParameter("unitName", unitNameText);
    xslXHTML.setNewParameter("logFlagValue", logFlagValueText);
    xslXHTML.setNewParameter("logPositionFlagValue", logPositionFlagValueText);
    xslXHTML.setNewParameter("fileFlagValue", fileFlagValueText);
    xslXHTML.setNewParameter("animationValue", animationValueText);
    xslXHTML.setNewParameter("dataName", dataNameText);
    xslXHTML.setNewParameter("startState", startStateText);
    xslXHTML.setNewParameter("progression",progressionText);
    xslXHTML.setNewParameter("stepsNumber", stepsNumberText);
    xslXHTML.setNewParameter("finalValue", finalValueText);
    xslXHTML.setNewParameter("agentsNbrUnit", agentsNbrUnitText);
    xslXHTML.setNewParameter("selectUnit", tr("Select Unit"));
    xslXHTML.setNewParameter("loggingLegend", tr("Logging"));
    xslXHTML.setNewParameter("logFlagLabel",  tr("For every steps, log: "));
    xslXHTML.setNewParameter("logFlagOption0", tr("partialy the selected rules"));
    xslXHTML.setNewParameter("logFlagOption1", tr("completely the selected rules"));
    xslXHTML.setNewParameter("logFlagOption2", tr("partialy all rules"));
    xslXHTML.setNewParameter("logFlagOption3", tr("completely all rules"));
    xslXHTML.setNewParameter("logFlagOption4", tr("any rules"));
    xslXHTML.setNewParameter("logPositionFlagLabel",  tr("For every steps, log: "));
    xslXHTML.setNewParameter("logPositionFlagOption0", tr("before the selection"));
    xslXHTML.setNewParameter("logPositionFlagOption1", tr("after the selection"));
    xslXHTML.setNewParameter("fileFlagLabel", tr("At begin, create a file for: "));
    xslXHTML.setNewParameter("fileFlagOption0", tr("complete log"));
    xslXHTML.setNewParameter("fileFlagOption1", tr("partial log"));
    xslXHTML.setNewParameter("fileFlagOption2", tr("any log"));
    xslXHTML.setNewParameter("legendClocking", tr("Clocking"));
    xslXHTML.setNewParameter("finalLabel", tr("At end: "));
    xslXHTML.setNewParameter("finalOption0", tr("reinitialize"));
    xslXHTML.setNewParameter("finalOption1", tr("save and reinitialize"));
    xslXHTML.setNewParameter("finalOption2", tr("update and reinitialize"));
    xslXHTML.setNewParameter("finalOption3", tr("no reinitialize"));
    xslXHTML.setNewParameter("stepsLabel", tr("Number of steps: "));
    xslXHTML.setNewParameter("animationLabel", tr("With animation (2Hz): "));
    xslXHTML.setNewParameter("animationOption0", tr("no"));
    xslXHTML.setNewParameter("animationOption1", tr("yes"));
    xslXHTML.setNewParameter("textHueLabel", tr("Hue of slab (°): "));
    xslXHTML.setNewParameter("textSpheresNbrLabel", tr("Number of spheres: "));
    xslXHTML.setNewParameter("agentLabel", tr("Agent #"));
    xslXHTML.setNewParameter("spheresNbrLabel", tr(" with a number of spheres to "));
    isGoing = false;
    killProcessus ("worldsquare");
    killProcessus ("pointsscatter");
}

Benchmark::~Benchmark (){
    freeWorldSquareXML ();
    if (worldsquare->pid() != 0){
        worldsquare->write("h\n");
        worldsquare->waitForFinished();
        delete worldsquare;
    }
    if (pointsscatter->pid() != 0){
        pointsscatter->write("h\n");
        pointsscatter->waitForFinished();
        delete pointsscatter;
    }
}

void Benchmark::initializeParameters(){
    hueSlab = "100";
    spheresNbrAgent = "0";
    unitNameText = "";
    xslXHTML.setParameter(0, unitNameText);
    logFlagValueText = "4";
    xslXHTML.setParameter(1, logFlagValueText);
    logPositionFlagValueText = "1";
    xslXHTML.setParameter(2, logPositionFlagValueText);
    fileFlagValueText = QString::number(NOMO_ANY_NEW_FILE);
    xslXHTML.setParameter(3, fileFlagValueText);
    animationValueText = "1";
    xslXHTML.setParameter(4, animationValueText);
    dataNameText = "";
    xslXHTML.setParameter(5, dataNameText);
    startStateText = "start";
    xslXHTML.setParameter(6, startStateText);
    progressionText = "101";
    xslXHTML.setParameter(7, progressionText);
    stepsNumberText = "1";
    xslXHTML.setParameter(8, stepsNumberText);
    finalValueText = "3";
    xslXHTML.setParameter(9, finalValueText);
    agentsNbrUnitText = "10000";
    xslXHTML.setParameter(10, agentsNbrUnitText);
}

QString Benchmark::openPoints(const QString path) {
    if(isValidPointsscatterCSV(path)){
        if (dataPath.contains(".map")){
            if (worldsquare->pid() != 0){
                worldsquare->write("h\n");
                worldsquare->waitForFinished();
            }
            QFile::remove("local.map");
            initializeParameters();
        }
        dataPath = path;
        QDir dir(path, "*.pts");
        dataNameText = dir.dirName().replace(".pts", "");
        xslXHTML.setParameter(5, dataNameText);
        startStateText ="start";
        xslXHTML.setParameter(6, startStateText);
        progressionText = "101";
        xslXHTML.setParameter(7, progressionText);
        xhtmlFile.open(QIODevice::WriteOnly | QIODevice::Text);
        xmlDocPtr doc = getSVGPointsscatterCSV(tr("density"));
        xmlChar* temp = xslXHTML.transformToFragment(doc);
        xmlFreeDoc(doc);
        xhtmlFile.write((const char*)temp);
        xmlFree(temp);
        webView->load (xhtmlUrl);
        xhtmlFile.close();
        if (pointsscatter->pid() != 0){
            pointsscatter->write("h\n");
            pointsscatter->waitForFinished();
        }
        isGoing = false;
        hasLogPartialFile = false;
        hasLogCompleteFile = false;
        return "";
    }
    else
        return tr("CSV format invalid.");
}

QString Benchmark::openMap(const QString path){
    if (dataPath.contains(".pts")){
        if (pointsscatter->pid() != 0){
            pointsscatter->write("h\n");
            pointsscatter->waitForFinished();
        }
        QFile::remove("local.csv");
        QFile::remove("points.svg");
        initializeParameters();
    }
    if (readWorldSquareXML(path.toUtf8().data())) {
        dataPath = path;
        QDir dir(path, "*.map");
        dataNameText = dir.dirName().replace(".map","");
        xslXHTML.setParameter(5, dataNameText);
        startStateText = "start";
        xslXHTML.setParameter(6, startStateText);
        progressionText = "101";
        xslXHTML.setParameter(7, progressionText);
        if (worldsquare->pid() != 0){
            worldsquare->write("h\n");
            worldsquare->waitForFinished();
        }
        isGoing = false;
        hasLogPartialFile = false;
        hasLogCompleteFile = false;
        repaintMap();
        return "";
    }
    return tr("XML format invalid.");
}


void Benchmark::reloadData(){
    dynamic_cast<MainWindow*>(mainWindow)->openProject(dataPath);
}

void Benchmark::updateProgressBar(const int progression){
     if (animationValueText == "1"){
        progressionText = QString::number(progression);
        xslXHTML.setParameter(7, progressionText);
     }
    else
        webView->page()->mainFrame()->evaluateJavaScript("setProgressBar("+QString::number(progression)+");");
}

QString Benchmark::linkUnit(){
    QString path = unitPath;
    QStringList arguments;
    arguments << "-link";
    arguments << path;
    QProcess nomotools;
    nomotools.start(toolsName, arguments, QIODevice::ReadOnly);
    nomotools.waitForFinished();
    QString result (nomotools.readAllStandardError ());
    if(result == "") {
        arguments.clear();
        arguments << "-parse";
        arguments << path.replace(".uni",".lnk");
        nomotools.start(toolsName, arguments, QIODevice::ReadOnly);
        nomotools.waitForFinished();
        result = nomotools.readAllStandardError ();
    }
    return result;
}

QString Benchmark::compileUnit(){
    QString path = unitPath;
    QStringList arguments;
    arguments << "-encode";
    arguments << path.replace(".uni",".lnk");
    QProcess nomotools;
    nomotools.start(toolsName, arguments, QIODevice::ReadOnly);
    nomotools.waitForFinished();
    QString result (nomotools.readAllStandardError ());
    if(result == ""){
        QFile::remove(path);
        arguments.clear();
        arguments << "-parse";
        arguments << path.replace(".lnk",".cod");
        nomotools.start(toolsName, arguments, QIODevice::ReadOnly);
        nomotools.waitForFinished();
        result = nomotools.readAllStandardError ();
        if (result == "") {
            cleanSeed();
            arguments.clear();
            arguments << "-compile";
            arguments << path;
            nomotools.start(toolsName, arguments, QIODevice::ReadOnly);
            nomotools.waitForFinished();
            result = nomotools.readAllStandardError ();
            if (result == "")
            {
                QDir codeDir(path);
                const QString seedName = codeDir.dirName().replace(".cod",".seed");
                codeDir.cdUp();
                codeDir.mkdir("benchmark");
                logPath = codeDir.path()+QDir::separator()+"benchmark"+QDir::separator();
                logPath = QDir::current ().relativeFilePath(logPath);
                arguments.clear();
                arguments << "-db";
                arguments << logPath;
                arguments << path;
                nomotools.start(toolsName, arguments, QIODevice::ReadOnly);
                nomotools.waitForFinished();
                xmlDocPtr cod = xmlReadFile(path.toUtf8().data(),NULL,0);
                const QString binaryLocation ((const char*)xmlGetProp(xmlDocGetRootElement(cod), (const xmlChar*) "binaryLocation"));
                const QString seedDir = codeDir.absoluteFilePath(binaryLocation);
                QFile::copy(seedDir+QDir::separator()+seedName, seedName);
                xmlFreeDoc(cod);
            }
        }
    }
    return result;
}

void Benchmark::openUnit(){
    QString path = unitPath;
    path.replace(".uni",".cod");
    QFile::remove(path);
    QDir dir(unitPath,"*.uni");
    unitNameText = dir.dirName().replace(".uni","");
    webView->page()->mainFrame()->evaluateJavaScript("openUnit('"+unitNameText+"');");
    xslXHTML.setParameter(0, unitNameText);
    startStateText = "start";
    xslXHTML.setParameter(6, startStateText);
    progressionText = "101";
    xslXHTML.setParameter(7, progressionText);
    isGoing = false;
    hasLogPartialFile = false;
    hasLogCompleteFile = false;
}

QString Benchmark::openMapUnit(QString path){
    unitPath = path;
    QString result = linkUnit();
    if(result == "") {
        xmlDocPtr doc = xmlReadFile(path.replace(".uni",".lnk").toUtf8().data(), NULL, 0);
        xmlXPathInit();
        xmlXPathContextPtr ctxt = xmlXPathNewContext(doc);
        xmlXPathRegisterNs(ctxt, (const xmlChar*)"model",(const xmlChar*)"http://www.nomoseed.org/model");
        xmlXPathObjectPtr xpathRes = xmlXPathEvalExpression((const xmlChar*)"count(//model:model/model:definition[@base='worldsquare'])", ctxt);
        result = tr("One model with worldsquare base is obligatory.");
        if (xmlXPathCastToNumber(xpathRes) != 0){
            result = tr("Command type unknown.");
            xmlXPathFreeObject(xpathRes);
            xpathRes = xmlXPathEvalExpression((const xmlChar*)"count(//model:command_type[@name!='motor' and @name!='sound'])", ctxt);
            if (xmlXPathCastToNumber(xpathRes) == 0){
                result = tr("Multiplexer mod is obligatory.");
                xmlXPathFreeObject(xpathRes);
                xmlXPathRegisterNs(ctxt, (const xmlChar*)"unit",(const xmlChar*)"http://www.nomoseed.org/unit");
                xpathRes = xmlXPathEvalExpression((const xmlChar*)"/unit:unit/@multiplexer='yes'", ctxt);
                if (xmlXPathCastToBoolean(xpathRes) == 1){
                    if (worldsquare->pid() != 0){
                        worldsquare->write("h\n");
                        worldsquare->waitForFinished();
                    }
                    result = compileUnit();
                    if (result == ""){
                        QFile fcode(path.replace(".lnk",".cod"));
                        fcode.open(QIODevice::ReadOnly);
                        short agentsNbr = (short) fcode.readAll().count("external_name=\"command_motor_");
                        fcode.close();
                        agentsNbrUnitText = QString::number(agentsNbr);
                        xslXHTML.setParameter(10, agentsNbrUnitText);
                        openUnit();
                        xmlXPathFreeObject(xpathRes);
                        xmlXPathFreeContext(ctxt);
                        xmlFreeDoc(doc);
                        return result;
                    }
                }
            }
        }
        if (xpathRes)
            xmlXPathFreeObject(xpathRes);
        xmlXPathFreeContext(ctxt);
        xmlFreeDoc(doc);
    }
    return result;
}

QString Benchmark::openPointsUnit(QString path){
    unitPath = path;
    QString result = linkUnit();
    if(result == "") {
        xmlDocPtr doc = xmlReadFile(path.replace(".uni",".lnk").toUtf8().data(), NULL, 0);
        xmlXPathInit();
        xmlXPathContextPtr ctxt = xmlXPathNewContext(doc);
        xmlXPathRegisterNs(ctxt, (const xmlChar*)"model",(const xmlChar*)"http://www.nomoseed.org/model");
        xmlXPathObjectPtr xpathRes = xmlXPathEvalExpression((const xmlChar*)"count(//*[(name()='new' or name()='inherite' ) and not( ancestor::*[name()='new' or name()='inherite']/@model=@model or following::*[name()='new' or name()='inherite']/@model=@model or preceding::*[name()='new' or name()='inherite']/@model=@model) ]/model:model/model:definition[@base='pointsscatter'])", ctxt);
        result = tr("One model with pointsscatter base is obligatory.");
        if (xmlXPathCastToNumber(xpathRes) != 0){
            xmlXPathFreeObject(xpathRes);
            xpathRes = xmlXPathEvalExpression((const xmlChar*)"count(//model:outputs/model:index[@base!='pointsscatter' and @base!='*'])", ctxt);
            result = tr("Command type unknown.");
            if (xmlXPathCastToNumber(xpathRes) == 0){
                xmlXPathFreeObject(xpathRes);
                xmlXPathRegisterNs(ctxt, (const xmlChar*)"unit",(const xmlChar*)"http://www.nomoseed.org/unit");
                xpathRes = xmlXPathEvalExpression((const xmlChar*)"/unit:unit/@multiplexer='yes'", ctxt);
                if (xmlXPathCastToBoolean(xpathRes) == 1){
                    if (pointsscatter->pid() != 0){
                        pointsscatter->write("h\n");
                        pointsscatter->waitForFinished();
                    }
                    result = compileUnit();
                    if (result == ""){
                        openUnit();
                        xmlXPathFreeObject(xpathRes);
                        xmlXPathFreeContext(ctxt);
                        xmlFreeDoc(doc);
                        return result;
                    }
                }
            }
        }
        if (xpathRes)
            xmlXPathFreeObject(xpathRes);
        xmlXPathFreeContext(ctxt);
        xmlFreeDoc(doc);
    }
    return result;
}

void Benchmark::reloadMapUnit(){
    const QString result = openMapUnit(unitPath);
    if(!result.isEmpty())
        QMessageBox::warning(dynamic_cast<MainWindow*>(mainWindow), tr("The reloading of the unit has failed !"), result);
    else
        repaintMap();
}

void Benchmark::reloadPointsUnit(){
    const QString result = openPointsUnit(unitPath);
    if(!result.isEmpty())
        QMessageBox::warning(dynamic_cast<MainWindow*>(mainWindow), tr("The reloading of the unit has failed !"), result);
    else
        refreshPoints();
}

void Benchmark::cleanSeed(){
    QDir dir;
    dir.setNameFilters(QStringList("*.seed"));
    QStringList list = dir.entryList ();
    for (int i = 0; i < list.size(); ++i)
        dir.remove(list.at(i));
}

void Benchmark::selectMapUnit(){
    const QStringList fileNames = dynamic_cast<MainWindow*>(mainWindow)->selectFile (tr("Open a unit for the benchmark..."), tr("All unit files (*.uni)"));
    if (!fileNames[0].isEmpty()) {
        const QString result = openMapUnit(fileNames[0]);
        if(!result.isEmpty())
            QMessageBox::warning(dynamic_cast<MainWindow*>(mainWindow), tr("The opening of the unit has failed !"), result);
    }
}

void Benchmark::selectPointsUnit(){
    const QStringList fileNames = dynamic_cast<MainWindow*>(mainWindow)->selectFile (tr("Open a unit for the benchmark..."), tr("All unit files (*.uni)"));
    if (!fileNames[0].isEmpty()){
        const QString result = openPointsUnit(fileNames[0]);
        if(!result.isEmpty())
            QMessageBox::warning(dynamic_cast<MainWindow*>(mainWindow), tr("The opening of the unit has failed !"), result);
    }
}

void Benchmark::selectMap(){
    QStringList fileNames = dynamic_cast<MainWindow*>(mainWindow)->selectFile (tr("Open a map for the benchmark..."), tr("All map files (*.map)"));
    if (!fileNames[0].isEmpty()) {
        QString result = openMap(fileNames[0]);
        if(!result.isEmpty())
            QMessageBox::warning(dynamic_cast<MainWindow*>(mainWindow), tr("The opening of the map has failed !"), result);
    }
}

void Benchmark::selectPoints(){
    QStringList fileNames = dynamic_cast<MainWindow*>(mainWindow)->selectFile (tr("Open a points scatter for the benchmark..."), tr("All points scatter files (*.pts)"));
    if (!fileNames[0].isEmpty()){
        QString result = openPoints(fileNames[0]);
        if(!result.isEmpty())
            QMessageBox::warning(dynamic_cast<MainWindow*>(mainWindow), tr("The opening of the points scatter has failed !"), result);
    }
}

void Benchmark::startMap(){
    saveWorldSquareXML("local.map");
    if (worldsquare->pid() == 0){
        QFile test("test.txt");
        test.open(QIODevice::WriteOnly | QIODevice::Text);
        QStringList arguments;
        arguments << "local.map";
        arguments << agentsNbrUnitText;
        arguments << logPath;
        arguments << QString::number(logPath.size());
        arguments << "500";
        hasLogPartialFile = false;
        hasLogCompleteFile = false;
#if WIN32
        worldsquare->start("worldsquare", arguments);
#else
        worldsquare->start("./worldsquare", arguments);
#endif
        if (!worldsquare->waitForStarted())
            return;
        webView->page()->mainFrame()->evaluateJavaScript("changeLogFlag();");
    }
    if (!hasLogPartialFile)
        hasLogPartialFile = fileFlagValueText.toInt() == NOMO_NEW_PARTIAL_FILE;
    if (!hasLogCompleteFile)
        hasLogCompleteFile = fileFlagValueText.toInt() == NOMO_NEW_FULL_FILE;
    startStateText = "pause";
    xslXHTML.setParameter(6, startStateText);
    if (isGoing)
        worldsquare->write("g\n");
    else{
        if (stepsNumberText == "1"){
            if (logFlagValueText.toInt() == NOMO_NO_LOG && fileFlagValueText.toInt() > 1)
               worldsquare->write("a\n");
            else
               worldsquare->write(("b"+logFlagValueText+logPositionFlagValueText+fileFlagValueText+"\n").toUtf8().data());
        }
        else{
            isGoing = true;
            if (animationValueText == "1"){
                if (logFlagValueText.toInt() == NOMO_NO_LOG && fileFlagValueText.toInt() > 1)
                    worldsquare->write(("c"+stepsNumberText+"\n").toUtf8().data());
                else
                    worldsquare->write(("d"+logFlagValueText+logPositionFlagValueText+fileFlagValueText+stepsNumberText+"\n").toUtf8().data());
            }
            else {
                if (logFlagValueText.toInt() == NOMO_NO_LOG && fileFlagValueText.toInt() > 1)
                    worldsquare->write(("e"+stepsNumberText+"\n").toUtf8().data());
                else
                    worldsquare->write(("f"+logFlagValueText+logPositionFlagValueText+fileFlagValueText+stepsNumberText+"\n").toUtf8().data());
            }
        }
    }
}

void Benchmark::actionWorldsquare(){
    const QString output = worldsquare->readAllStandardOutput().simplified();
    if (!output.isEmpty()){
        const int result = output.mid(output.lastIndexOf(' ')).toInt();
        const int stepsNumber = stepsNumberText.toInt();
        if (stepsNumber > 1 )
            updateProgressBar((result * 100) / stepsNumber);
        if (animationValueText == "1")
            refreshMap();
        if (result == stepsNumber){
            if (animationValueText != "1")
                refreshMap();
            QTimer::singleShot(500, this, SLOT(end()));
        }
    }
}

void Benchmark::startPoints(){
    if (pointsscatter->pid() == 0){
        QStringList arguments;
        arguments << "local.csv";
        arguments << logPath;
        arguments << QString::number(logPath.size());
        arguments << "500";
        hasLogPartialFile = false;
        hasLogCompleteFile = false;
#if WIN32
        pointsscatter->start("pointsscatter", arguments);
#else
        pointsscatter->start("./pointsscatter", arguments);
#endif
        if (!pointsscatter->waitForStarted())
            return;
        webView->page()->mainFrame()->evaluateJavaScript("changeLogFlag();");
    }
    if (!hasLogPartialFile)
        hasLogPartialFile = fileFlagValueText.toInt() == NOMO_NEW_PARTIAL_FILE;
    if (!hasLogCompleteFile)
        hasLogCompleteFile = fileFlagValueText.toInt() == NOMO_NEW_FULL_FILE;
    startStateText = "pause";
    xslXHTML.setParameter(6, startStateText);
    if (isGoing)
        pointsscatter->write("g\n");
    else{
        if (stepsNumberText == "1"){
            if (logFlagValueText.toInt() == NOMO_NO_LOG && fileFlagValueText.toInt() > 1)
                pointsscatter->write("a\n");
            else
                pointsscatter->write(("b"+logFlagValueText+logPositionFlagValueText+fileFlagValueText+"\n").toUtf8().data());
        }
        else{
            isGoing = true;
            if (animationValueText == "1"){
                if (logFlagValueText.toInt() == NOMO_NO_LOG && fileFlagValueText.toInt() > 1)
                    pointsscatter->write(("c"+stepsNumberText+"\n").toUtf8().data());
                else
                    pointsscatter->write(("d"+logFlagValueText+logPositionFlagValueText+fileFlagValueText+stepsNumberText+"\n").toUtf8().data());
            }
            else {
                if (logFlagValueText.toInt() == NOMO_NO_LOG && fileFlagValueText.toInt() > 1)
                    pointsscatter->write(("e"+stepsNumberText+"\n").toUtf8().data());
                else
                    pointsscatter->write(("f"+logFlagValueText+logPositionFlagValueText+fileFlagValueText+stepsNumberText+"\n").toUtf8().data());
            }
        }
    }
}

void Benchmark::actionPointsscatter(){
    const QString output = pointsscatter->readAllStandardOutput().simplified();
    if (!output.isEmpty()){
        const int result = output.mid(output.lastIndexOf(' ')).toInt();
        const int stepsNumber = stepsNumberText.toInt();
        if (stepsNumber > 1 )
            webView->page()->mainFrame()->evaluateJavaScript("setProgressBar("+QString::number((result*100)/stepsNumber)+");");
        if (result == stepsNumber)
            end();
        else if (animationValueText == "1")
            refreshPoints();
    }
}

void Benchmark::endMap(){
    fileFlagValueText = QString::number(NOMO_ANY_NEW_FILE);
    xslXHTML.setParameter(3, fileFlagValueText);
    startStateText = "start";
    xslXHTML.setParameter(6, startStateText);
    progressionText = "101";
    xslXHTML.setParameter(7, progressionText);
    if (finalValueText.toInt() != 3){
        worldsquare->write(("h"+finalValueText+"\n").toUtf8().data());
        hasLogPartialFile = false;
        hasLogCompleteFile = false;
    }
    repaintMap();
}

void Benchmark::endPoints(){
    refreshPoints();
    webView->page()->mainFrame()->evaluateJavaScript("end()");
    if (finalValueText.toInt() != 3){
        pointsscatter->write(("h"+finalValueText+"\n").toUtf8().data());
        hasLogPartialFile = false;
        hasLogCompleteFile = false;
    }
}

void Benchmark::end(){
    isGoing = false;
    if (dataPath.right(3) == "map")
        endMap();
    else
        endPoints();
}

void Benchmark::pause(){
    if (dataPath.right(3) == "map"){
        worldsquare->write("g\n");
        startStateText = "start";
        xslXHTML.setParameter(6, startStateText);
    }
    else
        pointsscatter->write("g\n");
}

void Benchmark::setHueSlab (const QString id, const QString hue){
    setHueSlabWorldSquareXML(id.toUtf8().data(), hue.toUtf8().data());
    hueSlab = hue;
    repaintMap();
}

void Benchmark::addSlab (const QString x, const QString y) {
    addSlabWorldSquareXML (x.toUtf8().data(), y.toUtf8().data(), hueSlab.toUtf8().data());
    repaintMap();
}

void Benchmark::suppAll(){
    suppAllWorldSquareXML((xSlab + ySlab).toUtf8().data());
    repaintMap();
}

void Benchmark::setSlab (const QString x, const QString y, const QString hue){
    xSlab = x;
    ySlab = y;
    hueSlab = hue;
}

void Benchmark::setSpheresNbrAgent (const QString spheresNbr){
    spheresNbrAgent = spheresNbr;
}

void Benchmark::setSpheresNbrAgent (const QString id, const QString spheresNbr){
    spheresNbrAgent = spheresNbr;
    setSpheresNbrWorldSquareXML(id.toUtf8().data(), spheresNbr.toUtf8().data());
}

const QString Benchmark::getSpheresNbrAgent (const QString id){
    QString answer = getSpheresNbrWorldSquareXML(id.toUtf8().data());
    if (answer!="")
        return answer;
    else
        return spheresNbrAgent;
}

const QString Benchmark::getSpheresNbrAgent (){
    return spheresNbrAgent;
}

void Benchmark::addSlab(){
    addSlab(xSlab.toUtf8().data(),ySlab.toUtf8().data());
}

void Benchmark::addSphere(){
    addSphereWorldSquareXML((xSlab + ySlab).toUtf8().data());
    repaintMap();
}

void Benchmark::addAgent(const QString direction){
    addAgentWorldSquareXML((xSlab + ySlab).toUtf8().data(), direction.toUtf8().data(), spheresNbrAgent.toUtf8().data());
    repaintMap();
}

void Benchmark::moveAgent(const QString on, const QString at){
    moveAgentWorldSquareXML(on.toUtf8().data(), at.toUtf8().data());
    repaintMap();
}

void Benchmark::repaintMap(){
    xslXHTML.transformSaveFile(getWorldSquareXML(), "web/benchmark/benchmark.xhtml");
    webView->load (xhtmlUrl);
}

void Benchmark::refreshMap(){
    updateWorldSquareXML("local.map");
    xslXHTML.transformSaveFile(getWorldSquareXML(), "web/benchmark/benchmark.xhtml");
    resetWorldSquareXML ();
    webView->load (xhtmlUrl);
}

void Benchmark::refreshPoints(){
    updateSVGPointsscatterCSV(tr("density"));
    webView->page()->mainFrame()->evaluateJavaScript("refresh()");
}

void Benchmark::suppSphere(const QString on){
    suppSphereWorldSquareXML(on.toUtf8().data());
    repaintMap();
}

void Benchmark::suppAgent(const QString on){
    suppAgentWorldSquareXML(on.toUtf8().data());
    repaintMap();
}

void Benchmark::suppSlab(const QString id){
    suppSlabWorldSquareXML(id.toUtf8().data());
    repaintMap();
}

void Benchmark::save(){
    if (dataPath.contains(".map"))
        saveWorldSquareXML(dataPath.toUtf8().data());
    else
        saveDataPointsscatterCSV(dataPath);
}

void Benchmark::close(){
    if (worldsquare->pid() != 0){
        worldsquare->write(("h"+QString::number(NOMO_ANY_MODIFICATION)+"\n").toUtf8().data());
        worldsquare->waitForFinished();
    }
    QFile::remove("local.map");
    if (pointsscatter->pid() != 0){
        pointsscatter->write(("h"+QString::number(NOMO_ANY_MODIFICATION)+"\n").toUtf8().data());
        pointsscatter->waitForFinished();
    }
    QFile::remove("local.csv");
    QFile::remove("points.svg");
    cleanSeed();
    initializeParameters();
}

void Benchmark::populateJavaScriptWindowObject(){
    webView->page()->mainFrame()->addToJavaScriptWindowObject("nomoSDK", this);
}

void Benchmark::setStepsNumber(const QString value){
    stepsNumberText = value;
    xslXHTML.setParameter(8, stepsNumberText);
    if (isGoing)
        end ();
}

void Benchmark::setLogFlag(const QString value){
    logFlagValueText = value;
    xslXHTML.setParameter(1, logFlagValueText);
    if (isGoing)
        end ();
}

void Benchmark::setLogPositionFlag(const QString value){
    logPositionFlagValueText = value;
    xslXHTML.setParameter(2, logPositionFlagValueText);
    if (isGoing)
        end ();
}

void Benchmark::setFileFlag(const QString value){
    fileFlagValueText = value;
    xslXHTML.setParameter(3, fileFlagValueText);
    if (isGoing)
        end ();
}

void Benchmark::setFinal(const QString value){
    finalValueText = value;
    xslXHTML.setParameter(9, finalValueText);
    if (isGoing)
        end ();
}

void Benchmark::setAnimation(const QString value){
    animationValueText = value;
    xslXHTML.setParameter(4, animationValueText);
    if (isGoing)
        end ();
}

bool Benchmark::getHasLogPartialFile(){
    return hasLogPartialFile;
}

bool Benchmark::getHasLogCompleteFile(){
    return hasLogCompleteFile;
}

void Benchmark::logFinished(int exitCode, QProcess::ExitStatus exitStatus){
    if (exitStatus != QProcess::NormalExit && exitCode != 125){
        if (worldsquare->pid() != 0){
            worldsquare->kill();
        }
        if (pointsscatter->pid() != 0){
            pointsscatter->kill();
        }
        QFile f("nomosdk.err");
        f.open(QFile::Append);
        f.write(("CrashExit"+ QString::number(exitCode)).toUtf8().data());
        f.close();
    }
 /*   else{
        QFile f("nomosdk.err");
        f.open(QFile::Append);
        f.write(("NormalExit" + QString::number(exitCode)).toUtf8().data());
        f.close();
    }*/
}
