/**
 *
 * @file src/core/Core.cpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Johnny Jazeix
 * @author Nolan Bredel
 * @author Mohamed Faycal Boullit
 * @author Mathieu Faverge
 * @author Thibault Soucarre
 * @author Thomas Herault
 * @author Kevin Coulomb
 * @author Pascal Noisette
 * @author Jule Marcoueille
 * @author Lucas Guedon
 * @author Augustin Gauchet
 * @author Olivier Lagrasse
 * @author Augustin Degomme
 * @author Luigi Cannarozzo
 *
 * @date 2024-07-17
 */
/*!
 *\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>

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

/* -- */
// #include <QtUiTools>/* for the run-time loading .ui file */
#include <QDir>
#include <QElapsedTimer>
#include <QFile>
#include <QFileDevice>
#include <QGLWidget>/* for the OpenGL Widget */
#include <QObject>
#include <QProcess>
#include <QThread>
#include <QTimer>
/* -- */
#include "common/common.hpp"
#include "common/Info.hpp"
#include "common/Errors.hpp"
#include "common/LogConsole.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 "parser/ParsingUpdateThread.hpp"
/* -- */
#include "interface/Node_select.hpp"
#include "interface/Interface_graphic.hpp"
#include "core/Core.hpp"
/* -- */
#ifdef USE_VULKAN
#include "render/vulkan/Render_vulkan.hpp"
#else
#include <QGLWidget>
#ifdef VITE_ENABLE_VBO
#include "render/vbo/Render_alternate.hpp"
#else
#include "render/opengl/Render_opengl.hpp"
#endif
#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

#if defined(HAVE_GETOPT_H) || defined(HAVE_GETOPT_LONG)
#include <getopt.h>
#endif /* defined(HAVE_GETOPT_H) || defined(HAVE_GETOPT_LONG) */

using namespace std;

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

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

