/**
 *
 * @file src/interface/Interface_graphic.cpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Johnny Jazeix
 * @author Arthur Redondy
 * @author Mohamed Faycal Boullit
 * @author Mathieu Faverge
 * @author Francois Trahay
 * @author Thibault Soucarre
 * @author Kevin Coulomb
 * @author Philippe Swartvagher
 * @author Olivier Lagrasse
 * @author Augustin Degomme
 *
 * @date 2024-07-17
 */
/*!
 *\file interface_graphic.cpp
 *\brief This is graphical interface C source code.
 */
#include <fstream>
#include <string>
#include <iostream>
#include <sstream>
#include <stack>
#include <map>
#include <list>
/* -- */
#include <QObject>
#include <QWidget>
#include <QtUiTools>/* for the run-time loading .ui file */
#include <QCloseEvent>
#include <QDir>
#include <QFileDialog>
#include <QGLWidget>/* for the OpenGL Widget */
#include <QRadioButton>
#include <QTextEdit>
#include <QCheckBox>
#include <QMessageBox>
#include <QProgressDialog>
/* -- */
#include "common/common.hpp"
#include "common/Info.hpp"
#include "common/Tools.hpp"
#include "common/Session.hpp"
/* -- */
#include "trace/values/Values.hpp"
#include "trace/DrawTrace.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/Trace.hpp"
/* -- */
#include "core/Core.hpp"
/* -- */
#include "interface/Interface_graphic.hpp"
#include "interface/resource.hpp"
#include "interface/Settings_window.hpp"
/* -- */
#include "plugin/Plugin_window.hpp"
/* -- */
#include "render/RenderLayout.hpp"
#ifdef USE_VULKAN
#include "render/vulkan/Render_vulkan.hpp"
#else
#ifdef VITE_ENABLE_VBO
#include "render/vbo/Render_alternate.hpp"
#else
#include "render/opengl/Render_opengl.hpp"
#endif
#endif

using namespace std;

/***********************************
 *
 *
 *
 * Constructors and destructor.
 *
 *
 *
 **********************************/

Interface_graphic::Interface_graphic(Core *core, QWidget *parent) :
    QMainWindow(parent) {

    setupUi(this);

    _core = core;
    _is_rendering_trace = false;
    _no_warning = Session::get_hide_warnings_setting(); /* display warnings */

    _reload_type = Session::get_reload_type_setting();
    no_warning->setChecked(Session::get_hide_warnings_setting());
    reload_from_file->setChecked(Session::get_reload_type_setting());
    vertical_line->setChecked(Session::get_vertical_line_setting());
    shaded_states->setChecked(Session::get_shaded_states_setting());

    load_windows();

    setMouseTracking(true); /* to catch mouse events */

    _progress_dialog = nullptr;
    _plugin_window = nullptr;
    _ui_settings = nullptr;
    _ui_node_selection = nullptr;
    _ui_interval_selection = nullptr;

    // For drag and drop operations
    setAcceptDrops(true);
}

Interface_graphic::~Interface_graphic() {
    /* Qt desallocates this, _ui_info_window and _render_area automatically */

    if (nullptr != _render_layout) {
        delete _render_layout;
        _render_layout = nullptr;
    }

    delete _plugin_window;
    _plugin_window = nullptr;
}

/***********************************
 *
 *
 *
 * Window function.
 *
 *
 *
 **********************************/

