/**
 *
 * @file src/render/vbo/Render_alternate.cpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Arthur Chevalier
 * @author Johnny Jazeix
 * @author Olivier Lagrasse
 * @author Mathieu Faverge
 * @author Thibault Soucarre
 * @author Philippe Swartvagher
 * @author Lucas Guedon
 * @author Augustin Gauchet
 * @author Luca Bourroux
 *
 * @date 2024-07-17
 */
/*!
 *\file Render_alternate.cpp
 */
#include <assert.h>
#include "common/common.hpp"
#include "common/Info.hpp"
#include "common/Message.hpp"
/* -- */

#include <map>
#include <GL/glew.h>
/* -- */
#include <QFile> // For loading the wait image
#include <QDate>
#include <QTimer>
#include <QImage>
#include <QEventLoop>
/* -- */
#include "interface/resource.hpp"
/* -- */
#include "common/common.hpp"
#include "common/Info.hpp"
#include "common/Message.hpp"
/* -- */
#include "trace/values/Values.hpp"
#include "trace/tree/Interval.hpp"
#include "trace/tree/Node.hpp"
#include "trace/tree/BinaryTree.hpp"
#include "trace/DrawTrace.hpp"
#include "trace/EntityValue.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/Trace.hpp"
/* -- */
#include "render/Ruler.hpp"
#include "render/vbo/Render_alternate.hpp"
#include "render/GanttDiagram.hpp"
#include "render/vbo/Shader.hpp"
#include "render/vbo/vbo.hpp"
/* -- */
#include "core/Core.hpp"
#include <iostream>

#define PI 3.14159265
#define NB_STEPS 20

using namespace std;

extern "C" {
#if !defined(NDEBUG)
static inline void checkGlError() {
    int rc = glGetError();
    assert(rc == GL_NO_ERROR);
}
#else
static inline void checkGlError() {
}
#endif
}
#define message *Message::get_instance() << "(" << __FILE__ << " l." << __LINE__ << "): "

static bool _draw_container;
static bool _draw_states;
static bool _draw_ruler;
static bool _draw_arrow;
static bool _draw_event;
static bool _draw_counter;
// const int Render_alternate::DRAWING_TIMER_DEFAULT = 10;

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

Render_alternate::Render_alternate(Interface_graphic *interface_graphic, const QGLFormat &format) :
    Hook_event(this, interface_graphic, format),
    _glsl(0),
    _container_height(_DRAWING_CONTAINER_HEIGHT_DEFAULT),
    _r(0.0),
    _g(0.0),
    _b(0.0),
    _modelview(glm::mat4(1.0)),
    _projection(glm::mat4(1.0)),
    _containers(nullptr),
    _arrows3(nullptr),
    _counters(nullptr),
    _ruler(nullptr),
    _wait(nullptr),
    _selection(nullptr),
    _time_line(nullptr),
    _current(nullptr),
    vertical_line(0) {
    _texts.clear();
    _variable_texts.clear();
    _links.clear();
    /*int error = FT_Init_FreeType(&library);
     if(error != 0)
     std::cout << "an error occurred during freetype initialization" << std::endl;*/
}

Render_alternate::~Render_alternate() {
    delete _wait;
    delete _wait_shader;
    delete _shader;
}

QWidget *Render_alternate::get_render_widget() {
    return this;
}

QImage Render_alternate::grab_frame_buffer() {
    return grabFrameBuffer(true); /* true = with alpha channel */
}

/***********************************
 *
 *
 *
 * Default QGLWidget functions.
 *
 *
 *
 **********************************/

void Render_alternate::initializeGL() {
    glewExperimental = GL_TRUE;
    GLenum err = glewInit();
    if (err != GLEW_OK) {
        std::cout << "ERROR : GlewInit failed" << std::endl;
    }

    // Reset the error code set to GL_INVALID_ENUM by glewInit()
    glGetError();

    checkGlError();
    glClearColor(0.5f, 0.5f, 0.55f, 0.0f);
    glEnable(GL_DEPTH_TEST);
    checkGlError();
    glClearStencil(0);
    checkGlError();

    // Check for OpenGL version support
    const GLubyte *version = glGetString(GL_SHADING_LANGUAGE_VERSION);
    checkGlError();
    if (version == nullptr) {
        std::cout << "ERROR : could not detect your GLSL version" << std::endl;
        return;
    }

    std::cout << "Version GLSL : " << version << std::endl;
    _glsl = (version[0] - '0') * 100 + (version[2] - '0') * 10 + version[3] - '0';

    delete _shader;
    _shader = new Shader(_glsl);
    _shader->charger();
    draw_wait();
    setAutoFillBackground(false);
    _interface_graphic->get_console()->waitGUIInit->quit();
    _modelview = glm::scale(_modelview, glm::vec3(1, -1, 1));
    _modelview = glm::translate(_modelview, glm::vec3(0, -Info::Render::height, 0));

    // code from test on freetype library
    // see http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Text_Rendering_02
    // face = FT_New_Face(library,
    //                    "/usr/share/fonts/truetype/freefont/FreeSerif.ttf",
    //                    0,
    //                    &face);

    // if(error)
    //     std::cout << "an error occurred when loading font" << std::endl;

    // error = FT_Set_Char_Size(face,
    //                          0,
    //                          16*64,
    //                          1920,
    //                          1080);

    // if(error)
    //     std::cout << "an error occurred when changing character size" << std::endl;

    // FT_Glyphslot g = face->glyph;
    // int w=0, h=0;
    // for(int i = 32; i < 128; i++) {
    //     if(FT_Load_Char(face, i, FT_LOAD_RENDER)) {
    //         fprintf(stderr, "Loading character %c failed!\n", i);
    //         continue;
    //     }

    //     w += g->bitmap.width;
    //     h = std::max(h, g->bitmap.rows);
    //     char_info[*p].ax = g->advance.x >> 6;
    //     char_info[*p].ay = g->advance.y >> 6;
    //     char_info[*p].bw = g->bitmap.width;
    //     char_info[*p].bh = g->bitmap.rows;
    //     char_info[*p].bl = g->bitmap_left;
    //     char_info[*p].bt = g->bitmap_top;
    //     char_info[*p].tx = (float)x / w;

    //     // you might as well save this value as it is needed later on
    // }
    // atlas_width = w;
    // GLuint tex;
    // glActiveTexture(GL_TEXTURE0);
    // glGenTextures(1, &tex);
    // glBindTexture(GL_TEXTURE_2D, tex);
    // glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    // glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0);
    // int x = 0;
    // for(int i = 32; i < 128; i++) {
    //     if(FT_Load_Char(face, i, FT_LOAD_RENDER))
    //         continue;
    //     glTexSubImage2D(GL_TEXTURE_2D, 0, x, 0, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);
    //     x += g->bitmap.width;
    // }
}

