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

#include <sstream>
#include <cmath>
/* -- */
// Needed on archlinux
#ifndef WIN32
#include <GL/glu.h>
#endif
/* -- */
#include <QFile> // For loading the wait image
#include <QDate>
#include <QTimer>
/* -- */
#include "interface/resource.hpp"
#include "interface/Interface.hpp"
/* -- */
#include "common/common.hpp"
#include "common/Info.hpp"
#include "common/Message.hpp"
/* -- */
#include "render/Ruler.hpp"
#include "render/Render_opengl.hpp"
#include "render/Render.hpp"
/* -- */
#include "core/Core.hpp"

using namespace std;


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


    const double angle       = M_PI/2.0;
    const double delta_angle = 2.0*M_PI/NB_STEPS;
    const double radius = .5;


template <int N> void Render_opengl::cos_table_builder(double table[]){
        cos_table_builder<N-1>(table);
        table[N-1]=cos(angle+delta_angle*(N-1))*radius;
}

template <> void Render_opengl::cos_table_builder<0>(double table[]){
        table[0]=cos(angle*radius);
}

template <int N> void Render_opengl::sin_table_builder(double table[]){
        sin_table_builder<N-1>(table);
        table[N-1]=sin(angle+delta_angle*(N-1))*(radius);
}

template <> void Render_opengl::sin_table_builder<0>(double table[]){
        table[0]=sin(angle)*(radius);
}

static double cos_table[NB_STEPS];
static double sin_table[NB_STEPS];



const int Render_opengl::DRAWING_TIMER_DEFAULT = 10;

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

Render_opengl::Render_opengl(Core* core, QWidget *parent, const QGLFormat& format)
    : Hook_event(this, core, parent, format){



    _arrows.clear();
    _circles.clear();
    _texts.clear();
    _variable_texts.clear();
    /* init the wait animation */
    _wait_list      = 0;
    _wait_angle     = 0.0f; /* begin with 0 rad angle */
    _wait_timer     = NULL;
    _wait_spf       = DRAWING_TIMER_DEFAULT;/* DRAWING_TIMER_DEFAULT milliseconds per frame */
    /* _minimap_widget = NULL;
       _minimap_image  = NULL;*/

#ifdef SPINNING_LOGO
    _wait_timer = new QTimer(this);
    connect(_wait_timer, SIGNAL(timeout()), _render_instance, SLOT(updateGL()));
    _wait_timer->start(_wait_spf);
#endif // SPINNING_LOGO
        vertical_line=0;
    vertical_line=0;
    setAutoFillBackground(false);


cos_table_builder<NB_STEPS>(cos_table);
sin_table_builder<NB_STEPS>(sin_table);

}


void Render_opengl::release(){
  /* Release the Rabbit and ruler lists */
    if (glIsList(_wait_list) == GL_TRUE)
        glDeleteLists(_wait_list, 1);

    /* Release timer (for Rabbit rotate) */
    if (_wait_timer != NULL){
        delete _wait_timer;
        _wait_timer=NULL;
    }

    //    release_minimap();
}

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

void  Render_opengl::initializeGL() {
    glClearColor(0.5f, 0.5f, 0.55f, 1.0f);

    glEnable(GL_DEPTH_TEST);

    glClearStencil(0);

    _wait_list = draw_wait();
    _core->waitGUIInit->quit();
}

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

    /* update informations about widget size */
    Info::Screen::width  = width;
    Info::Screen::height = height;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();



    if(DRAWING_STATE_WAITING == _state){// A wait is drawn
        glOrtho(-50, 50, -50, 50, 0, 1000);
    }else if (DRAWING_STATE_DRAWING == _state){// A trace is drawn
        glOrtho(0, Info::Render::width, 0, Info::Render::height, 0, -1000);
    }else{
        message << tr("Undefined value for the drawing state attribute - Render area").toStdString() << Message::ende;
    }


    glMatrixMode(GL_MODELVIEW);
}


