/*
 * AddRawRule.h
 *
 *  Created on: 22. 3. 2014
 *	  Author: Tomas Pecka
 */

#ifndef _ADD_RAW_RULE_H__
#define _ADD_RAW_RULE_H__

#include <alib/set>
#include <alib/map>
#include <grammar/ContextFree/CFG.h>
#include <grammar/ContextFree/EpsilonFreeCFG.h>
#include <grammar/ContextFree/GNF.h>
#include <grammar/ContextFree/CNF.h>
#include <grammar/ContextFree/LG.h>
#include <grammar/Regular/LeftLG.h>
#include <grammar/Regular/LeftRG.h>
#include <grammar/Regular/RightLG.h>
#include <grammar/Regular/RightRG.h>

#include <grammar/Grammar.h>

namespace grammar {

/**
 * Implementation of transformation from grammar specific rules to common representation, i.e. A -> (N \cup T) where A \in N and N is set of nonterminal symbols and T is set of terminal symbols of the grammar.
 */
class AddRawRule {
public:
	/**
	 * Get rules in most common format of rules as mapping from leftHandSide to rightHandSides represented as vectors of symbols.
	 *
	 * \tparam TerminalSymbolType the type of terminal symbols in the grammar
	 * \tparam NonterminalSymbolType the type of nontermnal symbols in the grammar
	 *
	 * \param grammar the source grammar of rules to transform
	 *
	 * \returns rules of the grammar in a common representation
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static bool addRawRule ( LG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static bool addRawRule ( GNF < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static bool addRawRule ( EpsilonFreeCFG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static bool addRawRule ( CNF < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static bool addRawRule ( CFG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static bool addRawRule ( LeftLG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static bool addRawRule ( LeftRG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static bool addRawRule ( RightLG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static bool addRawRule ( RightRG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide );
};

template < class TerminalSymbolType, class NonterminalSymbolType >
bool AddRawRule::addRawRule ( LG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide ) {
	typename ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > >::iterator nonterminalPosition = rightHandSide.begin ( );

	for ( ; nonterminalPosition != rightHandSide.end ( ); ++nonterminalPosition )
		if ( grammar.getNonterminalAlphabet ( ).count ( * nonterminalPosition ) ) break;

	if ( nonterminalPosition == rightHandSide.end ( ) ) {
		ext::vector < TerminalSymbolType > rhs;

		for ( ext::variant < TerminalSymbolType, NonterminalSymbolType > & symbol : rightHandSide )
			rhs.push_back ( std::move ( symbol.template get < TerminalSymbolType > ( ) ) );

		return grammar.addRule ( std::move ( leftHandSide ), std::move ( rhs ) );
	} else {
		ext::vector < TerminalSymbolType > rhs1;
		ext::vector < TerminalSymbolType > rhs2;

		for ( ext::variant < TerminalSymbolType, NonterminalSymbolType > & symbol : ext::make_iterator_range ( rightHandSide.begin ( ), nonterminalPosition ) )
			rhs1.push_back ( std::move ( symbol.template get < TerminalSymbolType > ( ) ) );
		for ( ext::variant < TerminalSymbolType, NonterminalSymbolType > & symbol : ext::make_iterator_range ( std::next ( nonterminalPosition ), rightHandSide.end ( ) ) )
			rhs2.push_back ( std::move ( symbol.template get < TerminalSymbolType > ( ) ) );

		return grammar.addRule ( leftHandSide, ext::make_tuple ( std::move ( rhs1 ), std::move ( nonterminalPosition->template get < NonterminalSymbolType > ( ) ), std::move ( rhs2 ) ) );
	}
}

template < class TerminalSymbolType, class NonterminalSymbolType >
bool AddRawRule::addRawRule ( GNF < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide ) {
	if ( rightHandSide.empty ( ) ) {
		if ( leftHandSide != grammar.getInitialSymbol ( ) )
			throw GrammarException ( "Illegal left hand side of epsilon rule" );

		bool res = grammar.getGeneratesEpsilon ( );
		grammar.setGeneratesEpsilon ( true );
		return !res;
	} else {
		TerminalSymbolType first = std::move ( rightHandSide[0].template get < TerminalSymbolType > ( ) );

		ext::vector < NonterminalSymbolType > rest;
		for ( ext::variant < TerminalSymbolType, NonterminalSymbolType > & element : ext::make_iterator_range ( rightHandSide.begin ( ) + 1, rightHandSide.end ( ) ) )
			rest.push_back ( std::move ( element.template get < NonterminalSymbolType > ( ) ) );

		return grammar.addRule ( std::move ( leftHandSide ), ext::make_pair ( std::move ( first ), std::move ( rest ) ) );
	}
}

template < class TerminalSymbolType, class NonterminalSymbolType >
bool AddRawRule::addRawRule ( EpsilonFreeCFG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide ) {
	if ( rightHandSide.empty ( ) ) {
		if ( leftHandSide != grammar.getInitialSymbol ( ) )
			throw GrammarException ( "Illegal left hand side of epsilon rule" );

		bool res = grammar.getGeneratesEpsilon ( );
		grammar.setGeneratesEpsilon ( true );
		return ! res;
	} else {
		return grammar.addRule ( std::move ( leftHandSide ), std::move ( rightHandSide ) );
	}
}

template < class TerminalSymbolType, class NonterminalSymbolType >
bool AddRawRule::addRawRule ( CNF < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide ) {
	if ( rightHandSide.empty ( ) ) {
		if ( leftHandSide != grammar.getInitialSymbol ( ) )
			throw GrammarException ( "Illegal left hand side of epsilon rule" );

		bool res = grammar.getGeneratesEpsilon ( );
		grammar.setGeneratesEpsilon ( true );
		return ! res;
	} else if ( rightHandSide.size ( ) == 1 ) {
		return grammar.addRule ( std::move ( leftHandSide ), std::move ( rightHandSide [ 0 ].template get < TerminalSymbolType > ( ) ) );
	} else if ( rightHandSide.size ( ) == 2 ) {
		return grammar.addRule ( std::move ( leftHandSide ), ext::make_pair ( std::move ( rightHandSide [ 0 ].template get < NonterminalSymbolType > ( ) ), std::move ( rightHandSide [ 1 ].template get < NonterminalSymbolType > ( ) ) ) );
	} else {
		throw GrammarException ( "Invalid right hand side" );
	}
}

template < class TerminalSymbolType, class NonterminalSymbolType >
bool AddRawRule::addRawRule ( CFG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide ) {
	return grammar.addRule ( std::move ( leftHandSide ), std::move ( rightHandSide ) );
}

template < class TerminalSymbolType, class NonterminalSymbolType >
bool AddRawRule::addRawRule ( LeftLG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide ) {
	if ( rightHandSide.empty ( ) )
		return grammar.addRule ( std::move ( leftHandSide ), ext::vector < TerminalSymbolType > { } );
	else if ( grammar.getNonterminalAlphabet ( ).count ( rightHandSide [ 0 ] ) ) {
		ext::vector < TerminalSymbolType > rhs;
		for ( ext::variant < TerminalSymbolType, NonterminalSymbolType > & element : ext::make_iterator_range ( rightHandSide.begin ( ) + 1, rightHandSide.end ( ) ) )
			rhs.push_back ( std::move ( element.template get < TerminalSymbolType > ( ) ) );

		return grammar.addRule ( std::move ( leftHandSide ), ext::make_pair ( std::move ( rightHandSide [ 0 ].template get < NonterminalSymbolType > ( ) ), std::move ( rhs ) ) );
	} else {
		ext::vector < TerminalSymbolType > rhs;
		for ( ext::variant < TerminalSymbolType, NonterminalSymbolType > & element : ext::make_iterator_range ( rightHandSide.begin ( ), rightHandSide.end ( ) ) )
			rhs.push_back ( std::move ( element.template get < TerminalSymbolType > ( ) ) );

		return grammar.addRule ( std::move ( leftHandSide ), std::move ( rhs ) );
	}
}

template < class TerminalSymbolType, class NonterminalSymbolType >
bool AddRawRule::addRawRule ( LeftRG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide ) {
	if ( rightHandSide.empty ( ) ) {
		if ( leftHandSide != grammar.getInitialSymbol ( ) )
			throw GrammarException ( "Illegal left hand side of epsilon rule" );

		bool res = grammar.getGeneratesEpsilon ( );
		grammar.setGeneratesEpsilon ( true );
		return res;
	} else if ( rightHandSide.size ( ) == 1 ) {
		return grammar.addRule ( std::move ( leftHandSide ), std::move ( rightHandSide [ 0 ].template get < TerminalSymbolType > ( ) ) );
	} else if ( rightHandSide.size ( ) == 2 ) {
		return grammar.addRule ( std::move ( leftHandSide ), ext::make_pair ( std::move ( rightHandSide [ 0 ].template get < NonterminalSymbolType > ( ) ), std::move ( rightHandSide [ 1 ].template get < TerminalSymbolType > ( ) ) ) );
	} else {
		throw GrammarException ( "Invalid right hand side" );
	}
}

template < class TerminalSymbolType, class NonterminalSymbolType >
bool AddRawRule::addRawRule ( RightRG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide ) {
	if ( rightHandSide.empty ( ) ) {
		if ( leftHandSide != grammar.getInitialSymbol ( ) )
			throw GrammarException ( "Illegal left hand side of epsilon rule" );

		bool res = grammar.getGeneratesEpsilon ( );
		grammar.setGeneratesEpsilon ( true );
		return res;
	} else if ( rightHandSide.size ( ) == 1 ) {
		return grammar.addRule ( std::move ( leftHandSide ), std::move ( rightHandSide[0].template get < TerminalSymbolType > ( ) ) );
	} else if ( rightHandSide.size ( ) == 2 ) {
		return grammar.addRule ( std::move ( leftHandSide ), ext::make_pair ( std::move ( rightHandSide[0].template get < TerminalSymbolType > ( ) ), std::move ( rightHandSide[1].template get < NonterminalSymbolType > ( ) ) ) );
	} else {
		throw GrammarException ( "Invalid right hand side" );
	}
}

template < class TerminalSymbolType, class NonterminalSymbolType >
bool AddRawRule::addRawRule ( RightLG < TerminalSymbolType, NonterminalSymbolType > & grammar, NonterminalSymbolType leftHandSide, ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > rightHandSide ) {
	if ( rightHandSide.empty ( ) )
		return grammar.addRule ( std::move ( leftHandSide ), ext::vector < TerminalSymbolType > { } );
	else if ( grammar.getNonterminalAlphabet ( ).count ( rightHandSide [ rightHandSide.size ( ) - 1 ] ) ) {
		ext::vector < TerminalSymbolType > rhs;
		for ( ext::variant < TerminalSymbolType, NonterminalSymbolType > & element : ext::make_iterator_range ( rightHandSide.begin ( ), rightHandSide.end ( ) - 1 ) )
			rhs.push_back ( std::move ( element.template get < TerminalSymbolType > ( ) ) );

		return grammar.addRule ( std::move ( leftHandSide ), ext::make_pair ( std::move ( rhs ), std::move ( rightHandSide [ rightHandSide.size ( ) - 1 ].template get < NonterminalSymbolType > ( ) ) ) );
	} else {
		ext::vector < TerminalSymbolType > rhs;
		for ( ext::variant < TerminalSymbolType, NonterminalSymbolType > & element : ext::make_iterator_range ( rightHandSide.begin ( ), rightHandSide.end ( ) ) )
			rhs.push_back ( std::move ( element.template get < TerminalSymbolType > ( ) ) );

		return grammar.addRule ( std::move ( leftHandSide ), std::move ( rhs ) );
	}
}

} /* namespace grammar */

#endif /* _ADD_RAW_RULE_H__ */
