/*
 * TreeFromStringParserCommon.h
 *
 * Created on: Sep 27, 2017
 * Author: Jan Travnicek
 */

#pragma once

#include <alib/vector>

#include <core/stringApi.hpp>

#include <string/StringFromStringLexer.h>

#include <alphabet/WildcardSymbol.h>
#include <alphabet/NonlinearVariableSymbol.h>

namespace tree {

class TreeFromStringParserCommon {
public:
	template < class SymbolType >
	static ext::tree < common::ranked_symbol < SymbolType > > parseRankedContent ( std::istream & input, bool & isPattern, ext::set < common::ranked_symbol < SymbolType > > & nonlinearVariables );
	template < class SymbolType >
	static ext::tree < SymbolType > parseUnrankedContent ( std::istream & input, bool & isPattern, ext::set < SymbolType > & nonlinearVariables );
};

template < class SymbolType >
ext::tree < common::ranked_symbol < SymbolType > > TreeFromStringParserCommon::parseRankedContent ( std::istream & input, bool & isPattern, ext::set < common::ranked_symbol < SymbolType > > & nonlinearVariables ) {
	TreeFromStringLexer::Token token = TreeFromStringLexer::next ( input );

	if ( token.type == TreeFromStringLexer::TokenType::SUBTREE_WILDCARD ) {
		isPattern = true;
		return ext::tree < common::ranked_symbol < SymbolType > > ( alphabet::WildcardSymbol::instance < common::ranked_symbol < SymbolType > > ( ), { } );
	} else if ( token.type == TreeFromStringLexer::TokenType::NONLINEAR_VARIABLE ) {
		isPattern = true;
		common::ranked_symbol < SymbolType > nonlinearVariable ( SymbolType ( alphabet::NonlinearVariableSymbol < SymbolType > ( SymbolType ( token.value ) ) ), 0 );
		nonlinearVariables.insert ( nonlinearVariable );
		return ext::tree < common::ranked_symbol < SymbolType > > ( std::move ( nonlinearVariable ), { } );
	} else {
		TreeFromStringLexer::putback ( input, token );
		SymbolType symbol = core::stringApi < SymbolType >::parse ( input );

		token = TreeFromStringLexer::next ( input );
		if ( token.type != TreeFromStringLexer::TokenType::RANK )
			throw exception::CommonException ( "Missing rank" );

		unsigned rank = ext::from_string < unsigned > ( token.value );

		ext::vector < ext::tree < common::ranked_symbol < SymbolType > > > childs;
		for ( unsigned i = 0; i < rank; i++ )
			childs.emplace_back ( ext::tree < common::ranked_symbol < SymbolType > > ( parseRankedContent < SymbolType > ( input, isPattern, nonlinearVariables ) ) );

		return ext::tree < common::ranked_symbol < SymbolType > > ( common::ranked_symbol < SymbolType > ( std::move ( symbol ), rank ), std::move ( childs ) );
	}
}

template < class SymbolType >
ext::tree < SymbolType > TreeFromStringParserCommon::parseUnrankedContent ( std::istream & input, bool & isPattern, ext::set < SymbolType > & nonlinearVariables ) {
	TreeFromStringLexer::Token token = TreeFromStringLexer::next ( input );

	if ( token.type == TreeFromStringLexer::TokenType::SUBTREE_WILDCARD ) {
		token = TreeFromStringLexer::next ( input );

		if ( token.type != TreeFromStringLexer::TokenType::BAR )
			throw exception::CommonException ( "Missing bar" );

		isPattern = true;
		return ext::tree < SymbolType > ( alphabet::WildcardSymbol::instance < SymbolType > ( ), { } );
	} else if ( token.type == TreeFromStringLexer::TokenType::NONLINEAR_VARIABLE ) {
		token = TreeFromStringLexer::next ( input );

		if ( token.type != TreeFromStringLexer::TokenType::BAR )
			throw exception::CommonException ( "Missing bar" );

		isPattern = true;
		SymbolType nonlinearVariable ( alphabet::NonlinearVariableSymbol < SymbolType > ( SymbolType ( token.value ) ) );
		nonlinearVariables.insert ( nonlinearVariable );
		return ext::tree < SymbolType > ( std::move ( nonlinearVariable ), { } );
	} else {
		TreeFromStringLexer::putback ( input, token );
		SymbolType symbol = core::stringApi < SymbolType >::parse ( input );

		ext::vector < ext::tree < SymbolType > > childs;

		token = TreeFromStringLexer::next ( input );

		while ( token.type != TreeFromStringLexer::TokenType::BAR ) {
			TreeFromStringLexer::putback ( input, token );
			childs.emplace_back ( parseUnrankedContent < SymbolType > ( input, isPattern, nonlinearVariables ) );
			token = TreeFromStringLexer::next ( input );
		}

		if ( token.type != TreeFromStringLexer::TokenType::BAR )
			throw exception::CommonException ( "Missing bar" );

		return ext::tree < SymbolType > ( std::move ( symbol ), std::move ( childs ) );
	}
}

} /* namespace tree */