void Interface_graphic::load_windows() {

    QUiLoader *loader = new QUiLoader();
    QFile file_info(QStringLiteral(":/window/info_window.ui"));
    QFile file_select_info(QStringLiteral(":/window/select_info_window.ui"));
    QFile file_selection_export(QStringLiteral(":/window/option_export_window.ui"));
    //  QFile file_kind_of_export(":/window/kind_of_export.ui");
    QFile file_counter_to_export(QStringLiteral(":/window/list_of_counter_to_export.ui"));

    /* Load the informative window from a .ui file */
    if (file_info.exists()) {
        file_info.open(QFile::ReadOnly);
        CKFP(_ui_info_window = loader->load(&file_info, this), "Cannot open the .ui file: :/window/info_window.ui");
        file_info.close();
    }
    else {
        cerr << __FILE__ << ":" << __LINE__ << ": The following .ui file doesn't exist: :/window/info_window.ui" << endl;
        exit(EXIT_FAILURE);
    }

    /* Load the window about information on a selection from a .ui file */
    if (file_select_info.exists()) {
        file_select_info.open(QFile::ReadOnly);
        CKFP(_ui_select_info_window = loader->load(&file_select_info, this), "Cannot open the .ui file: :/window/select_info_window.ui");
        file_select_info.close();
    }
    else {
        cerr << __FILE__ << ":" << __LINE__ << ": The following .ui file doesn't exist: :/window/select_info_window.ui" << endl;
        exit(EXIT_FAILURE);
    }

    /* Load the _option_export_window from a .ui file */
    if (file_selection_export.exists()) {
        file_selection_export.open(QFile::ReadOnly);
        CKFP(_ui_time_selection_export = loader->load(&file_selection_export, this), "Cannot open the .ui file : :/window/option_export_window.ui");
        file_selection_export.close();
    }
    else {
        cerr << __FILE__ << ":" << __LINE__ << ": The following .ui file doesn't exist: :/window/option_export_window.ui" << endl;
        exit(EXIT_FAILURE);
    }

    /* Load the choice counter for export box */
    if (file_counter_to_export.exists()) {
        file_counter_to_export.open(QFile::ReadOnly);
        CKFP(_ui_counter_choice_to_export = qobject_cast<QDialog *>(loader->load(&file_counter_to_export, this)), "Cannot open the .ui file : :/window/list_of_counter_to_export.ui");
        CKFP(_counter_list_names = _ui_counter_choice_to_export->findChild<QComboBox *>(QStringLiteral("combobox")), "Cannot find the svg export button in the .ui file");
        file_counter_to_export.close();
    }
    else {
        cerr << __FILE__ << ":" << __LINE__ << ": The following .ui file doesn't exist: :/window/list_of_counter_to_export.ui" << endl;
        exit(EXIT_FAILURE);
    }

    /* Set some windows properties */
    _ui_info_window->setWindowFlags(_ui_info_window->windowFlags() | Qt::WindowStaysOnTopHint); /* Always display info_window on top */
    _ui_select_info_window->setWindowFlags(_ui_select_info_window->windowFlags() | Qt::WindowStaysOnTopHint); /* Always display select_info_window on top */
    _ui_time_selection_export->setWindowFlags(_ui_time_selection_export->windowFlags() | Qt::WindowStaysOnTopHint);

    /* Load widget from the .ui file */
    CKFP(_ui_render_area_container_layout = this->findChild<QVBoxLayout *>(QStringLiteral("render_container_layout")), "Cannot find the render_area_container_layout in the .ui file");
    CKFP(_ui_render_area_container_widget = this->findChild<QWidget *>(QStringLiteral("render_container_widget")), "Cannot find the render_area_container_widget in the .ui file");

    CKFP(_ui_fullscreen_menu = this->findChild<QAction *>(QStringLiteral("fullscreen")), "Cannot find the fullscreen menu in the .ui file");
    CKFP(_ui_info_trace_text = _ui_info_window->findChild<QTextEdit *>(QStringLiteral("info_trace_text")), "Cannot find the info_trace_text QTextEdit widget in the .ui file");
    CKFP(_ui_info_selection_tabs = _ui_select_info_window->findChild<QTabWidget *>(QStringLiteral("info_selection_tab")), "Cannot find the info_selection_tab QTabWidget widget in the .ui file");
    CKFP(_ui_toolbar = this->findChild<QToolBar *>(QStringLiteral("toolBar")), "Cannot find the tool bar in the .ui file");

    CKFP(_ui_recent_files_menu = this->findChild<QMenu *>(QStringLiteral("menuRecent_Files")), "Cannot find the button \"menuRecent_Files\" in the .ui file");

    connect(_ui_counter_choice_to_export, &QDialog::accepted,
            this, &Interface_graphic::counter_choosed_triggered);

    // For the recent files menu
    for (auto &_recent_file_action: _recent_file_actions) {
        _recent_file_action = nullptr;
    }
    update_recent_files_menu();

    /*
     Special function of Qt which allows methods declared as slots and which
     name are 'on_[widget]_[action]()' to be called when the 'widget' triggered
     the signal corresponding to 'action'.
     /!\ -> use NULL as argument, else messages will be duplicated!
     */
    QMetaObject::connectSlotsByName(nullptr);

    delete loader;
    /* Display the main window */
    this->show();
}

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

void Interface_graphic::error(const string &s) const {
    if (true == _no_warning)
        return; /* do not display error messages */

    QString buf = QString::fromStdString(s);
    _ui_info_trace_text->moveCursor(QTextCursor::End); /* Insert the new text on the end */
    _ui_info_trace_text->insertHtml(QStringLiteral("<font color='red'>") + buf + QStringLiteral("</font><br /><br />"));

    _ui_info_window->show(); /* show info_window */
}