void Render_alternate::render_text(const QString &text, float x, float y, float w, float h) {
    /* Code trying to create text on QImage and create a texture from this. Then render the text thanks to this texture. Not working yet, the texture seems to be empty*/
    GLuint texture;
    checkGlError();
    glGenTextures(2, &texture);
    checkGlError();
    glBindTexture(GL_TEXTURE_2D, texture);
    checkGlError();
    QImage *image = new QImage(512, 512, QImage::Format_RGB32);
    image->fill(Qt::transparent);
    QPainter *p = new QPainter(image);
    // p.beginNativePainting();
    p->drawText(0, 0, 512, 512, Qt::AlignHCenter | Qt::AlignVCenter, text);
    delete p;
    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512, 512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr);
    checkGlError();
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 512, 512, GL_ALPHA, GL_UNSIGNED_BYTE, image->bits());
    checkGlError();
    glBindTexture(GL_TEXTURE_2D, 0);
    // p.endNativePainting();
    Vbo v(nullptr);
    v.add(x, y, 0, 1);
    v.add(x + w, y, 1, 1);
    v.add(x + w, y + h, 1, 0);
    v.add(x, y, 0, 1);
    v.add(x, y + h, 0, 0);
    v.add(x + w, y + h, 1, 0);
    v.config(_glsl);
    glUseProgram(_wait->get_shader()->getProgramID());
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    v.lock();
    glBindTexture(GL_TEXTURE_2D, texture);
    glDrawArrays(GL_TRIANGLES, 0, v.getNbVertex());
    glDisable(GL_BLEND);
    glBindTexture(GL_TEXTURE_2D, 0);
    v.unlock();
    glUseProgram(0);
    delete image;
    /*trying freetype*/
    // int n=0;
    // for(char*p = text; *p; p++){
    //     float x2 =  x + c[*p].bl * sx;
    //     float y2 = -y - c[*p].bt * sy;
    //     float w = c[*p].bw * sx;
    //     float h = c[*p].bh * sy;
    //     /* Advance the cursor to the start of the next character */
    //     x += c[*p].ax * sx;
    //     y += c[*p].ay * sy;

    //     /* Skip glyphs that have no pixels */
    //     if(!w || !h)
    //         continue;
    //     _text.add(x2, y2, c[*p].tx, 0);
    //     _text.add(x2+w, -y2, c[*p].tx + c[*p].bw / atlas_width, 0);
    //     _text.add(x2, -y2 - h, c[*p].tx, c[*p].bh / atlas_height); //remember: each glyph occupies a different amount of vertical space
    //     _text.add(x2 + w, -y2    , c[*p].tx + c[*p].bw / atlas_width,   0);
    //     _text.add(x2,     -y2 - h, c[*p].tx,                                          c[*p].bh / atlas_height);
    //     _text.add(x2 + w, -y2 - h, c[*p].tx + c[*p].bw / atlas_width, c[*p].bh / atlas_height);
    // }
}

void Render_alternate::resizeGL(int width, int height) {
    glViewport(0, 0, width, height);
    checkGlError();

    /* update information about widget size */
    Info::Screen::width = width;
    Info::Screen::height = height;
    if (_state == DRAWING_STATE_WAITING)
        _projection = glm::ortho(-50, 50, -50, 50); //, 0, 1);
    else if (_state == DRAWING_STATE_DRAWING)
        _projection = glm::ortho(0.f, Info::Render::width, 0.f, Info::Render::height, 0.f, 100.f);
    else {
        message << tr("Undefined value for the drawing state attribute - Render area").toStdString() << Message::ende;
    }
}

