/*
** 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;
    Element_pos _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, Element_pos 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, EntityValue * value) {
        Element_pos y = _position + _container_v_space/2;
        _draw_object->draw_state(starttime, endtime, y, _state_height, r, g, b, value);
    }

    /*!
     * \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, EntityValue* value) {
        Element_pos y = _position + _container_v_space/2;
        _draw_object->draw_event(time, y, _state_height, r, g, b, value);
    }

    /*!
     * \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, nullptr);
    }
};

/*!
 * \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++;

        const Event *event = node->get_element();
        EntityValue *value = event->get_value();
        const Color *color = value->get_used_color();
        double time = event->get_time().get_value();
        draw->draw_event(time, color->get_red(), color->get_green(), color->get_blue(), value);


    }
};

/*!
 * \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()){
                    const State *state = node->get_element()->get_left_state();
                    EntityValue * value = state->get_value();
                    const Color * color = value->get_used_color();
                    draw->draw_state(interval->_left.get_value(), state->get_end_time().get_value(),
                                         color->get_red(), color->get_green(), color->get_blue(), value);
                        //}



                }
            }

            if (!node->get_element()->get_right_state())
                return;
            const State *state = node->get_element()->get_right_state();
            EntityValue* value = state->get_value();
            const Color *color=value->get_used_color();
            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();
            draw->draw_state(state->get_start_time().get_value(), max,
                             color->get_red(), color->get_green(), color->get_blue(), state->get_value());

    }
};

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 enough)
        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 = nullptr;
            }

            // 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 = nullptr;
                node->get_left_child()->_right_interval = nullptr;
            }

            // 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 = nullptr;
            }
            // 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 = nullptr;
            }


            // 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 = nullptr;
            }

            // 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 = nullptr;
                }
                //If too close of the following node
                if(I._right - time < _min_size){
                    node->_right_interval = new Interval(time, I._right);
                    node->_left_interval = nullptr;
                }
            }


            // 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 = nullptr;
        }
        // Else he is before
        else if (n_children >= 2) {
            browse_tree(I, node->get_right_child(),b);
            node->_left_interval = nullptr;
            node->_right_interval = node->get_right_child()->_right_interval;
        }
    } // end else is in the interval
}

#endif