void Interface_graphic::warning(const string &s) const {
    if (true == _no_warning)
        return; /* do not display warning messages */

    QString buf = QString::fromStdString(s);
    _ui_info_trace_text->moveCursor(QTextCursor::End); /* Insert the new text on the end */
    _ui_info_trace_text->insertHtml(QStringLiteral("<font color='orange'>") + buf + QStringLiteral("</font><br /><br />"));

    _ui_info_window->show(); /* show info_window */
}

void Interface_graphic::information(const string &s) const {
    QString buf = QString::fromStdString(s);
    _ui_info_trace_text->moveCursor(QTextCursor::End); /* Insert the new text on the end */
    _ui_info_trace_text->insertHtml(QStringLiteral("<font color='green'>") + buf + QStringLiteral("</font><br /><br />"));
}

/***********************************
 *
 * Selected Trace entity informative function.
 *
 **********************************/

void Interface_graphic::selection_information_clear() const {
    _ui_info_selection_tabs->clear(); /* Remove all tabs in the QTabWidget*/
}

void Interface_graphic::selection_information_show() const {
    _ui_select_info_window->show(); /* show select_info_window */
}

void Interface_graphic::selection_information(const std::string &tab_name, const string &content) const {
    // Create and setup the tab
    QTextEdit *text_widget = new QTextEdit(_ui_info_selection_tabs);
    text_widget->setReadOnly(true);
    text_widget->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);

    // Fill the text content and add the tab to the window
    text_widget->insertHtml(QStringLiteral("<font color='blue'>") + QString::fromStdString(content) + QStringLiteral("</font>"));
    text_widget->moveCursor(QTextCursor::Start); // Make the text edit scroll to the top
    _ui_info_selection_tabs->addTab(text_widget, QString::fromStdString(tab_name));
}

/***********************************
 *
 * Tool functions.
 *
 **********************************/

void Interface_graphic::opening_file(const string &path) {

    information(string("File opened: ") + path);

    this->setWindowTitle(QStringLiteral("ViTE :: ") + QString::fromStdString(path));

    _is_rendering_trace = true;
}

/***********************************
 *
 * Widget slot functions.
 *
 **********************************/

void Interface_graphic::on_open_triggered() {

    /*
     Be careful! Here, this is used instead 'this' pointer because 'this' is not a window,
     it's an interface class (Interface_graphic*). The real window is this.
     If 'this' is put, the application closes after the getOpenFilename() dialog box closes.
     */
    QString filename = QFileDialog::getOpenFileName(this);

    if (!filename.isEmpty()) {
        open_trace(filename);
    } /* end if (!filename.isEmpty()) */
}

