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

#pragma once

#include <alib/memory>
#include <alib/utility>

#include <rte/formal/FormalRTEElements.h>
#include <rte/formal/FormalRTEStructure.h>
#include <rte/formal/FormalRTE.h>
#include <core/stringApi.hpp>

#include <rte/RTEFromStringLexer.h>

namespace core {

template < class SymbolType >
struct stringApi < rte::FormalRTEStructure < SymbolType > > {
	static rte::FormalRTEStructure < SymbolType > parse ( std::istream & input );
	static bool first ( std::istream & input );
	static void compose ( std::ostream & output, const rte::FormalRTEStructure < SymbolType > & rte );
private:
	static ext::ptr_value < rte::FormalRTEElement < SymbolType > > alternation(std::istream & input);
	static ext::ptr_value < rte::FormalRTEElement < SymbolType > > alternationCont(std::istream & input, ext::ptr_value < rte::FormalRTEElement < SymbolType > > left);

	static ext::ptr_value < rte::FormalRTEElement < SymbolType > > substitution(std::istream & input);
	static ext::ptr_value < rte::FormalRTEElement < SymbolType > > substitutionCont(std::istream & input, ext::ptr_value < rte::FormalRTEElement < SymbolType > > left);

	static ext::ptr_value < rte::FormalRTEElement < SymbolType > > factor(std::istream & input);
	static ext::ptr_value < rte::FormalRTEElement < SymbolType > > star(std::istream & input, rte::FormalRTEElement < SymbolType > && elem);
	static rte::FormalRTESymbolSubst < SymbolType > substitutionSymbol ( std::istream & input );
	static ext::ptr_vector < rte::FormalRTEElement < SymbolType > > arguments ( std::istream & input );

	enum class Priority {
		ALTERNATION,
		CONCATENATION,
		FACTOR
	};

	class Formal {
	public:
		static void visit( const rte::FormalRTEAlternation < SymbolType > & alternation, ext::tuple < Priority &, std::ostream & > & output);
		static void visit( const rte::FormalRTESubstitution < SymbolType > & substitution, ext::tuple < Priority &, std::ostream & > & output);
		static void visit( const rte::FormalRTEIteration < SymbolType > & iteration, ext::tuple < Priority &, std::ostream & > & output);
		static void visit( const rte::FormalRTESymbolAlphabet < SymbolType > & symbol, ext::tuple < Priority &, std::ostream & > & output);
		static void visit( const rte::FormalRTESymbolSubst < SymbolType > & symbol, ext::tuple < Priority &, std::ostream & > & output);
		static void visit( const rte::FormalRTEEmpty < SymbolType > & empty, ext::tuple < Priority &, std::ostream & > & output);
	};