void  Render_opengl::paintGL(){
    resizeGL(Render_opengl::QGLWidget::width(), Render_opengl::QGLWidget::height());
    Render_opengl::QGLWidget::setFocus(Qt::ActiveWindowFocusReason);/* give the focus to the render area for mouse and keyboard events */
    makeCurrent();
  //static int arrows_drawn=0;
    glClearDepth(1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glLoadIdentity();


    if(DRAWING_STATE_WAITING == _state){/* A wait is drawn */

        /* turn around y axis */
        _wait_angle+=0.1f;
        if (_wait_angle>=360) _wait_angle=0.0f;

        glPushMatrix();

        glScalef(15, 15, 0);

#ifdef SPINNING_LOGO
        glRotatef(-_wait_angle,0, 1, 0);
#endif // SPINNING_LOGO

        glCallList(_wait_list);

        glPopMatrix();


    }else if (DRAWING_STATE_DRAWING == _state){/* A trace is drawn */
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glPushMatrix();
        {
            glTranslated(0.0, Info::Render::height, 100.0);
            glRotated(180.0, 1.0, 0.0, 0.0);

            glPushMatrix();

            {
                glTranslatef(0.0f, _ruler_y + _ruler_height -_y_state_translate,  _z_container);
                glScalef(_x_scale_container_state/0.20, _y_state_scale, 1.0);

                if (glIsList(_list_containers) == GL_FALSE)
                    *Message::get_instance() << tr("ERROR LIST not exist for containers.").toStdString() << Message::ende;
                else
                    glCallList(_list_containers);

            }
            glPopMatrix();

            glPushMatrix();
            {
                glTranslated(_default_entity_x_translate - _x_state_translate, _ruler_y + _ruler_height - _y_state_translate,  _z_state);
                glScalef(_x_state_scale, _y_state_scale, 1);

                if (glIsList(_list_states) == GL_FALSE)
                    *Message::get_instance() << tr("ERROR LIST not exist for states.").toStdString() << Message::ende;
                else{
                    glCallList(_list_states);
                    glCallList(_list_counters);
                  // if( (false == Info::Render::_no_arrows) && arrows_drawn!=0 ) glCallList(_list_arrows);
                }
            }
            glPopMatrix();

        _minimap.update( ( ( (_x_state_translate - _default_entity_x_translate)/ (Info::Render::width*_x_state_scale) ) +  (_default_entity_x_translate/Info::Render::width) )*_minimap.width(),
                         ( (_y_state_translate - _ruler_height) / (Info::Render::height*_y_state_scale) + (_ruler_height/Info::Render::height) )*_minimap.height(),
                         _minimap.width()/_x_state_scale,
                         _minimap.height()/_y_state_scale
                         );
        //initialize the minimap if it hasn't been done yet
        if(!_minimap.is_initialized()){
            QImage buf = grabFrameBuffer(true);
            _minimap.init(buf);
         }
         if(false == Info::Render::_no_arrows){
           draw_stored_arrows();
        }



            if (false == Info::Render::_no_events)/* display events */
                draw_stored_circles();



            /* Untranslate ruler */
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            call_ruler();
            glDisable(GL_BLEND);

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

        }
        glPopMatrix();

        glPushMatrix();
        {
            glTranslated(0.0,  0.0, 100.0);/* not accurate */

            if (_mouse_pressed && (Info::Render::_key_ctrl == false) && !_mouse_pressed_inside_container){


                glTranslated(0.0, 0.0, _z_container_under);

                Element_pos old_x, old_y, new_x, new_y;



                old_x = screen_to_render_x(_mouse_x);
                old_y = Info::Render::height - screen_to_render_y(_mouse_y);

                new_x = screen_to_render_x(_new_mouse_x);
                new_y = Info::Render::height -  screen_to_render_y(_new_mouse_y);

#ifdef DEBUG_MODE_RENDER_OPENGL

                cerr << __FILE__ << " l." << __LINE__ << ":" << endl;
                cerr << "Selection rectangle position: (" << old_x << ", " << old_y << ") - (" << new_x << ", " << new_y << ")" << endl;
#endif


                glEnable(GL_BLEND);
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

                /* Square for selection */
                glBegin(GL_QUADS);
                {
                    glColor4f(0.9f, 1.0f, 0.9f, _selection_rectangle_alpha);glVertex2d(old_x, old_y);
                    glColor4f(0.9f, 0.9f, 0.9f, _selection_rectangle_alpha);glVertex2d(old_x, new_y);
                    glColor4f(0.9f, 0.9f, 0.9f, _selection_rectangle_alpha);glVertex2d(new_x, new_y);
                    glColor4f(0.9f, 0.9f, 0.9f, _selection_rectangle_alpha);glVertex2d(new_x, old_y);
                }
                glEnd();

                glDisable(GL_BLEND);

            }/* end  if (true==_mouse_pressed) */
        }
        glPopMatrix();



    }else{
        message << tr("Undefined value for the drawing state attribute - Render area").toStdString() << Message::ende;
    }

    glFlush();


    /* Check the errors */
    GLenum glerror;
    glerror = glGetError();
    if(glerror != GL_NO_ERROR)
        message << tr("Render area : the following OpengGL error occured: ").toStdString() << (char *)gluErrorString(glerror) << Message::endw;




    if(DRAWING_STATE_WAITING == _state)/* A wait is drawn, do not take car about the ruler drawing */
        return;


    QFont arial_font = QFont("Arial", 10);
    qglColor(Qt::white);

    const QFontMetrics metric(arial_font);

    //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("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(_texts[i].value.c_str(), Qt::ElideRight, _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++;}
        }

 // printf("skipped %d displayed %d\n", skipped, displayed);


    /* Draw ruler text */
         std::ostringstream buf_txt;
        Element_pos graduation_diff;
        Element_pos coeff_prefix;
        graduation_diff = Ruler::get_graduation_diff(Info::Render::_x_min_visible, Info::Render::_x_max_visible);
        coeff_prefix    = Ruler::get_coeff_for_common_prefix(Info::Render::_x_min_visible, Info::Render::_x_max_visible);

        arial_font.setPointSize(14);

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

        renderText (render_to_screen_x(trace_to_render_x(Info::Render::_x_min_visible)),
                    render_to_screen_y(3),
                    buf_txt.str().c_str(),
                    arial_font);

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

        renderText (render_to_screen_x(trace_to_render_x(Info::Render::_x_max_visible))-130,
                    render_to_screen_y(3),
                    buf_txt.str().c_str(),
                    arial_font);

        buf_txt.str("");
        buf_txt << Ruler::get_common_part_string(Info::Render::_x_min_visible, coeff_prefix) << "--";

        renderText (render_to_screen_x(
                                       trace_to_render_x(
                                                         (Info::Render::_x_min_visible +
                                                          Info::Render::_x_max_visible) / 2)),
                    render_to_screen_y(3),
                    buf_txt.str().c_str(),
                    arial_font);

        arial_font.setPointSize(10);

        for (Element_pos i = Info::Render::_x_min_visible ;
             i < Info::Render::_x_max_visible ;
             i+=graduation_diff){

            buf_txt.str("");/* flush the buffer */
            buf_txt << Ruler::get_variable_part(i, coeff_prefix, 2);

            renderText (render_to_screen_x(trace_to_render_x(i)+1),
                        render_to_screen_y(8),
                        buf_txt.str().c_str(),
                        arial_font);

        }

         //  update_minimap();





       //initialize the minimap if it hasn't been done yet
       if(!_minimap.is_initialized()){
           QImage buf = grabFrameBuffer(true);
           _minimap.init(buf);
       }

}