void Interface_graphic::on_export_file_triggered() {

    QStringList selected_files;
    QFileDialog file_dialog(this);
    QString path_to_export;
    QString extension;

    if (_is_rendering_trace == false)
        return;

    ExportSettings &ES = Session::getSessionExport();

    /* Try to recover the last file dialog state. */
    if (!ES.is_default()) {
        if (!file_dialog.restoreState(ES.file_dialog_state))
            warning("Cannot restore the export file dialog state");
    }

    file_dialog.setLabelText(QFileDialog::FileName, QString::fromStdString(get_trace()->get_filename().substr(0, get_trace()->get_filename().find_last_of('.'))) + QStringLiteral(".svg"));
    file_dialog.setFileMode(QFileDialog::AnyFile); /* Allow to browse among directories */
    file_dialog.setAcceptMode(QFileDialog::AcceptSave); /* Only save file */
    file_dialog.setNameFilter(tr("Vector (*.svg);;Bitmap PNG (*.png);;Bitmap JPEG (*.jpeg *.jpg);;Counter (*.txt)"));

    if (file_dialog.exec()) { /* Display the file dialog */
        selected_files = file_dialog.selectedFiles(); //"/tmp/test.png";//file_dialog.selectedFile();

        if (selected_files.size() == 1)
            path_to_export = selected_files[0]; /* Get the first filename */
        else {

            if (selected_files.size() == 0)
                warning("No file was selected.");
            else
                warning("Cannot save: too much files were selected.");

            return;
        }

        QString valid_extension[5] = {
            QStringLiteral("svg"),
            QStringLiteral("png"),
            QStringLiteral("jpeg"),
            QStringLiteral("jpg"),
            QStringLiteral("txt")
        };

        bool has_valid_extension = false;

        for (int i = 0; i < 5; i++) {
            if (path_to_export.endsWith(QStringLiteral(".") + valid_extension[i])) {
                extension = valid_extension[i];
                _export_filename = path_to_export;
                has_valid_extension = true;
                break;
            }
        }

        if (!has_valid_extension) { /* No extension or extension is not correct */

            /* Add it manually */
            const QString filter_selected = file_dialog.selectedNameFilter();

            if (filter_selected == QLatin1String("Vector (*.svg)")) {
                path_to_export += QLatin1String(".svg");
                extension = QStringLiteral("svg");
            }
            else if (filter_selected == QLatin1String("Bitmap PNG (*.png)")) {
                path_to_export += QLatin1String(".png");
                extension = QStringLiteral("png");
            }
            else if (filter_selected == QLatin1String("Bitmap JPEG (*.jpeg *.jpg)")) {
                path_to_export += QLatin1String(".jpg");
                extension = QStringLiteral("jpg");
            }
            else if (filter_selected == QLatin1String("Counter (*.txt)")) {
                path_to_export += QLatin1String(".txt");
                extension = QStringLiteral("txt");
            }
            /* Update the _export_filename attribute which will be used in SVG or Counter export functions */
            _export_filename = path_to_export;
        }
    }

    /* Save the file dialog state (History and current directory) */
    ES.file_dialog_state = file_dialog.saveState();

    if (extension == QLatin1String("svg")) {

        _core->export_trace_to_svg(get_trace(), path_to_export.toStdString());
    }
    else if (extension == QLatin1String("txt")) {
        // In order to easily retrieve the good variable, we store them with an
        // index representing the container name followed by the variabletype
        // name (because a variable has no name)
        _all_variables.clear();

        get_trace()->get_all_variables(_all_variables);
        map<string, Variable *>::const_iterator it_end = _all_variables.end();

        _counter_list_names->clear();
        for (map<string, Variable *>::const_iterator it = _all_variables.begin();
             it != it_end; ++it) {
            _counter_list_names->addItem(QString::fromStdString((*it).first));
        }

        if (_counter_list_names->count() == 0) {
            error("The trace has no counter");
            return;
        }

        _ui_counter_choice_to_export->show();
    }
    else if (extension == QLatin1String("png") || extension == QLatin1String("jpg") || extension == QLatin1String("jpeg")) {

        QImage tempImage = _render_layout->get_render_area()->grab_frame_buffer();

        if (!tempImage.save(path_to_export, extension.toUpper().toStdString().c_str())) {
            error("The trace cannot be exported into " + path_to_export.toStdString() + " file.");
        }
        else {
            information("The trace was exported into " + path_to_export.toStdString() + " file.");
        }
    }
    else if (selected_files.size() != 0) { // No error if we canceled the save
        error("Cannot recognize the file extension: " + extension.toStdString() + ".");
    }
}

void Interface_graphic::on_reload_triggered() {
    if (_is_rendering_trace) {
        Element_pos zoom[2] = { Info::Splitter::_x_min,
                                Info::Splitter::_x_max };
        if (_ui_settings != nullptr) {
            _ui_settings->refresh();
        }
        // if(_ui_node_selection!=NULL)
        //     _ui_node_selection->on_reset_button_clicked();
        // if(_ui_interval_selection!=NULL)
        //  _ui_interval_selection->on_reset_button_clicked();
        if (_reload_type) {
            release_render_area();
        }
        else {
            render_area_clean();
        }
        _core->draw_trace(get_trace());

        if (Info::Splitter::_x_max != 0.0) {
            render_zoom_on_interval(zoom[0], zoom[1]);
        }

        _render_layout->get_render_area()->update_render();

        // reset the slider to its original value
        _render_layout->get_scale_container_slider()->setValue(20);

        // update the interval selection display
        if (_ui_interval_selection != nullptr) {
            _ui_interval_selection->update_values();
        }
    }
}

void Interface_graphic::open_recent_file() {
    QAction *action = qobject_cast<QAction *>(sender());
    if (action)
        open_trace(action->data().toString());
}

void Interface_graphic::on_clear_recent_files_triggered() {
    Session::clear_recent_files();
    // We remove the elements from the menu
    for (auto &_recent_file_action: _recent_file_actions) {
        if (_recent_file_action != nullptr) {
            _ui_recent_files_menu->removeAction(_recent_file_action);
        }
    }
}

void Interface_graphic::counter_choosed_triggered() {
    // Il faut faire choisir le nom du fichier! et enfin on peut lancer l'action !
    // const QString path_by_default = QString(_trace_path.substr(0, _trace_path.find_last_of('.')).c_str()) + ".txt";
    // QString filename = QFileDialog::getSaveFileName(this, QObject::tr("Export File"),
    //                                                path_by_default,
    //                                                QObject::tr("All files"));

    Variable *temp = _all_variables[_counter_list_names->currentText().toStdString()];
    //    _core->export_variable(temp, filename.toStdString());
    _core->export_variable(temp, _export_filename.toStdString());
}