void Render_alternate::paintGL() {
    glGetError();
    checkGlError();
    glClearDepth(1.0);
    checkGlError();
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    checkGlError();

    /*Draw the home screen*/
    if (DRAWING_STATE_WAITING == _state) {
        glUseProgram(_wait->get_shader()->getProgramID());
        checkGlError();

        glEnable(GL_BLEND);
        checkGlError();

        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        checkGlError();

        _wait->lock();

        glBindTexture(GL_TEXTURE_2D, _textureID);
        checkGlError();

        // GL_QUADS is deprecated for Opengl greater than 3., so an invalide enum is returned by glGetError and needs to be reset
        // TODO: fix that by replacing by GL_TRIANGLES
        glDrawArrays(GL_TRIANGLES, 0, _wait->getNbVertex()); /*glGetError();*/
        checkGlError();
        glBindTexture(GL_TEXTURE_2D, 0);
        checkGlError();
        _wait->unlock();
        glUseProgram(0);
        checkGlError();
        return;
    }
    /*Else, draw the trace*/

    resizeGL(Render_alternate::QGLWidget::width(), Render_alternate::QGLWidget::height());
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    checkGlError();

    // select shader program
    glUseProgram(_shader->getProgramID());
    checkGlError();
    glm::mat4 tmp = _modelview;
    glm::mat4 tmp2 = _modelview;
    glm::mat4 mvp;

    start_ruler();
    call_ruler();
    end_ruler();

    /* drawing containers*/
    Shader *s = _containers.get_shader();
    glUseProgram(s->getProgramID());
    _containers.lock();
    _modelview = glm::translate(_modelview, glm::vec3(0.0, _ruler_y + _ruler_height - _y_state_translate, _z_container));
    _modelview = glm::scale(_modelview, glm::vec3(_x_scale_container_state / 0.20, _y_state_scale, 1.0));
    mvp = _projection * _modelview;
    glUniformMatrix4fv(glGetUniformLocation(_shader->getProgramID(), "MVP"), 1, GL_FALSE, glm::value_ptr(mvp));
    glDrawArrays(GL_TRIANGLES, 0, _containers.getNbVertex()); /*glGetError();*/
    checkGlError();
    _containers.unlock();
    _modelview = tmp;
    glUseProgram(0);
    /*drawing states*/
    _modelview = glm::translate(_modelview, glm::vec3(_default_entity_x_translate - _x_state_translate, _ruler_y + _ruler_height - _y_state_translate, _z_state));
    _modelview = glm::scale(_modelview, glm::vec3(_x_state_scale, _y_state_scale, 1));
    mvp = _projection * _modelview;
    std::map<EntityValue *, Vbo *>::iterator it_state;
    it_state = _states.begin();
    while (it_state != _states.end()) {
        Shader *s = it_state->second->get_shader();
        glUseProgram(s->getProgramID());
        it_state->second->lock();
        glUniformMatrix4fv(glGetUniformLocation(s->getProgramID(), "MVP"), 1, GL_FALSE, glm::value_ptr(mvp));
        if (it_state->first == nullptr || it_state->first->get_visible()) {
            glDrawArrays(GL_TRIANGLES, 0, it_state->second->getNbVertex()); /*glGetError();*/
            checkGlError();
        }
        it_state->second->unlock();
        ++it_state;
    }
    checkGlError();

    /*drawing counters*/
    glUseProgram(_counters.get_shader()->getProgramID());
    glUniformMatrix4fv(glGetUniformLocation(_counters.get_shader()->getProgramID(), "MVP"), 1, GL_FALSE, glm::value_ptr(mvp));

    _counters.lock();
    glDrawArrays(GL_LINES, 0, _counters.getNbVertex());
    _counters.unlock();
    _modelview = tmp;
    checkGlError();

    /*drawing links*/
    if (false == Info::Render::_no_arrows) {
        // matrix change that are necessary for each link
        _modelview = glm::translate(_modelview, glm::vec3(_default_entity_x_translate - _x_state_translate, _ruler_y + _ruler_height - _y_state_translate, _z_arrow));
        _modelview = glm::scale(_modelview, glm::vec3(_x_state_scale, _y_state_scale, 1.0));
        mvp = _projection * _modelview;
        // draw the lines
        std::map<EntityValue *, std::pair<Vbo *, Vbo *>>::iterator it_arrow = _arrows.begin();
        while (it_arrow != _arrows.end()) {
            Shader *s = it_arrow->second.first->get_shader();
            glUseProgram(s->getProgramID());
            it_arrow->second.first->lock();
            glUniformMatrix4fv(glGetUniformLocation(s->getProgramID(), "MVP"), 1, GL_FALSE, glm::value_ptr(mvp));
            if (it_arrow->first == nullptr || it_arrow->first->get_visible())
                glDrawArrays(GL_LINES, 0, it_arrow->second.first->getNbVertex());
            it_arrow->second.first->unlock();
            if (Info::Render::_arrows_shape == 1) { // head = points
                it_arrow->second.second->lock();
                glEnable(GL_PROGRAM_POINT_SIZE);
                glEnable(GL_POINT_SMOOTH);
                glPointSize(5);
                if (it_arrow->first == nullptr || it_arrow->first->get_visible())
                    glDrawArrays(GL_POINTS, 0, it_arrow->second.second->getNbVertex());
                it_arrow->second.second->unlock();
            }
            ++it_arrow;
        }
        if (Info::Render::_arrows_shape == 0) { // triangles
            std::map<EntityValue *, std::vector<Element_pos>>::iterator it_link = _links.begin();
            _modelview = glm::scale(_modelview, glm::vec3(1 / _x_state_scale, 1 / _y_state_scale, 1));
            tmp2 = _modelview;
            _arrows3.lock();
            while (it_link != _links.end()) {
                Shader *s = _arrows[it_link->first].first->get_shader();
                glUseProgram(s->getProgramID());
                std::vector<Element_pos> v = it_link->second;
                int n = v.size() / 3;
                if (it_link->first != nullptr && !it_link->first->get_visible()) {
                    ++it_link;
                    continue;
                }
                for (int i = 0; i < n; i++) {
                    _modelview = glm::translate(_modelview, glm::vec3(_x_state_scale * v[3 * i], _y_state_scale * v[3 * i + 1], 0));
                    _modelview = glm::rotate(_modelview, v[3 * i + 2], glm::vec3(0, 0, 1));
                    mvp = _projection * _modelview;
                    glUniformMatrix4fv(glGetUniformLocation(s->getProgramID(), "MVP"), 1, GL_FALSE, glm::value_ptr(mvp));

                    glDrawArrays(GL_TRIANGLES, 0, _arrows3.getNbVertex());
                    _modelview = tmp2;
                }
                ++it_link;
            }
            _arrows3.unlock();
        }
        _modelview = tmp;
    }
    checkGlError();

    /*drawing events*/
    if (false == Info::Render::_no_events) {
        _modelview = glm::translate(_modelview, glm::vec3(_default_entity_x_translate - _x_state_translate, _ruler_y + _ruler_height - _y_state_translate, _z_event));
        _modelview = glm::scale(_modelview, glm::vec3(_x_state_scale, _y_state_scale, 1.0));
        mvp = _projection * _modelview;
        std::map<EntityValue *, std::pair<Vbo *, Vbo *>>::iterator it_event = _events.begin();
        while (it_event != _events.end()) {
            Shader *s = it_event->second.first->get_shader();
            glUseProgram(s->getProgramID());
            it_event->second.first->lock();
            glUniformMatrix4fv(glGetUniformLocation(s->getProgramID(), "MVP"), 1, GL_FALSE, glm::value_ptr(mvp));
            if (it_event->first == nullptr || it_event->first->get_visible()) {
                // glDisable(GL_PROGRAM_POINT_SIZE);
                // draw points as circles instead of squares
                glEnable(GL_POINT_SMOOTH);
                glPointSize(5);
                glDrawArrays(GL_POINTS, 0, it_event->second.first->getNbVertex());
                it_event->second.first->unlock();
            }
            it_event->second.second->lock();
            glLineWidth(2);
            if (it_event->first == nullptr || it_event->first->get_visible())
                glDrawArrays(GL_LINES, 0, it_event->second.second->getNbVertex());
            it_event->second.second->unlock();
            ++it_event;
        }
        glLineWidth(1);
    }

    _modelview = tmp;
    glUseProgram(_shader->getProgramID());

    /*draw selection if necessary*/
    if (_mouse_pressed && (Info::Render::_key_ctrl == false) && !_mouse_pressed_inside_container) {
        // allow transparency
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        Element_pos x0, x1, y0, y1;
        x0 = screen_to_render_x(_mouse_x);
        y0 = screen_to_render_y(_mouse_y);
        x1 = screen_to_render_x(_new_mouse_x);
        y1 = screen_to_render_y(_new_mouse_y);
        _selection.add(x0, y0, 1.0, 1.0, 1.0);
        _selection.add(x0, y1, 1.0, 1.0, 1.0);
        _selection.add(x1, y1, 1.0, 1.0, 1.0);
        _selection.add(x1, y1, 1.0, 1.0, 1.0);
        _selection.add(x1, y0, 1.0, 1.0, 1.0);
        _selection.add(x0, y0, 1.0, 1.0, 1.0);
        _selection.config(_glsl);
        _selection.lock();
        glDrawArrays(GL_TRIANGLES, 0, _selection.getNbVertex());
        _selection.unlock();
        glDisable(GL_BLEND);
        _modelview = tmp;
    }

    QFont arial_font = QFont(QStringLiteral("Arial"), 10);
    const QFontMetrics metric(arial_font);

    if (Info::Render::_vertical_line) {
        draw_vertical_line();
        checkGlError();

        // deselect shader
        glUseProgram(0);
        checkGlError();
        QString t = QString().setNum(vertical_line);
        // draw time corresponding to the vertical line position
        qglColor(Qt::red);
        checkGlError();
        renderText(render_to_screen_x(trace_to_render_x(vertical_line)) - metric.size(Qt::TextSingleLine, t).width() - 5,
                   metric.height() - 1,
                   t,
                   arial_font);
        /*render_text(t,
         render_to_screen_x(trace_to_render_x(vertical_line))-metric.size(Qt::TextSingleLine,t).width() -5,
         metric.height()-1,
         100,
         100);*/
        glGetError();
        /*render_text("Test",
         vertical_line,
         0,
         1,
         1);*/

        checkGlError();
        // draw texts for variables
        std::map<long int, Variable_text_>::const_iterator it = _variable_texts.begin();
        const std::map<long int, Variable_text_>::const_iterator it_end = _variable_texts.end();

        for (; it != it_end; ++it) {
            renderText(render_to_screen_x(trace_to_render_x(vertical_line)) + 3,
                       render_to_screen_y(trace_to_render_y((*it).second.y) + 0.5),
                       QString().setNum((*it).second.value),
                       arial_font);
        }
        checkGlError();
    }

    qglColor(Qt::white);
    // we calculate the height of the interline we want : max height of the font + 1 pixel to avoid overlapping (metric.height() returns a bigger value, a bit too much)
    int height = metric.tightBoundingRect(QStringLiteral("fg")).height() + 1;

    /* Draw container text */
    const unsigned int texts_size = _texts.size();
    std::map<Element_pos, Element_pos> previous_by_column;

    // int skipped,displayed=0;
    for (unsigned int i = 0; i < texts_size; i++) {

        if (trace_to_render_y(_texts[i].y) + 0.5 < 9)
            continue; /* Do not display text if it is on the ruler area */

        // check if ye are not too close to another container to properly display the text
        std::map<Element_pos, Element_pos>::const_iterator it = previous_by_column.find(_texts[i].x);
        const std::map<Element_pos, Element_pos>::const_iterator it_end = previous_by_column.end();

        if (it == it_end || render_to_screen_y(trace_to_render_y(_texts[i].y)) - render_to_screen_y(trace_to_render_y((*it).second)) > height) {
            const QString text_elided = metric.elidedText(QString::fromStdString(_texts[i].value), Qt::ElideRight, 0.8 * _x_scale_container_state * Info::Screen::width / (Info::Trace::depth + 1.));
            renderText(render_to_screen_x(_texts[i].x * _x_scale_container_state / 0.20),
                       render_to_screen_y(trace_to_render_y(_texts[i].y) + 0.5),
                       text_elided,
                       arial_font);

            // push only displayed values in the map
            previous_by_column[_texts[i].x] = _texts[i].y;
            // displayed++;
        } // else{skipped++;}
    }

    checkGlError();
}
/***********************************
 *
 *
 *
 * Building functions.
 *
 *
 *
 **********************************/

