/*
 ** This file is part of the ViTE project.
 **
 ** This software is governed by the CeCILL-A license under French law
 ** and abiding by the rules of distribution of free software. You can
 ** use, modify and/or redistribute the software under the terms of the
 ** CeCILL-A license as circulated by CEA, CNRS and INRIA at the following
 ** URL: "http://www.cecill.info".
 **
 ** As a counterpart to the access to the source code and rights to copy,
 ** modify and redistribute granted by the license, users are provided
 ** only with a limited warranty and the software's author, the holder of
 ** the economic rights, and the successive licensors have only limited
 ** liability.
 **
 ** In this respect, the user's attention is drawn to the risks associated
 ** with loading, using, modifying and/or developing or reproducing the
 ** software by the user in light of its specific status of free software,
 ** that may mean that it is complicated to manipulate, and that also
 ** therefore means that it is reserved for developers and experienced
 ** professionals having in-depth computer knowledge. Users are therefore
 ** encouraged to load and test the software's suitability as regards
 ** their requirements in conditions enabling the security of their
 ** systems and/or data to be ensured and, more generally, to use and
 ** operate it in the same conditions as regards security.
 **
 ** The fact that you are presently reading this means that you have had
 ** knowledge of the CeCILL-A license and that you accept its terms.
 **
 **
 ** ViTE developers are (for version 0.* to 1.0):
 **
 **        - COULOMB Kevin
 **        - FAVERGE Mathieu
 **        - JAZEIX Johnny
 **        - LAGRASSE Olivier
 **        - MARCOUEILLE Jule
 **        - NOISETTE Pascal
 **        - REDONDY Arthur
 **        - VUCHENER Clément
 **
 */
/*!
 *\file Core.cpp
 *\brief This is the console interface C source code.
 */
#include <queue>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <list>
#include <map>
#include <vector>
#include <stack>
#ifndef WIN32
#include <cmath>
#include <unistd.h>
#if defined(HAVE_GETOPT_H)
#include <getopt.h>
#endif  /* defined(HAVE_GETOPT_H) */

#else
#if !defined (__MINGW32__)
#define sscanf sscanf_s
#endif
#include <core/getopt.h>
#endif

/* -- */
#include <QObject>
//#include <QtUiTools>/* for the run-time loading .ui file */
#include <QTextCodec>
#include <QElapsedTimer>
#include <QDir>
#include <QGLWidget>/* for the OpenGL Widget */
#include <QMutex>
#include <QThread>
#include <QProcess>

/* -- */
#include "interface/Interface.hpp"
#include "common/common.hpp"
#include "common/Info.hpp"
#include "common/Errors.hpp"
#include "common/Tools.hpp"
#include "common/Session.hpp"
#include "common/Message.hpp"
#include "common/Palette.hpp"
#include "common/Session.hpp"
/* -- */
#include "render/Geometry.hpp"
#include "render/Hook_event.hpp"
#include "render/Ruler.hpp"
#include "render/GanttDiagram.hpp"
#include "render/Render.hpp"
#include "render/Render_svg.hpp"
/* -- */
#include "trace/values/Values.hpp"
#include "trace/tree/Interval.hpp"
#include "trace/tree/Node.hpp"
#include "trace/tree/BinaryTree.hpp"
#include "trace/EntityValue.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/Trace.hpp"
#include "trace/DrawTree.hpp"
#include "trace/DrawTrace.hpp"
/* -- */
#include "parser/Parser.hpp"
#include "parser/ParserFactory.hpp"
#include "parser/ParsingThread.hpp"
/* -- */
#include "interface/Node_select.hpp"
#include "interface/Interface_graphic.hpp"
#include "core/Core.hpp"
/* -- */
#ifdef VITE_ENABLE_VBO
#include "render/Render_alternate.hpp"
#else
#include "render/Render_opengl.hpp"
#endif

#ifdef WIN32
#include <direct.h>
#else
#include <sys/stat.h>
#include <sys/types.h>

#endif

#ifdef USE_MPI
#include <boost/mpi/environment.hpp>
#include <boost/mpi/communicator.hpp>
#endif

using namespace std;

#define message *Message::get_instance() << "(" << __FILE__ << " l." << __LINE__ << "): "

/***********************************
 *
 *
 *
 * Constructor and destructor.
 *
 *
 *
 **********************************/