	class FormalReplace {
	public:
		static std::optional < rte::FormalRTESymbolSubst < SymbolType > > visit( rte::FormalRTEAlternation < SymbolType > & alternation, ext::set < common::ranked_symbol < SymbolType > > & constants);
		static std::optional < rte::FormalRTESymbolSubst < SymbolType > > visit( rte::FormalRTESubstitution < SymbolType > & substitution, ext::set < common::ranked_symbol < SymbolType > > & constants);
		static std::optional < rte::FormalRTESymbolSubst < SymbolType > > visit( rte::FormalRTEIteration < SymbolType > & iteration, ext::set < common::ranked_symbol < SymbolType > > & constants);
		static std::optional < rte::FormalRTESymbolSubst < SymbolType > > visit( rte::FormalRTESymbolAlphabet < SymbolType > & symbol, ext::set < common::ranked_symbol < SymbolType > > & constants);
		static std::optional < rte::FormalRTESymbolSubst < SymbolType > > visit( rte::FormalRTESymbolSubst < SymbolType > & epsilon, ext::set < common::ranked_symbol < SymbolType > > & constants);
		static std::optional < rte::FormalRTESymbolSubst < SymbolType > > visit( rte::FormalRTEEmpty < SymbolType > & empty, ext::set < common::ranked_symbol < SymbolType > > & constants);
	};
};

template < class SymbolType >
rte::FormalRTEStructure < SymbolType > stringApi < rte::FormalRTEStructure < SymbolType > >::parse ( std::istream & input ) {
	rte::FormalRTEStructure < SymbolType > res ( alternation ( input ) );
	ext::set < common::ranked_symbol < SymbolType > > constants = res.getStructure ( ).computeMinimalAlphabets ( ).second;
	rte::FormalRTEElement < SymbolType > & elem = res.getStructure ( );
	elem.template accept < std::optional < rte::FormalRTESymbolSubst < SymbolType > >, stringApi < rte::FormalRTEStructure < SymbolType > >::FormalReplace > ( constants );
	return res;
}

template < class SymbolType >
ext::ptr_value < rte::FormalRTEElement < SymbolType > > stringApi < rte::FormalRTEStructure < SymbolType > >::alternation(std::istream & input) {
	return alternationCont(input, substitution(input));
}

template < class SymbolType >
ext::ptr_value < rte::FormalRTEElement < SymbolType > > stringApi < rte::FormalRTEStructure < SymbolType > >::alternationCont(std::istream & input, ext::ptr_value < rte::FormalRTEElement < SymbolType > > left) {
	rte::RTEFromStringLexer::Token token = rte::RTEFromStringLexer::next(input);
	if(token.type == rte::RTEFromStringLexer::TokenType::PLUS) {
		ext::ptr_value < rte::FormalRTEElement < SymbolType > > res ( rte::FormalRTEAlternation < SymbolType > ( std::move ( left ), substitution ( input ) ) );
		return alternationCont ( input, std::move ( res ) );
	} else {
		rte::RTEFromStringLexer::putback(input, token);
		return left;
	}
}

template < class SymbolType >
ext::ptr_value < rte::FormalRTEElement < SymbolType > > stringApi < rte::FormalRTEStructure < SymbolType > >::substitution(std::istream & input) {
	return substitutionCont(input, factor(input));
}

template < class SymbolType >
ext::ptr_value < rte::FormalRTEElement < SymbolType > > stringApi < rte::FormalRTEStructure < SymbolType > >::substitutionCont(std::istream & input, ext::ptr_value < rte::FormalRTEElement < SymbolType > > left) {
	rte::RTEFromStringLexer::Token token = rte::RTEFromStringLexer::next(input);
	if ( token.type == rte::RTEFromStringLexer::TokenType::DOT ) {
		rte::FormalRTESymbolSubst < SymbolType > symbol = substitutionSymbol ( input );
		ext::ptr_value < rte::FormalRTEElement < SymbolType > > res ( rte::FormalRTESubstitution < SymbolType > ( std::move ( left ), factor ( input ), std::move ( symbol ) ) );
		return substitutionCont ( input, std::move ( res ) );
	} else {
		rte::RTEFromStringLexer::putback(input, token);
		return left;
	}
}
template < class SymbolType >
rte::FormalRTESymbolSubst < SymbolType > stringApi < rte::FormalRTEStructure < SymbolType > >::substitutionSymbol ( std::istream & input ) {
	SymbolType symbol = core::stringApi < SymbolType >::parse ( input );

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

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

	if ( rank != 0 )
		throw exception::CommonException ( "Substitution symbol must have zero rank." );

	return rte::FormalRTESymbolSubst < SymbolType > ( common::ranked_symbol < SymbolType > ( std::move ( symbol ), rank ) );
}

template < class SymbolType >
ext::ptr_value < rte::FormalRTEElement < SymbolType > > stringApi < rte::FormalRTEStructure < SymbolType > >::factor(std::istream & input) {
	rte::RTEFromStringLexer::Token token = rte::RTEFromStringLexer::next(input);
	if(token.type == rte::RTEFromStringLexer::TokenType::LPAR) {
		ext::ptr_value < rte::FormalRTEElement < SymbolType > > base = alternation(input);
		token = rte::RTEFromStringLexer::next(input);
		if(token.type != rte::RTEFromStringLexer::TokenType::RPAR) throw exception::CommonException("Expected RPAR");
		return star(input, std::move ( base ) );
	} else if(token.type == rte::RTEFromStringLexer::TokenType::EMPTY) {
		return star(input, ext::ptr_value < rte::FormalRTEElement < SymbolType > > ( rte::FormalRTEEmpty < SymbolType > ( ) ) );
	} else if(token.type == rte::RTEFromStringLexer::TokenType::ERROR) {
		rte::RTEFromStringLexer::putback(input, token);

		SymbolType symbol = core::stringApi < SymbolType >::parse ( input );

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

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

		ext::ptr_vector < rte::FormalRTEElement < SymbolType > > children = arguments ( input );

		rte::FormalRTESymbolAlphabet < SymbolType > res ( common::ranked_symbol < SymbolType > ( std::move ( symbol ), rank ), children );
		return star ( input, std::move ( res ) );
	} else {
		throw exception::CommonException("Unrecognised token at factor rule");
	}
}

template < class SymbolType >
ext::ptr_value < rte::FormalRTEElement < SymbolType > > stringApi < rte::FormalRTEStructure < SymbolType > >::star(std::istream & input, rte::FormalRTEElement < SymbolType > && elem) {
	rte::RTEFromStringLexer::Token token = rte::RTEFromStringLexer::next(input);
	if(token.type == rte::RTEFromStringLexer::TokenType::STAR) {
		rte::FormalRTESymbolSubst < SymbolType > symbol = substitutionSymbol ( input );
		rte::FormalRTEIteration < SymbolType > iter ( std::move ( elem ), std::move ( symbol ) );
		return star ( input, std::move ( iter ) );
	} else {
		rte::RTEFromStringLexer::putback(input, token);
		return ext::ptr_value < rte::FormalRTEElement < SymbolType > > ( std::move ( elem ) );
	}
}

template < class SymbolType >
ext::ptr_vector < rte::FormalRTEElement < SymbolType > > stringApi < rte::FormalRTEStructure < SymbolType > >::arguments ( std::istream & input ) {
	rte::RTEFromStringLexer::Token token = rte::RTEFromStringLexer::next(input);
	if ( token.type != rte::RTEFromStringLexer::TokenType::LPAR ) {
		rte::RTEFromStringLexer::putback ( input, token );
		return { };
	}

	token = rte::RTEFromStringLexer::next(input);
	if ( token.type == rte::RTEFromStringLexer::TokenType::RPAR )
		return { };

	ext::ptr_vector < rte::FormalRTEElement < SymbolType > > res;
	rte::RTEFromStringLexer::putback ( input, token );
	do {
		res.push_back ( ( rte::FormalRTEElement < SymbolType > && ) alternation ( input ) );
		token = rte::RTEFromStringLexer::next ( input );
	} while ( token.type == rte::RTEFromStringLexer::TokenType::COMMA );

	if ( token.type != rte::RTEFromStringLexer::TokenType::RPAR )
		throw exception::CommonException("Expected RPAR");

	return res;
}

template < class SymbolType >
std::optional < rte::FormalRTESymbolSubst < SymbolType > > stringApi < rte::FormalRTEStructure < SymbolType > >::FormalReplace::visit( rte::FormalRTEAlternation < SymbolType > & alternation, ext::set < common::ranked_symbol < SymbolType > > & constants) {
	std::optional < rte::FormalRTESymbolSubst < SymbolType > > left = alternation.getLeftElement ( ).template accept < std::optional < rte::FormalRTESymbolSubst < SymbolType > >, stringApi < rte::FormalRTEStructure < SymbolType > >::FormalReplace > ( constants );
	if ( left )
		alternation.setLeftElement ( std::move ( left.value ( ) ) );

	std::optional < rte::FormalRTESymbolSubst < SymbolType > > right = alternation.getRightElement ( ).template accept < std::optional < rte::FormalRTESymbolSubst < SymbolType > >, stringApi < rte::FormalRTEStructure < SymbolType > >::FormalReplace > ( constants );
	if ( right )
		alternation.setRightElement ( std::move ( right.value ( ) ) );

	return std::nullopt;
}

template < class SymbolType >
std::optional < rte::FormalRTESymbolSubst < SymbolType > > stringApi < rte::FormalRTEStructure < SymbolType > >::FormalReplace::visit( rte::FormalRTESubstitution < SymbolType > & substitution, ext::set < common::ranked_symbol < SymbolType > > & constants) {
	std::optional < rte::FormalRTESymbolSubst < SymbolType > > left = substitution.getLeftElement ( ).template accept < std::optional < rte::FormalRTESymbolSubst < SymbolType > >, stringApi < rte::FormalRTEStructure < SymbolType > >::FormalReplace > ( constants );
	if ( left )
		substitution.setLeftElement ( std::move ( left.value ( ) ) );

	std::optional < rte::FormalRTESymbolSubst < SymbolType > > right = substitution.getRightElement ( ).template accept < std::optional < rte::FormalRTESymbolSubst < SymbolType > >, stringApi < rte::FormalRTEStructure < SymbolType > >::FormalReplace > ( constants );
	if ( right )
		substitution.setRightElement ( std::move ( right.value ( ) ) );

	return std::nullopt;
}

template < class SymbolType >
std::optional < rte::FormalRTESymbolSubst < SymbolType > > stringApi < rte::FormalRTEStructure < SymbolType > >::FormalReplace::visit( rte::FormalRTEIteration < SymbolType > & iteration, ext::set < common::ranked_symbol < SymbolType > > & constants) {
	std::optional < rte::FormalRTESymbolSubst < SymbolType > > element = iteration.getElement ( ).template accept < std::optional < rte::FormalRTESymbolSubst < SymbolType > >, stringApi < rte::FormalRTEStructure < SymbolType > >::FormalReplace > ( constants );
	if ( element )
		iteration.setElement ( std::move ( element.value ( ) ) );

	return std::nullopt;
}

template < class SymbolType >
std::optional < rte::FormalRTESymbolSubst < SymbolType > > stringApi < rte::FormalRTEStructure < SymbolType > >::FormalReplace::visit( rte::FormalRTESymbolAlphabet < SymbolType > & symbol, ext::set < common::ranked_symbol < SymbolType > > & constants) {
	if ( constants.count ( symbol.getSymbol ( ) ) )
		return rte::FormalRTESymbolSubst < SymbolType > ( symbol.getSymbol ( ) );

	for ( unsigned i = 0; i < symbol.getElements ( ).size ( ); ++ i ) {
		rte::FormalRTEElement < SymbolType > & elem = symbol.getElement ( i );
		std::optional < rte::FormalRTESymbolSubst < SymbolType > > res = elem.template accept < std::optional < rte::FormalRTESymbolSubst < SymbolType > >, stringApi < rte::FormalRTEStructure < SymbolType > >::FormalReplace > ( constants );
		if ( res )
			symbol.setElement ( i, std::move ( res.value ( ) ) );
	}

	return std::nullopt;
}

template < class SymbolType >
std::optional < rte::FormalRTESymbolSubst < SymbolType > > stringApi < rte::FormalRTEStructure < SymbolType > >::FormalReplace::visit( rte::FormalRTESymbolSubst < SymbolType > &, ext::set < common::ranked_symbol < SymbolType > > & ) {
	return std::nullopt;
}

template < class SymbolType >
std::optional < rte::FormalRTESymbolSubst < SymbolType > > stringApi < rte::FormalRTEStructure < SymbolType > >::FormalReplace::visit( rte::FormalRTEEmpty < SymbolType > &, ext::set < common::ranked_symbol < SymbolType > > & ) {
	return std::nullopt;
}

template < class SymbolType >
bool stringApi < rte::FormalRTEStructure < SymbolType > >::first ( std::istream & ) {
	return true;
}

template < class SymbolType >
void stringApi < rte::FormalRTEStructure < SymbolType > >::compose ( std::ostream & output, const rte::FormalRTEStructure < SymbolType > & rte ) {
	Priority tmp = Priority::ALTERNATION;
	ext::tuple < Priority &, std::ostream & > out = ext::tie ( tmp, output );
	rte.getStructure ( ).template accept < void, stringApi < rte::FormalRTEStructure < SymbolType > >::Formal > ( out );
}

template < class SymbolType >
void stringApi < rte::FormalRTEStructure < SymbolType > >::Formal::visit( const rte::FormalRTEAlternation < SymbolType > & alternation, ext::tuple < Priority &, std::ostream & > & output ) {
	Priority outerPriorityMinimum = std::get < 0 > ( output );
	if ( outerPriorityMinimum == Priority::CONCATENATION || outerPriorityMinimum == Priority::FACTOR ) std::get < 1 > ( output ) << '(';

	std::get < 0 > ( output ) = Priority::ALTERNATION;
	alternation.getLeftElement ( ).template accept < void, stringApi < rte::FormalRTEStructure < SymbolType > >::Formal > ( output );
	std::get < 1 > ( output ) << '+';
	alternation.getRightElement ( ).template accept < void, stringApi < rte::FormalRTEStructure < SymbolType > >::Formal > ( output );

	if ( outerPriorityMinimum == Priority::CONCATENATION || outerPriorityMinimum == Priority::FACTOR ) std::get < 1 > ( output ) << ')';
}

template < class SymbolType >
void stringApi < rte::FormalRTEStructure < SymbolType > >::Formal::visit( const rte::FormalRTESubstitution < SymbolType > & substitution, ext::tuple < Priority &, std::ostream & > & output ) {
	Priority outerPriorityMinimum = std::get < 0 > ( output );
	if ( outerPriorityMinimum == Priority::FACTOR ) std::get < 1 > ( output ) << '(';

	std::get < 0 > ( output ) = Priority::CONCATENATION;
	substitution.getLeftElement ( ).template accept < void, stringApi < rte::FormalRTEStructure < SymbolType > >::Formal > ( output );
	std::get < 1 > ( output ) << '.';
	stringApi < rte::FormalRTEStructure < SymbolType > >::Formal::visit ( substitution.getSubstitutionSymbol ( ), output );
	std::get < 1 > ( output ) << ' ';
	std::get < 0 > ( output ) = Priority::CONCATENATION;
	substitution.getRightElement ( ).template accept < void, stringApi < rte::FormalRTEStructure < SymbolType > >::Formal > ( output );

	if ( outerPriorityMinimum == Priority::FACTOR ) std::get < 1 > ( output ) << ')';
}

template < class SymbolType >
void stringApi < rte::FormalRTEStructure < SymbolType > >::Formal::visit( const rte::FormalRTEIteration < SymbolType > & iteration, ext::tuple < Priority &, std::ostream & > & output ) {
	std::get < 0 > ( output ) = Priority::FACTOR;
	iteration.getElement ( ).template accept < void, stringApi < rte::FormalRTEStructure < SymbolType > >::Formal > ( output );
	std::get < 1 > ( output ) << "*";
	stringApi < rte::FormalRTEStructure < SymbolType > >::Formal::visit ( iteration.getSubstitutionSymbol ( ), output );
}

template < class SymbolType >
void stringApi < rte::FormalRTEStructure < SymbolType > >::Formal::visit( const rte::FormalRTESymbolAlphabet < SymbolType > & symbol, ext::tuple < Priority &, std::ostream & > & output ) {
	core::stringApi < SymbolType >::compose ( std::get < 1 > ( output ), symbol.getSymbol ( ).getSymbol ( ) );

	std::get < 1 > ( output ) << " " << ext::to_string ( symbol.getSymbol ( ).getRank ( ) );

	if ( symbol.getElements ( ).empty ( ) )
		return;

	std::get < 1 > ( output ) << " ( ";

	bool first = true;
	for ( const rte::FormalRTEElement < SymbolType > & c : symbol.getElements ( ) ) {
		if ( first )
			first = false;
		else
			std::get < 1 > ( output ) << ", ";

		std::get < 0 > ( output ) = Priority::ALTERNATION;
		c.template accept < void, stringApi < rte::FormalRTEStructure < SymbolType > >::Formal > ( output );
	}

	std::get < 1 > ( output ) << ")";
}

template < class SymbolType >
void stringApi < rte::FormalRTEStructure < SymbolType > >::Formal::visit( const rte::FormalRTESymbolSubst < SymbolType > & symbol, ext::tuple < Priority &, std::ostream & > & output ) {
	core::stringApi < SymbolType >::compose ( std::get < 1 > ( output ), symbol.getSymbol ( ).getSymbol ( ) );

	std::get < 1 > ( output ) << " " << ext::to_string ( symbol.getSymbol ( ).getRank ( ) );
}

template < class SymbolType >
void stringApi < rte::FormalRTEStructure < SymbolType > >::Formal::visit( const rte::FormalRTEEmpty < SymbolType > &, ext::tuple < Priority &, std::ostream & > & output ) {
	std::get < 1 > ( output ) << "#0";
}

template < class SymbolType >
struct stringApi < rte::FormalRTE < SymbolType > > {
	static rte::FormalRTE < SymbolType > parse ( std::istream & input );
	static bool first ( std::istream & input );
	static void compose ( std::ostream & output, const rte::FormalRTE < SymbolType > & rte );
};

template < class SymbolType >
rte::FormalRTE < SymbolType > stringApi < rte::FormalRTE < SymbolType > >::parse ( std::istream & input ) {
	return rte::FormalRTE < SymbolType > ( core::stringApi < rte::FormalRTEStructure < SymbolType > >::parse ( input ) );
}

template < class SymbolType >
bool stringApi < rte::FormalRTE < SymbolType > >::first ( std::istream & input ) {
	return core::stringApi < rte::FormalRTEStructure < SymbolType > >::first ( input );
}

template < class SymbolType >
void stringApi < rte::FormalRTE < SymbolType > >::compose ( std::ostream & output, const rte::FormalRTE < SymbolType > & rte ) {
	core::stringApi < rte::FormalRTEStructure < SymbolType > >::compose ( output, rte.getRTE ( ) );
}

} /* namespace core */