/***********************************
 *
 *
 *
 * Building functions.
 *
 *
 *
 **********************************/



bool Render_opengl::build(){

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

    /* disable some OpenGL features to enhance the rendering */
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);

    replace_scale(1);/* for states scaling */
    init_geometry();/* Initialize geometry */

#ifdef SPINNING_LOGO
    if (_wait_timer != NULL){
        _wait_timer->stop();
        delete(_wait_timer);
        _wait_timer = NULL;
    }
#endif // SPINNING_LOGO


    if (NULL == _render_instance)
        return true;


   // updateGL();
 //   QImage buf = grabFrameBuffer(true);
   // _minimap.init(buf);
    //updateGL();


   // QImage buf = grabFrameBuffer(true);
   // _minimap.init(buf);
   // updateGL();

    return true;//_render_instance->display_build();
}



bool Render_opengl::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_TEXTURE_2D);

    glEnable(GL_BLEND);/* enable blending for the alpha color */
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glColor3f(1.0, 1.0, 1.0);/* init color to white */


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

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

    _wait_angle=0.0f;/* begin with 0 rad angle */


    /* clear lists to store container texts */
    _text_pos.clear();
    _text_value.clear();

    _arrows.clear();
    _circles.clear();
    _texts.clear();

    /* empty the selection stack */
   // while(false == _previous_selection.empty())
     //   _previous_selection.pop();

    if (NULL == _render_instance)
        return true;

    /* Now, timer is set */
