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

#ifndef DRAW_HDIAGRAM_HPP
#define DRAW_HDIAGRAM_HPP

/*!
 * \class DrawHDiagram
 * \brief Browse the stats and call back T drawing methods
 */
template<class T>
class DrawHDiagram : public DrawStats<T> {
protected:
    double _startx;
    double _starty;
    double _graph_width;
    double _draw_width;
    double _draw_height;
    double _legend_width;
    double _legend_height;
    std::set<const EntityValue*> _setstates;

public:
    /*
     * \brief The default constructor
     */
    DrawHDiagram() {
        _startx = _START_HISTOGRAM_X_DEFAULT;
        _starty = 100.0f;

        this->_size_for_one_container = _HEIGHT_FOR_ONE_CONTAINER_DEFAULT;
        this->_percentage_height_default = (this->_size_for_one_container - _START_HISTOGRAM_Y_DEFAULT) / 100.;

        this->_pos_x_container_name   = _POS_X_CONTAINER_NAME;
        this->_pos_y_container_name   = _POS_Y_CONTAINER_NAME;

        /* Size for rectangles in the legend */
        this->_width_for_rect_legend  = 20.;
        this->_height_for_rect_legend = 15.;
    }


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


    /*!
     * \fn build(T* draw_object, std::vector<Container *> containers_to_print)
     * \brief The trace building function.
     * \param draw_object the kind of object which will be drawn (OpenGL, SVG...).
     * \param containers_to_print the container's data.
     */
    void build(T* draw_object, std::vector<Container *> containers_to_print) override {
        int nbcont;

        draw_object->clear();

        _draw_height               = draw_object->height();
        _draw_width                = draw_object->width();
        _legend_height             = draw_object->height();
        _legend_width              = draw_object->width();
        _graph_width               = _draw_width - _startx - 30.;
        this->_containers_to_print = containers_to_print;
        nbcont = this->_containers_to_print.size();

        draw_object->start_draw();
        draw_object->set_total_height( (nbcont+1) * _WIDTH_HISTOGRAM_DEFAULT + 10. );
        draw_object->set_total_width( draw_object->width() );

        draw_object->draw_axis(_startx,
                               _starty - (nbcont-1)*_WIDTH_HISTOGRAM_DEFAULT,
                               _graph_width + 10.,
                               (nbcont+1)*_WIDTH_HISTOGRAM_DEFAULT);

        draw_object->draw_horizontal_line(_startx,
                                          _starty + _WIDTH_HISTOGRAM_DEFAULT,
                                          _graph_width + 10.);

        for (int i=25; i < 101; i+=25)
        {
            // Draw Up Scale
            draw_object->draw_vertical_line(_startx + i*_graph_width/100.,
                                            _starty + _WIDTH_HISTOGRAM_DEFAULT - 2.5,
                                            5.);
            draw_object->draw_text(_startx + i*_graph_width/100 - 10.,
                                   this->_size_for_one_container-(_starty + _WIDTH_HISTOGRAM_DEFAULT + 15.),
                                   QString::number(i, 'd', 1).toStdString()+"%");
            // Draw Down Scale
            draw_object->draw_vertical_line(_startx + i*_graph_width/100.,
                                            _starty - (nbcont-1)*_WIDTH_HISTOGRAM_DEFAULT - 2.5,
                                            5.);
            draw_object->draw_text(_startx + i*_graph_width/100 - 10.,
                                   this->_size_for_one_container-(_starty - (nbcont-1)*_WIDTH_HISTOGRAM_DEFAULT - 15.),
                                   QString::number(i, 'd', 1).toStdString()+"%");
        }

        for(int i = 0 ; i < nbcont ; i ++) {
            draw_diagram(draw_object, i);
        }
        draw_legend(draw_object,
                    _POS_X_LEGEND_DEFAULT + _startx,
                    _POS_Y_LEGEND_DEFAULT + _starty - (nbcont+3)*_WIDTH_HISTOGRAM_DEFAULT);
        this->end_draw(draw_object);
    }

