/**
 * CopyRight F. J. Madrid-Cuevas <fjmadrid@uco.es>
 *
 * Sólo se permite el uso de este código en la docencia de las asignaturas sobre
 * Estructuras de Datos de la Universidad de Córdoba.
 *
 * Está prohibido su uso para cualquier otro objetivo.
 */
#pragma once

#include <cassert>
#include <exception>
#include <memory>
#include <iostream>
#include <sstream>

#include "btree.hpp"

template <class T>
bool BTree<T>::is_empty() const
{
    bool ret_v = false;

    ret_v= (_root==nullptr);

    return ret_v;
}

template <class T>
BTree<T>::BTree(const T &it)
{
    create_root(it);
    
    assert(!is_empty());
    assert(item() == it);
    assert(left().is_empty());
    assert(right().is_empty());
}

template <class T>
const T &BTree<T>::item() const
{
    assert(!is_empty());

    return _root->item();
}

template <class T>
BTree<T> BTree<T>::left() const
{
    assert(!is_empty());

    return BTree(_root->left());
}

template <class T>
BTree<T> BTree<T>::right() const
{
    assert(!is_empty());

    return BTree(_root->right());
}

template <class T>
void BTree<T>::create_root(const T &it)
{
    assert(is_empty());

    _root = BTNode<T>::create(it);

    assert(!is_empty());
    assert(item() == it);
    assert(left().is_empty());
    assert(right().is_empty());
}

template <class T>
void BTree<T>::set_item(const T &new_it)
{
    assert(!is_empty());

    _root->set_item(new_it);

    assert(item() == new_it);
}

template <class T>
void BTree<T>::set_left(BTree<T> new_left)
{
    assert(!is_empty());

    _root->set_left(new_left.root());

    assert(left().root() == new_left.root());
}

template <class T>
void BTree<T>::set_right(BTree<T> new_right)
{
    assert(!is_empty());

    _root->set_right(new_right.root());

    assert(right().root() == new_right.root());
}

template <class T>
BTree<T>::BTree(typename BTNode<T>::Ref &&n)
{
    _root=n;

    assert(root() == n);
}

template <class T>
BTree<T>::BTree(typename BTNode<T>::Ref &n) : BTree<T>(std::move(n))
{
}

template <class T>
typename BTNode<T>::Ref BTree<T>::root() const
{
    return _root;
}

template <class T>
void BTree<T>::set_root(typename BTNode<T>::Ref &new_root)
{
    _root=new_root;

    assert(root() == new_root);
}

template <class T>
std::ostream &
operator<<(std::ostream &out, const BTree<T> &tree) noexcept
{
    if(tree.is_empty()){
        out<<"[]";
    }else{
        out << "[ ";
        out << tree.item();
        out << " ";
        out << tree.left();
        out << " ";
        out << tree.right();
        out << " ]";
    }

    return out;
}

template <class T>
std::istream &
operator>>(std::istream &in, BTree<T> &tree) noexcept(false)
{
    std::string token;
    tree = BTree<T>(); 

    in>>token;

    if (token == "[") {
        T item;
        BTree<T> left, right;

        if (!(in >> item)) {
            throw std::runtime_error("Wrong input format.");
        }

        in >> left;
        in >> right;
        in >> token;
        
        if (token != "]") {
            throw std::runtime_error("Wrong input format.");
        }

        tree.create_root(item);
        tree.set_left(left);
        tree.set_right(right);
    } 

    else if (token != "[]") {
        throw std::runtime_error("Wrong input format.");
    }

    return in;
}