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


#ifndef DRAW_TRACE_HPP
#define DRAW_TRACE_HPP

#include <iostream>
#include <cassert>
#include <cmath>

/*
 * Theses constants can not be put as static const float because it is a template and there binary representation is not normed by the C++ langage.
 */

/*!
 * \def _DRAWING_CONTAINER_HEIGHT_DEFAULT
 * \brief The default height for basic containers.
 */
#define _DRAWING_CONTAINER_HEIGHT_DEFAULT 1.2f

/*!
 * \def _DRAWING_CONTAINER_WIDTH_DEFAULT
 * \brief The default width for containers.
 */
#define _DRAWING_CONTAINER_WIDTH_DEFAULT 2.5f

/*!
 * \def _DRAWING_CONTAINER_H_SPACE_DEFAULT
 * \brief The default horizontal space between containers.
 */
#define _DRAWING_CONTAINER_H_SPACE_DEFAULT 0.1f

/*!
 * \def _DRAWING_CONTAINER_V_SPACE_DEFAULT
 * \brief The default vertical space between containers.
 */
#define _DRAWING_CONTAINER_V_SPACE_DEFAULT 0.2f


/*!
 * \def _DRAWING_STATE_HEIGHT_DEFAULT
 * \brief The default height for states.
 */
#define _DRAWING_STATE_HEIGHT_DEFAULT 1.2f

/*!
 * \def _DRAWING_STATE_V_SPACE_DEFAULT
 * \brief The default vertical space between states.
 */
#define _DRAWING_STATE_V_SPACE_DEFAULT 0.2f


/*!
 * \class DrawTrace
 * \brief Browse the trace and call back T drawing methods
 */
/*template<class T>*/
class DrawTrace
{

protected:

    //  Interface_graphic* _window;

    /*!
     * \brief Containers with states or events
     */
    std::list<const Container *> _entity_containers;
    /*!
     * \brief Containers with links
     */
    std::list<const Container *> _link_containers;
    /*!
     * \brief Containers with variables
     */
    std::list<const Container *> _variable_containers;
    std::map<const Container *, Element_pos, std::less<const Container *> > _container_positions;
    std::map<const Container *, Element_pos, std::less<const Container *> > _container_sizes;
    std::map<const Variable*, Element_pos> _var_positions;

    // Geometrical informations about the trace shape.
    /*!
     * \brief  _container_width width of the container
     */
    Element_pos _container_width;
    /*!
     * \brief _container_height height of the container
     */
    Element_pos _container_height;
    /*!
     * \brief _container_h_space Horizontal space beetween 2 containers
     */
    Element_pos _container_h_space;
    /*!
     * \brief _container_v_space Vertical space between 2 containers
     */
    Element_pos _container_v_space;
    /*!
     * \brief _state_height Height of the state
     */
    Element_pos _state_height;
    /*!
     * \brief _state_v_space Vertical space between 2 states
     */
    Element_pos _state_v_space;

public:

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

    /*!
     * \brief The default constructor
     */
    DrawTrace() {
        _container_width   = _DRAWING_CONTAINER_WIDTH_DEFAULT;
        _container_height  = _DRAWING_CONTAINER_HEIGHT_DEFAULT;
        _container_h_space = _DRAWING_CONTAINER_H_SPACE_DEFAULT;
        _container_v_space = _DRAWING_CONTAINER_V_SPACE_DEFAULT;

        _state_height  = _DRAWING_STATE_HEIGHT_DEFAULT;
        _state_v_space = _DRAWING_STATE_V_SPACE_DEFAULT;
    }


    /*!
     * \brief The destructor
     */
    virtual ~DrawTrace() = default;

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