Core::Core(int &argc, char **argv) {

    /* Initialize global variable */
    waitGUIInit = nullptr;
    _main_window = nullptr;

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

    /*
     * Store application name and current directory
     */
    _run_env[0] = new QString(QString::fromStdString(QDir::currentPath().toStdString()));
    _run_env[1] = new QString(QString::fromStdString(argv[0]));

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

    _log_console = new LogConsole();
    /* define which interface will receive messages */
    /* MathieuTOmyself: No idea what is this */
    Message::set_log_support(_log_console);

    /* 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 */
    int 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 (_run_env[0] != nullptr)
        delete _run_env[0];

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

    if (nullptr != _main_window && _main_window->get_render_area() != nullptr) {
        _main_window->get_render_area()->release();
        /* Qt desallocates _main_window->get_render_area() 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:hvT:"

#if defined(HAVE_GETOPT_LONG)

static struct option long_options[] = {
    { "interval", required_argument, 0, 't' },
    { "load_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' },
    { "version", no_argument, 0, 'v' },
    { 0, 0, 0, 0 }
};
#endif

int Core::get_options(int &argc, char **argv) {
    int state = 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(largc, largv, GETOPT_STRING,
                        long_options, nullptr);
#else
        c = getopt(largc, largv, GETOPT_STRING);
#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_to_open = optarg;
            if (state == 0)
                state = _STATE_OPEN_FILE;
            break;

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

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

        case 'v':
            state = _STATE_VERSION_DISPLAY;
            goto cleanup;

        case 'c':
            _xml_config_file = optarg;
            break;

        case 'T':
            double T1, T2;
            if (sscanf(optarg, "%lf:%lf", &T1, &T2) != 2) {
                cerr << QObject::tr("Interval is not in the correct format [%d:%d]").toStdString() << endl;
                state = _STATE_DISPLAY_HELP;
                goto cleanup;
            }
            if (T1 >= T2) {
                cerr << QObject::tr("End time must be after begin time for the -T option").toStdString() << endl;
                state = _STATE_DISPLAY_HELP;
                goto cleanup;
            }
            if (T1 < 0.0) {
                cerr << QObject::tr("Begin time must has a positive value for the -T option").toStdString() << endl;
                state = _STATE_DISPLAY_HELP;
                goto cleanup;
            }
            if (T2 < 0.0) {
                cerr << QObject::tr("End time must has a positive value for the -T option").toStdString() << endl;
                state = _STATE_DISPLAY_HELP;
                goto cleanup;
            }
            _time_start = T1;
            _time_end = T2;
            Trace::select_sub_trace(Date(T1), Date(T2));
            break;

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

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

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

cleanup:
    free(largv);
    return state;
}

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

void Core::draw_trace(Trace *trace) {

    // Set application in working state
    QApplication::setOverrideCursor(Qt::WaitCursor);

    // We add the filename to the recent files opened list
    Session::add_recent_file(QFileInfo(QString::fromStdString(trace->get_filename())).absoluteFilePath());
    _main_window->update_recent_files_menu();

    DrawTrace drawing_ogl;
    GanttDiagram render(_main_window->get_render_area());
    drawing_ogl.build(&render, trace);
    _main_window->get_render_area()->build();
    _main_window->get_render_area()->refresh_scroll_bars();

    QApplication::restoreOverrideCursor();
}

Trace *Core::build_trace(const std::string &filename, bool is_state_splitting) {

    Parser *parser = nullptr;
    // 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 nullptr;
    }

    /* 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 *trace = new Trace(filename);

#if defined(USE_ITC) && defined(BOOST_SERIALIZE)
    if (Info::Splitter::path.empty())
        Info::Splitter::path = QFileInfo(filename.c_str()).dir().path().toStdString();
    if (Info::Splitter::filename.empty())
        Info::Splitter::filename = QFileInfo(filename.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) */

    // Create a thread to update the progress bar every second
    QTimer update_timer(this);
    ParsingUpdateThread parsing_update_thread(parser, &update_timer);
    connect(&parsing_update_thread, &ParsingUpdateThread::update_progress, this, &Core::update_parsing_progress);

    // Start the ParsingUpdateThread
    parsing_update_thread.start();

    // Start the timer inside the ParsingUpdateThread
    // This can only be done inside the Main (GUI) Thread
    update_timer.start(1000);

    // Create an event loop
    QEventLoop event_loop(this);

    if (nullptr != _main_window) { // If there is a graphical interface we show a progress bar
        _main_window->init_parsing(filename);

        // When the cancel button is pressed or the ProgressDialog is closed
        // quit the event loop
        connect(_main_window, &Interface_graphic::stop_parsing_event_loop, &event_loop, &QEventLoop::quit);
    }

    // Create parsing thread
    ParsingThread parsing_thread(parser, trace);

    qRegisterMetaType<std::string>("std::string");
    // in case of splitting
    connect(this, &Core::dump,
            &parsing_thread, &ParsingThread::dump);
    // The event loop end when the parsing is finished
    connect(parser, &Parser::stop_parsing_event_loop, &event_loop, &QEventLoop::quit);

    // Give a pointer of the event_loop to the parser so it can tell if the event_loop has started
    parser->set_event_loop(&event_loop);

    // Start the ParsingThread that goes into an event loop
    parsing_thread.start();

    // Launch the event loop, will only be interrupt when quit is called on it
    // This loop is necessary to keep refreshing the windows while the parser is working
    event_loop.exec();

    // Stops the update of progress bar
    update_timer.stop();

    // If the event loop was quit because parsing was finished (normal behaviour)
    if (parser->is_end_of_parsing()) {
        if (!is_state_splitting) {
            parsing_thread.finish_build();
        }
        else {
#if defined(USE_ITC) && defined(BOOST_SERIALIZE)
            Q_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
        }
    }
    else { // If the event loop was quit by cancelling the parsing an error is shown
        int loaded = (int)(parser->get_percent_loaded() * 100.0f);
        cout << QObject::tr("Canceled at ").toStdString() << loaded << "%" << endl;
        *Message::get_instance() << QObject::tr("The trace opening was canceled by the user at ").toStdString() << loaded << "%" << Message::ende;

        // Stops the parsing of the file
        parser->set_canceled();
    }

    if (nullptr != _main_window) { // If there is a graphical interface
        // The progress dialog is closed
        _main_window->end_parsing();
    }

    // Stop parsing update thread event loop
    parsing_update_thread.quit();

    // Wait for parsing update thread to return
    parsing_update_thread.wait();

    // Wait for parsing thread to return
    parsing_thread.wait();
    delete parser;

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

    if (nullptr == trace) {
        return nullptr;
    }

    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
        // Set the config of the trace to be the one of the xml file passed as argument
        bool xml_success = trace->load_config_from_xml(QString::fromStdString(_xml_config_file));
        if (!xml_success) {
            message << "Error while parsing" << _xml_config_file << Message::ende;
        }
    }

    return trace;
}

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

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

    switch (state) {

    case _STATE_VERSION_DISPLAY:
        cout << "ViTE " << VITE_VERSION << endl;
        break;

    case _STATE_DISPLAY_HELP:
        display_help();
        break;

    case _STATE_OPEN_FILE:
        message << QObject::tr("Trying to open file: ").toStdString() + _file_to_open
                << Message::endi;

        __attribute__((fallthrough));

    case _STATE_LAUNCH_GRAPHICAL_INTERFACE: {

        waitGUIInit = new QEventLoop(this);

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

        //   format.setOverlay(true);
        _main_window->init_render_area();

        /* Wait that the open gl renderer calls waitGUIInit->quit() to load the file */
        waitGUIInit->exec();

        if (_STATE_OPEN_FILE == state) {
            QString qstring_filepath = QString::fromStdString(_file_to_open);
            bool can_open = true;

            if (!QFile::exists(qstring_filepath)) {
                _main_window->error(string("File : ") + _file_to_open + string(" does not exists"));
                can_open = false;
            }
            else if (!(QFile::permissions(qstring_filepath) & QFileDevice::ReadUser)) {
                _main_window->error(string("File : ") + _file_to_open + string(" does not have read permission"));
                can_open = false;
            }

            if (!can_open) {
                Error::set(Error::VITE_ERR_OPEN, Error::VITE_ERRCODE_ERROR);
                Error::print_numbers();
                Error::flush("log.txt", _file_to_open);
            }
            else {
                _main_window->opening_file(_file_to_open); /* Must be called after binding the render area to the main window */

                Trace *trace = build_trace(_file_to_open);

                if (nullptr == trace) {
                    _main_window->on_quit_triggered();
                    break;
                }

                draw_trace(trace);

                // 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.) {
                    _main_window->get_render_area()->apply_zoom_on_interval(_time_start, _time_end);
                }

                _main_window->set_trace(trace);
            }
        }
        break;
    }
    case _STATE_SPLITTING: { // Brackets are important here to avoid cross initialization
        *Message::get_instance() << "Splitting " << _file_to_open << Message::endi;
        Trace *trace = build_trace(_file_to_open, true);
        if (nullptr == trace) {
            *Message::get_instance() << "Trace parsing has not finished properly." << Message::endw;
            return;
        }
        delete trace;
    } break;

    case _STATE_EXPORT_FILE: {
        Trace *trace = build_trace(_file_to_open);
        export_trace_to_svg(trace, _path_to_export);
        delete trace;
    } break;

    default: /* like _STATE_UNKNOWN */
        display_help();
        *Message::get_instance() << "Cannot determine the arguments past. Please check the correct syntax." << Message::endw;
    }
}

