/*
 ** 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
#define sscanf sscanf_s /*avoid a warning in Windows by using the securized version of scanf*/
#include <core/getopt.h>
#endif

/* -- */
#include <QObject>
//#include <QtUiTools>/* for the run-time loading .ui file */
#include <QTextCodec>
#include <QTime>
#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/Render.hpp"
#include "render/Render_template.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/tree/Interval.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 "statistics/Stats_window.hpp"
/* -- */
#include "interface/Node_select.hpp"
#include "interface/Interface_graphic.hpp"
#include "core/Core.hpp"
/* -- */
#ifdef WITH_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 = NULL;
    console_app = NULL;

    /* 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 gloabl variable */
    _main_window   = NULL;
    _render_opengl = NULL;
    _trace         = NULL;
    _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);
    }

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


Core::~Core(){

    if( _trace != NULL )
        delete _trace;

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

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

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

    /* Qt desallocates _main_window and _render_opengl automatically */
    Message::kill();
    if ( console_app != NULL ) delete console_app;
    if ( graphic_app != NULL ) 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;
                }
            }
            //cerr << "Interval: from " << _time_start << " to " << _time_end << endl;
            break;

        case 'e' :
            _path_to_export = optarg;
            state = _STATE_EXPORT_FILE;
            //cerr << "Export in file:  " << _path_to_export << endl;
            break;

        case 'i' :
            _file_opened = optarg;
            if (state == 0) state = _STATE_OPEN_FILE;
            //cerr << "Open file:  " << _file_opened << endl;
            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;
            //cerr << "Export in file:  " << _path_to_export << endl;
            break;

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

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

    // This is here where we put new options !! (for filters for example)

    //     if((string)argv[i] == "-epsilon") {
    //         i ++;
    //         if(i > argc) {
    //             cerr << QObject::tr("need another argument for epsilon").toStdString() << endl;
    //             state=_STATE_UNKNOWN;
    //             goto cleanup;
    //         }
    //         else {
    //             double accuracy = convert_to_double(argv[i]);
    //             if(accuracy < 0.) {
    //                 cerr << QObject::tr("need a positive accuracy for epsilon").toStdString() << endl;
    //                 state=_STATE_UNKNOWN;
    //                 goto cleanup;
    //             }
    //             else {
    //                 //  Svg::set_accuracy(accuracy);
    //             }
    //         }
    //     }
    //     else if((string)argv[i] == "-wide") {
    //         i ++;
    //         if (i > argc) {
    //             cerr << "need another argument for wide" << endl;
    //             state=_STATE_UNKNOWN;
    //             goto cleanup;
    //         }
    //         double wide = convert_to_double(argv[i]);
    //         if(wide < 0.) {
    //             cerr << "need a positive width for the export" << endl;
    //             state=_STATE_UNKNOWN;
    //             goto cleanup;
    //         }
    //         else {
    //             //  Svg::set_wide_factor(wide);
    //         }
    //     }
    //     else if((string)argv[i] == "-grow") {
    //         i ++;
    //         if (i > argc) {
    //             cerr << "need another argument for grow" << endl;
    //             state=_STATE_UNKNOWN;
    //             goto cleanup;
    //         }
    //         double grow = convert_to_double(argv[i]);
    //         if(grow < 0.) {
    //             cerr << "need a positive grown for the export" << endl;
    //             state=_STATE_UNKNOWN;
    //             goto cleanup;
    //         }
    //         else {
    //             //  Svg::set_height_factor(grow);
    //         }
    //     }
    //     else if((string)argv[i] == "-scale") {
    //         i ++;
    //         if (i > argc) {
    //             cerr << "need another argument for scale" << endl;
    //             state=_STATE_UNKNOWN;
    //             goto cleanup;
    //         }
    //         double scale = convert_to_double(argv[i]);
    //         if(scale < 0.) {
    //             cerr << "need a positive scale for the export" << endl;
    //             state=_STATE_UNKNOWN;
    //             goto cleanup;
    //         }
    //         else {
    //             //  Svg::set_scale_frequency(scale);
    //         }
    //     }
    //     else
    //  if((string)argv[i] == "-interval") {

    //     if (!(i +2 < argc)) {
    //         cerr << "interval argc" << endl;
    //         state=_STATE_UNKNOWN;
    //         goto cleanup;
    //     }
    //     if (argv[i+1][0] == '-') {
    //         cerr << "interval -" << endl;
    //         state=_STATE_UNKNOWN;
    //         goto cleanup;
    //     }
    //     if (!(argv[i+2][0] != '-'))  {
    //         cerr << "interval -" << endl;
    //         state=_STATE_UNKNOWN;
    //         goto cleanup;
    //     }

    //     double t1 = atof(argv[i+1]);
    //     double t2 = atof(argv[i+2]);

    //     if (t1 >= t2) {
    //         cerr << "interval neg" << endl;
    //         state=_STATE_UNKNOWN;
    //         goto cleanup;
    //     }

    //     // Svg::set_interval(t1, t2);
    //     i += 2;
    // }
    // else {
    //     cerr << "the argument "<< argv[i] << " is unknown." << endl;
    //     state=_STATE_DISPLAY_HELP;
    //     goto cleanup;
    // }

    // }
    // else{ // This is a file to open
    //     _file_opened = argv[i];
    //     state = _STATE_OPEN_FILE;
    // }

  cleanup:
    free(largv);
    return state;
}

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