    /*!
     * \fn build(T* draw_object, Trace* trace)
     * \brief The trace building function that do not draw all the containers but only a part.
     * \param draw_object the kind of object which will be drawn (OpenGL, SVG...).
     * \param trace the trace data.
     */
    template<class T>
    void build(T* draw_object, Trace* trace) {
        Info::Container::y_max=0;//reset the vertical zoom when rebuilding
        double zoom = trace->get_filter();
        Interval * interval;// = trace->get_interval_constrained();
        //clear entities
        _container_positions.clear();
        _container_sizes.clear();
        _entity_containers.clear();
        _link_containers.clear();
        _variable_containers.clear();

        std::vector<const Container *> *container = trace->get_selected_container();
        if( (container == nullptr) ||
            container->empty() )
        {
            container = new std::vector<const Container *>();

            trace->set_filter(0);

            std::stack<Container *> containers;
            const Container::Vector *root_containers = trace->get_view_root_containers();
            if(root_containers->empty())root_containers= trace->get_root_containers();
            for (const auto &root_container : *root_containers){
                containers.push(root_container);
                container->push_back(root_container);
            }
            add(container, &containers);

        }
        //Adding the parent containers if not added yet
        else{
            bool ended = false;
            std::vector<const Container *> store;
            store.clear();
            while (!ended){
                ended = true;
                for(std::vector<const Container *>::const_iterator it = container->begin() ;
                    it != container->end();
                    it ++){

                    if( *it && (*it)->get_parent())
                        if(!is_in_set((*it)->get_parent(),container)){
                            store.push_back((*it)->get_parent());
                            ended = false;
                        }
                }//end for
                //TODO Warning the same container should be added more than 1 time
                for(std::vector<const Container *>::const_iterator it = store.begin() ;
                    it != store.end();
                    it ++){
                    if( !is_in_set(*it,container))
                        container->push_back(*it);
                }
                store.clear();
            }//end while
        }//end else


        interval = trace->get_interval_constrained();

        draw_object->start_draw();

        draw_object->start_draw_containers();
        browse_container_tree(draw_object, trace,container);
        draw_object->end_draw_containers();
        browse_entities(draw_object,zoom,interval,container);

        draw_object->end_draw();
    }

    /***********************************
     *
     * Browsing functions.
     *
     **********************************/
    /*
     * \fn browse_container_tree
     * \brief Function that browses the containers of the trace argument that
     * are in the set container and make them painted with a T object
     */
    template<class T>
    inline void browse_container_tree(T* draw_object, Trace* trace, std::vector<const Container *> *container){
        /*** Drawing containers ***/
        Element_pos y = 0.;

        const Container::Vector *root_containers = trace->get_view_root_containers();
        if (root_containers->empty())
            root_containers = trace->get_root_containers();

        if (root_containers->empty()) {
            *Message::get_instance() << QObject::tr("There is no container. The trace can not be drawn.").toStdString() << Message::endw;
        }
        else {
            for (const auto &root_container : *root_containers)
                // Use the fact that if a container is selected, then it implies that all his ancestors are
                if(is_in_set(root_container,container))
                    y += browse_container(draw_object, root_container, 0., y, _container_width, 0., container)+_container_v_space;
        }
    }/* end browse_container_tree */


    /*
     * \fn browse_container
     * \brief Recursive function that browse a container to draw it if it is in
     * the set of container with a T painting object in position, knowing the
     * current depth in the tree
     */
    template<class T>
    Element_pos browse_container(T* draw_object, const Container *container, Element_pos x, Element_pos y,
                                 Element_pos w, Element_pos h, std::vector<const Container *> *set_container) {
        //int size = 0;

        // Draw children:
        // we want to display only children meant to be displayed
        const Container::Vector *children = container->get_view_children();
        if( children->empty() )
            children = container->get_children();
        /*if(!children.empty())
         h = 0.;*/
        for (const auto &i : *children) {
            h += browse_container(draw_object, i, x + _container_width + _container_h_space, y + h, _container_width, 0., set_container) + _container_v_space;
        }

        // h -= _container_v_space;
        /*if(is_in_set(*i,set_container))
         size += browse_container(draw_object, (*i), position+size, depth+1,set_container);*/

        // Use one line for states and events
#ifdef USE_ITC
        if ( (container->get_states() != NULL && !container->get_states()->empty()) ||
             (container->get_events() != NULL && !container->get_events()->empty()) )
#else
            if ( !container->get_states()->empty() ||
                 !container->get_events()->empty() )
#endif
            {
                h += _container_height + _container_v_space;
                _entity_containers.push_back(container);
            }


        // Store the position to draw links
        _container_positions[container] = y;
        _container_sizes[container] = h;

        // Use one line for each variable
        if (container->get_variable_number() > 0) {
            _variable_containers.push_back(container);
            h += (_container_height + _container_v_space) * container->get_variable_number();
        }

        if (h < _container_height) // Minimum size
            h = _container_height;

        // Push containers with links to draw
        if (!container->get_links()->empty())
            _link_containers.push_back(container);

        h -= _container_v_space;

        // Draw this container
        draw_container(draw_object, x, y, w, h, container->get_Name().to_string());

        return h;
    }