/*void Interface_graphic::on_export_file_triggered(){
 if(_is_rendering_trace == false)
 return;

 _ui_kind_of_export_choice->show();
 }*/

void Interface_graphic::option_export_ok_pressed() {
    // We have to save the option from _ui_time_selection_export and hide her if it is not do automatically

    _ui_time_selection_export->hide();

    if (!_export_filename.isEmpty()) {

        information(string("Exporting trace to ") + _export_filename.toStdString());

        _core->export_trace_to_svg(get_trace(), _export_filename.toStdString());
    }
    else {
        error("No file specified for exportation");
    }
}

void Interface_graphic::on_close_triggered() {

    if (_is_rendering_trace == false)
        return;

    /*
     * Clear the informative window texts and hide it.
     */
    _ui_info_trace_text->clear(); /* Clear the current text (if exists) */
    _ui_info_selection_tabs->clear(); /* Clear the current text (if exists) */
    _ui_info_window->hide(); /* Hide the informative window */
    _ui_select_info_window->hide(); /* Hide the selection information window */

    if (_ui_settings) {
        _ui_settings->hide();
    }

    if (_plugin_window != nullptr) {
        _plugin_window->hide();
        //_plugin_window->clear_plugins();
    }

    release_render_area();

    _is_rendering_trace = false;

    information(string("File closed."));

    this->setWindowTitle(QStringLiteral("ViTE"));
}

void Interface_graphic::on_quit_triggered() {

    if (nullptr != _ui_node_selection)
        ((QWidget *)_ui_node_selection)->close();
    if (nullptr != _ui_interval_selection)
        ((QWidget *)_ui_interval_selection)->close();
    if (nullptr != _ui_info_window)
        ((QWidget *)_ui_info_window)->close();
    if (nullptr != _ui_select_info_window)
        ((QWidget *)_ui_select_info_window)->close();
    ((QWidget *)this)->close();
}

void Interface_graphic::on_fullscreen_triggered() {

    /*
     Notice that some problems can appears under X systems with
     the window decoration. Please refer to the Qt official documentation.
     */

    /*
     The menu is checked before function call, so if 'fullscreen menu' is checked,
     the main window is displayed in fullscreen mode
     */

    this->setWindowState(this->windowState() ^ Qt::WindowFullScreen);
}

void Interface_graphic::on_vertical_line_triggered() {
    Info::Render::_vertical_line = !Info::Render::_vertical_line;
    Session::update_vertical_line_setting(Info::Render::_vertical_line);
}

void Interface_graphic::on_shaded_states_triggered() {
    Info::Render::_shaded_states = !Info::Render::_shaded_states;
    Session::update_shaded_states_setting(Info::Render::_shaded_states);

    /* Reload render area */
    if (_is_rendering_trace) {
        if (_reload_type)
            release_render_area();
        else
            render_area_clean();
        _core->draw_trace(get_trace());
    }
}

void Interface_graphic::on_toolbar_menu_triggered() {
    static bool checked = true; /* Suppose that toolbar_menu is initially checked */

    checked = !checked;

    if (checked)
        _ui_toolbar->show();
    else
        _ui_toolbar->hide();
}

void Interface_graphic::on_minimap_menu_triggered() {
    render_show_minimap();
}

void Interface_graphic::on_show_info_triggered() {
    if (_ui_info_window->isHidden())
        _ui_info_window->show();
    else
        _ui_info_window->hide();
}

void Interface_graphic::on_about_triggered() {

    QMessageBox::about(this, tr("About ViTE"),
                       tr("<h2>ViTE</h2>"
                          "the <b>Vi</b><i>sual </i><b>T</b><i>race</i> <b>E</b><i>xplorer</i> - <i>version " VITE_VERSION
                          "</i> - <i>" VITE_DATE
                          "</i>.<br /><br />"
                          "Under the <a href=\"http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt\">CeCILL A</a> licence."
                          "<br>"
                          "<br>"
                          "Copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria, Univ. Bordeaux. All rights reserved."
                          "<br>"
                          "<br>"
                          "The project main page is:"
                          "<br>"
                          "<a href=\"" VITE_WEBSITE "\">" VITE_WEBSITE
                          "</a>.<br /><br />"));
}

void Interface_graphic::on_documentation_triggered() {
    QDesktopServices::openUrl(QUrl(QString::fromStdString("https://solverstack.gitlabpages.inria.fr/vite/documentation.html")));
}