Core::Core(int &argc, char ** argv)
{
    /*
     * graphic_app is used if a graphical interface is used
     * console_app is used if we only care about the console interface
     */
    graphic_app = nullptr;
    console_app = nullptr;

    /* Qt uses the default system encoding for QString (used when opening a file) */
#if QT_VERSION < 0x050000
    QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
#endif
    /*
     * Store application name and current directory
     */
    _run_env[0] = new QString(QDir::currentPath().toStdString().c_str());
    _run_env[1] = new QString(argv[0]);

    /* Initialize global variable */
    _main_window   = nullptr;
    _render_opengl = nullptr;
    _trace         = nullptr;
    _is_window_displayed = false;
    _is_trace_loaded     = false;

    /* Define the visible interval */
    _time_start = 0.;
    _time_end   = 0.;

    /* define which interface will receive messages */
    /* MathieuTOmyself: No idea what is this */
    Message::set_interface(this);

    /* Set the options according to the parameters given
     * Warning: Work on a copy of argv to keep parameter
     * that could be usefull to boost or Qt, as psn
     * on Mac for example */
    _state = get_options(argc, argv);

#ifdef USE_MPI
    boost::mpi::environment env(argc, argv);
#endif

    /* Create the Qt application */
    if ( (_state != _STATE_SPLITTING  ) &&
         (_state != _STATE_EXPORT_FILE) ) {
        graphic_app = new QApplication(argc, argv);
    }
    else {
        console_app = new QCoreApplication(argc, argv);
    }

    /* Trick to avoid problems with menus not showing up in Qt5 */
    QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);

    /* Start to launch actions */
    launch_action(_state, nullptr);
}


Core::~Core(){

    if( _trace != nullptr )
        delete _trace;

    if ( _run_env[0] != nullptr )
        delete _run_env[0];

    if ( _run_env[1] != nullptr )
        delete _run_env[1];

    if ( _render_opengl != nullptr ) {
        _render_opengl->release();
        delete _render_opengl;
    }

    /* Qt desallocates _main_window and _render_opengl automatically */
    Message::kill();
    if ( console_app != nullptr ) delete console_app;
    if ( graphic_app != nullptr ) delete graphic_app;
}

/***********************************
 *
 * The command line parameter processing functions.
 *
 **********************************/
#define GETOPT_STRING "t:e:c:i:s:h"

#if defined(HAVE_GETOPT_LONG)
static struct option long_options[] =
{
    {"interval",    required_argument,  0, 't'},
    {"export",      required_argument,  0, 'e'},
    {"load_containers_config",    required_argument,  0, 'c'},
    {"input",       required_argument,  0, 'i'},
    {"split",       required_argument,  0, 's'},
    {"help",        no_argument,        0, 'h'},
    {0, 0, 0, 0}
};
#endif

int Core::get_options(int &argc, char **argv)
{
    int state = 0;
    int opt = 0;
    int c;
    int largc;
    char **largv;

    /* Ignore any -psn_%d_%d argument, which is Mac OS specific */
    largc = 0;
    largv = (char**)calloc(argc+1, sizeof(char**));
    for(c = 0; c < argc; c++) {
        if( strncmp(argv[c], "-psn", 4) )
            largv[largc++] = argv[c];
    }

    // No parameters, launch the window interface
    if( largc == 1 ){
        state = _STATE_LAUNCH_GRAPHICAL_INTERFACE;
        goto cleanup;
    }

    do
    {
#if defined(HAVE_GETOPT_LONG)
        c = getopt_long_only(largc, largv, "",
                             long_options, &opt);
#else
        c = getopt(largc, largv, GETOPT_STRING);
        (void) opt;
#endif  // defined(HAVE_GETOPT_LONG)

        switch(c) {
        case 't' :
            double t1, t2;
            // Only the end
            if( optarg[0] == ':' ) {
                if ( sscanf( optarg, ":%lf", &t2) == 1 ) {
                    _time_end   = t2;
                } else {
                    cerr << QObject::tr("Interval is not in the correct format [%d:%d]").toStdString() << endl;
                    state=_STATE_DISPLAY_HELP;
                    goto cleanup;
                }
            } else {
                int ret = sscanf( optarg, "%lf:%lf", &t1, &t2);
                switch( ret ) {
                case 2:
                    _time_end   = t2;
                case 1 :
                    _time_start = t1;
                    break;
                case 0:
                    cerr << QObject::tr("Interval is not in the correct format [%d:%d]").toStdString() << endl;
                    state=_STATE_DISPLAY_HELP;
                    goto cleanup;
                }
            }
            break;

        case 'e' :
            _path_to_export = optarg;
            state = _STATE_EXPORT_FILE;
            break;

        case 'i' :
            _file_opened = optarg;
            if (state == 0)
                state = _STATE_OPEN_FILE;
            break;

        case 's' :
            // We want to split the file and save its parts
            _file_opened = optarg;
            Info::Splitter::split=true;
            if (state == 0) state = _STATE_SPLITTING ;
            break;

        case 'h' :
            state=_STATE_DISPLAY_HELP;
            goto cleanup;

        case 'c' :
            _xml_config_file = optarg;
            break;

        default:
            continue;
        }
    } while(-1 != c);

    if(optind < argc)
    {
        _file_opened = largv[optind];
        if (state == 0) state = _STATE_OPEN_FILE;
    }

  cleanup:
    free(largv);
    return state;
}