#ifdef SPINNING_LOGO
    if (_wait_timer == NULL){
        _wait_angle = 0.0f;

        _wait_timer = new QTimer(this);
        connect(_wait_timer, SIGNAL(timeout()), _render_instance, SLOT(updateGL()));
        _wait_timer->start(_wait_spf);
    }
#endif // SPINNING_LOGO
    _minimap.release();

    init_geometry();/* Initialize geometry */

    return true;//_render_instance->display_unbuild();
}





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



GLuint Render_opengl::draw_wait() {
    GLuint object;
    GLuint texture;

    object = glGenLists(1);/* create the list */

    if (object == 0)
        message << tr("Error when creating list").toStdString() << Message::endw;


    glGenTextures(1, &texture);/* create the texture and link it with the list previously created */

    QFile texture_file(QString(":/img/img/logo") +  QDate::currentDate().toString("MMdd") + QString(".png"));

    if (true == texture_file.exists())/* The texture exists */
        texture = bindTexture(QPixmap(texture_file.fileName()), GL_TEXTURE_2D);
    else/* use the default picture */
        texture = bindTexture(QPixmap(":/img/img/logo.png"), GL_TEXTURE_2D);

    glNewList(object, GL_COMPILE);/* open the list */
    {
        glBindTexture(GL_TEXTURE_2D, texture);/* load texture for drawing */

        glBegin(GL_QUADS);/* draw a square */
        {
            glTexCoord2d(0,0); glVertex2f(-1, -1);
            glTexCoord2d(1,0); glVertex2f(1, -1);
            glTexCoord2d(1,1); glVertex2f(1, 1);
            glTexCoord2d(0,1); glVertex2f(-1, 1);
        }
        glEnd();


    }
    glEndList();/* close the list */

    /* apply some parameters on the texture */
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glEnable(GL_TEXTURE_2D);

    glEnable(GL_BLEND);/* enable blending for the alpha color */
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


    /* Now, timer is set */
    if (_wait_timer == NULL){
        _wait_angle = 0.0f;
        /*
        _wait_timer = new QTimer(this);
        connect(_wait_timer, SIGNAL(timeout()), _render_instance, SLOT(updateGL()));
        _wait_timer->start(_wait_spf);*/
    }

    return object;
}


void Render_opengl::call_ruler(){
    Element_pos graduation_diff;
    //Element_pos coeff_prefix;
    const Element_pos offset_x = _default_entity_x_translate;
    const Element_pos offset_y = _ruler_y + _ruler_height+3.5;

    update_visible_interval_value();

    graduation_diff = Ruler::get_graduation_diff(Info::Render::_x_min_visible, Info::Render::_x_max_visible);
   // coeff_prefix    = Ruler::get_coeff_for_common_prefix(Info::Render::_x_min_visible, Info::Render::_x_max_visible);

    set_color(1.0, 1.0, 1.0);

    for (Element_pos i = Info::Render::_x_min_visible ;
         i < Info::Render::_x_max_visible ;
         i+=graduation_diff){

        const Element_pos grad_div_by_5 = graduation_diff/5;
        for (Element_pos j = (i+grad_div_by_5) ; j<(i+graduation_diff) ; j+= grad_div_by_5){

            if (j>=Info::Render::_x_max_visible)
                break;

            draw_line( trace_to_render_x(j) + offset_x, offset_y  ,
                       trace_to_render_x(j) + offset_x, 2+offset_y, _z_ruler);
        }

        if (i>=Info::Render::_x_max_visible)
            break;

        draw_line( trace_to_render_x(i) + offset_x, offset_y-1  ,
                   trace_to_render_x(i) + offset_x, 4+offset_y, _z_ruler);
    }

    glBegin(GL_QUADS);
    {
        glColor4f(0.0f, 0.0f, 1.0f, 0.8f);
        glVertex3d(trace_to_render_x(Info::Render::_x_min_visible) , 3, _z_ruler_under );
        glVertex3d(trace_to_render_x(Info::Render::_x_min_visible) , 8, _z_ruler_under );
        glVertex3d(trace_to_render_x(Info::Render::_x_max_visible) , 8, _z_ruler_under );
        glVertex3d(trace_to_render_x(Info::Render::_x_max_visible) , 3, _z_ruler_under );
    }
    glEnd();

    glBegin(GL_QUADS);
    {
        glColor4f(0.0, 0.0, 0.0, 1.0);
        glVertex3d(trace_to_render_x(Info::Render::_x_min_visible) , 0, _z_ruler_over );
        glVertex3d(trace_to_render_x(Info::Render::_x_min_visible) , 3, _z_ruler_over );
        glVertex3d(trace_to_render_x(Info::Render::_x_max_visible) , 3, _z_ruler_over );
        glVertex3d(trace_to_render_x(Info::Render::_x_max_visible) , 0, _z_ruler_over );
    }
    glEnd();

    return;
}