bool Render_alternate::build() {

    _state = DRAWING_STATE_DRAWING; /* change the drawing state */

    /* disable some OpenGL features to enhance the rendering */
    glDisable(GL_BLEND);
    replace_scale_x(1); /* for states scaling */
    _x_state_translate = 0; /* for states translation */
    _y_state_scale = 1; /* for states scaling */
    _y_state_translate = 0; /* for states translation */
    checkGlError();
    if (nullptr == _render_instance)
        return true;

    return true;
}

/* trying to render text with qpainter outside paintGL
 Not working yet*/
/*void Render_alternate::paintEvent(QPaintEvent *Event){
 paintGL();
 QPainter p(this);
 QFont arial_font = QFont("Arial", 10);
 const QFontMetrics metric(arial_font);
 QString t =QString().setNum(vertical_line);
 p.drawText(render_to_screen_x(trace_to_render_x(vertical_line))-metric.size(Qt::TextSingleLine,t).width() -5,
 metric.height()-1,
 t);
 glGetError();
 checkGlError();
 }*/

bool Render_alternate::unbuild() {

    /**********************
     *
     * Init OpenGL features
     *
     **********************/

    /*enable some OpenGL features*/
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glEnable(GL_BLEND); /* enable blending for the alpha color */
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    /*****************************
     *
     * Init render area attributes
     *
     *****************************/

    _state = DRAWING_STATE_WAITING; /* change the drawing state */

    /* clear lists and maps*/
    _text_pos.clear();
    _text_value.clear();
    clear_text();
    _links.clear();
    for (auto &x: _states)
        delete x.second;
    _states.clear();
    for (auto &x: _arrows) {
        delete x.second.first;
        delete x.second.second;
    }
    _arrows.clear();
    for (auto &x: _events) {
        delete x.second.first;
        delete x.second.second;
    }
    _events.clear();

    // >SHADER_POOL
    // We need a shader pool here because VBO used to own their shader so they used to delete it
    // Then when we do "for (auto& x : _arrows) { delete x.second.first; delete x.second.second; }"
    // we try to delete the same shader twice (second.first and second.second share the same shader)
    // So we removed the shader from the hands of the VBO and we need to have a shader pool.

    // Having a shader pool allows us to not manually allocate a shader for each VBO and to not
    // fragment our memory. We need to use a special data structure that don't allocate a lot
    // and that have stable pointer. For this we want an array of array basically a Bucket Array
    // The implementation of this in the stl is std::deque, but... it's kind of bad we can't even
    // specify the bucket sizes, we might want to implement it ourself.
    _shader_pool.clear();

    return true;
}

template <typename... Args>
Shader *new_shader(std::deque<Shader> &_shader_pool, Args &&...args) noexcept {
    _shader_pool.emplace_back(std::forward<Args>(args)...);
    return &_shader_pool.back();
}

/***********************************
 *
 *
 *
 * Drawing function for the wait screen.
 *
 *
 *
 **********************************/

GLuint Render_alternate::draw_wait() {
    if (_wait == nullptr) {
        _wait_shader = new Shader(_glsl, 0);

        _wait = new Vbo();
        _wait_shader->charger();
        _wait->set_shader(_wait_shader);

        glGenTextures(1, &_textureID);
        glBindTexture(GL_TEXTURE_2D, _textureID);

        QFile texture_file(QStringLiteral(":/img/img/logo") + QDate::currentDate().toString(QStringLiteral("MMdd")) + QStringLiteral(".png"));
        if (true == texture_file.exists()) /* The texture exists */
            _textureID = bindTexture(QPixmap(texture_file.fileName()), GL_TEXTURE_2D);
        else /* use the default picture */
            _textureID = bindTexture(QPixmap(QStringLiteral(":/img/img/logo.png")), GL_TEXTURE_2D);

        /* Coordinates of the two triangles storing the texture */
        _wait->add(-0.25, -0.25, 0, 0);
        _wait->add(0.25, -0.25, 1, 0);
        _wait->add(0.25, 0.25, 1, 1);
        _wait->add(0.25, 0.25, 1, 1);
        _wait->add(-0.25, 0.25, 0, 1);
        _wait->add(-0.25, -0.25, 0, 0);
        _wait->config(_glsl);
    }

    return 1;
}

