//---------------------------------------------------------------------------

#include "stdafx.h"

using namespace std;

#ifdef _MSC_VER
#pragma hdrstop
#endif

#include "myExcept.h"
#include "bitvector.h"
#include "Cube.h"
#include "Term.h"

#include "TTree.h"

TTreeNode::TTreeNode( const unsigned int _level )
{
    one = NULL;
    dc = NULL;
    zero = NULL;
    level = _level;
	isterm = false;
}

TTreeNode::~TTreeNode(void)
{
    delete one;
    delete dc;
    delete zero;
}

TTreeNode& TTreeNode::operator=( const TTreeNode& n)
{
    this->one = n.one;
    this->dc = n.dc;
    this->zero = n.zero;
    this->level = n.level;

    return *this;
}

TTreeNode::TTreeNode( const TTreeNode& n )
{
	if ( n.one != NULL ) {
		one = new TTreeNode(*n.one);
		if ( one == NULL) throw RuntimeException( "TTree: Not enough memory" );
	}
    else one = NULL;
	if ( n.dc != NULL ) {
		dc = new TTreeNode(*n.dc);
		if ( dc == NULL) throw RuntimeException( "TTree: Not enough memory" );
	}
    else dc = NULL;
	if ( n.zero != NULL ) {
		zero = new TTreeNode(*n.zero);
		if ( zero == NULL) throw RuntimeException( "TTree: Not enough memory" );
	}
    else zero = NULL;
    level = n.level;
}

void TTreeNode::SetTerm( const list<Term>::iterator _term )
{
	if ( isterm ) throw RuntimeException("TTreeNode::SetTerm:: Setting already set term");
	term = _term;
	isterm = true;
}

list<Term>::iterator TTreeNode::getTerm(void)
{
	if ( !isterm ) throw RuntimeException("TTreeNode::getTerm:: Retrieving non-existing term");
	return term;
}

TTree& TTree:: operator=( const TTree& n)
{
	if ( n.root != NULL ) {
		this->root = new TTreeNode(*n.root);
		if ( this->root == NULL) throw RuntimeException( "TTree: Not enough memory" );
	}
    else this->root = NULL;
    this->vars = n.vars;
    this->nodes = n.nodes;
    this->terms = n.terms;
    return *this;
}

TTree::TTree( const unsigned int _vars )
{
    nodes = 0;
    terms = 0;
    vars = _vars;
    root = NULL;
}

TTree::TTree(void)
{
    nodes = 0;
    terms = 0;
    vars = 0;
    root = NULL;
}

TTree::~TTree(void)
{
    delete root;
}

void TTree::DeleteTree( TTreeNode *node )
{
    if ( node == NULL ) return;
    if ( node->one != NULL ) {
        DeleteTree( node->one );
        node->one = NULL;
    }
    if ( node->dc != NULL ) {
        DeleteTree( node->dc );
        node->dc = NULL;
    }
    if ( node->zero != NULL ) {
        DeleteTree( node->zero );
        node->zero = NULL;
    }
    delete node;
    nodes--;
}

void TTree::clear(void)
{
    delete root;
    nodes = 0;
    terms = 0;
    vars = 0;
    root = NULL;
}

TTreeNode *TTree::AddTTreeNode( const unsigned int _level )
{
    TTreeNode *node;

    nodes++;
    node = new TTreeNode( _level );
	if ( node == NULL) throw RuntimeException( "TTree: Not enough memory" );
    return node;
}

TTreeNode *TTree::InsertTerm( const string term )
/*  Inserts a term into a TT.
    Returns a pointer to the terminal node
*/
{
    unsigned int i;
    TTreeNode *node;
    bool found;

    if ( term.size() != vars ) throw RuntimeException( "TTree:InsertTerm: Invalid term size" );
    if ( root == NULL ) root = AddTTreeNode( 0 );
    node = root;
    found = true;
    for ( i = 0; i < vars; i++ ) {
        if ( term[i] == '1' ) {
            if ( node->one == NULL ) {
                node->one = AddTTreeNode( i+1 );
                found = false;
            }
            node = node->one;
        }
        else if ( term[i] == '-' ) {
            if ( node->dc == NULL ) {
                node->dc = AddTTreeNode( i+1 );
                found = false;
            }
            node = node->dc;
        }
        else if ( term[i] == '0' ) {
            if ( node->zero == NULL ) {
                node->zero = AddTTreeNode( i+1 );
                found = false;
            }
            node = node->zero;
        }
    }
    if ( !found ) terms++;
    return node;
}

TTreeNode *TTree::FindTerm( const string term ) const
/*
    Returns a pointer to the terminal node or NULL
*/
{
    unsigned int i;
    TTreeNode *node;

    if ( term.size() != vars ) throw RuntimeException( "TTree:FindTerm: Invalid term size" );
    node = root;
    if ( node == NULL ) return NULL;
    for ( i = 0; i < vars; i++ ) {
        if ( term[i] == '1' ) node = node->one;
        else if ( term[i] == '-' ) node = node->dc;
        else if ( term[i] == '0' ) node = node->zero;
        if ( node == NULL ) return NULL;
    }
    return node;
}

bool TTree::DeleteTerm( const string term )
/*  Deletes a term from a TT.
    Returns false if it is not present
*/
{
    unsigned int i;
    TTreeNode *node, *last;
    enum { one, dc, zero } pol = one;

    node = root;
    if ( node == NULL ) return false;
    last = root;
    for ( i = 0; i < vars; i++ ) {
        switch ( term[i] ) {
          case '1':
            if ( node->dc != NULL || node->zero != NULL ) {
                last = node;
                pol = one;
            }
            node = node->one;
            break;
          case '-':
            if ( node->one != NULL || node->zero != NULL ) {
                last = node;
                pol = dc;
            }
            node = node->dc;
            break;
          case '0':
            if ( node->one != NULL || node->dc != NULL ) {
                last = node;
                pol = zero;
            }
            node = node->zero;
            break;
          default: throw RuntimeException( "TTree:DeleteTerm: Bad PLA" );
        }
        if ( node == NULL ) return false;
    }
    switch ( pol ) {
        case one:
            DeleteTree( last->one );
            last->one = NULL;
            break;
        case dc:
            DeleteTree( last->dc );
            last->dc = NULL;
            break;
        case zero:
            DeleteTree( last->zero );
            last->zero = NULL;
            break;
    }
    terms--;
    return true;
}

bool TTree::DeleteTerm_Keep( const string term )
/*	 Does not actually delete the term from the TT, just sets its "isterm" to false
	Returns false if it is not present
*/
{
	TTreeNode *node;

	node = FindTerm(term);
	if ( node == NULL ) return false;
	node->isterm = false;
	return true;
}

void TTree::setInputs( const unsigned int _vars )
{
    if ( vars != 0 && vars != _vars ) throw RuntimeException( "TTree: Trying to incorrectly change # of input variables" );
    else vars = _vars;
}