    /*!
     * \brief Draw a container
     * \param draw_object Object that contains drawing methods to call
     * \param position Starting line of the container
     * \param size Height of the container in number of line
     * \param depth Depth of container in the tree
     * \param text Name of the container
     */
    template<class T>
    inline void draw_container(T* draw_object, Element_pos x, Element_pos y, Element_pos w, Element_pos h, const std::string &text) {
        //Element_pos x      = depth   *(_container_width +_container_h_space) + _container_h_space/2;
        //Element_pos y      = position*(_container_height+_container_v_space) + _container_v_space/2;
        //Element_pos height = size    *(_container_height+_container_v_space) - _container_v_space;
        draw_object->draw_container(x, y+_container_v_space/2, w, h, text);
        //draw_object->draw_container_text(x, y+h/2, text);
    }

    /*!
     * \brief Browse the states list and draw them
     */
    template<class T>
    inline void browse_entities(T* draw_object, double zoom, Interval* interval, std::vector<const Container *> *set_container) {
        //////////////////////////////////////////////////////////
        const Container *container;
        StateChange::Tree *state_tree;
        Event::Tree *event_tree;
        const Link::Vector *link_list;
        Link *link;
        const std::map<VariableType *, Variable *> *variable_map;
        Variable *var;
        const std::list<std::pair<Date, Double> > *variable_values;
        Element_pos position;
        std::map<std::string, Value *>::const_iterator field;
        Element_pos lvl_zoom;

        if(zoom>=0)
            lvl_zoom = zoom;
        else
            lvl_zoom = 0;

        draw_object->start_draw_states();
        draw_object->start_draw_events();
        for (std::list<const Container *>::const_iterator c = _entity_containers.begin();
             c != _entity_containers.end();
             c++) {
            if(is_in_set(*c,set_container)){
                container = *c;

                position = _container_positions[container] + _container_sizes[container] - (_container_height + _container_v_space);

                state_tree = container->get_states();
                event_tree = container->get_events();
#ifdef USE_ITC
                if(state_tree!=NULL){
#endif
                    if ( !state_tree->empty() ) {
                        //printf("drawing states for %s\n", container->get_Name().to_string().c_str());
                        // Browse states
                        DrawTree<T, StateChange>(draw_object, position, lvl_zoom,
                                                 _container_height, _container_v_space, _state_height, _state_v_space)
                            .draw_tree(state_tree, *interval);
                    }
#ifdef USE_ITC
                }
                if(event_tree!=NULL){
#endif
                    if ( !event_tree->empty()){
                        //printf("drawing events for %s\n", container->get_Name().to_string().c_str());
                        // Browse events
                        DrawTree<T, Event>(draw_object, position, lvl_zoom,
                                           _container_height, _container_v_space, _state_height, _state_v_space)
                            .draw_tree(event_tree, *interval);
                    }
#ifdef USE_ITC
                }
#endif
            }
        }/* end for (!_stack_states.empty()) */

        draw_object->end_draw_events();
        draw_object->end_draw_states();


        draw_object->start_draw_arrows();

        for (std::list<const Container *>::const_iterator c = _link_containers.begin();
             c != _link_containers.end();
             c++) {
            if(is_in_set(*c,set_container)){
                container = *c;

                // Browse links
                link_list = container->get_links();
                for (const auto &it : *link_list) {
                    link = it;
                    EntityValue *value = link->get_value();
                    assert(value);
                    const Color *color = value->get_used_color();
                    assert(color);
                    if(link_is_in_set(link,set_container,interval))
                        draw_link(draw_object, link, color, value);
                    // /*bool display=false;
                    //  color=NULL;
                    //  if(!Session::get_use_palette("link_types")) display = true;
                    //  else{
                    //  if (link->get_type()){
                    //  Palette *lt = Session::get_palette("link_types", Session::get_current_palette("link_types"));

                    //  color = lt->get_color(link->get_type()->get_name().to_string());
                    //  if(color) display=true;
                    //  */
                    // /*for(std::list<std::string>::const_iterator it2= link_types->get_->begin();
                    //  it2!= link_types->end();
                    //  it2++ ){
                    //  if(*it2 ==  link->get_type()->get_name().to_string()){
                    //  display=true;
                    //  break;

                    //  }
                    //  } */

                    // //}
                    // /*}
                    //  if(display &&
                    //  link_is_in_set(link,set_container,interval)){

                    //  // Search the color
                    //  if (color==NULL &&
                    //  link->get_type()&&
                    //  link->get_type()->get_extra_fields()!=NULL &&
                    //  !link->get_type()->get_extra_fields()->empty() &&
                    //  ((field = link->get_type()->get_extra_fields()->find(std::string("Color"))) != link->get_type()->get_extra_fields()->end()))
                    //  /* Call the object link drawing function with the link color */
                    // /*                  color = (const Color *)(*field).second;

                    //  draw_link(draw_object, link, color );
                    //  }
                    //  }/* end for */
                }//end for
            }/* end while (!_stack_states.empty()) */
        }

        draw_object->end_draw_arrows();
        draw_object->start_draw_counter();

        for (std::list<const Container *>::const_iterator c = _variable_containers.begin();
             c != _variable_containers.end();
             c++) {

            if(is_in_set(*c,set_container)){

                container = *c;
                position = _container_positions[container] + _container_sizes[container];

                // Browse variables
                variable_map = container->get_variables();

                for (const auto &i : *variable_map) {

                    var = i.second;
                    double min = var->get_min().get_value();
                    double max = var->get_max().get_value();
                    variable_values = var->get_values();

                    double first_value = 0.;
                    double second_value = 0.;

                    //draw_object->draw_text_value((long int)var,0.0, (position+1)*(_container_height+_container_v_space) - _container_v_space/2 - 0.5*_container_height);
                    draw_object->draw_text_value((long int)var, 0.0, position + 0.5 * _container_height + _container_v_space/2);
                    _var_positions[var]=position;
                    draw_variable_value(draw_object, 0.0, 0.0, position);
                    for (const auto & variable_value : *variable_values) {

                        /* Call the object state drawing function.
                         We pass the first value if correspond to the beginning */
                        first_value = variable_value.first.get_value();

                        // min and max can be equal if all values are equal (ie 0) or if there is only one value for the variable
                        if (fabs(max-min) < std::numeric_limits<double>::epsilon()) {
                            second_value = 0;
                        }
                        else {
                            second_value =(variable_value.second.get_value()-min)/(max-min);
                        }

                        if(!(first_value == 0. && second_value == 0.)) {
                            draw_variable_value(draw_object, first_value, second_value, position);
                        }
                    }
                    draw_variable_value(draw_object, 0.0, 0.0, position);
                    position += _container_height + _container_v_space; // One line was used
                }/* end for */
            }
        }//end for

        draw_object->end_draw_counter();
    }