/* Function called by paintGL. Draw ruler vbo*/
void Render_alternate::call_ruler() {
    // Clean ruler buffer
    _ruler.setNbVertex(0);

    update_visible_interval_value();

    // draw quads
    set_color(0.0, 0.0, 1.0);
    _ruler.add(trace_to_render_x(Info::Render::_x_min_visible), 3, _r, _g, _b);
    _ruler.add(trace_to_render_x(Info::Render::_x_min_visible), 8, _r, _g, _b);
    _ruler.add(trace_to_render_x(Info::Render::_x_max_visible), 8, _r, _g, _b);
    _ruler.add(trace_to_render_x(Info::Render::_x_max_visible), 8, _r, _g, _b);
    _ruler.add(trace_to_render_x(Info::Render::_x_max_visible), 3, _r, _g, _b);
    _ruler.add(trace_to_render_x(Info::Render::_x_min_visible), 3, _r, _g, _b);
    set_color(0.0, 0.0, 0.0); // Black
    _ruler.add(0, 0, _r, _g, _b);
    _ruler.add(0, 3, _r, _g, _b);
    _ruler.add(Info::Render::width, 3, _r, _g, _b);
    _ruler.add(Info::Render::width, 3, _r, _g, _b);
    _ruler.add(Info::Render::width, 0, _r, _g, _b);
    _ruler.add(0, 0, _r, _g, _b);

    // Buffer graduation lines
    set_color(1.0, 1.0, 1.0); // White

    QFont arial_font = QFont(QStringLiteral("Arial"), 10);
    std::ostringstream buf_txt;

    const Element_pos offset_x = _default_entity_x_translate;

    const Element_pos graduation_diff = Ruler::get_graduation_diff(render_to_trace_x(0), render_to_trace_x(Info::Render::width));
    const Element_pos grad_div_by_5 = graduation_diff / 5;

    const Element_pos first_grad_pos = Info::Render::_x_min_visible - fmodf(Info::Render::_x_min_visible, graduation_diff);
    const Element_pos last_grad_pos = Info::Render::_x_max_visible - fmodf(Info::Render::_x_max_visible, graduation_diff);

    for (Element_pos i = first_grad_pos; i < last_grad_pos + graduation_diff; i += graduation_diff) {
        Element_pos j = (i + grad_div_by_5);
        for (int loop_index = 0; loop_index < 4; loop_index++) {
            if (j > Info::Render::_x_max_visible) {
                break;
            }
            draw_line(trace_to_render_x(j) + offset_x, 3,
                      trace_to_render_x(j) + offset_x, 5.5, _z_ruler);
            j += grad_div_by_5;
        }

        draw_line(trace_to_render_x(i) + offset_x, 3,
                  trace_to_render_x(i) + offset_x, 8, _z_ruler);
    }

    // Draw ruler without text
    _ruler.config(_glsl);
    _ruler.lock();

    // Draw Blue and Black rectangles
    glm::mat4 local_modelview = glm::translate(_modelview, glm::vec3(0.0, 0.0, _z_ruler));
    glm::mat4 mvp = _projection * local_modelview;
    glUniformMatrix4fv(glGetUniformLocation(_shader->getProgramID(), "MVP"), 1, GL_FALSE, glm::value_ptr(mvp));
    checkGlError();
    /*The first 8 elements of the vbo are the coordinates of the quads, others elements are coordinates of the graduation*/
    glDrawArrays(GL_TRIANGLES, 0, 12);
    checkGlError();

    // Draw graduations
    local_modelview = glm::translate(_modelview, glm::vec3(0.0, 0.0, _z_ruler_over));
    mvp = _projection * local_modelview;
    glUniformMatrix4fv(glGetUniformLocation(_shader->getProgramID(), "MVP"), 1, GL_FALSE, glm::value_ptr(mvp));
    checkGlError();
    glDrawArrays(GL_LINES, 12, _ruler.getNbVertex() - 12);
    checkGlError();
    _ruler.unlock();

    arial_font.setPointSize(14);

    // Display min visible value
    buf_txt.str(""); /* flush the buffer */
    buf_txt << "min: " << (double)Info::Render::_x_min_visible;

    renderText(render_to_screen_x(0),
               render_to_screen_y(3),
               QString::fromStdString(buf_txt.str()),
               arial_font);

    // Display max visible value
    buf_txt.str(""); /* flush the buffer */
    buf_txt << "max: " << (double)Info::Render::_x_max_visible;

    renderText(render_to_screen_x(Info::Render::width) - 130,
               render_to_screen_y(3),
               QString::fromStdString(buf_txt.str()),
               arial_font);

    // Display common part
    const std::string common_part_string = Ruler::get_common_part_string(Info::Render::_x_min_visible, Info::Render::_x_max_visible);
    buf_txt.str("");
    buf_txt << common_part_string << "-- ";

    renderText(render_to_screen_x(Info::Render::width / 2),
               render_to_screen_y(3),
               QString::fromStdString(buf_txt.str()),
               arial_font);

    // Display visible duration
    buf_txt.str("");
    buf_txt << "(duration : "
            << Info::Render::_x_max_visible - Info::Render::_x_min_visible
            << ")";

    renderText(render_to_screen_x(Info::Render::width / 4),
               render_to_screen_y(3),
               QString::fromStdString(buf_txt.str()),
               arial_font);

    // Display graduation text

    // Check the number of digits after the floating point
    const std::size_t length_after_point = max(0, -int(floor(log10(graduation_diff))));

    for (Element_pos i = first_grad_pos; i <= last_grad_pos; i += graduation_diff) {
        renderText(render_to_screen_x(trace_to_render_x(i) + 1),
                   render_to_screen_y(8),
                   QString::fromStdString(Ruler::get_variable_part(i, common_part_string, length_after_point)),
                   arial_font);
    }

    return;
}

void Render_alternate::set_color(float r, float g, float b) {
    _r = r;
    _g = g;
    _b = b;
}

void Render_alternate::draw_text(const Element_pos x, const Element_pos y, const Element_pos size, const std::string &s) {
    Container_text_ buf;
    buf.x = x;
    buf.y = render_to_trace_y(y); /* Cancel previous transformation. */
    buf.value = s;
    buf.size = size;
    _texts.push_back(buf);
}

