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

#ifndef DRAWTREE_HPP
#define DRAWTREE_HPP

/*#include "tree/BinaryTree.hpp"
#include "Event.hpp"

#include <iostream>
*/



/*!
 * \class DrawTree
 * \brief Class called to draw a binary tree of type E  using a D painting object
 */
class Interval;
template <typename E> class Node;
template <typename E> class BinaryTree;


template<class D, class E>
class DrawTree {
private:
    D *_draw_object;
    int _position;
    double _min_size;

    Element_pos _container_height;
    Element_pos _container_v_space;

    Element_pos _state_height;
    Element_pos _state_v_space;

public:
    /*!
     *\brief Default constructor
     */
    DrawTree(D *draw_object, int position, double min_size,
             Element_pos container_height, Element_pos container_v_space,
             Element_pos state_height, Element_pos state_v_space):
        _draw_object(draw_object), _position(position), _min_size(min_size),
        _container_height(container_height), _container_v_space(container_v_space),
        _state_height(state_height), _state_v_space(state_v_space){
    }

    /*!
     * \fn draw_tree( BinaryTree<E> * tree, const Interval &I)
     * \brief Function called to draw on ViTE the elements of a tree
     * \param tree  : The tree we want to draw
     * \param I : The interval we have to draw the tree
     */
    void draw_tree(BinaryTree<E> *tree, const Interval &I){
        Node<E> *node = tree->get_root();
        bool b = false;
        if (node)
            browse_tree(I, node,b);
    }

    /*!
     * \fn browse_tree(const Interval &I, Node<E> *node, bool &b)
     * \brief Function called to draw on ViTE a node
     * \param I : The interval we have to draw node
     * \param node : The node we want to draw
     */
    void browse_tree(const Interval &I, Node<E> *node, bool &b);

    /*!
     * \fn draw_state(double starttime, double endtime,  double r, double g, double b)
     * \brief Draw a state
     * \param starttime Time when the state begins
     * \param endtime Time when the state ends
     * \param r Red value of the state color
     * \param g Green value of the state color
     * \param b Blue value of the state color
     */
    inline void draw_state(double starttime, double endtime,  double r, double g, double b) {
        Element_pos y = _position*(_container_height+_container_v_space) + _container_v_space/2;
        _draw_object->draw_state(starttime, endtime, y, _state_height, r, g, b);
    }

    /*!
     * \fn draw_event(double time, double r, double g, double b)
     * \brief Draw an event
     * \param time Time of the event
     * \param r The red component of the color
     * \param g The green component of the color
     * \param b The blue component of the color
     */
    inline void draw_event(double time, double r, double g, double b) {
        Element_pos y = _position*(_container_height+_container_v_space) + _container_v_space/2;
        _draw_object->draw_event(time, y, _state_height, r, g, b);
    }

    /*!
     * \fn draw_busy(Interval *I)
     * \brief Function that display a purple square in the interval selected
     */
    inline void draw_busy(Interval *I) {
        draw_state(I->_left.get_value(), I->_right.get_value(), 0.2, 0.2, 0.2);
    }
};

/*!
 * \struct DrawNode
 * \brief Structure to draw a node of type E with a painting object of type D
 */
template<class D, class E>
struct DrawNode {
    /*!
     * \fn draw_node(DrawTree<D, E> *draw, Node<E> *node, bool &b, const Interval *i)
     * \brief To draw a node of type E with a painting object of type D
     */
    static void draw_node(DrawTree<D, E> *draw, Node<E> *node, bool &b, const Interval *i) { }
};

/*!
 * \struct DrawNode
 * \brief Draw a Node<Event> with a D painting object
 */
