/*
** 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.hpp
 */

#ifndef RENDER_HPP
#define RENDER_HPP

/*!
 * \brief Structure used to store container information.
 */
struct Container_{
    /*!
     * \brief Coordinates.
     */
    Element_pos x, y, w, h;
};

/*!
 * \brief Structure used to store container text.
 */
struct Container_text_{
    /*!
     * \brief Coordinates.
     */
    Element_pos x, y;
    /*!
     * \brief Text.
     */
    std::string value;
};



/*!
 * \brief This class is used to called opengl or svg primitive functions to draw trace elements.
 */
template<class T>
class Render: public Geometry
{
    private:

    /*!
     * This attribute store the instance of a drawing class. (OpenGL or SVG for example)
     */
    T* drawing_instance;

    /*!
     * \brief Used to draw counter.
     */
    bool _start_new_line;


    std::vector<Container_> _containers;
    std::vector<Container_text_> _container_texts;

    /*!
     * \brief The default constructor is in private scope to prevent Render instanciation without
     * provide a drawing instance.
     */
    Render(){
    }

public:

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

    /*!
     * \brief The default constructor
     */
    Render(T* instance){
        drawing_instance = instance;
    }

    /*!
     * \brief The destructor
     */
    virtual ~Render(){
    }


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

    /*!
     * \brief Proceeds with the initialization of the draw functions.
     */
    inline void start_draw(){
        /* clear vectors */
        _containers.clear();
        _container_texts.clear();
        
        drawing_instance->start_draw();
    }

    /*!
     * \brief Initialize container draws.
     */
    inline void start_draw_containers(){
    }

    /*!
     * \brief Draw a container according to the parameters
     * \param x the x position of the container
     * \param y the y position of the container
     * \param w the width of the container
     * \param h the height of the container
     */
    inline void draw_container(const Element_pos x, const Element_pos y, const Element_pos w, const Element_pos h){
        
        Container_ buf;

        buf.x = x;
        buf.y = y;
        buf.w = w;
        buf.h = h;

        _containers.push_back(buf);
     
        if ((x+w)>Info::Container::x_max)
            Info::Container::x_max = x+w;
        
        if ((y+h)>Info::Container::y_max)
            Info::Container::y_max = y+h;
        
        if (Info::Container::x_min > x)
            Info::Container::x_min = x;
        
        if (Info::Container::y_min > y)
            Info::Container::y_min = y;

        
        
#ifdef DEBUG_MODE_RENDER_AREA
        
        std::cerr << __FILE__ << " l." << __LINE__ << ":" << std::endl;
        std::cerr < "Container drawing:" << std::endl;
        std::cerr << "x: " << x << " y: " << y << " w: " << w << " h: " << h << " xmax-xmin: " << Info::Container::x_max << " - " << Info::Container::x_min << " ymax-ymin: " << Info::Container::y_max << " - " << Info::Container::y_min << std::endl;
        
#endif
        
    }

    /*!
     * \brief Draw the text of a container.
     * \param x the x position of the text.
     * \param y the y position of the text.
     * \param value the string value of the text.
     *
     * This function stores text in a list. This list will be display each time the render area need to be updated.
     */
    inline void draw_container_text(const Element_pos x, const Element_pos y, const std::string value){
      
        Container_text_ buf;
        buf.x     = x;
        buf.y     = y;
        buf.value = value;

        _container_texts.push_back(buf);
    }

    /*!
     * \brief Closes the container display list.
     */
    inline void end_draw_containers(){
        float j = 0.6f;

        //   std::cout << __FILE__ << " ~~ " << __LINE__ << " " << Info::Container::y_max << " " << Info::Container::y_min << std::endl;
        /* Before calling start_draw_container(), Info::Container::y_max should have a correct value */
        drawing_instance->start_draw_containers();

        for (unsigned int i=0 ; i<_containers.size() ; i++){
            drawing_instance->set_color(0, 0, j);
            drawing_instance->draw_quad(_containers[i].x * (Info::Render::width/Info::Container::x_max)*_x_scale_container_state,
                                        trace_to_render_y(_containers[i].y),// * _y_state_scale*(_render_height-_ruler_height)/Info::Container::y_max) - _y_state_translate + _ruler_y + _ruler_height,
                                        _z_container, 
                                        _containers[i].w * (Info::Render::width/Info::Container::x_max)*_x_scale_container_state,
                                        _containers[i].h * _y_state_scale*(Info::Render::height-_ruler_height)/Info::Container::y_max);
        }
        //  std::cout << __FILE__ << " ~ " << __LINE__ << " " << Info::Container::y_max << " " << Info::Container::y_min << std::endl;
  
        for (unsigned int i=0 ; i<_container_texts.size() ; i++){
            _container_texts[i].x *= (Info::Render::width/Info::Container::x_max)*_x_scale_container_state;
            _container_texts[i].y = trace_to_render_y(_container_texts[i].y);// + _render_height-_ruler_height)*coeff_trace_render_y();

            drawing_instance->draw_text(_container_texts[i].x, _container_texts[i].y, _z_container+1, _container_texts[i].value);
        }
        
        drawing_instance->end_draw_containers();

    }