/***********************************
 *
 *
 *
 * Running function.
 *
 *
 *
 **********************************/

bool Core::draw_trace(const string & filename, const int format) {
    parser = nullptr;
    DrawTrace  drawing_ogl;
    bool killed=false;

    QElapsedTimer time_elapsed;
    ostringstream buf_txt;
    int           time_buf = 0;

    // Select the Parser in function of the file's extension
    if(! ParserFactory::create(&parser, filename)){
        *Message::get_instance() << QObject::tr("Error : parser not found for input file").toStdString() << Message::ende;
        return false;
    }

    // Set application in working state
    if (_DRAW_OPENGL == format)
        QApplication::setOverrideCursor(Qt::WaitCursor);

    if (_DRAW_SVG == format) {
    }
    else if(_STATE_SPLITTING!=format){
        // We add the filename to the recent files opened list
        Session::add_recent_file(QFileInfo(filename.c_str()).absoluteFilePath());
        _main_window->update_recent_files_menu();
    }

    if (nullptr == _trace) { /* no trace is loaded, parse the file */

        /* Init data */
        Info::Entity::x_min          = 0;
        Info::Entity::x_max          = 0;
        Info::Render::_x_min_visible = 0;
        Info::Render::_x_max_visible = 0;

        _trace = new Trace();

        // Init of the thread
        _finished = new QWaitCondition();
        _closed   = new QWaitCondition();
        _mutex    = new QMutex();

        QThread thread;

        ParsingThread pt(parser, _trace, _finished,_closed, _mutex);

        pt.moveToThread(&thread);
        qRegisterMetaType<std::string>("std::string");
        pt.connect((const QObject*)this,
                   SIGNAL(run_parsing()),
                   SLOT(run()));

        pt.connect((const QObject*)this,
                   SIGNAL(build_finished(bool)),
                   SLOT(finish_build(bool)));
        //in case of splitting
        pt.connect((const QObject*)this,
                   SIGNAL(dump(std::string,std::string)),
                   SLOT(dump(std::string,std::string)));

        int loaded = 0;
        float loaded_f = 0.0f;/* floating value of the loading file state. (between 0 and 1) */

        if(_main_window != nullptr) { // If we have a window we show a progress bar
            _main_window->init_parsing(filename);
        }

#if defined(USE_ITC) && defined(BOOST_SERIALIZE)
        if(Info::Splitter::path.empty())Info::Splitter::path=QFileInfo(_file_opened.c_str()).dir().path().toStdString();
        if(Info::Splitter::filename.empty())Info::Splitter::filename=QFileInfo(_file_opened.c_str()).baseName().toStdString();

        if(Info::Splitter::xml_filename.empty() && !_xml_config_file.empty())Info::Splitter::xml_filename=_xml_config_file;
        if(Info::Splitter::split==true){
            std::stringstream str;
            str<<Info::Splitter::path<<"/"<<Info::Splitter::filename;
#ifdef WIN32
            _mkdir(str.str().c_str());
#else
            mkdir(str.str().c_str(), 01777);
#endif
        }

        if((Info::Splitter::_x_max==0.0)&&(Info::Splitter::_x_min==0.0)){

#if defined(USE_MPI)

            if (_world.rank() == 0) {

                double min = _time_start;
                double max = 0;
                if(_time_end == 0.) {
                    max = _trace->get_max_date().get_value();
                }
                else {
                    max =_time_end;
                }

                double interval = (max-min)/ _world.size();
                for(int i=1;i<_world.size(); i++){
                    //send the new working interval for each node
                    // double out_msg[2]={min+i*interval,min+(i+1)*interval};
                    _world.send(i, 0,min+i*interval );
                    _world.send(i, 1,min+(i+1)*interval );

                }
                //0 displays the first interval
                _time_start=min;
                _time_end=min+interval;

            } else {
                double msg[2];
                _world.recv(0, 0, msg[0]);
                _world.recv(0, 1, msg[1]);
                _time_start = msg[0];
                _time_end   = msg[1];
            }
#endif /* defined(USE_MPI) */

            Info::Splitter::_x_min = _time_start;

            if(_time_end == 0.) {
                Info::Splitter::_x_max = _trace->get_max_date().get_value();
            }
            else {
                Info::Splitter::_x_max =_time_end;
            }
        }
#endif /* defined(USE_ITC) && defined(BOOST_SERIALIZE) */

        thread.start();
        time_elapsed.start();
        buf_txt.str("");
        bool parsing_finished=false;
        emit run_parsing();

        while( !parsing_finished && !killed) {

            if(!parser->is_end_of_parsing()){
                //locks the mutex and automatically unlocks it when going out of scope
                QMutexLocker locker(_mutex);
                // We wait 1 second
                _finished->wait(_mutex, 1000);
            }
            killed=pt.is_killed();
            if(killed)break;
            loaded_f = parser->get_percent_loaded();
            loaded   = (int)(loaded_f*100.0f);

            if(parser->is_end_of_parsing() && !killed){
                if(!_xml_config_file.empty() && _trace->get_view_root_containers()->empty()){//if we have a partial loading, process it here, but only once
                    launch_action(_DRAW_OPENGL_PARTIAL,nullptr);
                }

                QMutexLocker locker(_mutex);
                if(format != _STATE_SPLITTING)   {
                    emit build_finished(true);
                }
                else{
#if defined(USE_ITC) && defined(BOOST_SERIALIZE)
                    emit dump(Info::Splitter::path, Info::Splitter::filename);

#elif defined(USE_ITC)
                    *Message::get_instance() << QObject::tr("Splitting was asked but BOOST_SERIALIZE flag was not set ").toStdString() << Message::ende;
#elif defined(BOOST_SERIALIZE)
                    *Message::get_instance() << QObject::tr("Splitting was asked but USE_ITC flag was not set ").toStdString() << Message::ende;
#else
                    *Message::get_instance() << QObject::tr("Splitting was asked but neither USE_ITC nor BOOST_SERIALIZE flags were set ").toStdString() << Message::ende;
#endif
                }
                _closed->wait(_mutex);

                parsing_finished=true;
            }

            cout << QObject::tr("Loading of the trace : ").toStdString() << loaded << "%" ;

            if (loaded_f>0.0f){
                // divided by to have in second 1000 since time_elapsed.elapsed() returns ms */
                time_buf = (int)(time_elapsed.elapsed() * (1.0 / loaded_f - 1.0) / 1000);
                buf_txt.str(""); // Reinitialisation

                if (time_buf>=3600){ /* convert second in hour and min */
                    buf_txt << "Parsing... Remaining: " <<  time_buf/3600 << " h " << (time_buf%3600)/60 << " min " << time_buf%60 << " s";
                }
                else if (time_buf>=60){/* convert second in min */
                    buf_txt << "Parsing... Remaining: " << time_buf/60 << " min " << time_buf%60 << " s";
                }
                else{
                    buf_txt << "Parsing... Remaining: " << time_buf << " s";
                }

                cout << "  ~  " << buf_txt.str();
            }

            cout << endl;


            if(_main_window != nullptr) { // If we have a window we show a progress bar
                _main_window->update_progress_bar(QString(buf_txt.str().c_str()), loaded);
                QApplication::processEvents();

                if(_main_window->is_parsing_canceled()) {
                    cout << QObject::tr("Canceled at ").toStdString() << loaded << "%" << endl;
                    parser->set_canceled();
                    *Message::get_instance() << QObject::tr("The trace opening was canceled by the user at ").toStdString() << loaded << "%" << Message::ende;
                    //locks the mutex and automatically unlocks it when going out of scope

                    emit build_finished(true);
                    QMutexLocker locker(_mutex);
                    _closed->wait(_mutex);
                    break; // Quit the loop
                }
            }
        }
        delete parser;
        parser=nullptr;
        thread.quit();
        thread.wait();

        Error::print_numbers();
        Error::flush("log.txt");

        // Wait for the end of the thread
        //while(!thread.wait()) {
        //}

        if(_main_window != nullptr) { // If we have a window we show a progress bar
            _main_window->end_parsing();
        }

        delete parser;
        parser=nullptr;
    }
    else if ( _file_opened != filename) {/* just check if execution is normal */
        *Message::get_instance() << "Try to use file: " << filename << " instead of a previous parsed file: " << _file_opened << Message::ende;
    }
    if(_trace!=nullptr && !killed){
        if (Info::Render::_x_min_visible == Info::Render::_x_max_visible){ // first time
            _trace->set_interval_constrained(new Interval(0,_trace->get_max_date()));
        }else{
            _trace->set_interval_constrained(new Interval(Info::Render::_x_min_visible, Info::Render::_x_max_visible));
        }

        if(!_xml_config_file.empty() && _trace->get_view_root_containers()->empty()){//if we have a partial loading, process it here, but only once
            launch_action(_DRAW_OPENGL_PARTIAL,nullptr);
        }
        if (_DRAW_OPENGL == format) {
            GanttDiagram render(_render_opengl);
            drawing_ogl.build(&render, _trace);
            _render_opengl->build();
            // _render_opengl->updateGL();
            _render_opengl->refresh_scroll_bars();
            _file_opened = filename;/* store filename for a future export */
        }
        else if (_DRAW_SVG == format) {

            Render_svg *rsvg = new Render_svg(&_path_to_export);
            Element_pos buf_min, buf_max;

            /* Store current view */
            buf_min = Info::Entity::x_min;
            buf_max = Info::Entity::x_max;

            /* Change current view to fit the user zoom */
            Info::Render::_x_min_visible = _time_start;

            if(_time_end == 0.) {
                Info::Render::_x_max_visible = _trace->get_max_date().get_value();
                Info::Entity::x_max = Info::Render::_x_max_visible;
            }
            else {
                Info::Render::_x_max_visible = _time_end;
            }

            if(Info::Entity::x_max >= Info::Render::_x_min_visible) {
                Info::Entity::x_min = Info::Render::_x_min_visible;
            }
            if(_time_end > _time_start && _time_end <= Info::Entity::x_max) {
                Info::Entity::x_max = _time_end;
            }
            cout << "Exporting trace to " << _path_to_export<<" ... Please Wait " << endl ;
            GanttDiagram render(rsvg);
            drawing_ogl.build(&render, _trace);

            delete(rsvg);/* Release the svg render */

            /* Restore previous view */
            Info::Entity::x_min = buf_min;
            Info::Entity::x_max = buf_max;

        }else if (_STATE_SPLITTING != format){/* error */
            *Message::get_instance() << "No kind of render recognized" << Message::ende;
            return false;
        }
        //   std::cout << _trace->get_max_date().get_value() << __FILE__ << __LINE__ << std::endl;


        _is_trace_loaded = true;
        if (_DRAW_OPENGL == format)QApplication::restoreOverrideCursor();

        return true;
    }else{
        // *Message::get_instance() << "Parsing was not finished properly" << Message::ende;
        _is_window_displayed=false;
        emit close_windows();
        return false;
    }
}