    /*!
     * \fn draw_diagram(T* draw_object, const int container_id)
     * \brief Print the i-th diagram
     * \param draw_object the kind of object which will be drawn (OpenGL, SVG...).
     * \param container_id the id of the container.
     */
    void draw_diagram(T* draw_object, const int container_id) {
        Statistic                           *stat_temp;
        std::map<const EntityValue*, stats*> temp_states;
        double                               duration;
        int                                  pos_x, pos_y;
        std::string                          ctname;

        stat_temp   = new Statistic();
        this->_containers_to_print[container_id]->fill_stat(stat_temp, Interval(this->_start_time, this->_end_time));
        temp_states = stat_temp->get_states();
        duration = this->_end_time - this->_start_time;

        // Printing of the trace
        //std::cout << "nb states " << temp_states.size() << std::endl;

        // Depending on the kind of diagram...
        pos_x = _startx;
        pos_y = _starty - container_id * _WIDTH_HISTOGRAM_DEFAULT;

        // Draw the container name
        ctname = this->_containers_to_print[container_id]->get_Name().to_string();
        draw_object->draw_text(_POS_X_CONTAINER_NAME,
                               this->_size_for_one_container-(pos_y + _WIDTH_HISTOGRAM_DEFAULT / 2. - 5.),
                               ctname);

        // Draw the stats
        for (auto & temp_state : temp_states)
        {
            double      length  = temp_state.second->_total_length;
            double      percent = length / duration ;
            double      width   = percent * _graph_width;
            double      red     = 0.7;
            double      green   = 0.7;
            double      blue    = 0.75;

            // We search for a color
            if(temp_state.first->get_extra_fields()->find(std::string("Color")) != temp_state.first->get_extra_fields()->end())
            {
                const Color *color = (const Color *)temp_state.first->get_extra_fields()->find(std::string("Color"))->second;
                red   = color->get_red();
                green = color->get_green();
                blue  = color->get_blue();
            }

            draw_object->draw_rect(pos_x, pos_y,
                                   width, _WIDTH_HISTOGRAM_DEFAULT,
                                   red, green, blue);

            if ( width > 30. )
                draw_object->draw_text(pos_x + width / 2 - 10.,
                                       this->_size_for_one_container-(pos_y + _WIDTH_HISTOGRAM_DEFAULT / 2. - 5.),
                                       QString::number(percent*100., 'f', 1).toStdString()+"%");

            pos_x += width;
            this->_setstates.insert(temp_state.first);

        }

        delete stat_temp;
    }

    /*!
     * \fn draw_legend(T* draw_object, int pos_x, int pos_y)
     * \brief Print the legend for the i-th element (eg the color of each stats)
     * \param draw_object the kind of object which will be drawn (OpenGL, SVG...).
     * \param pos_x the x position of the legend.
     * \param pos_y the y position of the legend.
     */
    void draw_legend(T* draw_object, int pos_x, int pos_y) {
        std::set<const EntityValue *>::const_iterator it;
        std::set<const EntityValue *>::const_iterator end;
        const double w = this->_width_for_rect_legend;
        const double h = this->_height_for_rect_legend;

        /* used to print legend on 3 rows */
        int decalage = 0;

        end = this->_setstates.end();
        for (it  = this->_setstates.begin();
             it != end;
             it ++, decalage ++) {
            std::string name = (*it)->get_name();

            draw_object->draw_text(pos_x+w+_POS_X_LEGEND_DEFAULT,
                                   this->_size_for_one_container-pos_y,
                                   name.substr(0, 10));

            if((*it)->get_extra_fields()->find(std::string("Color")) != (*it)->get_extra_fields()->end())
            {
                const Color *color = (const Color *)(*it)->get_extra_fields()->find(std::string("Color"))->second;
                draw_object->draw_rect(pos_x, pos_y, w, h, color->get_red(), color->get_green(), color->get_blue());
            }
            else
            {
                draw_object->draw_rect(pos_x, pos_y, w, h, 0.7f, 0.7f, 0.75f);
            }

            switch(decalage%3) {
            case 2:
                pos_x += 100;
                pos_y += 40;
                break;
            default:
                pos_y -= 20;
                break;
            }
        }

        this->_max_width = pos_x + w + _POS_X_LEGEND_DEFAULT;
        this->set_geometrical_informations_object(draw_object);

    }
};

#endif
