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

#include "RankedNonlinearPattern.h"
#include "../TreeException.h"

#include <iostream>
#include <algorithm>
#include <sstream>

#include "../unranked/UnrankedPattern.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 <cast/CastApi.hpp>

namespace tree {

RankedNonlinearPattern::RankedNonlinearPattern ( alphabet::RankedSymbol subtreeWildcard, std::set < alphabet::RankedSymbol > nonlinearVariables, std::set < alphabet::RankedSymbol > alphabet, RankedNode pattern ) : std::Components < RankedNonlinearPattern, alphabet::RankedSymbol, std::tuple < GeneralAlphabet, NonlinearAlphabet >, std::tuple < SubtreeWildcard > > ( std::make_tuple ( std::move ( alphabet ), std::move ( nonlinearVariables ) ), std::make_tuple ( std::move ( subtreeWildcard ) ) ), pattern ( nullptr ) {
	setTree ( std::move ( pattern ) );
}

RankedNonlinearPattern::RankedNonlinearPattern ( alphabet::RankedSymbol subtreeWildcard, std::set < alphabet::RankedSymbol > nonlinearVariables, RankedNode pattern ) : RankedNonlinearPattern ( subtreeWildcard, nonlinearVariables, pattern.computeMinimalAlphabet() + nonlinearVariables + std::set < alphabet::RankedSymbol > { subtreeWildcard }, pattern ) {
}

RankedNonlinearPattern::RankedNonlinearPattern ( alphabet::RankedSymbol subtreeWildcard, RankedNode pattern ) : RankedNonlinearPattern ( subtreeWildcard, std::set < alphabet::RankedSymbol > { }, pattern ) {
}

RankedNonlinearPattern::RankedNonlinearPattern ( const RankedNonlinearPattern & other ) : std::Components < RankedNonlinearPattern, alphabet::RankedSymbol, std::tuple < GeneralAlphabet, NonlinearAlphabet >, std::tuple < SubtreeWildcard > > ( std::make_tuple ( other.getAlphabet ( ), other.getNonlinearVariables ( ) ), std::make_tuple ( other.getSubtreeWildcard ( ) ) ), pattern ( other.pattern ) {
	this->pattern->attachAlphabet ( & ( this->getAlphabet() ) );
}

RankedNonlinearPattern::RankedNonlinearPattern ( RankedNonlinearPattern && other ) noexcept : std::Components < RankedNonlinearPattern, alphabet::RankedSymbol, std::tuple < GeneralAlphabet, NonlinearAlphabet >, std::tuple < SubtreeWildcard > > ( std::make_tuple ( std::move ( other.accessComponent < GeneralAlphabet > ( ).get ( ) ), std::move ( other.accessComponent < NonlinearAlphabet > ( ).get ( ) ) ), std::make_tuple ( std::move ( other.accessElement < SubtreeWildcard > ( ).get ( ) ) ) ), pattern ( std::move ( other.pattern ) ) {
	this->pattern->attachAlphabet ( & ( this->getAlphabet() ) );
}

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

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

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

	* this = RankedNonlinearPattern ( other );

	return * this;
}

RankedNonlinearPattern & RankedNonlinearPattern::operator =( RankedNonlinearPattern && other ) noexcept {
	std::swap ( this->pattern, other.pattern );
	std::swap ( accessComponent < GeneralAlphabet > ( ).get ( ), other.accessComponent < GeneralAlphabet > ( ).get ( ) );
	std::swap ( accessComponent < NonlinearAlphabet > ( ).get ( ), other.accessComponent < NonlinearAlphabet > ( ).get ( ) );
	std::swap ( accessElement < SubtreeWildcard > ( ).get ( ), other.accessElement < SubtreeWildcard > ( ).get ( ) );

	return * this;
}

RankedNonlinearPattern::~RankedNonlinearPattern ( ) noexcept {
}

const RankedNode & RankedNonlinearPattern::getRoot ( ) const {
	return * pattern;
}

RankedNode & RankedNonlinearPattern::getRoot ( ) {
	return * pattern;
}

void RankedNonlinearPattern::setTree ( RankedNode pattern ) {
	this->pattern = std::make_smart < RankedNode > ( std::move ( pattern ) );

	if ( !this->pattern->attachAlphabet ( & ( this->getAlphabet ( ) ) ) )
		throw TreeException ( "Input symbols not in the alphabet." );
}

void RankedNonlinearPattern::operator >>( std::ostream & out ) const {
	out << "(RankedNonlinearPattern " << * ( this->pattern ) << ")";
}

int RankedNonlinearPattern::compare ( const RankedNonlinearPattern & other ) const {
	auto first = std::tie ( * pattern, getAlphabet(), getSubtreeWildcard(), getNonlinearVariables() );
	auto second = std::tie ( * other.pattern, other.getAlphabet(), other.getSubtreeWildcard(), getNonlinearVariables() );

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

	return comp ( first, second );
}

void RankedNonlinearPattern::nicePrint ( std::ostream & os ) const {
	pattern->nicePrint ( os );
}

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