int Core::run(){
    /*
     * If a window is displayed, enter in the Qt event loop.
     */
    if (_is_window_displayed){
        return graphic_app->exec();
    }
    else{/* else, quit the application */
        return EXIT_SUCCESS;
    }
}




void Core::launch_action(int state, void* arg) {

    DrawTrace buf;
    QGLFormat format (QGL::HasOverlay);
#ifdef USE_QT5
    /* Use compatibility profile for renderText function from QGLWidget, that need deprecated function.
     It should be replaced so we can use Core Profile.
     Version 3.0 is the minimum. If would be better to find a function giving the most recent version available. */
    format.setVersion(3,0);
    //format.setProfile(QGLFormat::CoreProfile);
    format.setProfile(QGLFormat::CompatibilityProfile);
#endif
    switch(state) {

    case _STATE_DISPLAY_HELP :
        display_help ();
        break;

    case _STATE_OPEN_FILE:
        message << QObject::tr("Opening the file: ").toStdString ()+_file_opened
                << Message::endi;

    case _STATE_LAUNCH_GRAPHICAL_INTERFACE :

        waitGUIInit = new QEventLoop(this);

        _main_window = new Interface_graphic (this);/* launch the window interface */
        Message::set_interface (_main_window);/* define which interface will receive messages */

        _main_window->connect((const QObject*)this,
                              SIGNAL(close_windows()),
                              SLOT(on_quit_triggered()));
        //   format.setOverlay(true);
#ifdef VITE_ENABLE_VBO
        _render_opengl = new Render_alternate (this, _main_window, format);
#else
        _render_opengl = new Render_opengl (this, _main_window, format);
#endif

        if ( nullptr == _render_opengl){
            message << QObject::tr("Cannot allocate memory for an OpengGL instance").toStdString ()
                    << Message::ende;
            break;
        }

        if (nullptr == _render_opengl->context()){
            message << QObject::tr("Cannot allocate an OpengGL context").toStdString ()
                    << Message::ende;
            break;
        }

        if (false == _render_opengl->isValid()){
            message << QObject::tr("Invalid context: OpenGL is not supported on your system").toStdString ()
                    << Message::ende;
        }

        // if (false == _render_opengl->format().hasOverlay ()){
        //     /* If no overlay was created, check the original format */

        //     if (false == _render_opengl->context()->requestedFormat().hasOverlay())
        //         message <<  QObject::tr("No overlay format originally created").toStdString () << Message::endw;
        //     else
        //         message <<  QObject::tr("An overlay format was originally asked, but was not created").toStdString () << Message::endw;
        // }
        _main_window->bind_render_area((QGLWidget*)_render_opengl);

        /* Wait that the open gl renderer calls waitGUIInit->quit() to load the file */
        waitGUIInit->exec();
        if ( _STATE_OPEN_FILE == state ){
            _main_window->opening_file(_file_opened);/* Must be called after binding the render area to the main window */
            if( false == draw_trace(_file_opened, _DRAW_OPENGL) ){
                message << QObject::tr("Draw trace failed").toStdString ()
                        << Message::ende;
                break;
            }
        }

        // If -t has been set, we define the interval to zoom in
        if ( _time_start != 0 && _time_end == 0. )
            _time_end = _trace->get_max_date().get_value();

        if ( _time_end != 0. ) {
            _render_opengl->apply_zoom_on_interval(_time_start, _time_end);
        }

        _is_window_displayed = true;
        break;

    case _STATE_SPLITTING:
        *Message::get_instance() << "Splitting " << _file_opened << Message::endi;
        draw_trace(_file_opened, _STATE_SPLITTING);
        break;


    case _STATE_OPEN_FILE_IN_AN_INTERVAL:
        launch_action(_STATE_OPEN_FILE, nullptr);
        break;

    case _STATE_RELEASE_RENDER_AREA:
    {
        /*if(parser!=NULL ){
         parser->set_canceled();
         //locks the mutex and automatically unlocks it when going out of scope
         QMutexLocker locker(_mutex);
         emit build_finished(false);
         emit close_windows();
         while(parser!=NULL)
         _closed->wait(_mutex,100);
         }*/

        if (_render_opengl->unbuild()==false)
            message << "Close file : an error occured with trace releasing." << Message::ende;
        _file_opened.clear();


        if (false == _is_trace_loaded) {
            *Message::get_instance() << "Try to release a render area whereas no file was loaded" << Message::ende;
        } else {
            _is_trace_loaded = false;
        }

        if (nullptr == _trace) {
            *Message::get_instance() << "Try to release a render area whereas no trace is loaded" << Message::ende;
        } else {
            delete _trace;
            _trace = nullptr;
        }
        _render_opengl->updateGL();

        /* Release all data */
        Info::release_all();
    }
    break;

    case _STATE_EXPORT_FILE:
    case _STATE_EXPORT_FILE_IN_INTERVAL:
    {
        if (_file_opened.empty()) {
            *Message::get_instance() << "Please to previously open a trace." << Message::endw;
            return;
        }

        cout << "export of " << _file_opened << " to " << _path_to_export << " between ";
        if(_time_start == 0){
            cout << "the beginning of the trace to ";
        }
        else{
            cout << _time_start << " seconds to ";
        }
        if(_time_end != 0){
            cout << _time_end << " seconds." << endl;
        }
        else{
            cout << "the end of the trace." << endl;
        }

        draw_trace(_file_opened, _DRAW_SVG);
    }
    break;

    case _STATE_RENDER_AREA_CHANGE_TRANSLATE:
        _render_opengl->change_translate( *((int*)arg) );
        break;

    case _STATE_RENDER_AREA_CHANGE_SCALE:
        _render_opengl->change_scale( *((Element_pos*)arg) );
        break;

    case _STATE_RENDER_AREA_CHANGE_SCALE_Y:
        _render_opengl->change_scale_y( *((Element_pos*)arg) );
        break;

    case _STATE_RENDER_AREA_CHANGE_CONTAINER_SCALE:
        _render_opengl->change_scale_container_state( *((int*)arg) );
        break;

    case _STATE_RENDER_AREA_REPLACE_SCALE:
        _render_opengl->replace_scale( *((Element_pos*)arg) );
        _render_opengl->replace_scale_y( *((Element_pos*)arg) );
        break;

    case _STATE_RENDER_AREA_REPLACE_TRANSLATE:
        _render_opengl->replace_translate( *((Element_pos*)arg) );
        break;

    case _STATE_RENDER_AREA_REPLACE_TRANSLATE_Y:
        _render_opengl->replace_translate_y( *((Element_pos*)arg) );
        break;

    case _STATE_RENDER_AREA_REGISTERED_TRANSLATE:
        _render_opengl->registered_translate( *((int*)arg) );
        break;

    case _STATE_AJUST_SCROLL_BARS:
        //        cerr << "console: x_max " << ((Element_pos*)arg)[0] << " | y_max " << ((Element_pos*)arg)[1] << endl;
        _main_window->set_scroll_bars_length( ((Element_pos*)arg)[0], ((Element_pos*)arg)[1] );
        break;

    case _STATE_REFRESH_SCROLL_BARS:
        _main_window->linking_scroll_bars( ((Element_pos*)arg)[0], ((Element_pos*)arg)[1] );
        break;

    case _STATE_ZOOM_BOX_VALUE:
        _main_window->change_zoom_box_value( *((int*)arg) );
        break;

    case _STATE_RENDER_DISPLAY_INFORMATION:
        if(_trace != nullptr){
            buf.display_information(_trace, Info::Render::_info_x, Info::Render::_info_y, Info::Render::_info_accurate);
        }
        break;

    case _STATE_RENDER_UPDATE:
        _render_opengl->updateGL();
        break;

    case _STATE_CLEAN_RENDER_AREA:
    {
        Info::Render::_x_min_visible = 0.0;
        Info::Render::_x_max_visible = 0.0;
        if (_render_opengl->unbuild() == false)
            message << "Close file : an error occured while cleaning the render." << Message::ende;
        _render_opengl->release();
        _render_opengl->updateGL();
    }
    break;

    case _STATE_RENDER_SHOW_MINIMAP:
        _render_opengl->show_minimap();
        break;

    case _STATE_SWITCH_CONTAINERS:
    {
        Element_pos yr = ((Element_pos*)arg)[0];
        Element_pos yr2 = ((Element_pos*)arg)[1];
        const Container* container=nullptr;
        const Container* container2=nullptr;
        const Container::Vector *root_containers = _trace->get_view_root_containers();
        if(root_containers->empty())root_containers= _trace->get_root_containers();
        if (!root_containers->empty()){
            for (const auto &root_container : *root_containers)
                if ((container = buf.search_container_by_position(root_container, yr)))
                    break;

            for (const auto &root_container : *root_containers)
                if ((container2 = buf.search_container_by_position(root_container, yr2)))
                    break;


        }

        // If the clic is out
        if (!container)
            return;
        if (!container2)
            return;

        //we found the two children containers, we have to know the depth
        Element_pos xr = ((Element_pos*)arg)[2] * (_trace->get_depth()+1);
        if(xr<0)return;

        Element_pos xr2 = ((Element_pos*)arg)[3] * (_trace->get_depth()+1);
        if(xr2<0)return;
        for(int i=0; i < (const_cast<Container*>(container)->get_depth() - xr); i++){
            container= container->get_parent();
        }

        for(int i=0; i < (const_cast<Container*>(container2)->get_depth() - xr2); i++){
            container2=container2->get_parent();
        }

        //we cannot switch when containers' parents are not the same
        if( container->get_parent() != container2->get_parent() )
            return;

        if( container == container2 )
            return;

        Container* parent= const_cast<Container*>(container->get_parent());

        //printf("we ask to switch %s and %s \n", container->get_name().to_string().c_str(), container2->get_name().to_string().c_str());

        const std::list<Container*>* children = nullptr;
        bool children_allocated = false;

        if( parent == nullptr ) {//we switch top level containers
            children = _trace->get_view_root_containers();
            if( children->empty() )
                children = _trace->get_root_containers();
        } else {
            children = new std::list<Container*>(*parent->get_view_children());
            if( children->empty() ) {
                delete children;
                children = parent->get_children();
            }
            else {
                children_allocated = true;
            }
        }

        std::list<Container*>::const_iterator it= children->begin();
        const std::list<Container*>::const_iterator it_end= children->end();
        if( parent != nullptr ) {
            parent->clear_view_children();
            for(; it!=it_end; it++){
                if( (*it) == container )
                    parent->add_view_child(const_cast<Container*>(container2));
                else if( (*it) == container2 )
                    parent->add_view_child(const_cast<Container*>(container));
                else
                    parent->add_view_child(*it);
            }
        } else {//for root containers we have to build a new list and fill it
            std::list<Container*>* new_list= new std::list<Container*> ();
            for(; it!=it_end; it++){
                if( (*it) == container ) {
                    new_list->push_back(const_cast<Container*>(container2));
                }
                else if( (*it) == container2 )
                    new_list->push_back(const_cast<Container*>(container));
                else
                    new_list->push_back(*it);
            }
            _trace->set_view_root_containers(*new_list);
        }

        Element_pos zoom[2]={Info::Render::_x_min, Info::Render::_x_max};
        Info::Render::_x_min_visible = 0.0;
        Info::Render::_x_max_visible = 0.0;

        launch_action(Core:: _STATE_CLEAN_RENDER_AREA);
#if defined(USE_ITC) && defined(BOOST_SERIALIZE)
        _trace->updateTrace(new Interval(zoom[0], zoom[1]));
#endif
        draw_trace(get_filename(),Core::_DRAW_OPENGL);
        launch_action(Core:: _STATE_ZOOM_IN_AN_INTERVAL, &zoom);
        launch_action(Core:: _STATE_RENDER_UPDATE);

        if ( children_allocated ) {
            delete children;
        }

        break;
    }

    case _DRAW_OPENGL_PARTIAL:
    {
        //_main_window->get_node_select()->set_trace(_trace);
        bool xml_success = _trace->load_config_from_xml(QString(_xml_config_file.c_str()));
        if(!xml_success){
            //std::list<Container *> displayed_containers;
            //_main_window->get_node_select()->build_displayed_nodes(displayed_containers);
            //_trace->set_view_root_containers(displayed_containers);
            //}else{
            message << "Error while parsing" << _xml_config_file << Message::ende;
        }
        break;
    }
    case _STATE_ZOOM_IN_AN_INTERVAL:
    {
        _render_opengl->apply_zoom_on_interval(((Element_pos*)arg)[0], ((Element_pos*)arg)[1]);
    }
    break;

    case _STATE_UPDATE_VARVALUES:
    {
        if( nullptr != _trace ) {
            std::map<long int, double> var_map = _trace->update_text_variable_values(((Element_pos*)arg)[0]);
            buf.draw_text_variable_values(_render_opengl,&var_map,((Element_pos*)arg)[0]);
        }
    }
    break;

    case _LOAD_DATA:
    {
        //case where data has to be loaded from serialized files, after a zoom in the preview
        //by default, we do that in another window
        _time_start=Info::Splitter::_x_min;
        _time_end=Info::Splitter::_x_max;
        //_trace->finish();

        char args[512];
        sprintf(args,"-t %lf:%lf", _time_start, _time_end);
        QStringList arguments = (QStringList() << args);
        arguments << _file_opened.c_str();
        QString program;

        QDir::setCurrent(*_run_env[0]);
        QString run_cmd = *_run_env[1];


        if (run_cmd.startsWith("."))
            program = *_run_env[0]+(run_cmd.remove(0,1));
        else
            program = run_cmd;

        QProcess new_process;
        new_process.startDetached(program, arguments);

        break;
    }
    default:/* like _STATE_UNKNOWN */
        display_help();
        warning(string("Cannot determine the arguments past. Please check the correct syntax."));

    }
}