    /*!
     * \brief Draw a point of a variable curve
     * \param draw_object Object that contains the drawing methods
     * \param time Time of the point
     * \param value Value of of the variable (between 0.0 and 1.0)
     * \param position Line where the variable is drawn
     */
    template<class T>
    inline void draw_variable_value(T *draw_object, double time, double value, Element_pos position) {
        Element_pos y = position + (_container_height+_container_v_space) -_container_v_space/2 -
            value*_container_height;
        draw_object->draw_counter(time, y);
    }

    /*!
     * \brief Draw a link
     * \param draw_object Object that contains the drawing methods
     * \param starttime Time of the start of the link
     * \param endtime Time of the end of the link
     * \param start Line of the start of the link
     * \param end Line of the end of the link
     * \param color The link color
     */
    template<class T>
    inline void draw_link(T *draw_object, const Link *link, const Color* color, EntityValue *value)
        {
            double starttime = link->get_start_time().get_value();
            double endtime   = link->get_end_time().get_value();
            double srcpos    = _container_positions[link->get_source()];
            double dstpos    = _container_positions[link->get_destination()];
            double srcsize   = _container_sizes[link->get_source()];
            double dstsize   = _container_sizes[link->get_destination()];

            Element_pos y1 = (srcpos + 0.5 * srcsize);//*(_container_height+_container_v_space);
            Element_pos y2 = (dstpos + 0.5 * dstsize);//*(_container_height+_container_v_space);

            if (color != nullptr)
                draw_object->draw_arrow(starttime, endtime, y1, y2, color->get_red(), color->get_green(), color->get_blue(), value);
            else/* Draw white link */
                draw_object->draw_arrow(starttime, endtime, y1, y2, 1.0, 1.0, 1.0, value);
        }