RankedNonlinearPattern RankedNonlinearPattern::parse ( std::deque < sax::Token >::iterator & input ) {
	sax::FromXMLParserHelper::popToken ( input, sax::Token::TokenType::START_ELEMENT, RankedNonlinearPattern::getXmlTagName() );
	alphabet::RankedSymbol subtreeWildcardSymbol = TreeFromXMLParser::parseSubtreeWildcardRankedSymbol ( input );
	std::set < alphabet::RankedSymbol > nonlinearVariables = TreeFromXMLParser::parseRankedNonlinearVariables ( input );
	std::set < alphabet::RankedSymbol > rankedAlphabet = TreeFromXMLParser::parseRankedAlphabet ( input );
	RankedNode root = alib::xmlApi < RankedNode >::parse ( input );
	RankedNonlinearPattern tree ( std::move ( subtreeWildcardSymbol ), std::move ( nonlinearVariables ), std::move ( rankedAlphabet ), std::move ( root ) );

	sax::FromXMLParserHelper::popToken ( input, sax::Token::TokenType::END_ELEMENT, RankedNonlinearPattern::getXmlTagName() );
	return tree;
}

void RankedNonlinearPattern::compose ( std::deque < sax::Token > & out ) const {
	out.emplace_back ( RankedNonlinearPattern::getXmlTagName(), sax::Token::TokenType::START_ELEMENT );
	TreeToXMLComposer::composeSubtreeWildcard ( out, getSubtreeWildcard() );
	TreeToXMLComposer::composeNonlinearVariables ( out, getNonlinearVariables() );
	TreeToXMLComposer::composeAlphabet ( out, getAlphabet() );
	alib::xmlApi < RankedNode >::compose ( out, getRoot() );
	out.emplace_back ( RankedNonlinearPattern::getXmlTagName(), sax::Token::TokenType::END_ELEMENT );
}

} /* namespace tree */

namespace std {

template < >
bool tree::RankedNonlinearPattern::Component < tree::RankedNonlinearPattern, alphabet::RankedSymbol, tree::GeneralAlphabet >::used ( const alphabet::RankedSymbol & symbol ) const {
	const tree::RankedNonlinearPattern * pattern = static_cast < const tree::RankedNonlinearPattern * > ( this );

	return pattern->getRoot ( ).testSymbol ( symbol ) || pattern->accessElement < tree::SubtreeWildcard > ( ).get ( ) == symbol || pattern->accessComponent < tree::NonlinearAlphabet > ( ).get ( ).count ( symbol );
}

template < >
bool tree::RankedNonlinearPattern::Component < tree::RankedNonlinearPattern, alphabet::RankedSymbol, tree::GeneralAlphabet >::available ( const alphabet::RankedSymbol & ) const {
	return true;
}

template < >
void tree::RankedNonlinearPattern::Component < tree::RankedNonlinearPattern, alphabet::RankedSymbol, tree::GeneralAlphabet >::valid ( const alphabet::RankedSymbol & ) const {
}

template < >
bool tree::RankedNonlinearPattern::Component < tree::RankedNonlinearPattern, alphabet::RankedSymbol, tree::NonlinearAlphabet >::used ( const alphabet::RankedSymbol & ) const {
	return false;
}

template < >
bool tree::RankedNonlinearPattern::Component < tree::RankedNonlinearPattern, alphabet::RankedSymbol, tree::NonlinearAlphabet >::available ( const alphabet::RankedSymbol & symbol ) const {
	const tree::RankedNonlinearPattern * pattern = static_cast < const tree::RankedNonlinearPattern * > ( this );

	return pattern->accessComponent < tree::GeneralAlphabet > ( ).get ( ).count ( symbol );
}

template < >
void tree::RankedNonlinearPattern::Component < tree::RankedNonlinearPattern, alphabet::RankedSymbol, tree::NonlinearAlphabet >::valid ( const alphabet::RankedSymbol & symbol ) const {
	const tree::RankedNonlinearPattern * pattern = static_cast < const tree::RankedNonlinearPattern * > ( this );

	if( symbol.getRank().getData() != 0 )
		throw tree::TreeException ( "SubtreeWildcard symbol has nonzero arity" );

	if ( pattern->accessElement < tree::SubtreeWildcard > ( ).get ( ) == symbol )
		throw tree::TreeException ( "Symbol " + ( std::string ) symbol + "cannot be set as nonlinear variable since it is already subtree wildcard" );
}

template < >
bool tree::RankedNonlinearPattern::Element < tree::RankedNonlinearPattern, alphabet::RankedSymbol, tree::SubtreeWildcard >::available ( const alphabet::RankedSymbol & symbol ) const {
	const tree::RankedNonlinearPattern * pattern = static_cast < const tree::RankedNonlinearPattern * > ( this );

	return pattern->accessComponent < tree::GeneralAlphabet > ( ).get ( ).count ( symbol );
}

template < >
void tree::RankedNonlinearPattern::Element < tree::RankedNonlinearPattern, alphabet::RankedSymbol, tree::SubtreeWildcard >::valid ( const alphabet::RankedSymbol & symbol ) const {
	const tree::RankedNonlinearPattern * pattern = static_cast < const tree::RankedNonlinearPattern * > ( this );

	if( symbol.getRank().getData() != 0 )
		throw tree::TreeException ( "SubtreeWildcard symbol has nonzero arity" );

	if ( pattern->accessComponent < tree::NonlinearAlphabet > ( ).get ( ).count ( symbol ) )
		throw tree::TreeException ( "Symbol " + ( std::string ) symbol + "cannot be set as subtree wildcard since it is already nonlinear variable" );
}

} /* namespace std */

namespace alib {

auto RankedNonlinearPatternParserRegister  = xmlApi < tree::Tree >::ParserRegister < tree::RankedNonlinearPattern > ( );
auto RankedNonlinearPatternParserRegister2 = xmlApi < tree::RankedTreeWrapper >::ParserRegister < tree::RankedNonlinearPattern > ( );
auto RankedNonlinearPatternParserRegister3 = xmlApi < alib::Object >::ParserRegister < tree::RankedNonlinearPattern > ( );

} /* namespace alib */