void Interface_graphic::on_mail_triggered() {
    QDesktopServices::openUrl(QUrl(QString::fromStdString("mailto:vite-issues@inria.fr")));
}
void Interface_graphic::on_show_plugins_triggered() {
    // Don't open the window if no trace is loaded.
    // This avoids crashes when the plugin does not check if the trace is valid
    if (get_trace() == nullptr) {
        return;
    }

    if (_plugin_window == nullptr) { // Creation of the window
        _plugin_window = new Plugin_window(this);
    }

    if (get_trace() != nullptr) {
        _plugin_window->update_trace();
    }

    if (get_render_area() != nullptr) {
        _plugin_window->update_render();
    }

    _plugin_window->show();
}

void Interface_graphic::on_show_settings_triggered() {

    if (!_ui_settings) {
        _ui_settings = new Settings_window(this);
        connect(_ui_settings, &Settings_window::settings_changed, this, &Interface_graphic::update_settings);
        // To close the window when we quit the application
        connect(quit, &QAction::triggered, _ui_settings, &QWidget::close);
    }
    _ui_settings->show();
}

void Interface_graphic::on_node_selection_triggered() {

    if (get_trace() != nullptr) {
        if (!_ui_node_selection) {
            _ui_node_selection = new Node_select(this, nullptr);
            connect(quit, &QAction::triggered, _ui_node_selection, &QWidget::close);
        }
        _ui_node_selection->init_window();
        _ui_node_selection->show();
    }
    else {
        error("Must load a trace before using node selection on a trace");
    }
}

void Interface_graphic::on_interval_selection_triggered() {

    if (get_trace() != nullptr) {
        if (!_ui_interval_selection) {
            _ui_interval_selection = new Interval_select(this);
            connect(quit, &QAction::triggered, _ui_interval_selection, &QWidget::close);
        }
        _ui_interval_selection->init();
        _ui_interval_selection->show();
    }
    else {
        error("Must load a trace before using interval selection on a trace");
    }
}

void Interface_graphic::on_no_warning_triggered() {
    _no_warning = !_no_warning;
    Session::update_hide_warnings_settings(_no_warning);
}

void Interface_graphic::on_no_arrows_triggered() {
    std::cout << "no_arrows triggered" << std::endl;
    Info::Render::_no_arrows = !Info::Render::_no_arrows;
    _render_layout->get_render_area()->update_render();
}

void Interface_graphic::on_no_events_triggered() {
    Info::Render::_no_events = !Info::Render::_no_events;
    _render_layout->get_render_area()->update_render();
}

void Interface_graphic::on_reload_from_file_triggered() {
    _reload_type = !_reload_type;
    Session::update_reload_type_setting(_reload_type);
}

void Interface_graphic::on_goto_start_triggered() {
    int id;

    if (true == Info::Render::_key_alt) /* on Y axe */
        id = Info::Render::Y_TRACE_BEGINNING;
    else /* on X axe */
        id = Info::Render::X_TRACE_BEGINNING;

    render_area_registered_translate(id);
}

void Interface_graphic::on_goto_end_triggered() {
    int id;

    if (true == Info::Render::_key_alt) /* on Y axe */
        id = Info::Render::Y_TRACE_ENDING;
    else /* on X axe */
        id = Info::Render::X_TRACE_ENDING;

    render_area_registered_translate(id);
}

void Interface_graphic::on_show_all_trace_triggered() {
    int id = Info::Render::X_TRACE_ENTIRE; /* on X axe */
    render_area_registered_translate(id);
    id = Info::Render::Y_TRACE_ENTIRE; /* on Y axe */
    render_area_registered_translate(id);
}

void Interface_graphic::closeEvent(QCloseEvent *event) {
    //   _ui_help_widget->close();
    /* Now, release render */
    if (_is_rendering_trace)
        release_render_area();
    _is_rendering_trace = false;

    if (isEnabled()) {
        event->accept(); /* accept to hide the window for a further destruction */
    }
    else {
        event->ignore();
    }
    if (_ui_settings)
        _ui_settings->close();

    if (_ui_node_selection)
        _ui_node_selection->close();

    if (_ui_interval_selection)
        _ui_interval_selection->close();
}

Core *Interface_graphic::get_console() {
    return _core;
}

Node_select *Interface_graphic::get_node_select() {
    return _ui_node_selection;
}

Interval_select *Interface_graphic::get_interval_select() {
    return _ui_interval_selection;
}