template<class D>
struct DrawNode<D, Event> {
    /*!
     * \fn draw_node(DrawTree<D, Event> *draw, Node<Event> *node, bool &b, const Interval *i)
     * \brief To draw a node of type Event with a painting object of type D
     */
    static void draw_node(DrawTree<D, Event> *draw, Node<Event> *node, bool &b, const Interval *i) {
        //Just to avoid warning at the compilation, these 2 parameters are only useful in draw_node<stateChange>
        b=true;
        i++;

        std::map<std::string, Value *>::const_iterator field;
        const Event *event = node->get_element();

        bool display=false;
        const Color *color = NULL;

        //if the color has been changed by a palette, we need to know it now.
        //if the Event has to be hidden, we can know it (get_color will return NULL), and never disply this event.
        if(!Session::get_use_palette("event_types")) display = true;
        else{
            if (event->get_type()){
                Palette *et = Session::get_palette("event_types", Session::get_current_palette("event_types"));
                //const std::list<std::string> * event_types = Session::get_event_types();
                color = et->get_color(event->get_type()->get_name().to_string());
                if(color) display=true;

            }
        }

        if(display){

            const std::map<std::string, Value *> *extra_fields = event->get_extra_fields();


            double time = node->get_element()->get_time().get_value();
            // Search the color in the event first
            if (color==NULL && extra_fields!=NULL && ((field = extra_fields->find(std::string("Color"))) != extra_fields->end())) {
                // Call the object event drawing function with the event color
                color = (const Color *)(*field).second;
            }
            else if(color==NULL && event->get_type()->get_extra_fields()!=NULL && ((field = event->get_type()->get_extra_fields()->find(std::string("Color"))) != event->get_type()->get_extra_fields()->end())) {
                // Search in the type after
                /* Call the object event drawing function with the event type color */
                color = (const Color *)(*field).second;
            }

            if(color != NULL) {
                draw->draw_event(time, color->get_red(), color->get_green(), color->get_blue());
            }
            else {
                /* Call the object event drawing function with default color */
                draw->draw_event(time, 1.0, 1.0, 1.0);
            }
        }
    }
};

/*!
 * \struct DrawNode
 * \brief Draw a Node<StateChange> with a D painting object
 */
template<class D>
struct DrawNode<D, StateChange> {
    static void draw_node(DrawTree<D, StateChange> *draw, Node<StateChange> *node, bool& b, const Interval * interval) {
            // Part to display the first state

            if(!b){
                b = true;
                //                Node<StateChange> n = *node;
                //                n.get_element()->get_left_state()->set_left_state(interval->_left);
                if(/*node &
                     node->get_element() &*/
                   node->get_element()->get_left_state()){
                    std::map<std::string, Value *>::const_iterator field;
                    const State *state = node->get_element()->get_left_state();
                    const std::map<std::string, Value *> *extra_fields;
                    const Color *color=NULL;
                    extra_fields = state->get_value()->get_extra_fields();

                    bool display=false;
                    //if the color has been changed by a palette, we need to know it now.
                    //if the State has to be hidden, we can know it (get_color will return NULL), and never display it.

                    //if we don't use palettes, display all states.
                    if(!Session::get_use_palette("palette")) display = true;
                    else{
                        if (state->get_type()){
                            Palette *sp = Session::get_palette("palette", Session::get_current_palette("palette"));
                        //const std::list<std::string> * event_types = Session::get_event_types();
                            color = sp->get_color(state->get_type()->get_name().to_string());
                            if(color) display=true;
                       }
                    }

                    if(display){
                    // Search the color
                        if(color==NULL){
                            if (state->get_value() &&
                                                        extra_fields!=NULL &&
                                !extra_fields->empty() &&
                                ((field = extra_fields->find(std::string("Color"))) != extra_fields->end())) {
                                /* Call the object state drawing function with the state color */
                                color = (const Color *)(*field).second;
                            }else{
                                color=new Color(0.7, 0.7, 0.75);
                            }
                        }
                        draw->draw_state(interval->_left.get_value(), state->get_end_time().get_value(),
                                             color->get_red(), color->get_green(), color->get_blue());
                    }



                }
            }

            if (!node->get_element()->get_right_state())
                return;
            std::map<std::string, Value *>::const_iterator field;
            const State *state = node->get_element()->get_right_state();
            const std::map<std::string, Value *> *extra_fields;
            const Color *color=NULL;
            extra_fields = state->get_value()->get_extra_fields();


            bool display=false;

            if(!Session::get_use_palette("palette")) display = true;
            else{
                if (state->get_value()){
                    Palette * sp = Session::get_palette("palette", Session::get_current_palette("palette"));
                //const std::list<std::string> * event_types = Session::get_event_types();
                    color = sp->get_color(state->get_value()->get_name().to_string());
                    if(color) display=true;

                }
            }
            if(display){
                // Max is made to not display farther than the interval
                double max;
                if(state->get_end_time().get_value() > interval->_right.get_value())
                    max = interval->_right.get_value();
                else
                    max = state->get_end_time().get_value();

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

                    }
                    else {
                        /* Call the object state drawing function with default color */
                        color= new Color (0.7, 0.7, 0.75);
                    }
                }
                draw->draw_state(state->get_start_time().get_value(), max,
                                         color->get_red(), color->get_green(), color->get_blue());

            }
    }
};