void Render_alternate::draw_quad(Element_pos x, Element_pos y, Element_pos z, Element_pos w, Element_pos h) {
    checkGlError();
    Element_pos offset_x;
    const Element_pos offset_y = -_ruler_y - _ruler_height;

    offset_x = 0;
    if (_draw_container) {
        _containers.add(x + offset_x, y + offset_y, _r, _g, _b);
        _containers.add((x + w) + offset_x, y + offset_y, _r, _g, _b);
        _containers.add((x + w) + offset_x, (y + h) + offset_y, _r, _g, _b);
        _containers.add((x + w) + offset_x, (y + h) + offset_y, _r, _g, _b);
        _containers.add(x + offset_x, (y + h) + offset_y, _r, _g, _b);
        _containers.add(x + offset_x, y + offset_y, _r, _g, _b);
    }
    else if (_draw_states) {
        offset_x = -_default_entity_x_translate;
        if (_glsl < 330) { // float for color gradient
            _current->add(x + offset_x, y + offset_y, 2.0f);
            _current->add((x + w) + offset_x, y + offset_y, 1.0f);
            _current->add((x + w) + offset_x, (y + h) + offset_y, 1.0f);
            _current->add((x + w) + offset_x, (y + h) + offset_y, 1.0f);
            _current->add(x + offset_x, (y + h) + offset_y, 2.0f);
            _current->add(x + offset_x, y + offset_y, 2.0f);
        }
        else { // char for color gradient
            char c1 = 1;
            char c2 = 2;
            _current->add(x + offset_x, y + offset_y, c2);
            _current->add((x + w) + offset_x, y + offset_y, c1);
            _current->add((x + w) + offset_x, (y + h) + offset_y, c1);
            _current->add((x + w) + offset_x, (y + h) + offset_y, c1);
            _current->add(x + offset_x, (y + h) + offset_y, c2);
            _current->add(x + offset_x, y + offset_y, c2);
        }
    }
    checkGlError();

    (void)z;
}

void Render_alternate::draw_triangle(Element_pos, Element_pos,
                                     Element_pos, Element_pos) {
}

void Render_alternate::draw_line(Element_pos x1, Element_pos y1, Element_pos x2, Element_pos y2, Element_pos z) {

    const Element_pos offset_x = -_default_entity_x_translate;
    const Element_pos offset_y = -_ruler_y - _ruler_height;

    if (_draw_counter) {
        _counters.add(x1 + offset_x, y1 + offset_y); //,_r,_g,_b);
        _counters.add(x2 + offset_x, y2 + offset_y); //,_r,_g,_b);
    }
    else if (_draw_ruler) {
        _ruler.add(x1 + offset_x, y1, _r, _g, _b);
        _ruler.add(x2 + offset_x, y2, _r, _g, _b);
    }

    (void)z;
}

void Render_alternate::draw_circle(Element_pos /*x*/, Element_pos /*y*/, Element_pos /*z*/, Element_pos /*r*/) {
}

void Render_alternate::start_draw() {
    _draw_ruler = false;
    _draw_container = false;
    _draw_arrow = false;
    _draw_event = false;
}

void Render_alternate::start_draw_containers() {
    _draw_container = true;
    Shader *s = new_shader(_shader_pool, _glsl);
    s->charger();
    _containers.set_shader(s);
    _containers.setNbVertex(0);
}

void Render_alternate::draw_container(const Element_pos, const Element_pos, const Element_pos, const Element_pos) {
}

void Render_alternate::draw_container_text(const Element_pos, const Element_pos, const std::string &) {
}

void Render_alternate::end_draw_containers() {
    _draw_container = false;
    checkGlError();
    _containers.config(_glsl);
    checkGlError();
}

void Render_alternate::start_draw_states() {
    _draw_states = true;
}

void Render_alternate::draw_state(const Element_pos x,
                                  const Element_pos y,
                                  const Element_pos z,
                                  const Element_pos w,
                                  const Element_pos h,
                                  EntityValue *value) {

    // Not sure if value is meant to be nullptr
    // It is equal to nullptr when the parsing is cancelled or an error occurred while parsing
    if (nullptr == value) {
        return;
    }

    if (_states.count(value) == 0) { // if there is no vbo corresponding to value
        // we create a new vbo and a new shader corresponding to the current color*/
        Shader *s = new_shader(_shader_pool, _glsl, _r, _g, _b, true);
        s->charger();
        // assert(value); // TODO: check why busy state exists and uncomment this assert
        Vbo *v = new Vbo(s);
        std::pair<EntityValue *, Vbo *> p2(value, v);
        _states.insert(p2);

        connect(value, &EntityValue::changedColor,
                this, &Render_alternate::update_ev);
    }
    // add the state to the vbo
    _current = _states[value];
    draw_quad(x, y, z, w, h);
}

void Render_alternate::end_draw_states() {

    _draw_states = false;
    std::map<EntityValue *, Vbo *>::iterator it;
    it = _states.begin();
    while (it != _states.end()) {
        // std::cout << it->first->get_name().get_name() << std::endl;
        it->second->config(_glsl);
        ++it;
    }
    checkGlError();
}

void Render_alternate::start_draw_arrows() {
    _draw_arrow = true;
}

void Render_alternate::draw_arrow(const Element_pos start_time,
                                  const Element_pos end_time,
                                  const Element_pos start_height,
                                  const Element_pos end_height,
                                  const Element_col red,
                                  const Element_col green,
                                  const Element_col blue,
                                  EntityValue *value) {
    // Not sure if value is meant to be nullptr
    // It is equal to nullptr when the parsing is cancelled or an error occurred while parsing
    if (nullptr == value) {
        return;
    }

    if (_arrows.count(value) == 0) { // if there is no vbo corresponding to value
        /* Create a new vbo and a new shader*/
        Shader *s = new_shader(_shader_pool, _glsl, _r, _g, _b, false);
        s->charger();
        Vbo *v1 = new Vbo(s);
        Vbo *v2 = new Vbo(s);
        std::pair<Vbo *, Vbo *> p1(v1, v2);
        std::pair<EntityValue *, std::pair<Vbo *, Vbo *>> p2(value, p1);
        _arrows.insert(p2);
        std::vector<Element_pos> v;
        std::pair<EntityValue *, std::vector<Element_pos>> p3(value, v);
        _links.insert(p3);

        connect(value, &EntityValue::changedColor,
                this, &Render_alternate::update_ev);
    }
    // store data in vbo
    const Element_pos offset_x = -_default_entity_x_translate;
    const Element_pos offset_y = -_ruler_y - _ruler_height;
    Element_pos x0, x1, y0, y1, l, alpha, cosa, sina;
    x0 = start_time + offset_x;
    x1 = end_time + offset_x;
    y0 = start_height + offset_y;
    y1 = end_height + offset_y;
    // size of the arrow
    l = sqrt(pow(x1 - x0, 2) + pow(y1 - y0, 2));
    // calculate angle beetween arrow and horizontal axe
    cosa = (x1 - x0) / l;
    sina = (y1 - y0) / l;
    alpha = acos(cosa);
    if (sina < 0)
        alpha *= -1;
    _current = _arrows[value].first;
    _current->add(x0, y0);
    _current->add(x1, y1);
    _current = _arrows[value].second;
    _current->add(x1, y1);
    /* Need this to draw triangles heads. Necessary for matrix operations*/
    std::vector<Element_pos> *v = &_links[value];
    v->push_back(x1);
    v->push_back(y1);
    v->push_back(alpha);

    (void)red;
    (void)green;
    (void)blue;
}

