/*
 * PrefixRankedPattern.cpp
 *
 *  Created on: Nov 23, 2013
 *      Author: Jan Travnicek
 */

#include "PrefixRankedPattern.h"
#include "../../exception/AlibException.h"

#include <sstream>
#include <algorithm>
#include <deque>

#include "RankedPattern.h"

#include "../../sax/FromXMLParserHelper.h"
#include "../common/TreeFromXMLParser.h"
#include "../common/TreeToXMLComposer.h"
#include "../Tree.h"
#include "../RankedTreeWrapper.h"
#include "../../object/Object.h"
#include "../../XmlApi.hpp"
#include "../../CastApi.hpp"

namespace tree {

PrefixRankedPattern::PrefixRankedPattern ( alphabet::RankedSymbol subtreeWildcard, std::set < alphabet::RankedSymbol > alphabet, std::vector < alphabet::RankedSymbol > data ) : RankedPatternAlphabet ( std::move ( subtreeWildcard ) ) {
	addSymbolsToAlphabet ( std::move ( alphabet ) );
	setContent ( std::move ( data ) );
}

PrefixRankedPattern::PrefixRankedPattern ( alphabet::RankedSymbol subtreeWildcard, std::vector < alphabet::RankedSymbol > data ) : RankedPatternAlphabet ( std::move ( subtreeWildcard ) ) {
	arityChecksum ( data );

	addSymbolsToAlphabet ( std::set < alphabet::RankedSymbol > ( data.begin ( ), data.end ( ) ) );
	m_Data = std::move ( data );
}

PrefixRankedPattern::PrefixRankedPattern ( const RankedPattern & tree ) : RankedPatternAlphabet ( tree.getSubtreeWildcard ( ) ) {
	toPrefixRanked ( tree.getRoot ( ) );
	addSymbolsToAlphabet ( tree.getAlphabet ( ) );
}

void PrefixRankedPattern::toPrefixRanked ( const RankedNode & node ) {
	if ( node.getSymbol ( ) == subtreeWildcard ) {
		m_Data.push_back ( node.getSymbol ( ) );
	} else {
		m_Data.push_back ( node.getSymbol ( ) );

		for ( const RankedNode * child : node.getChildren ( ) )
			toPrefixRanked ( * child );
	}
}

RankedTreeBase * PrefixRankedPattern::clone ( ) const {
	return new PrefixRankedPattern ( * this );
}

RankedTreeBase * PrefixRankedPattern::plunder ( ) && {
	return new PrefixRankedPattern ( std::move ( * this ) );
}

bool PrefixRankedPattern::removeSymbolFromAlphabet ( const alphabet::RankedSymbol & symbol ) {
	if ( std::any_of ( m_Data.begin ( ), m_Data.end ( ), [&] ( const alphabet::RankedSymbol & s ) {
			return s == symbol;
		} ) )
		throw exception::AlibException ( "Input symbol \"" + ( std::string ) symbol + "\" is used." );

	if ( this->subtreeWildcard == symbol )
		throw exception::AlibException ( "Input symbol \"" + ( std::string ) symbol + "\" is subtreeWildcard." );

	return alphabet.erase ( symbol );
}

const std::vector < alphabet::RankedSymbol > & PrefixRankedPattern::getContent ( ) const {
	return this->m_Data;
}

void PrefixRankedPattern::setContent ( std::vector < alphabet::RankedSymbol > data ) {
	arityChecksum ( data );

	std::set < alphabet::RankedSymbol > minimalAlphabet ( data.begin ( ), data.end ( ) );
	std::set < alphabet::RankedSymbol > unknownSymbols;
	std::set_difference ( minimalAlphabet.begin ( ), minimalAlphabet.end ( ), alphabet.begin ( ), alphabet.end ( ), std::inserter ( unknownSymbols, unknownSymbols.end ( ) ) );

	if ( unknownSymbols.size ( ) > 0 )
		throw exception::AlibException ( "Input symbols not in the alphabet." );

	this->m_Data = std::move ( data );
}

void PrefixRankedPattern::arityChecksum ( const std::vector < alphabet::RankedSymbol > & data ) {
	int arityChecksum = 1;

	for ( const alphabet::RankedSymbol & symbol : data ) {
		arityChecksum += symbol.getRank ( ).getData ( );
		arityChecksum -= 1;
	}

	if ( arityChecksum != 0 ) throw exception::AlibException ( "The string does not form a tree" );
}

bool PrefixRankedPattern::isEmpty ( ) const {
	return this->m_Data.size ( ) == 0;
}

int PrefixRankedPattern::compare ( const PrefixRankedPattern & other ) const {
	auto first	= std::tie ( m_Data, alphabet );
	auto second = std::tie ( other.m_Data, other.alphabet );

	std::compare < decltype ( first ) > comp;

	return comp ( first, second );
}

void PrefixRankedPattern::operator >>( std::ostream & out ) const {
	if ( this->isEmpty ( ) ) {
		out << "(Epsilon)";
	} else {
		out << "(PrefixRankedPattern ";

		for ( const alphabet::RankedSymbol & symbol : this->m_Data )
			out << symbol;

		out << ")";
	}
}

PrefixRankedPattern::operator std::string ( ) const {
	std::stringstream ss;

	if ( this->isEmpty ( ) ) {
		ss << "E";
	} else {
		ss << "\"";

		for ( const alphabet::RankedSymbol & symbol : this->m_Data )
			ss << ( std::string ) symbol;

		ss << "\"";
	}

	return std::move ( ss ).str ( );
}

const std::string PrefixRankedPattern::XML_TAG_NAME = "PrefixRankedPattern";

PrefixRankedPattern PrefixRankedPattern::parse ( std::deque < sax::Token >::iterator & input ) {
	sax::FromXMLParserHelper::popToken ( input, sax::Token::TokenType::START_ELEMENT, PrefixRankedPattern::XML_TAG_NAME );
	alphabet::RankedSymbol subtreeWildcardSymbol = TreeFromXMLParser::parseSubtreeWildcardRankedSymbol ( input );
	std::set < alphabet::RankedSymbol > rankedAlphabet = TreeFromXMLParser::parseRankedAlphabet ( input );
	std::vector < alphabet::RankedSymbol > data = TreeFromXMLParser::parseContentData ( input );
	sax::FromXMLParserHelper::popToken ( input, sax::Token::TokenType::END_ELEMENT, PrefixRankedPattern::XML_TAG_NAME );

	return PrefixRankedPattern ( std::move ( subtreeWildcardSymbol ), std::move ( rankedAlphabet ), std::move ( data ) );
}

void PrefixRankedPattern::compose ( std::deque < sax::Token > & out ) const {
	out.emplace_back ( PrefixRankedPattern::XML_TAG_NAME, sax::Token::TokenType::START_ELEMENT );
	TreeToXMLComposer::composeSubtreeWildcard ( out, subtreeWildcard );
	TreeToXMLComposer::composeAlphabet ( out, alphabet );
	TreeToXMLComposer::composeContent ( out, m_Data );
	out.emplace_back ( PrefixRankedPattern::XML_TAG_NAME, sax::Token::TokenType::END_ELEMENT );
}

} /* namespace tree */

