/*
** 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 
**
*/
#ifndef BINARYTREE_HPP
#define BINARYTREE_HPP

/*!
 * \file BinaryTree.hpp
 */
/*!
 * \class BinaryTree
 * \brief Store a tree made from a list
 */
template<typename E>
class BinaryTree {
private:
    Node<E> *_root;

public:

 /*!
     * \brief Constructor
     * \param list List from which the tree is made
     * \param size Number of element in the list
     */
    BinaryTree(E * list[], unsigned int size) {
        if (size == 0 || list==NULL ||list[0]==NULL) {
            _root = NULL;
            return;
        }
    
        // Calculate n and m as size = 2^n - 1 + m and m < 2^n
        int n = 0;
        unsigned int a = 1;
        while (size >= 2*a - 1) {
            a *= 2;
            n++;
        }
        int m = size - a + 1;
        
        // Initialize the array
        Node<E> **temp = new Node<E> *[n+1];
        int start = 0;
        int i = 0;
        for (i = 0; i < n+1; i++)
            temp[i] = NULL;
        
        i = 0;
        // Reads elements from the list and empty it
       for (unsigned int j=0; j<size; j++)  {
            E *element = list[j];
            
            while (temp[i]) // Find the first free element in temp
                i++;
            
            temp[i] = new Node<E>(element);
            
            if (i == 0 && m > 0) { // The node is a leaf and its depth is n+1
                m--;
                if (m == 0)
                    start = 1;
            }
            
            if (i < n && temp[i+1]) { // The node has a parent
                temp[i]->_parent = temp[i+1];
                temp[i+1]->_right_child = temp[i];
            }
            
            if (i > 0 && temp[i-1]) { // The node is not a leaf
                temp[i]->_left_child = temp[i-1];
                temp[i-1]->_parent = temp[i];
                while (i > 0) {
                    i--;
                    temp[i] = NULL;
                }
                i = start;
            }
        }
        
        _root = temp[n-1+start];
        
        delete[] temp;
       // delete[] list;
    }
    
    
     /*!
     * \brief Constructor
     * \param list List from which the tree is made
     * \param size Number of element in the list
     */
    BinaryTree(E * list, unsigned int size) {
        if (size == 0 || list==NULL) {
            _root = NULL;
            return;
        }
    
        // Calculate n and m as size = 2^n - 1 + m and m < 2^n
        int n = 0;
        unsigned int a = 1;
        while (size >= 2*a - 1) {
            a *= 2;
            n++;
        }
        int m = size - a + 1;
        
        // Initialize the array
        Node<E> **temp = new Node<E> *[n+1];
        int start = 0;
        int i = 0;
        for (i = 0; i < n+1; i++)
            temp[i] = NULL;
        
        i = 0;
        // Reads elements from the list and empty it
       for (unsigned int j=0; j<size; j++)  {
            E *element = &(list[j]);
            
            while (temp[i]) // Find the first free element in temp
                i++;
            
            temp[i] = new Node<E>(element);
            
            if (i == 0 && m > 0) { // The node is a leaf and its depth is n+1
                m--;
                if (m == 0)
                    start = 1;
            }
            
            if (i < n && temp[i+1]) { // The node has a parent
                temp[i]->_parent = temp[i+1];
                temp[i+1]->_right_child = temp[i];
            }
            
            if (i > 0 && temp[i-1]) { // The node is not a leaf
                temp[i]->_left_child = temp[i-1];
                temp[i-1]->_parent = temp[i];
                while (i > 0) {
                    i--;
                    temp[i] = NULL;
                }
                i = start;
            }
        }
        
        _root = temp[n-1+start];
        
        delete[] temp;
       // delete[] list;
    }
    
    
    
    
    /*!
     * \brief Constructor
     * \param list List from which the tree is made
     * \param size Number of element in the list
     */
    BinaryTree(std::list<E *> &list, unsigned int size) {
        if (size == 0 || list.empty()) {
            _root = nullptr;
            return;
        }
    
        // Calculate n and m as size = 2^n - 1 + m and m < 2^n
        int n = 0;
        unsigned int a = 1;
        while (size >= 2*a - 1) {
            a *= 2;
            n++;
        }
        int m = size - a + 1;
        
        // Initialize the array
        Node<E> **temp = new Node<E> *[n+1];
        int start = 0;
        int i = 0;
        for (i = 0; i < n+1; i++)
            temp[i] = nullptr;
        
        i = 0;
        // Reads elements from the list and empty it
        while (!list.empty()) {
            E *element = list.front();
            list.pop_front();
            
            while (temp[i]) // Find the first free element in temp
                i++;
            
            temp[i] = new Node<E>(element);
            
            if (i == 0 && m > 0) { // The node is a leaf and its depth is n+1
                m--;
                if (m == 0)
                    start = 1;
            }
            
            if (i < n && temp[i+1]) { // The node has a parent
                temp[i]->_parent = temp[i+1];
                temp[i+1]->_right_child = temp[i];
            }
            
            if (i > 0 && temp[i-1]) { // The node is not a leaf
                temp[i]->_left_child = temp[i-1];
                temp[i-1]->_parent = temp[i];
                while (i > 0) {
                    i--;
                    temp[i] = nullptr;
                }
                i = start;
            }
        }
        
        _root = temp[n-1+start];
        
        delete[] temp;
    }
    
    /*!
     * \brief Constructor
     * \param list List from which the tree is made
     * \param size Number of element in the list
     */
    BinaryTree(std::vector<E *> &list, unsigned int size) {
        if (size == 0 || list.empty()) {
            _root = NULL;
            return;
        }
    
        // Calculate n and m as size = 2^n - 1 + m and m < 2^n
        int n = 0;
        unsigned int a = 1;
        while (size >= 2*a - 1) {
            a *= 2;
            n++;
        }
        int m = size - a + 1;
        
        // Initialize the array
        Node<E> **temp = new Node<E> *[n+1];
        int start = 0;
        int i = 0;
        for (i = 0; i < n+1; i++)
            temp[i] = NULL;
        
        i = 0;
        // Reads elements from the list and empty it
	int it  = 0;
	int end = list.size();
        for(it=0; it <end; it++){
            E *element = list[it];
            
            while (temp[i]) // Find the first free element in temp
                i++;
            
            temp[i] = new Node<E>(element);
            
            if (i == 0 && m > 0) { // The node is a leaf and its depth is n+1
                m--;
                if (m == 0)
                    start = 1;
            }
            
            if (i < n && temp[i+1]) { // The node has a parent
                temp[i]->_parent = temp[i+1];
                temp[i+1]->_right_child = temp[i];
            }
            
            if (i > 0 && temp[i-1]) { // The node is not a leaf
                temp[i]->_left_child = temp[i-1];
                temp[i-1]->_parent = temp[i];
                while (i > 0) {
                    i--;
                    temp[i] = NULL;
                }
                i = start;
            }
        }

	list.clear();
        _root = temp[n-1+start];
        
        delete[] temp;
    }

    /*!
     * \brief Destructor
     */    
    ~BinaryTree() {
        delete _root;
        _root = NULL;
    }
    
    /*!
     * \brief Get the root node of the tree
     */
    Node<E> *get_root() const {
        return _root;
    }

    /*!
     * \fn empty() const
     * \brief Return if the tree is empty
     */
    bool empty() const{
        return (_root == nullptr);
    }
};

#endif