void Core::display_help() {
    cout << endl
         << "Usage: vite [OPTION...] [[-i] inputfile] [-e outputfile] " << endl
         << endl
         << "  -h                 display this help                  " << endl
         << "  -v                 display ViTE version" << endl
         << "  -t [T0]:[T1]       display the interval [T0:T1] (Default: T0 = 0 and T1 = Tmax)" << endl
         << "  -T [T0]:[T1]       load in memory only the interval [T0:T1] (Default: T0 = 0 and T1 = Tmax)" << endl
         << "  -i inputfile       load a file to display" << 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;
}

void Core::load_data(Trace *trace) {
    // 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;

    char args[512];
    sprintf(args, "-t %lf:%lf", _time_start, _time_end);
    QStringList arguments = (QStringList() << QString::fromStdString(args));
    arguments << QString::fromStdString(trace->get_filename());
    QString program;

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

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

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

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

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;
}

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");
    }
}

void Core::export_trace_to_svg(Trace *trace, const std::string &path_to_export) {

    // Error case when no trace is null
    if (nullptr == trace) {
        *Message::get_instance() << "Please to previously open a trace." << Message::endw;
        return;
    }

    // Console message depending on the time of start and end of the export
    cout << "export of " << trace->get_filename() << " 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;
    }

    // Build the image 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;
    Render_svg *rsvg = new Render_svg(path_to_export);
    GanttDiagram render(rsvg);
    DrawTrace drawing_ogl;
    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;
}

void Core::update_parsing_progress(const int time_elapsed, const float percent_loaded) {
    ostringstream buf_txt;
    int time_buf = 0;
    buf_txt.str("");
    int loaded = 0;
    float loaded_f = 0.0f; /* floating value of the loading file state. (between 0 and 1) */
    loaded_f = percent_loaded;
    loaded = (int)(loaded_f * 100.0f);

    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 * (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::fromStdString(buf_txt.str()), loaded);
    }
}

#include "moc_Core.cpp"