void Render_alternate::end_draw_arrows() {
    // coordinates that we use for draw the head all arrows when parameter arrows_shape is triangle, we will change the angle and scale of using the modelview matrix
    _draw_arrow = false;
    _arrows3.add(0, 0, 1, 1, 1);
    _arrows3.add(-1.2, -0.4, 1, 1, 1);
    _arrows3.add(-1.2, 0.4, 1, 1, 1);
    _arrows3.setNbVertex(3);
    // send datas to vbo
    std::map<EntityValue *, std::pair<Vbo *, Vbo *>>::iterator it = _arrows.begin();
    while (it != _arrows.end()) {
        it->second.first->config(_glsl);
        it->second.second->config(_glsl);
        ++it;
    }
    //_arrows.config(_glsl);
    //_arrows2.config(_glsl);
    _arrows3.config(_glsl);
    checkGlError();
}

void Render_alternate::start_draw_events() {
    _draw_event = true;
}

void Render_alternate::draw_event(const Element_pos time, const Element_pos height, const Element_pos container_height, EntityValue *value) {
    // Not sure if value is meant to be nullptr
    // It is equal to nullptr when the parsing is cancelled or an error occurred while parsing
    if (nullptr == value) {
        return;
    }

    if (_events.count(value) == 0) {
        Shader *s = new_shader(_shader_pool, _glsl, _r, _g, _b, false);
        s->charger();
        Vbo *v1 = new Vbo(s);
        Vbo *v2 = new Vbo(s);
        std::pair<Vbo *, Vbo *> p1(v1, v2);
        std::pair<EntityValue *, std::pair<Vbo *, Vbo *>> p2(value, p1);
        _events.insert(p2);

        connect(value, &EntityValue::changedColor,
                this, &Render_alternate::update_ev);
    }
    const Element_pos offset_x = -_default_entity_x_translate;
    const Element_pos offset_y = -_ruler_y - _ruler_height;
    _current = _events[value].first;
    _current->add(time + offset_x, height + offset_y);
    _current = _events[value].second;
    _current->add(time + offset_x, height + offset_y);
    _current->add(time + offset_x, height + offset_y + container_height / 2);
}

void Render_alternate::end_draw_events() {
    _draw_event = false;
    std::map<EntityValue *, std::pair<Vbo *, Vbo *>>::iterator it = _events.begin();
    while (it != _events.end()) {
        it->second.first->config(_glsl);
        it->second.second->config(_glsl);
        ++it;
    }
    _draw_event = false;
    checkGlError();
}

void Render_alternate::start_draw_counter() {
    _draw_counter = true;
    /* create shader for counters : all vertex are white */
    Shader *s = new_shader(_shader_pool, _glsl, 1, 1, 1, false);
    s->charger();
    _counters.set_shader(s);
}

void Render_alternate::draw_counter(const Element_pos, const Element_pos) {
}

void Render_alternate::end_draw_counter() {
    _draw_counter = false;
    _counters.config(_glsl);
    checkGlError();
}

void Render_alternate::start_ruler() {
    _draw_ruler = true;
}

void Render_alternate::end_ruler() {
    checkGlError();
    _draw_ruler = false;
}

void Render_alternate::end_draw() {
    checkGlError();
}

void Render_alternate::draw_stored_texts() {
}

void Render_alternate::draw_stored_arrows() {
}

void Render_alternate::draw_stored_circles() {
}

void Render_alternate::release() {
}

void Render_alternate::draw_text_value(long int id, double text, double y) {
    if (y != -1) {
        Variable_text_ buf;
        buf.y = y;
        buf.value = text;
        // printf("adding %f at %f for %p\n", text, y, (void*)id);
        _variable_texts[id] = buf;
    }
    else {
        // it's an update
        Variable_text_ buf = _variable_texts[id];
        buf.value = text;
        // printf("updating %f at %f for %p\n", text, y, (void*)id);
        _variable_texts[id] = buf;
    }
}

void Render_alternate::show_minimap() {
}

/*!
 * \brief draws the vertical helper line
 */

void Render_alternate::draw_vertical_line() {
    glm::mat4 mvp;
    if (vertical_line == 0)
        return;
    _time_line.add(trace_to_render_x(vertical_line), 0.0, 1.0, 0.0, 0.0);
    _time_line.add(trace_to_render_x(vertical_line), Info::Render::height, 1.0, 0.0, 0.0);
    _time_line.config(_glsl);
    _time_line.lock();
    mvp = _projection * _modelview;
    checkGlError();
    glUniformMatrix4fv(glGetUniformLocation(_shader->getProgramID(), "MVP"), 1, GL_FALSE, glm::value_ptr(mvp));
    glDrawArrays(GL_LINES, 0, 2);
    _time_line.unlock();
    checkGlError();
}

/*!
 * \brief slot connected to the simple click event
 */
void Render_alternate::update_vertical_line() {
    if (_mouse_pressed_inside_container)
        set_vertical_line(0);
    else {
        const Element_pos d = render_to_trace_x(screen_to_render_x(_mouse_x));
        set_vertical_line(d);
        DrawTrace buf;
        std::map<long int, double> var_map = _layout->get_trace()->update_text_variable_values(d);
        buf.draw_text_variable_values(_layout->get_render_area(), &var_map);
    }
    updateGL();
}

/*!
 * \brief set the vertical line offset
 * \param l the line offset.
 */
void Render_alternate::set_vertical_line(Element_pos new_coord) {
    if (new_coord == vertical_line)
        vertical_line = 0;
    else
        vertical_line = new_coord;
}

Element_pos Render_alternate::get_vertical_line() {
    return vertical_line;
}

void Render_alternate::clear_text() {
    _texts.clear();
    _variable_texts.clear();
}

/* Change the shader associated to entityType entity */
void Render_alternate::update_ev_single(EntityValue *ev,
                                        std::map<EntityValue *, Vbo *> *evmap,
                                        bool shaded) {
    std::map<EntityValue *, Vbo *>::iterator it;

    it = evmap->find(ev);
    if (it != evmap->end()) {
        Vbo *vbo = it->second;
        const Color *c = ev->get_used_color();

        // delete previous shader
        vbo->delete_shader();

        // create and load the new shader*/
        Shader *s = new_shader(_shader_pool, _glsl,
                               c->get_red(),
                               c->get_green(),
                               c->get_blue(),
                               shaded);
        s->charger();
        vbo->set_shader(s);
    }
}