void Interface_graphic::update_recent_files_menu() {
    const QStringList filenames = Session::get_recent_files();
    QString absoluteFilename;

    for (int i = 0; i < Session::_MAX_NB_RECENT_FILES && i < filenames.size(); ++i) {
        if (_recent_file_actions[i] != nullptr) {
            delete _recent_file_actions[i];
        }
        _recent_file_actions[i] = new QAction(this);
        _recent_file_actions[i]->setVisible(true);
        absoluteFilename = QFileInfo(filenames[i]).absoluteFilePath();

        const QString text = tr("&%1 %2").arg(i + 1).arg(absoluteFilename);
        _recent_file_actions[i]->setText(text);
        _recent_file_actions[i]->setData(absoluteFilename);
        connect(_recent_file_actions[i], &QAction::triggered,
                this, &Interface_graphic::open_recent_file);

        // We add to the menu
        _ui_recent_files_menu->addAction(_recent_file_actions[i]);
    }
}

void Interface_graphic::dragEnterEvent(QDragEnterEvent *event) {
    setBackgroundRole(QPalette::Highlight);
    event->acceptProposedAction();
}

void Interface_graphic::dragMoveEvent(QDragMoveEvent *event) {
    event->acceptProposedAction();
}

void Interface_graphic::dragLeaveEvent(QDragLeaveEvent *event) {
    event->accept();
}

void Interface_graphic::dropEvent(QDropEvent *event) {
    const QMimeData *mimeData = event->mimeData();

    if (!mimeData->hasUrls()) {
        return;
    }
    const QList<QUrl> &urls = event->mimeData()->urls();
    for (const QUrl &url: urls) {
#ifdef WIN32
        const QString filename = url.toString().right(url.toString().size() - 8);
#else
        // We remove file:// from the filename
        const QString filename = url.toString().right(url.toString().size() - 7);
#endif
        open_trace(filename);
    }
}

/* Graphical handling for parsing files */
void Interface_graphic::init_parsing(const std::string &filename) {
    if (!_progress_dialog) {
        _progress_dialog = new QProgressDialog(QObject::tr("Parsing"), QObject::tr("Cancel"), 0, 100, this);
        connect(_progress_dialog, &QProgressDialog::canceled, this, &Interface_graphic::cancel_parsing);
    }
    _progress_dialog->reset();
    _progress_dialog->setWindowTitle(QObject::tr("Loading of ") + QString::fromStdString(filename));
    _progress_dialog->show();
    setDisabled(true); // Disable the main window

    _progress_dialog->setDisabled(false); // to be able to cancel while parsing
}

void Interface_graphic::update_progress_bar(const QString &text, const int loaded) {
    _progress_dialog->setLabelText(text);
    _progress_dialog->setValue(loaded);
    _progress_dialog->update();
    QApplication::processEvents();
}

bool Interface_graphic::is_parsing_canceled() {
    return _progress_dialog->wasCanceled();
}

void Interface_graphic::cancel_parsing() {
    Q_EMIT stop_parsing_event_loop();
}

void Interface_graphic::end_parsing() {
    _progress_dialog->hide();
    _progress_dialog->deleteLater();
    _progress_dialog = nullptr;
    setDisabled(false); // Enable the main window
}

void Interface_graphic::open_trace(const QString &filename) {

    // We open the trace
    if (_is_rendering_trace == true) { /* open a new process */

        QStringList arguments = (QStringList() << filename);
        QString program;
        const QString **run_env = _core->get_runenv();
        QDir::setCurrent(*run_env[0]);
        QString run_cmd = *run_env[1];

#ifdef VITE_DEBUG
        cout << __FILE__ << " " << __LINE__ << " : " << run_env[0]->toStdString() << " " << run_env[1]->toStdString() << endl;
#endif

        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);
    }
    else {
        opening_file(filename.toStdString());

        Trace *trace = _core->build_trace(filename.toStdString());

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

        _core->draw_trace(trace);

        set_trace(trace);

    } /* end else of if (_is_rendering_trace == true) */
}

void Interface_graphic::update_settings() {
    // Reload plugins
    if (_plugin_window) {
        _plugin_window->reload_plugins();
    }
    cout << "Settings changed, need to update classes" << endl;

    _render_layout->get_render_area()->update_render();
}

Render_windowed *Interface_graphic::get_render_area() {
    return _render_layout->get_render_area();
}

void Interface_graphic::render_area_registered_translate(const int &id) {
    _render_layout->get_render_area()->registered_translate(id);
}

void Interface_graphic::render_area_clean() {
    Info::Render::_x_min_visible = 0.0;
    Info::Render::_x_max_visible = 0.0;
    if (_render_layout->get_render_area()->unbuild() == false) {
        std::stringstream stream;
        stream << "(" << __FILE__ << " l." << __LINE__ << "): Close file : an error occurred while cleaning the render.";
        error(stream.str());
    }

    _render_layout->get_render_area()->release();
    _render_layout->get_render_area()->update_render();
}