void Render_opengl::set_color(float r, float g, float b){
    _red = r;
    _green = g;
    _blue = b;
}

void Render_opengl::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_opengl::draw_text(const Element_pos x, const Element_pos y, const Element_pos, const std::string s){
    if (_draw_ruler) return;/* do not draw text for ruler */
    if (_draw_state) message << "Fatal error! Cannot put text in state lists!" << Message::ende;

        Container_text_ buf;

        buf.x     = x;
        buf.y     = render_to_trace_y(y);/* Cancel previous transformation. */
        buf.value = s;

        _texts.push_back(buf);
}


void Render_opengl::draw_quad(Element_pos x, Element_pos y, Element_pos /*z*/, Element_pos w, Element_pos h){

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

    if (_draw_ruler) return;/* do not draw quad for ruler */

    offset_x = 0;

    if (!_draw_container)
        offset_x = -_default_entity_x_translate;


    glBegin(GL_QUADS);
    {
        glColor3f(_red, _green, _blue);

        glVertex2d(x + offset_x    , y + offset_y);
        glVertex2d(x + offset_x    , y + h + offset_y);


        if (Info::Render::_shaded_states){/* State color is shaded */
                glColor3f(_red*0.6, _green*0.6, _blue*0.6);

                glVertex2d(x + w + offset_x, y + h + offset_y);
                glVertex2d(x + w + offset_x, y + offset_y);
        }else{/* State color must be uniform */
                glColor3f(_red, _green, _blue);

                /* Multiply by 0.98 to allow distinction between two States with the same color. Temporary solution
                   before using a border.
                */
                glVertex2d(x + w*0.98 + offset_x, y + h + offset_y);
                glVertex2d(x + w*0.98 + offset_x, y + offset_y);
        }
    }
    glEnd();
}

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

void Render_opengl::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_ruler || _draw_arrow || _draw_event) return;/* do not draw line for ruler or for arrow or for event */

    if (_draw_state) message << "Fatal error! Cannot put line in state lists!" << Message::ende;


    glBegin(GL_LINES);
    {
        glColor3f(_red, _green, _blue);
        glVertex3d(x1 + offset_x, y1 + offset_y, z);
        glVertex3d(x2 + offset_x, y2 + offset_y, z);
    }
    glEnd();
}

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


void Render_opengl::start_draw(){

    _draw_container = false;
    _draw_state     = false;
    _draw_ruler     = false;
    _draw_arrow     = false;
    _draw_event     = false;

    init_geometry();/* Initialize geometry */

}

void Render_opengl::start_draw_containers(){
    _list_containers = glGenLists(1);/* create the list */
    if (_list_containers == 0) {
        *Message::get_instance() << tr("Error when creating containers list.").toStdString() << Message::endw;
    }

    _draw_container = true;
    glNewList(_list_containers, GL_COMPILE);/* open the list */
}

void Render_opengl::draw_container(const Element_pos , const Element_pos , const Element_pos , const Element_pos ){

}

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

void Render_opengl::end_draw_containers(){
    _draw_container = false;
    glEndList();/* close the list */
}

void Render_opengl::start_draw_states(){
    _list_states = glGenLists(1);/* create the list */
    if (_list_states == 0) {
        *Message::get_instance() << tr("Error when creating states list.").toStdString() << Message::endw;
    }

    _draw_state = true;
    glNewList(_list_states, GL_COMPILE);/* open the list */
}