void Core::display_help(){
    cout << endl
         << "Usage: vite [OPTION...] [[-f] inputfile] [-e outputfile] " << endl
         << endl
         << "  -h                 display this help                  " << endl
         << "  -f inputfile       open the inputfile                 " << endl
         << "  -a                 display all the inputfile (Default)" << endl
         << "  -t [T0]:[T1]       display the interval [T0:T1] (Default: T0 = 0 and T1 = Tmax)" << endl
         << "  -e outputfile      export the trace file in the svg format to outpufile"        << endl
         << "  -c xmlfile         import a list of containers to display from an xml file"        << endl
         << endl;
}


/***********************************
 *
 *
 *
 * Informative message functions.
 *
 *
 *
 **********************************/

void Core::error(const string &s) const{
    cerr << "ERROR: " << s <<endl;
}


void Core::warning(const string &s) const{
    cerr << "Warning: "<< s <<endl;
}

void Core::information(const string &s) const{
    cerr << s <<endl;
}

const string Core::get_filename() const{
    return _file_opened;
}

void Core::set_path_to_export(const string& path){
    _path_to_export = path;
}

const QString** Core::get_runenv() const{
    return (const QString**)_run_env;
}

void Core::set_min_value_for_export(const double d) {
    _time_start = d;
}

void Core::set_max_value_for_export(const double d) {
    _time_end = d;
}

Trace *Core::get_trace() const {
    return _trace;
}


void Core::export_variable(Variable *var, const string &filename) {
    const list<pair<Date, Double> > *variable_values = var->get_values();

    ofstream file(filename.c_str(), ios::out | ios::trunc);

    if(file) {
        double first_value = 0.;
        double second_value = 0.;
        const double min = var->get_min().get_value();
        const double max = var->get_max().get_value();

        for(const auto & variable_value : *variable_values) {
            first_value = variable_value.first.get_value();
            second_value =(variable_value.second.get_value()-min)/(max-min) ;
            file << first_value << "\t" << second_value << endl;
        }

        file.close();
    }
    else {
        // Error
        _main_window->error("Unable to open " + filename + " in order to export the counter");
    }
}