    /*
     * \brief Assuming someone has clicked in (x, y), display the description corresponding to the item clicked
     */
    void display_information(const Trace *trace, double x, double y, double d) {
        const Container *container = nullptr;
        const Container *ancestor = nullptr;
        const Link *link;
        const Event *event;
        const State *state;
        const Variable *variable;

        // find container needs to know the position of each container
        Element_pos yr = y;
        const Container::Vector *root_containers = trace->get_view_root_containers();
        if(root_containers->empty())root_containers= trace->get_root_containers();
        if (!root_containers->empty())
            for (const auto &root_container : *root_containers)
                if ((container = search_container_by_position(root_container, yr)))
                    break;

        // If the clic is out
        if (!container)
            return;

        // Calculate the container positions
        int position = 0;
        for (const auto &root_container : *root_containers)
            position += calc_container_positions(root_container, position);

        // First we browse to find a communication
        ancestor = container;
        if (!Info::Render::_no_arrows) {

            while (ancestor) {
                if ((link = get_link(ancestor, x, y, d))) {
                    *Message::get_instance() << "<center><strong>Link</strong></center>"
                                             << "<strong>Value:</strong> " << link->get_value()->get_name() << "<br />"
                                             << "<strong>Source:</strong> " << link->get_source()->get_Name().to_string() << "<br />"
                                             << "<strong>Destination:</strong> " << link->get_destination()->get_Name().to_string() << "<br />"
                                             << "<strong>Type:</strong> " << link->get_type()->get_name() << "<br />"
                                             << "<strong>Date:</strong> " << link->get_start_time().get_value() << " - " << link->get_end_time().get_value() << "<br />"
                                             << "<strong>Duration:</strong> " << link->get_duration() << "<br />";
                    print_extra_fields("Link", link->get_extra_fields());
                    print_extra_fields("Value", link->get_value()->get_extra_fields());
                    print_extra_fields("Type", link->get_type()->get_extra_fields());
                    *Message::get_instance() << Message::endsi;
                    return;
                }
                else
                    ancestor = ancestor->get_parent();
            }

            //if not found, the link may hav been assigned to the first root container.
            if ((link = get_link(root_containers->front(), x, y, d))) {
                *Message::get_instance() << "<center><strong>Link</strong></center>"
                                         << "<strong>Value:</strong> " << link->get_value()->get_name() << "<br />"
                                         << "<strong>Source:</strong> " << link->get_source()->get_Name().to_string() << "<br />"
                                         << "<strong>Destination:</strong> " << link->get_destination()->get_Name().to_string() << "<br />"
                                         << "<strong>Type:</strong> " << link->get_type()->get_name() << "<br />"
                                         << "<strong>Date:</strong> " << link->get_start_time().get_value() << " - " << link->get_end_time().get_value() << "<br />"
                                         << "<strong>Duration:</strong> " << link->get_duration() << "<br />";
                print_extra_fields("Link", link->get_extra_fields());
                print_extra_fields("Value", link->get_value()->get_extra_fields());
                print_extra_fields("Type", link->get_type()->get_extra_fields());
                *Message::get_instance() << Message::endsi;
                return;
            }
        }
        // Now browsing for the events of the container root
        // Verification if it is a clic on an event
#ifndef USE_ITC
        if ((!container->get_events()->empty() || !container->get_states()->empty()) && yr < _container_height+_container_v_space) {
#else
            if (((container->get_states()!=NULL && !container->get_states()->empty()) ||(container->get_events()!=NULL && !container->get_events()->empty())) && yr < _container_height+_container_v_space) {
#endif
                if (!Info::Render::_no_events)
                    if ((event = find_event(container, x, d))) {
                        *Message::get_instance() << "<center><strong>Event</strong></center>"
                                                 << "<strong>Value:</strong> "     << event->get_value()->get_name()                 << "<br />"
                                                 << "<strong>Container:</strong> " << event->get_container()->get_Name().to_string() << "<br />"
                                                 << "<strong>Type:</strong> "      << event->get_type()->get_name()                  << "<br />"
                                                 << "<strong>Date:</strong> "      << event->get_time().get_value()                  << "<br />";
                        print_extra_fields("Event", event->get_extra_fields());
                        print_extra_fields("Value", event->get_value()->get_extra_fields());
                        print_extra_fields("Type",  event->get_type()->get_extra_fields());
                        *Message::get_instance() << Message::endsi;
                        return;
                    }
                if ((state = find_state(container, x))) {
                    *Message::get_instance() << "<center><strong>State</strong></center>"
                                             << "<strong>Value:</strong> " << state->get_value()->get_name() << "<br />"
                                             << "<strong>Container:</strong> " << state->get_container()->get_Name().to_string() << "<br />"
                                             << "<strong>Type:</strong> " << state->get_type()->get_name() << "<br />"
                                             << "<strong>Date:</strong> " << state->get_start_time().get_value() << " - " << state->get_end_time().get_value() << "<br />"
                                             << "<strong>Duration:</strong> " << state->get_duration() << "<br />";
                    print_extra_fields("State", state->get_extra_fields());
                    print_extra_fields("Value", state->get_value()->get_extra_fields());
                    print_extra_fields("Type", state->get_type()->get_extra_fields());
                    *Message::get_instance() << Message::endsi;
                    return;
                }
            }
            else {
#ifdef USE_ITC
                if ((container->get_states()!=NULL && !container->get_states()->empty()) ||(container->get_events()!=NULL && !container->get_events()->empty()))
#else
                    if (!container->get_events()->empty() || !container->get_states()->empty())
#endif
                        yr -= _container_height+_container_v_space;
                const std::map<VariableType *, Variable *> *variable_map = container->get_variables();
                std::map<VariableType *, Variable *>::const_iterator i = variable_map->begin();
                while (yr > _container_height+_container_v_space) {
                    yr -= _container_height+_container_v_space;
                    i++;
                }
                if (i != variable_map->end()) {
                    variable = (*i).second;
                    *Message::get_instance() << "<center><strong>Variable</strong></center>"
                                             << "<strong>Container:</strong> " << variable->get_container()->get_Name().to_string() << "<br />"
                                             << "<strong>Type:</strong> " << variable->get_type()->get_name() << "<br />"
                                             << "<strong>Value:</strong> " << variable->get_value_at(x) << "<br />"
                                             << "<strong>Value:</strong> " << variable->get_value_at(x) << "<br />"
                                             << "<strong>Min:</strong> " << variable->get_min().get_value()  << "<br />"
                                             << "<strong>Max:</strong> " << variable->get_max().get_value() << "<br />";
                    print_extra_fields("Type", variable->get_type()->get_extra_fields());
                    *Message::get_instance() << Message::endsi;

                    return;
                }
            }

            *Message::get_instance() << Message::endsi;
            // Nothing has been found
            return;


        }

        void print_extra_fields(const std::string &name, const std::map<std::string, Value *> *extra_fields) {
            if (extra_fields!=nullptr &&!extra_fields->empty()) {
                *Message::get_instance() << "<em>" << name << " extra fields</em><br />";
                for (const auto & extra_field : *extra_fields)
                    *Message::get_instance() << "<strong>" << extra_field.first << ":</strong> " << extra_field.second->to_string() << "<br />";
            }
        }

        const Container *search_container_by_position(const Container *container, Element_pos &y) {
            const Container *result;
            // Search if the result is a descendant
            const Container::Vector *children = container->get_view_children();//we want to display only children meant to be displayed
            if(children->empty())children = container->get_children();
            for (const auto &i : *children) {
                if ((result = search_container_by_position(i, y)) != nullptr)
                    return result;
            }

            // Calculate the size of the container (without its children)
            int size = 0;
#ifdef USE_ITC
            if ((container->get_states()!=NULL && !container->get_states()->empty()) ||(container->get_events()!=NULL && !container->get_events()->empty()))
#else
                if (!container->get_states()->empty() || !container->get_events()->empty())
#endif
                    size++;
            if (container->get_variable_number() > 0)
                size += container->get_variable_number();


            if (children->empty() && size < 1) // Minimum size
                size = 1;

            // Test if the position is in this container
            if (y < size*(_container_height+_container_v_space))
                return container;
            else
                y -= size*(_container_height+_container_v_space);

            // The position is outside this container
            return nullptr;
        }

        /*
         * \fn browse_container(T* draw_object, const Container *container, int position, int depth)
         * \brief Recursive function that browse a container to draw it with a T painting object in position, knowing the current depth in the tree
         */
        int calc_container_positions(const Container *container, int position) {
            int size = 0;

            // Draw children
            const Container::Vector *children = container->get_view_children();//we want to display only children meant to be displayed
            if( children->empty() )
                children = container->get_children();
            for (const auto &i : *children) {
                size += calc_container_positions(i, position+size);
            }

            // Store the position to draw links
            _container_positions[container] = position; // First line after children
            _container_sizes[container]     = size; // First line after children

            // Use one line for states and events
#ifdef USE_ITC
            if ((container->get_states()!=NULL && !container->get_states()->empty()) ||(container->get_events()!=NULL && !container->get_events()->empty())){
#else
                if (!container->get_states()->empty() || !container->get_events()->empty()) {
#endif
                    size++;
                }

                // Use one line for each variable
                if (container->get_variable_number() > 0) {
                    size += container->get_variable_number();
                }

                return size;
            }

            /*!
             * \brief Tries to find a link passing by x and y in the container
             */
            const Link* get_link(const Container *container, Element_pos x, Element_pos y, Element_pos d) {
                const Link::Vector *link_list;
                Link *link;
                double a, b, c; // Equation: ax + by + c = 0
                double x1, x2, y1, y2;

                if(!container)
                    return nullptr;

                // Browse links
                link_list = container->get_links();
                for (const auto &it : *link_list) {

                    link = it;

                    double srcpos  = _container_positions[link->get_source()];
                    double dstpos  = _container_positions[link->get_destination()];
                    double srcsize = _container_sizes[link->get_source()];
                    double dstsize = _container_sizes[link->get_destination()];

                    x1 = link->get_start_time().get_value();
                    x2 = link->get_end_time().get_value();

                    y1 = (srcpos + 0.5 * srcsize)*(_container_height+_container_v_space);
                    y2 = (dstpos + 0.5 * dstsize)*(_container_height+_container_v_space);

                    if (((x1-d <= x && x <= x2+d) || (x2-d <= x && x <= x1+d)) &&
                        ((y1-d <= y && y <= y2+d) || (y2-d <= y && y <= y1+d))) { // Test the interval
                        a = y1 - y2;
                        b = x2 - x1;
                        c = -(a*x1 + b*y1);

                        double e = a*x + b*y + c;

                        if (e*e/(a*a + b*b) < d*d) // Test the distance
                            return link;
                    }
                }

                return nullptr;
            }

            /*
             * \brief Returns the event that occurs at the time x in the container
             */
            const Event *find_event(const Container *container, Element_pos x, Element_pos d) {
                if(!container || container->get_events()==nullptr)
                    return nullptr;

                Node<Event> *node = container->get_events()->get_root();

                while(node) {
                    Element_pos t = node->get_element()->get_time().get_value();
                    if (x < t) {
                        if (t - x < d)
                            return node->get_element();
                        node = node->get_left_child();
                    }
                    else {
                        if (x - t < d)
                            return node->get_element();
                        node = node->get_right_child();
                    }
                }

                return nullptr;
            }

            /*
             * \brief Returns the state at the time x
             */
            const State * find_state(const Container *container, Element_pos x) {
                if(!container || container->get_states()==nullptr)
                    return nullptr;

                Node<StateChange> *node = container->get_states()->get_root();

                while(node) {
                    Element_pos t = node->get_element()->get_time().get_value();
                    if (x < t) {
                        if (node->get_element()->get_left_state() && node->get_element()->get_left_state()->get_start_time().get_value() < x)
                            return node->get_element()->get_left_state();
                        node = node->get_left_child();
                    }
                    else {
                        if (node->get_element()->get_right_state() && x < node->get_element()->get_right_state()->get_end_time().get_value())
                            return node->get_element()->get_right_state();
                        node = node->get_right_child();
                    }
                }

                return nullptr;
            }


            bool link_is_in_set(Link * link, std::vector<const Container *> *set_container, Interval * interval ){
                const Container * src = link->get_source();
                const Container * dest = link->get_destination();
                for(unsigned int i = 0 ; i < set_container->size() ; i++){
                    if(src == (*set_container)[i]){
                        for(const auto & j : *set_container)
                            if(dest == j)
                                if(
                                    (interval->_left.get_value() < link->get_start_time().get_value() && interval->_right.get_value() > link->get_end_time().get_value()) ||
                                    (interval->_left.get_value() < link->get_end_time().get_value() && interval->_right.get_value() > link->get_start_time().get_value()))
                                    return true;
                    }
                }
                return false;
            }

            bool is_in_set(const Container * c, std::vector<const Container *> *set_container){
                if(!c || !set_container)
                    return false;
                for(const auto & i : *set_container)
                    if(c==i)
                        return true;
                return false;
            }



            void add( std::vector<const Container *> *container,std::stack<Container *> *containers){


                while (!containers->empty()) {
                    const Container * c = containers->top();
                    containers->pop();
                    const Container::Vector *children = c->get_view_children();//we want to display only children meant to be displayed
                    if(children->empty())children = c->get_children();
                    for (const auto &i : *children){
                        containers->push(i);
                        container->push_back(i);
                    }
                    add(container,containers);
                }
            }

            template<class T>
                void draw_text_variable_values(T* render, std::map<long int, double>* var_map,  double date){
                std::map<long int, double>::const_iterator it=var_map->begin();
                const std::map<long int, double>::const_iterator it_end=var_map->end();

                while(it!=it_end){
                    render->draw_text_value((*it).first,(*it).second, -1);
                    it++;
                }
                (void)date;
            }
        };
#endif