template<class D, class E>
/*!
 *\fn DrawTree<D, E>::browse_tree(const Interval &I, Node<E> * node, bool &b)
 *\brief Function that browses a tree to display
 *\param I The interval we have to display node
 *\param node The node in the tree we want to display
 */
void DrawTree<D, E>::browse_tree(const Interval &I, Node<E> *node, bool &b) {

    if(!node)
        return;

    bool displayed = false;// To remember if node has already been displayed
    int n_children;

    // If the node has 2 children
    if (node->get_right_child())
        n_children = 2;
    // Else if only a left child
    else if (node->get_left_child())
        n_children = 1;
    // Else no child
    else
        n_children = 0;


    // If the node is in the interval
    Date time = node->get_element()->get_time();
    if(time <= I._right && time >= I._left) {

        // If the node can be displayed (interval is wide enought)
        if (I._right - I._left > _min_size){
            // Launching Recursively in children
            if (n_children >= 1)//left
                browse_tree(Interval(I._left, time),
                            node->get_left_child(),
                            b);
            if (n_children >= 2)//right
                browse_tree(Interval(time, I._right),
                            node->get_right_child(),
                            b);

            // If node's left son has a conflict on it's left side
            if(n_children >= 1 && node->get_left_child()->_left_interval) {
                // Setting the node's left interval as busy
                node->_left_interval = new Interval(node->get_left_child()->_left_interval->_left,
                                                    node->get_left_child()->_left_interval->_right);
            }
            // Else no problem on the left (if a leaf, treated later)
            else {
                node->_left_interval = NULL;
            }

            // Looking for conflict to display the node
            if(n_children >= 2 &&
               node->get_left_child()->_right_interval &&
               node->get_right_child()->_left_interval) {

                // Drawing a crowded area
                draw_busy(new Interval(node->get_left_child()->_right_interval->_left,
                                       node->get_right_child()->_left_interval->_right));
                displayed = true;

                delete node->get_left_child()->_right_interval;
                delete node->get_right_child()->_left_interval;
                node->get_right_child()->_left_interval = NULL;
                node->get_left_child()->_right_interval = NULL;
            }

            // Else if problem possible with the left child : it's right interval
            else if (n_children >= 1 && node->get_left_child()->_right_interval) {

                draw_busy(new Interval(node->get_left_child()->_right_interval->_left, time));
                delete node->get_left_child()->_right_interval;
                node->get_left_child()->_right_interval = NULL;
            }
            // Else if problem possible with the right child : it's left interval
            else if (n_children >= 2 && node->get_right_child()->_left_interval) {
                displayed = true;
                draw_busy(new Interval(time, node->get_right_child()->_left_interval->_right));
                delete node->get_right_child()->_left_interval;
                node->get_right_child()->_left_interval = NULL;
            }


            // If node's right son has a conflict on it's right side
            if(n_children >= 2 && node->get_right_child()->_right_interval) {
                // Setting the node's right interval as busy
                node->_right_interval = node->get_right_child()->_right_interval;
            }
            // Else no problem on the right (if a leaf, treated after)
            else {
                node->_right_interval = NULL;
            }

            // Treating the special case of a leaf
            if(n_children == 0){
                //If too close of the previous node
                if(time - I._left < _min_size){
                    node->_left_interval= new Interval(I._left, time);
                    node->_right_interval = NULL;
                }
                //If too close of the following node
                if(I._right - time < _min_size){
                    node->_right_interval = new Interval(time, I._right);
                    node->_left_interval = NULL;
                }
            }


            // Making sure node has been displayed
            if(!displayed) {
                DrawNode<D, E>::draw_node(this, node,b,&I);
            }
        } // end if has enough space

        else {
            // Cannot display node so the busy intervals are the same and value I
            node->_left_interval = new Interval(I);
            node->_right_interval = new Interval(I);
        }
    } //end if is in the interval
    else {
        // If node is after the interval
        if (n_children >= 1 && node->get_element()->get_time() > I._right) {
            browse_tree(I, node->get_left_child(),b);
            node->_left_interval = node->get_left_child()->_left_interval;
            node->_right_interval = NULL;
        }
        // Else he is before
        else if (n_children >= 2) {
            browse_tree(I, node->get_right_child(),b);
            node->_left_interval = NULL;
            node->_right_interval = node->get_right_child()->_right_interval;
        }
    } // end else is in the interval
}

#endif