void Interface_graphic::render_show_minimap() {
    _render_layout->get_render_area()->show_minimap();
}

void Interface_graphic::render_zoom_on_interval(const Element_pos &x, const Element_pos &y) {
    _render_layout->get_render_area()->apply_zoom_on_interval(x, y);
}

void Interface_graphic::init_render_area() {
    Render_windowed *render_area = create_render();

    /* Bind the render area to a rander layout */
    // The render layout is directly added and visible on the window
    _render_layout = new RenderLayout(this, _ui_render_area_container_widget, _ui_render_area_container_layout, render_area);
    render_area->set_layout(_render_layout);
}

void Interface_graphic::render_display_information() {
    if (get_trace() != nullptr) {
        DrawTrace buf;
        buf.display_information(this, get_trace(), Info::Render::_info_x, Info::Render::_info_y, Info::Render::_info_accurate);
    }
}

void Interface_graphic::switch_container(const Element_pos &x1, const Element_pos &x2, const Element_pos &y1, const Element_pos &y2) {
    Element_pos yr = y1;
    Element_pos yr2 = y2;
    const Container *container = nullptr;
    const Container *container2 = nullptr;
    const Container::Vector *root_containers = get_trace()->get_view_root_containers();
    if (root_containers->empty())
        root_containers = get_trace()->get_root_containers();
    if (!root_containers->empty()) {
        DrawTrace buf;
        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 = x1 * (get_trace()->get_depth() + 1);
    if (xr < 0)
        return;

    Element_pos xr2 = x2 * (get_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 = get_trace()->get_view_root_containers();
        if (children->empty())
            children = get_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);
        }
        get_trace()->set_view_root_containers(*new_list);
        delete 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;

    render_area_clean();
#if defined(USE_ITC) && defined(BOOST_SERIALIZE)
    get_trace()->updateTrace(new Interval(zoom[0], zoom[1]));
#endif
    _core->draw_trace(get_trace());
    render_zoom_on_interval(zoom[0], zoom[1]);

    if (children_allocated) {
        delete children;
    }
}

Render_windowed *Interface_graphic::create_render() {
    // Check the compatibility of the system with the render engine
#ifdef USE_VULKAN
    return new Render_vulkan(this);
#else

    QGLFormat format(QGL::HasOverlay);

    /* 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::CompatibilityProfile);

    if (!QGLFormat::hasOpenGL()) {
        qFatal("This system has no OpenGL support");
    }

#ifdef VITE_ENABLE_VBO
    return new Render_alternate(this, format);
#else
    return new Render_opengl(this, format);
#endif
#endif
}

/***********************************
 *
 * Render area functions.
 *
 **********************************/

void Interface_graphic::release_render_area() {

    if (_render_layout->get_render_area()->unbuild() == false) {
        error("Close file : an error occurred with trace releasing.");
    }

    if (nullptr == _render_layout->get_trace()) {
        error("Try to release a render area whereas no trace is loaded");
    }
    else {
        _render_layout->delete_trace();
    }
    _render_layout->get_render_area()->update_render();

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

Trace *Interface_graphic::get_trace() const {
    return _render_layout->get_trace();
}

void Interface_graphic::set_trace(Trace *trace) {
    _render_layout->set_trace(trace);
}

void Interface_graphic::set_axis_icon(const bool is_along_y_axis) {
    if (is_along_y_axis) {
        QIcon icon_start_y;
        icon_start_y.addFile(QString::fromUtf8(":/icon/icon/goto_start_y.png"), QSize(), QIcon::Normal, QIcon::Off);
        goto_start->setIcon(icon_start_y);

        QIcon icon_end_y;
        icon_end_y.addFile(QString::fromUtf8(":/icon/icon/goto_end_y.png"), QSize(), QIcon::Normal, QIcon::Off);
        goto_end->setIcon(icon_end_y);
        return;
    }

    QIcon icon_start_x;
    icon_start_x.addFile(QString::fromUtf8(":/icon/icon/goto_start.png"), QSize(), QIcon::Normal, QIcon::Off);
    goto_start->setIcon(icon_start_x);

    QIcon icon_end_x;
    icon_end_x.addFile(QString::fromUtf8(":/icon/icon/goto_end.png"), QSize(), QIcon::Normal, QIcon::Off);
    goto_end->setIcon(icon_end_x);
}

void Interface_graphic::update_interval_select() {
    if (nullptr != _ui_interval_selection) {
        _ui_interval_selection->update_values();
    }
}

#include "moc_Interface_graphic.cpp"
