/*
 * SuffixTrieNodeTerminatingSymbol.cpp
 *
 *  Created on: Nov 23, 2013
 *      Author: Stepan Plachy
 */

#include "SuffixTrieNodeTerminatingSymbol.h"
#include "SuffixTrieTerminatingSymbol.h"
#include <exception/CommonException.h>
#include <sstream>
#include <alib/tuple>

namespace indexes {

SuffixTrieNodeTerminatingSymbol::SuffixTrieNodeTerminatingSymbol ( ext::map < DefaultSymbolType, SuffixTrieNodeTerminatingSymbol * > children ) : m_children ( std::move ( children ) ), parentTree ( nullptr ) {
	for ( auto & element : this->m_children )
		element.second->parent = this;

	this->attachTree ( nullptr );
	this->parent = nullptr;
}

SuffixTrieNodeTerminatingSymbol::SuffixTrieNodeTerminatingSymbol ( const SuffixTrieNodeTerminatingSymbol & other ) : parentTree ( nullptr ) {
	for ( const auto & element : other.m_children )
		m_children.insert ( std::make_pair ( element.first, element.second->clone ( ) ) );

	for ( auto & element : this->m_children )
		element.second->parent = this;

	this->attachTree ( nullptr );
	this->parent = nullptr;
}

SuffixTrieNodeTerminatingSymbol::SuffixTrieNodeTerminatingSymbol ( SuffixTrieNodeTerminatingSymbol && other ) noexcept : m_children ( std::move ( other.m_children ) ), parentTree ( nullptr ) {
	other.m_children.clear ( );

	for ( auto & element : this->m_children )
		element.second->parent = this;

	this->attachTree ( nullptr );
	this->parent = nullptr;
}

SuffixTrieNodeTerminatingSymbol & SuffixTrieNodeTerminatingSymbol::operator =( const SuffixTrieNodeTerminatingSymbol & other ) {
	if ( this == & other )
		return * this;

	* this = SuffixTrieNodeTerminatingSymbol ( other );

	return * this;
}

SuffixTrieNodeTerminatingSymbol & SuffixTrieNodeTerminatingSymbol::operator =( SuffixTrieNodeTerminatingSymbol && other ) noexcept {
	std::swap ( this->m_children, other.m_children );
	std::swap ( this->parentTree, other.parentTree ); // this->parentTree is stored within other.parentTree and it is reattached on the next line

	for ( auto & element : this->m_children )
		element.second->parent = this;

	this->attachTree ( other.parentTree );

	return * this;
}

SuffixTrieNodeTerminatingSymbol::~SuffixTrieNodeTerminatingSymbol ( ) noexcept {
	for ( const auto& element : m_children )
		delete element.second;

	m_children.clear ( );
}

const ext::map < const DefaultSymbolType, const SuffixTrieNodeTerminatingSymbol * > & SuffixTrieNodeTerminatingSymbol::getChildren ( ) const {
	return * reinterpret_cast < const ext::map < const DefaultSymbolType, const SuffixTrieNodeTerminatingSymbol * > * > ( & m_children );
}

const ext::map < DefaultSymbolType, SuffixTrieNodeTerminatingSymbol * > & SuffixTrieNodeTerminatingSymbol::getChildren ( ) {
	return m_children;
}

SuffixTrieNodeTerminatingSymbol & SuffixTrieNodeTerminatingSymbol::getChild ( const DefaultSymbolType & symbol ) {
	ext::map < DefaultSymbolType, SuffixTrieNodeTerminatingSymbol * >::const_iterator iter = m_children.find ( symbol );

	if ( iter == m_children.end ( ) ) throw exception::CommonException ( "child does not exist" );

	return * iter->second;
}

const SuffixTrieNodeTerminatingSymbol & SuffixTrieNodeTerminatingSymbol::getChild ( const DefaultSymbolType & symbol ) const {
	ext::map < DefaultSymbolType, SuffixTrieNodeTerminatingSymbol * >::const_iterator iter = m_children.find ( symbol );

	if ( iter == m_children.end ( ) ) throw exception::CommonException ( "child does not exist" );

	return * iter->second;
}

bool SuffixTrieNodeTerminatingSymbol::hasChild ( const DefaultSymbolType & symbol ) const {
	return m_children.find ( symbol ) != m_children.end ( );
}

SuffixTrieNodeTerminatingSymbol & SuffixTrieNodeTerminatingSymbol::addChild ( DefaultSymbolType symbol, SuffixTrieNodeTerminatingSymbol node ) {
	ext::map < DefaultSymbolType, SuffixTrieNodeTerminatingSymbol * >::iterator iter = m_children.find ( symbol );

	if ( iter != m_children.end ( ) ) throw exception::CommonException ( "child already exist" );

	if ( ( this->parentTree != nullptr ) && ! ( this->parentTree->getAlphabet ( ).contains ( symbol ) ) ) throw exception::CommonException ( "Symbol is not in the alphabet" );

	return * m_children.insert ( std::make_pair ( std::move ( symbol ), new SuffixTrieNodeTerminatingSymbol ( std::move ( node ) ) ) ).first->second;
}

SuffixTrieNodeTerminatingSymbol * SuffixTrieNodeTerminatingSymbol::getParent ( ) {
	return parent;
}

const SuffixTrieNodeTerminatingSymbol * SuffixTrieNodeTerminatingSymbol::getParent ( ) const {
	return parent;
}

void SuffixTrieNodeTerminatingSymbol::swap ( SuffixTrieNodeTerminatingSymbol & other ) {
	const SuffixTrieTerminatingSymbol * thisParentTree = this->parentTree;
	const SuffixTrieTerminatingSymbol * otherParentTree = other.parentTree;

	SuffixTrieNodeTerminatingSymbol tmp = std::move ( other );

	other  = std::move ( * this );
	* this = std::move ( tmp );

	this->attachTree ( thisParentTree );
	other.attachTree ( otherParentTree );
}

SuffixTrieNodeTerminatingSymbol * SuffixTrieNodeTerminatingSymbol::clone ( ) const & {
	return new SuffixTrieNodeTerminatingSymbol ( * this );
}

SuffixTrieNodeTerminatingSymbol * SuffixTrieNodeTerminatingSymbol::clone ( ) && {
	return new SuffixTrieNodeTerminatingSymbol ( std::move ( * this ) );
}

void SuffixTrieNodeTerminatingSymbol::operator >>( std::ostream & out ) const {
	out << "(SuffixTrieNodeTerminatingSymbol " << " children = " << this->m_children << ")";
}

std::ostream & operator <<( std::ostream & out, const SuffixTrieNodeTerminatingSymbol & node ) {
	node >> out;
	return out;
}

bool SuffixTrieNodeTerminatingSymbol::testSymbol ( const DefaultSymbolType & symbol ) const {
	for ( const auto & child : this->m_children ) {
		if ( symbol == child.first ) return true;

		if ( child.second->testSymbol ( symbol ) ) return true;
	}

	return false;
}

bool SuffixTrieNodeTerminatingSymbol::attachTree ( const SuffixTrieTerminatingSymbol * tree ) {
	if ( this->parentTree == tree ) return true;

	this->parentTree = tree;

	for ( const auto & child : this->m_children ) {
		if ( ( this->parentTree != nullptr ) && ! ( this->parentTree->getAlphabet ( ).contains ( child.first ) ) ) return false;

		if ( !child.second->attachTree ( tree ) ) return false;
	}

	return true;
}

ext::set < DefaultSymbolType > SuffixTrieNodeTerminatingSymbol::computeMinimalAlphabet ( ) const {
	ext::set < DefaultSymbolType > res;
	computeMinimalAlphabet ( res );
	return res;
}

void SuffixTrieNodeTerminatingSymbol::computeMinimalAlphabet ( ext::set < DefaultSymbolType > & alphabet ) const {
	for ( const auto & child : this->m_children ) {
		alphabet.insert ( child.first );
		child.second->computeMinimalAlphabet ( alphabet );
	}
}

SuffixTrieNodeTerminatingSymbol::operator std::string ( ) const {
	std::stringstream ss;
	ss << * this;
	return ss.str ( );
}

} /* namespace indexes */
