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

#ifndef GANTTDIAGRAM_HPP
#define GANTTDIAGRAM_HPP
#include <iostream>
#include "Render.hpp"

class Trace;
/*!
 * \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, size;
    /*!
     * \brief Text.
     */
    std::string value;
};

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

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

    /*!
     * \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.
     */
    GanttDiagram() = default;

public:

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

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

    /*!
     * \brief The destructor
     */
    ~GanttDiagram() override = default;

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

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

        _render->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,
                               const std::string &value)
        {
            Container_text_ buft;
            Container_      buf;

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

            _containers.push_back(buf);

            buft.x     = x + w/5;
            buft.y     = y + h/2;
            buft.value = value;

            _container_texts.push_back(buft);

            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)
        {
            (void)x;
            (void)y;
            (void)value;
            return;
        }

    /*!
     * \brief Closes the container display list.
     */
    inline void end_draw_containers()
        {
            static float j = 0.6f;
            float coeffx =  (Info::Render::width                /Info::Container::x_max) * _x_scale_container_state;
            float coeffy = ((Info::Render::height-_ruler_height)/Info::Container::y_max) * _y_state_scale;

            /* Before calling start_draw_container(), Info::Container::y_max should have a correct value */
            _render->start_draw_containers();

            for (const auto & _container : _containers){
                _render->set_color(0, 0, j);
                _render->draw_quad(_container.x * coeffx,
                                   trace_to_render_y(_container.y),
                                   _z_container,
                                   _container.w * coeffx,
                                   _container.h * coeffy);
            }

            for (unsigned int i=0 ; i<_container_texts.size() ; i++){
                _container_texts[i].x *= coeffx;
                _container_texts[i].y  = trace_to_render_y(_container_texts[i].y);

                _render->draw_text(_container_texts[i].x,
                                   _container_texts[i].y,
                                   _containers[i].w * coeffx,
                                   _container_texts[i].value);
            }

            _render->end_draw_containers();
        }

    /*!
     * \brief Creates and opens the display list for state draws.
     */
    inline void start_draw_states()
        {
            _render->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,
                           EntityValue * value)
        {
            _render->set_color(r, g, b);
            _render->draw_state(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(),
                                value);
        }

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

    /*!
     * \brief Open the arrow display list.
     */
    inline void start_draw_arrows()
        {
            _render->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,
                           EntityValue* value)
        {
            _render->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);

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

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

    inline void start_draw_events()
        {
            _render->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,
                           EntityValue* value)
        {
            /* For SVG */
            _render->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();

            /* For OpenGL */
            _render->draw_event(time, height, container_height, value);
        }

    inline void end_draw_events()
        {
            _render->end_draw_events();
        }

    /*!
     * \brief Creates and opens the display list for counter draws.
     */
    inline void start_draw_counter()
        {
            _render->set_color(1.0, 1.0, 1.0);
            _counter_last_x=0.0;
            _counter_last_y=0.0;
            _render->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)
        {
            _render->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 */
                    _render->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. */
                    _render->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 );
                    _render->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 */
                _render->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 );
            }
            _render->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 coeff_prefix;

            /* Now, draw ruler */
            /*Rendering is done in paintGL because we need to recalculate graduations each time paintGL is called, not just when launching the program*/
            _render->start_ruler();
            {
                //_render->call_ruler();
            }
            _render->end_ruler();

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

        }
};

#endif