void Render_opengl::draw_state(const Element_pos , const Element_pos , const Element_pos , const Element_pos , const Element_col , const Element_col , const Element_col ){
}

void Render_opengl::end_draw_states(){
    glEndList();/* close the states list */
    _draw_state = false;
}

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

void Render_opengl::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){

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

    buf.start_time   = start_time   + offset_x;
    buf.end_time     = end_time     + offset_x;
    buf.start_height = start_height + offset_y;
    buf.end_height   = end_height   + offset_y;
    buf.red          = red;
    buf.green        = green;
    buf.blue         = blue;

    _arrows.push_back(buf);
        //printf("sizeof eventlist : %d %d %d %d\n",_circles.size(), _circles.size()*sizeof(Event_),_arrows.size(),_arrows.size()*sizeof(Arrow_));
}

void Render_opengl::end_draw_arrows(){
    _draw_arrow = false;
}

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

void Render_opengl::draw_event(const Element_pos time, const Element_pos height,  const Element_pos container_height){
    Event_ buf;
    const Element_pos offset_x = -_default_entity_x_translate;
    const Element_pos offset_y = -_ruler_y - _ruler_height;

    buf.time             = time   + offset_x;
    buf.height           = height + offset_y;
    buf.container_height = -container_height/2   ;
    buf.red              = _red;
    buf.green            = _green;
    buf.blue             = _blue;

    _circles.push_back(buf);
        //printf("sizeof eventlist : %d %d\n", _circles.size()*sizeof(Event_),_arrows.size()*sizeof(Arrow_));
}


void Render_opengl::end_draw_events(){
    _draw_event = false;
   // printf("sizeof eventlist : %d %d %d %d\n",_circles.size(), _circles.size()*sizeof(Event_),_arrows.size(),_arrows.size()*sizeof(Arrow_));
}

void Render_opengl::start_draw_counter(){
    _list_counters = glGenLists(1);/* create the list */
    if (_list_counters == 0) {
        *Message::get_instance() << tr("Error when creating counters list").toStdString() << Message::ende;
    }

    glNewList(_list_counters, GL_COMPILE);/* open the list */
}

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

void Render_opengl::end_draw_counter(){
    glEndList();/* close the list */
}

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

void Render_opengl::end_ruler(){
    _draw_ruler = false;
}

void Render_opengl::end_draw(){
//printf("sizeof eventlist : %d %d %d %d %d %d\n",_circles.size(), _circles.size()*sizeof(Event_),_arrows.size(),_arrows.size()*sizeof(Arrow_), _texts.size(), _texts.size()*sizeof(Container_text_));
}


void Render_opengl::draw_stored_texts(){
}

void Render_opengl::draw_stored_arrows(){

    /* Only draw triangle. Lines are already in a display list */
    const Element_pos coeff = 180.0/M_PI;
    Element_pos angle;
    Element_pos start_time, end_time, start_height, end_height;
    Element_pos scaled_start_time, scaled_end_time, scaled_start_height, scaled_end_height;



    const unsigned int arrow_size = _arrows.size();
    for (unsigned int i=0 ; i<arrow_size ; i++){

        start_time   = _arrows[i].start_time;// + _x_state_scale*_x_state_translate;
        end_time     = _arrows[i].end_time;// + _x_state_scale*_x_state_translate;
        start_height = _arrows[i].start_height;
        end_height   = _arrows[i].end_height;

        glColor3f(_arrows[i].red, _arrows[i].green, _arrows[i].blue);

        scaled_start_time   = start_time   * _x_state_scale;
        scaled_end_time     = end_time     * _x_state_scale;
        scaled_start_height = start_height * _y_state_scale;
        scaled_end_height   = end_height   * _y_state_scale;

        glPushMatrix();
        {

            glTranslated(_default_entity_x_translate + _x_state_scale*end_time - _x_state_translate,
                         _ruler_y + _ruler_height + _y_state_scale*end_height - _y_state_translate,
                         _z_arrow);/*

                         glTranslated( _x_state_scale*end_time,
                        _y_state_scale*end_height ,
                         0);*/

            if (start_time != end_time){
                angle = atan2((scaled_end_height - scaled_start_height), (scaled_end_time - scaled_start_time))*coeff;/* arc tangent */
                glRotated(angle, 0, 0, 1);
            }
            else
                glRotated(90, 0, 0, 1);/* vertical alignment */

            glBegin(GL_TRIANGLES);
            {
                // glColor3d(_red, _green, _blue);
                glVertex2d(0.0, 0.0);
                glVertex2d(-1.2, -0.4);
                glVertex2d(-1.2, 0.4);
            }
            glEnd();

        }
        glPopMatrix();
}

    for (unsigned int i=0 ; i<arrow_size ; i++){

        start_time   = _arrows[i].start_time;// + _x_state_scale*_x_state_translate;
        end_time     = _arrows[i].end_time;// + _x_state_scale*_x_state_translate;
        start_height = _arrows[i].start_height;
        end_height   = _arrows[i].end_height;

        glColor3f(_arrows[i].red, _arrows[i].green, _arrows[i].blue);

        glPushMatrix();
        {

           glTranslated(_default_entity_x_translate - _x_state_translate,
                         _ruler_y + _ruler_height - _y_state_translate,
                         _z_arrow);
            glScaled(_x_state_scale, _y_state_scale, 1.0);

            glBegin(GL_LINES);
            {
                glVertex2d(start_time, start_height);
                glVertex2d(end_time  , end_height  );
            }
            glEnd();
        }
        glPopMatrix();
    }
}