bool Core::draw_trace(const string & filename, const int format) {

    parser = NULL;
    DrawTrace  drawing_ogl;
    bool killed=false;

#ifdef WITH_VBO
    Render<Render_alternate>render(_render_opengl) ;
#else
    Render<Render_opengl> render(_render_opengl) ;
#endif
    _render=(void*)&render;

    QTime         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) {
        _render_svg = new Render_svg(&_path_to_export);
    }
    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 (NULL == _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 != NULL) { // 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)){

#ifdef 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



            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


        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,NULL);
                }

                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 != NULL) { // 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=NULL;
        thread.quit();
        thread.wait();

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

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

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

        delete parser;
        parser=NULL;
    }
    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!=NULL && !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));
            //
            // WARNING I HAVE COMMENTED THIS LINE BECAUSE I FOUND IT USELESS. I TRIED AFTER AND NO BUG APPEARED
            // MOREOVER IT ENABLES THE PLUGIN TIME SELECTION
            //
        }



        //     if(_state & _STATE_IN_AN_INTERVAL) {
        //         _trace->set_interval_constrained(new Interval(_time_start,_time_end));
        //     }

        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,NULL);
        }
        if (_DRAW_OPENGL == format) {


            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) {

            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 ;
            Render<Render_svg> render(_render_svg);
            drawing_ogl.build(&render, _trace);

            delete _render_svg;/* Release the svg render */

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

            // svg.end();
        }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);

    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 WITH_VBO
        _render_opengl = new Render_alternate (this, _main_window, format);
#else
        _render_opengl = new Render_opengl (this, _main_window, format);
#endif

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

        if (NULL == _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, NULL);
        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 (NULL == _trace) {
            *Message::get_instance() << "Try to release a render area whereas no trace is loaded" << Message::ende;
        } else {
            delete _trace;
            _trace = NULL;
        }
        _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 != NULL){
            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=NULL;
        const Container* container2=NULL;
        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 (Container::VectorIt i = root_containers->begin();
                 i != root_containers->end();
                 i++)
                if ((container = buf.search_container_by_position(*i, yr)))
                    break;

            for (Container::VectorIt i = root_containers->begin();
                 i != root_containers->end();
                 i++)
                if ((container2 = buf.search_container_by_position(*i, 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=NULL;

        if(parent==NULL){//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()) children = parent->get_children();
        }

        std::list<Container*>::const_iterator it= children->begin();
        const std::list<Container*>::const_iterator it_end= children->end();
        if(parent!=NULL){
            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);

        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( NULL != _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(list<pair<Date, Double> >::const_iterator value = variable_values->begin();
            value != variable_values->end();
            value++) {
            first_value = (*value).first.get_value();
            second_value =((*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");
    }
}