namespace alib {

xmlApi < tree::Tree >::ParserRegister < tree::PrefixRankedPattern > prefixRankedPatternParserRegister = xmlApi < tree::Tree >::ParserRegister < tree::PrefixRankedPattern > ( tree::PrefixRankedPattern::XML_TAG_NAME, tree::PrefixRankedPattern::parse );
xmlApi < tree::RankedTreeWrapper >::ParserRegister < tree::PrefixRankedPattern > prefixRankedPatternParserRegister2 = xmlApi < tree::RankedTreeWrapper >::ParserRegister < tree::PrefixRankedPattern > ( tree::PrefixRankedPattern::XML_TAG_NAME, tree::PrefixRankedPattern::parse );
xmlApi < alib::Object >::ParserRegister < tree::PrefixRankedPattern > prefixRankedPatternParserRegister3 = xmlApi < alib::Object >::ParserRegister < tree::PrefixRankedPattern > ( tree::PrefixRankedPattern::XML_TAG_NAME, tree::PrefixRankedPattern::parse );

auto PrefixRankedPatternFromRankedPattern = castApi::CastRegister < tree::PrefixRankedPattern, tree::RankedPattern > ( );
auto PrefixRankedPatternCastBinder = castApi::CastPoolStringBinder < tree::PrefixRankedPattern > ( tree::PrefixRankedPattern::XML_TAG_NAME );

} /* namespace alib */