void Render_opengl::draw_stored_circles(){


   //tables to store values used for circles drawings, with run-time data (zoom, size of the window), using pre-generated tables
   double cos_table2[NB_STEPS];
   double sin_table2[NB_STEPS];

    for(int j = 0 ; j < NB_STEPS ; j ++){
                     cos_table2[j]=  cos_table[j]/_x_state_scale;
                     sin_table2[j]= (sin_table[j]*Info::Screen::width)/(_y_state_scale*Info::Screen::height);
    }

    const unsigned int size = _circles.size();

    for (unsigned int i=0 ; i<size ; i++){

        glPushMatrix();
        {

            glColor3f(1.0 - _circles[i].red, 1.0 - _circles[i].green, 1.0 - _circles[i].blue);

            glTranslated(_default_entity_x_translate - _x_state_translate,
                         _ruler_y + _ruler_height - _y_state_translate,
                         _z_arrow);
            glScalef(_x_state_scale, _y_state_scale, 1.0);



             // Draw the outer circle

             glBegin(GL_POLYGON);
             {
                 for(int j = 0 ; j < NB_STEPS ; j ++){
                     glVertex2d(_circles[i].time + cos_table2[j],
                                _circles[i].height + sin_table2[j]);
                 }
             }
             glEnd();
               }
        glPopMatrix();
    }

    for (unsigned int i=0 ; i<size ; i++){

        glPushMatrix();
        {


             glColor3f(_circles[i].red, _circles[i].green, _circles[i].blue);
                             glTranslated(_default_entity_x_translate - _x_state_translate,
                         _ruler_y + _ruler_height - _y_state_translate,
                         _z_arrow);
             glScalef(_x_state_scale, _y_state_scale, 1.0);

             // Draw the sub-circle
             glBegin(GL_POLYGON);
             {
                 for(int j = 0 ; j < NB_STEPS ; j ++){
                     glVertex3d(_circles[i].time + cos_table2[j]/1.2 ,
                                _circles[i].height + sin_table2[j]/1.2 ,
                                0.1);
                 }
             }
             glEnd();
                     }
        glPopMatrix();
    }

    //draw the line in another loop for better performance
    for (unsigned int i=0 ; i<size ; i++){

        glPushMatrix();
        {

            glColor3f(_circles[i].red, _circles[i].green, _circles[i].blue);
                           glTranslated(_default_entity_x_translate - _x_state_translate,
                         _ruler_y + _ruler_height - _y_state_translate,
                         _z_arrow);
            glScalef(_x_state_scale, _y_state_scale, 1.0);

             glLineWidth( 3.0 );
            //Draw the line
             glBegin(GL_LINES);
            {
                glVertex2d(_circles[i].time, _circles[i].height-_circles[i].container_height);
                glVertex2d(_circles[i].time, _circles[i].height);

            }
            glEnd();
        }
        glPopMatrix();
    }

    glLineWidth( 1.0 );
}