void Render_alternate::update_ev_couple(EntityValue *ev,
                                        std::map<EntityValue *, std::pair<Vbo *, Vbo *>> *evmap,
                                        bool shaded) {
    std::map<EntityValue *, std::pair<Vbo *, Vbo *>>::iterator it;

    it = evmap->find(ev);
    if (it != evmap->end()) {
        Vbo *vbo1 = it->second.first;
        Vbo *vbo2 = it->second.second;
        const Color *c = ev->get_used_color();

        // >TODO
        // Because now we have a shader pool we might just prefer to update the shader of vbo1
        // in place. But i'm keeping this merge request short.

        // delete previous shader
        vbo1->delete_shader();

        // create and load the new shader*/
        Shader *s = new_shader(_shader_pool, _glsl,
                               c->get_red(),
                               c->get_green(),
                               c->get_blue(),
                               shaded);
        s->charger();
        vbo1->set_shader(s);
        vbo2->set_shader(s);
    }
}

void Render_alternate::update_ev(EntityValue *ev) {
    EntityClass_t ec = ev->get_class();

    switch (ec) {
    case _EntityClass_Variable:
        // NOT handled for now
        break;

    case _EntityClass_State:
        update_ev_single(ev, &_states, true);
        break;

    case _EntityClass_Event:
        update_ev_couple(ev, &_events, false);
        break;

    case _EntityClass_Link:
        update_ev_couple(ev, &_arrows, false);
    }
}

/* Change the shader associated to entityType entity */
void Render_alternate::change_color(const std::string &entity,
                                    Element_col r,
                                    Element_col g,
                                    Element_col b) {
    std::map<EntityValue *, Vbo *>::iterator it;
    it = _states.begin();
    while (it != _states.end()) {
        // assert(it->first); // TODO: check why busy state exists and uncomment this assert
        if (it->first && (it->first->get_name() == entity)) { // if we find a shader corresponding to parameter entity
            it->first->set_used_color(Color(r, g, b));
            // delete previous shader
            it->second->delete_shader();
            // create and load the new shader*/
            Shader *s = new_shader(_shader_pool, _glsl, r, g, b, true);
            s->charger();
            it->second->set_shader(s);
            break;
        }
        ++it;
    }
}

/*Same operating mode as change_color */
void Render_alternate::change_event_color(const std::string &event, Element_col r, Element_col g, Element_col b) {
    std::map<EntityValue *, std::pair<Vbo *, Vbo *>>::iterator it;
    it = _events.begin();
    while (it != _events.end()) {
        // assert(it->first); // TODO: check why busy state exists and uncomment this assert
        if (it->first && (it->first->get_name() == event)) {
            it->first->set_used_color(Color(r, g, b));
            it->second.first->delete_shader();
            Shader *s = new_shader(_shader_pool, _glsl, r, g, b, false);
            s->charger();
            it->second.first->set_shader(s);
            it->second.second->set_shader(s);
            break;
        }
        ++it;
    }
}
/*Same operating mode as change_color */
void Render_alternate::change_link_color(const std::string &link, Element_col r, Element_col g, Element_col b) {
    std::map<EntityValue *, std::pair<Vbo *, Vbo *>>::iterator it;
    it = _arrows.begin();
    while (it != _arrows.end()) {

        // assert(it->first); // TODO: check why busy state exists and uncomment this assert
        if (it->first && (it->first->get_name() == link)) {
            it->first->set_used_color(Color(r, g, b));
            it->second.first->delete_shader();
            Shader *s = new_shader(_shader_pool, _glsl, r, g, b, false);
            s->charger();
            it->second.first->set_shader(s);
            it->second.second->set_shader(s);
            break;
        }
        ++it;
    }
}

void Render_alternate::change_visible(const std::string &entity, bool visible) {
    std::map<EntityValue *, Vbo *>::iterator it;
    it = _states.begin();
    while (it != _states.end()) {
        // assert(it->first); // TODO: check why busy state exists and uncomment this assert
        if (it->first && (it->first->get_name() == entity)) {
            it->first->set_visible(visible);
            break;
        }
        ++it;
    }
}

void Render_alternate::change_event_visible(const std::string &event, bool visible) {
    std::map<EntityValue *, std::pair<Vbo *, Vbo *>>::iterator it;
    it = _events.begin();
    while (it != _events.end()) {
        // assert(it->first); // TODO: check why busy state exists and uncomment this assert
        if (it->first && (it->first->get_name() == event)) {
            it->first->set_visible(visible);
            break;
        }
        ++it;
    }
}

void Render_alternate::change_link_visible(const std::string &link, bool visible) {
    std::map<EntityValue *, std::pair<Vbo *, Vbo *>>::iterator it;
    it = _arrows.begin();
    while (it != _arrows.end()) {
        // assert(it->first); // TODO: check why busy state exists and uncomment this assert
        if (it->first && (it->first->get_name() == link)) {
            it->first->set_visible(visible);
            break;
        }
        ++it;
    }
}

/* reset colors to filecolors*/
void Render_alternate::reload_states() {
    std::map<EntityValue *, Vbo *>::iterator it = _states.begin();
    while (it != _states.end()) {
        if (it->first) {
            it->first->reload_file_color();
            it->first->set_visible(true);
            it->second->delete_shader();
            Shader *s = new_shader(_shader_pool, _glsl, it->first->get_used_color()->get_red(),
                                   it->first->get_used_color()->get_green(),
                                   it->first->get_used_color()->get_blue(),
                                   true);
            s->charger();
            it->second->set_shader(s);
        }
        ++it;
    }
}
void Render_alternate::reload_links() {
    std::map<EntityValue *, std::pair<Vbo *, Vbo *>>::iterator it = _arrows.begin();
    while (it != _arrows.end()) {
        if (it->first) {
            it->first->reload_file_color();
            it->first->set_visible(true);
            it->second.first->delete_shader();
            Shader *s = new_shader(_shader_pool, _glsl, it->first->get_used_color()->get_red(),
                                   it->first->get_used_color()->get_green(),
                                   it->first->get_used_color()->get_blue(),
                                   false);
            s->charger();
            it->second.first->set_shader(s);
            it->second.second->set_shader(s);
        }
        ++it;
    }
}

void Render_alternate::reload_events() {
    std::map<EntityValue *, std::pair<Vbo *, Vbo *>>::iterator it = _events.begin();
    while (it != _events.end()) {
        if (it->first) {
            it->first->reload_file_color();
            it->first->set_visible(true);
            it->second.first->delete_shader();
            Shader *s = new_shader(_shader_pool, _glsl, it->first->get_used_color()->get_red(),
                                   it->first->get_used_color()->get_green(),
                                   it->first->get_used_color()->get_blue(),
                                   false);
            s->charger();
            it->second.first->set_shader(s);
            it->second.second->set_shader(s);
        }
        ++it;
    }
}

#include "moc_Render_alternate.cpp"