    static int bb;

    /*!
     * \brief Creates and opens the display list for state draws.
     */
    inline void start_draw_states(){
        drawing_instance->start_draw_states();
    }



    /*!
     * \brief Draw a state of the trace.
     * \param start the beginning time of the state.
     * \param end the ending time of the state.
     * \param base vertical position of the state.
     * \param height the state height.
     * \param r the red color rate of the state.
     * \param g the green color rate of the state.
     * \param b the blue color rate of the state.
     */

    inline void draw_state(const Element_pos start, const Element_pos end, const Element_pos base, const Element_pos height, const Element_col r, const Element_col g, const Element_col b) {
       

        drawing_instance->set_color(r, g, b);
        
        drawing_instance->draw_quad(trace_to_render_x(start - Info::Render::_x_min_visible),
                                    trace_to_render_y(base),
                                    _z_state, 
                                    (end-start)*coeff_trace_render_x(), 
                                    height*coeff_trace_render_y());
    }

    /*!
     * \brief Closes the state display list.
     */
    inline void end_draw_states(){
        drawing_instance->end_draw_states();
    }

    /*!
     * \brief Open the arrow display list.
     */
    inline void start_draw_arrows(){
        drawing_instance->start_draw_arrows();
    }

    /*!
     * \brief Draw an arrow.
     * \param start_time the beginning time of the arrow.
     * \param end_time the ending time of the arrow.
     * \param start_height vertical position of the begining time of the arrow.
     * \param end_height vertical position of the ending time of the arrow.
     *
     * This function stores all the information of the arrow to display it each time the render area need to be updated.
     */
    inline void draw_arrow(Element_pos start_time, Element_pos end_time, Element_pos start_height, Element_pos end_height, const Element_col r, const Element_col g, const Element_col b){
 
        /* For SVG */
        const Element_pos triangle_size = 2.0;
        const Element_pos coeff         = 180.0f/M_PI;
        Element_pos angle;
        
        drawing_instance->set_color(r, g, b);

        start_time   = trace_to_render_x(start_time - Info::Render::_x_min_visible);
        end_time     = trace_to_render_x(end_time - Info::Render::_x_min_visible);
        start_height = trace_to_render_y(start_height);
        end_height   = trace_to_render_y(end_height);
        
        if (start_time != end_time)
            angle = atan2((end_height - start_height), (end_time - start_time))*coeff;/* arc tangent */
        else
            angle = 90;
       
        drawing_instance->draw_triangle(end_time, end_height, triangle_size, angle);
         
        drawing_instance->draw_line(start_time, start_height, 
                  end_time,   end_height,   _z_arrow);

        /* For OpenGL */
        drawing_instance->draw_arrow(start_time, end_time, start_height, end_height, r, g, b);

        drawing_instance->set_color(1, 1, 1);/* init */
    }

    /*!
     * \brief Closes the arrow display list.
     */
    inline void end_draw_arrows(){
        drawing_instance->end_draw_arrows();
    }
    
    inline void start_draw_events(){
        drawing_instance->start_draw_events();
    }

    /*!
     * \brief Draw an event.
     * \param time time when the event occurs.
     * \param height vertical position of the event.
     * \param container_height information to draw event. It corresponds to the container height when they are drawn horizontally.
     *
     * This function stores all the information of the event to display it each time the render area need to be updated.
     */
    inline void draw_event(Element_pos time, Element_pos height, Element_pos container_height, const Element_col r, const Element_col g, const Element_col b){

      /* For SVG */

        Element_pos radius;/* the circle radius */

        drawing_instance->set_color(r, g, b);
        
        time             = trace_to_render_x(time - Info::Render::_x_min_visible);
        height           = trace_to_render_y(height);
        container_height = container_height*coeff_trace_render_y();
        radius           = .5f;    
        
        drawing_instance->draw_circle(time, height, _z_event, radius);
        drawing_instance->draw_line(time, height,
                                    time, height+container_height, _z_event);

        /* For OpenGL */
        drawing_instance->draw_event(time, height, container_height);
    }
    
    inline void end_draw_events(){
        drawing_instance->end_draw_events();
    }

    /*!
     * \brief Creates and opens the display list for counter draws.
     */
    inline void start_draw_counter(){
        drawing_instance->set_color(1.0, 1.0, 1.0);
        _counter_last_x=0.0;
        _counter_last_y=0.0;
        drawing_instance->start_draw_counter();
        _start_new_line = true;
    }