void Render_opengl::update_vertical_line(){


        if (_mouse_pressed_inside_container)
            set_vertical_line(0);
        else
            set_vertical_line(render_to_trace_x( screen_to_render_x(_mouse_x)));
        updateGL();
}

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

void Render_opengl::set_vertical_line(Element_pos new_coord){
    if(new_coord==vertical_line)vertical_line=0;
    else vertical_line=new_coord;

}


void Render_opengl::draw_vertical_line(){

    if(vertical_line==0)return;
    glPushMatrix();
        {
                glLineWidth( 1.0 );
                /* Draw the line */
                glBegin(GL_LINES);
                {
                        glColor3d(1.0,0.0,0.0);
                        glVertex2d( trace_to_render_x(vertical_line),0);
                        glVertex2d( trace_to_render_x(vertical_line),Info::Render::height);
                }
                glEnd();
        }
    glPopMatrix();
    QFont arial_font = QFont("Arial", 10);
    const QFontMetrics metric(arial_font);
    QString t =QString().setNum(vertical_line);
    // draw time
    renderText ( render_to_screen_x(trace_to_render_x(vertical_line))-metric.size(Qt::TextSingleLine,t).width() -5,
              metric.height()-1,
               t,
               arial_font);


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

    }
}

// void Render_opengl::create_minimap(const int width, const int height){

//     _minimap_widget = new QLabel;
//     _minimap_image = new QImage;

//     if (!_minimap_widget || !_minimap_image){
//         message << "Cannot allocate memory for minimap" << Message::endw;
//         return;
//     }

//     paintGL();

//     *_minimap_image = grabFrameBuffer(true);

//     //    _default_entity_x_translate = 20; /* TODO: use constant value define in config file */
//     /*_ruler_height               = 8.5;
//       _ruler_y                    = 0.0;*/

//     //  *_minimap_image = _minimap_image->scaled(QSize(width, height), Qt::IgnoreAspectRatio);
//     _minimap_widget->resize(width, height);
//     _minimap_widget->hide();/* Do not display it */
//     //  update_minimap();
// }

// void Render_opengl::update_minimap(){
//     QPixmap buf;
//     static int i = 0;

//     if (!_minimap_image)
//         return;

//     if (!_minimap_widget)
//         return;

//     buf = QPixmap::fromImage(*_minimap_image);

//     if (buf.isNull())
//         return;

//     QPainter painter;
//     QPen pen;
//     painter.begin(&buf);

//     pen.setColor(Qt::red);
//     pen.setWidth(4);

//     painter.setPen(pen);
//     painter.setBrush(QBrush(QColor::fromRgbF(1, 1, 0, 0.5)));

//     painter.drawRect ( ( ( (_x_state_translate - _default_entity_x_translate)/ (Info::Render::width*_x_state_scale) ) +  (_default_entity_x_translate/Info::Render::width) )*_minimap_image->width(),
//                       //render_to_screen_x(trace_to_render_x(Info::Render::_x_min_visible))*200/Info::Screen::width,
//                        ( (_y_state_translate - _ruler_height) / (Info::Render::height*_y_state_scale) + (_ruler_height/Info::Render::height))*_minimap_image->height(),
//                        _minimap_image->width()/_x_state_scale,//render_to_screen_x(trace_to_render_x(Info::Render::_x_max_visible - Info::Render::_x_min_visible)),
//                        _minimap_image->height()/_y_state_scale);
//     painter.end();


//     _minimap_widget->setPixmap(buf);
//     _minimap_widget->setScaledContents(true);

//     if (!_minimap_widget->pixmap())
//         message << tr("No Pixmap set for the minimap").toStdString() << Message::endw;
// }


void Render_opengl::show_minimap(){

    //if (state)
        _minimap.show();
        /*    else
              _minimap.hide();*/
    // if (!_minimap_widget) return;

    // if (state)
    //     _minimap_widget->show();
    // else
    //     _minimap_widget->hide();
}


// void Render_opengl::release_minimap(){

//     if (_minimap_image){
//         delete _minimap_image;
//         _minimap_image = NULL;
//     }

//     if (_minimap_widget)
//         _minimap_widget->close();

//     /* Qt will release automatically _minimap_widget */
// }


void Render_opengl::clear_arrow (){
    _arrows.clear();
}

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