/**
 *
 * @file src/render/GanttDiagram.hpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Johnny Jazeix
 * @author Mathieu Faverge
 * @author Thibault Soucarre
 * @author Luigi Cannarozzo
 *
 * @date 2024-07-17
 */
/*!
 *\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;
        _start_new_line = false;
    }

    /*!
     * \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.size = x * y;
        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