    /*!
     * \brief Draw a text with the value of a variable
     * \param text text to draw.
     * \param y y position of the point.
     *
     */
    inline void draw_text_value(long int id, double text, double y){
        drawing_instance->draw_text_value(id,text,y);
    }
    /*!
     * \brief Draw a point of the counter.
     * \param x x position of the point.
     * \param y y position of the point.
     *
     * Each time counter is increased, this function is called with the coordinates of the new point.
     */
    inline void draw_counter(const Element_pos x, const Element_pos y){

        if (_start_new_line){/* Start a new line */
            
            _counter_last_x = x;
            _counter_last_y = y;
            _start_new_line = false;
            
        }else{/* line is already started */
            
            if (x <= 0.0){/* convention: the line is over */
                
                //                drawing_instance->draw_line( _counter_last_x,  _counter_last_y,
                //                           _state_x_max,       _counter_last_y,  _z_counter);
              
                drawing_instance->draw_line( trace_to_render_x(_counter_last_x - Info::Render::_x_min_visible), trace_to_render_y(_counter_last_y),
                                             trace_to_render_x(Info::Entity::x_max),    trace_to_render_y(_counter_last_y),  _z_counter);
              
  
                _start_new_line = true;
            }else{/* add new points to the line */
                
                /* Each time, the method draw the line from the previous point to 
                   the current point (x, y). This last is stored for the next line drawing. */
                //                drawing_instance->draw_line( _counter_last_x,  _counter_last_y,
                //                           x,                               _counter_last_y, _z_counter);
                // drawing_instance->draw_line( x,  _counter_last_y,
                //                           x,  y,                              _z_counter);
               
                drawing_instance->draw_line(  trace_to_render_x(_counter_last_x - Info::Render::_x_min_visible),  trace_to_render_y(_counter_last_y),
                                              trace_to_render_x(x - Info::Render::_x_min_visible),                trace_to_render_y(_counter_last_y), _z_counter);
                drawing_instance->draw_line(  trace_to_render_x(x - Info::Render::_x_min_visible),                trace_to_render_y(_counter_last_y),
                                              trace_to_render_x(x - Info::Render::_x_min_visible),                trace_to_render_y(y),               _z_counter);
               
                _counter_last_x = x;
                _counter_last_y = y;
            }
        }
    }


    /*!
     * \brief Closes the counter display list.
     */
    inline void end_draw_counter(){

        if (!_start_new_line) {/* If a line was previously opened */
            /* Draw the last line */
            //  drawing_instance->draw_line( _counter_last_x,  _counter_last_y,
            //                             _state_x_max,       _counter_last_y, _z_counter);
            drawing_instance->draw_line( trace_to_render_x(_counter_last_x - Info::Render::_x_min_visible), trace_to_render_y(_counter_last_y),
                                         trace_to_render_x(Info::Entity::x_max),    trace_to_render_y(_counter_last_y), _z_counter);
        }
        drawing_instance->end_draw_counter();
    }
    
    /*!
     * \brief Do nothing (it is present for compatibility of the Render class).
     */
    inline void end_draw(){
        std::ostringstream buf_txt;
        Element_pos graduation_diff;
        //Element_pos coeff_prefix;
        const Element_pos offset_x = 0;
        const Element_pos offset_y = 0;
        
  
        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);


        /* Now, draw ruler */

        drawing_instance->start_ruler();
        {
            /* Since SVG has no depth, first draw the quads then the graduation */
                         
            /* Draw quads */
            drawing_instance->set_color(0.0, 0.0, 1.0);

            drawing_instance->draw_quad(trace_to_render_x(0),
                                        3,
                                        _z_ruler_under,
                                        trace_to_render_x(Info::Render::_x_max_visible) - trace_to_render_x(Info::Render::_x_min_visible),
                                        5.5);

            drawing_instance->set_color(0.0, 0.0, 0.0);

            drawing_instance->draw_quad(trace_to_render_x(0), 
                                        0, 
                                        _z_ruler_over, 
                                        trace_to_render_x(Info::Render::_x_max_visible) - trace_to_render_x(Info::Render::_x_min_visible), 
                                        3);

            /* Draw graduations */
            drawing_instance->set_color(1, 1, 1);

          
            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){
                    drawing_instance->draw_line( trace_to_render_x(j - Info::Render::_x_min_visible) + offset_x, offset_y+3,
                                                 trace_to_render_x(j - Info::Render::_x_min_visible) + offset_x, 5+offset_y, _z_ruler);
                }
                
                drawing_instance->draw_line( trace_to_render_x(i - Info::Render::_x_min_visible) + offset_x, offset_y+3,
                                             trace_to_render_x(i - Info::Render::_x_min_visible) + offset_x, 7+offset_y, _z_ruler);
            }
        }

        drawing_instance->end_ruler();

        /* clear vectors */
        _containers.clear();
        _container_texts.clear();
        drawing_instance->end_draw();

    }
};



#endif
