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

#ifndef _RAW_RULES_H__
#define _RAW_RULES_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 RawRules {
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 ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > getRawRules ( const LG < TerminalSymbolType, NonterminalSymbolType > & grammar );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > getRawRules ( const GNF < TerminalSymbolType, NonterminalSymbolType > & grammar );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > getRawRules ( const EpsilonFreeCFG < TerminalSymbolType, NonterminalSymbolType > & grammar );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > getRawRules ( const CNF < TerminalSymbolType, NonterminalSymbolType > & grammar );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > getRawRules ( const CFG < TerminalSymbolType, NonterminalSymbolType > & grammar );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > getRawRules ( const LeftLG < TerminalSymbolType, NonterminalSymbolType > & grammar );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > getRawRules ( const LeftRG < TerminalSymbolType, NonterminalSymbolType > & grammar );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > getRawRules ( const RightLG < TerminalSymbolType, NonterminalSymbolType > & grammar );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > getRawRules ( const RightRG < TerminalSymbolType, NonterminalSymbolType > & grammar );
};

template < class TerminalSymbolType, class NonterminalSymbolType >
ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > RawRules::getRawRules ( const LG < TerminalSymbolType, NonterminalSymbolType > & grammar ) {
	ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > res;

	for ( const auto & rule : grammar.getRules ( ) )
		for ( const auto & rhs : rule.second ) {
			ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > tmp;

			if ( rhs.template is < ext::vector < TerminalSymbolType > > ( ) ) {
				for ( const TerminalSymbolType & symbol : rhs.template get < ext::vector < TerminalSymbolType > > ( ) )
					tmp.push_back ( ext::variant < TerminalSymbolType, NonterminalSymbolType > ( symbol ) );
			} else {
				const auto & rhsTuple = rhs.template get < ext::tuple < ext::vector < TerminalSymbolType >, NonterminalSymbolType, ext::vector < TerminalSymbolType > > > ( );

				for ( const TerminalSymbolType & symbol : ext::make_iterator_range ( std::get < 0 > ( rhsTuple ).begin ( ), std::get < 0 > ( rhsTuple ).end ( ) ) )
					tmp.push_back ( ext::variant < TerminalSymbolType, NonterminalSymbolType > ( symbol ) );

				tmp.push_back ( ext::variant < TerminalSymbolType, NonterminalSymbolType > ( std::get < 1 > ( rhsTuple ) ) );

				for ( const TerminalSymbolType & symbol : ext::make_iterator_range ( std::get < 2 > ( rhsTuple ).begin ( ), std::get < 2 > ( rhsTuple ).end ( ) ) )
					tmp.push_back ( ext::variant < TerminalSymbolType, NonterminalSymbolType > ( symbol ) );
			}

			res[rule.first].insert ( std::move ( tmp ) );
		}

	return res;
}

template < class TerminalSymbolType, class NonterminalSymbolType >
ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > RawRules::getRawRules ( const GNF < TerminalSymbolType, NonterminalSymbolType > & grammar ) {
	ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > res;

	for ( const auto & rule : grammar.getRules ( ) )
		for ( const auto & rhs : rule.second ) {
			ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > tmp;
			tmp.push_back ( ext::variant < TerminalSymbolType, NonterminalSymbolType > ( rhs.first ) );

			for ( const NonterminalSymbolType & symbol : ext::make_iterator_range ( rhs.second.begin ( ), rhs.second.end ( ) ) )
				tmp.push_back ( ext::variant < TerminalSymbolType, NonterminalSymbolType > ( symbol ) );

			res[rule.first].insert ( std::move ( tmp ) );
		}

	if ( grammar.getGeneratesEpsilon ( ) )
		res [ grammar.getInitialSymbol ( ) ].insert ( ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > { } );

	return res;
}

template < class TerminalSymbolType, class NonterminalSymbolType >
ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > RawRules::getRawRules ( const EpsilonFreeCFG < TerminalSymbolType, NonterminalSymbolType > & grammar ) {
	ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > res = grammar.getRules ( );

	if ( grammar.getGeneratesEpsilon ( ) )
		res [ grammar.getInitialSymbol ( ) ].insert ( ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > { } );

	return res;
}

template < class TerminalSymbolType, class NonterminalSymbolType >
ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > RawRules::getRawRules ( const CNF < TerminalSymbolType, NonterminalSymbolType > & grammar ) {
	ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > res;

	for ( const auto & rule : grammar.getRules ( ) )
		for ( const auto & rhs : rule.second ) {
			if ( rhs.template is < TerminalSymbolType > ( ) ) {
				ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > tmp { ext::variant < TerminalSymbolType, NonterminalSymbolType > ( rhs.template get < TerminalSymbolType > ( ) ) };
				res[rule.first].insert ( std::move ( tmp ) );
			} else {
				const auto & realRHS = rhs.template get < ext::pair < NonterminalSymbolType, NonterminalSymbolType > > ( );
				ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > tmp { ext::variant < TerminalSymbolType, NonterminalSymbolType > ( realRHS.first ), ext::variant < TerminalSymbolType, NonterminalSymbolType > ( realRHS.second ) };
				res[rule.first].insert ( std::move ( tmp ) );
			}
		}

	if ( grammar.getGeneratesEpsilon ( ) )
		res [ grammar.getInitialSymbol ( ) ].insert ( ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > { } );

	return res;
}

template < class TerminalSymbolType, class NonterminalSymbolType >
ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > RawRules::getRawRules ( const CFG < TerminalSymbolType, NonterminalSymbolType > & grammar ) {
	return grammar.getRules ( );
}

template < class TerminalSymbolType, class NonterminalSymbolType >
ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > RawRules::getRawRules ( const LeftLG < TerminalSymbolType, NonterminalSymbolType > & grammar ) {
	ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > res;

	for ( const auto & rule : grammar.getRules ( ) )
		for ( const auto & rhs : rule.second ) {
			ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > tmp;

			if ( rhs.template is < ext::vector < TerminalSymbolType > > ( ) ) {
				for ( const TerminalSymbolType & symbol : rhs.template get < ext::vector < TerminalSymbolType > > ( ) )
					tmp.push_back ( ext::variant < TerminalSymbolType, NonterminalSymbolType > ( symbol ) );
			} else {
				const auto & rhsTuple = rhs.template get < ext::pair < NonterminalSymbolType, ext::vector < TerminalSymbolType > > > ( );

				tmp.push_back ( ext::variant < TerminalSymbolType, NonterminalSymbolType > ( rhsTuple.first ) );
				for ( const TerminalSymbolType & symbol : ext::make_iterator_range ( rhsTuple.second.begin ( ), rhsTuple.second.end ( ) ) )
					tmp.push_back ( ext::variant < TerminalSymbolType, NonterminalSymbolType > ( symbol ) );
			}

			res[rule.first].insert ( std::move ( tmp ) );
		}

	return res;
}

template < class TerminalSymbolType, class NonterminalSymbolType >
ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > RawRules::getRawRules ( const LeftRG < TerminalSymbolType, NonterminalSymbolType > & grammar ) {
	ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > res;

	for ( const auto & rule : grammar.getRules ( ) )
		for ( const auto & rhs : rule.second ) {
			if ( rhs.template is < TerminalSymbolType > ( ) ) {
				ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > tmp { ext::variant < TerminalSymbolType, NonterminalSymbolType > ( rhs.template get < TerminalSymbolType > ( ) ) };
				res[rule.first].insert ( std::move ( tmp ) );
			} else {
				const auto & rhsPair = rhs.template get < ext::pair < NonterminalSymbolType, TerminalSymbolType > > ( );
				ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > tmp { ext::variant < TerminalSymbolType, NonterminalSymbolType > ( rhsPair.first ), ext::variant < TerminalSymbolType, NonterminalSymbolType > ( rhsPair.second ) };
				res[rule.first].insert ( std::move ( tmp ) );
			}
		}

	if ( grammar.getGeneratesEpsilon ( ) )
		res [ grammar.getInitialSymbol ( ) ].insert ( ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > { } );

	return res;
}

template < class TerminalSymbolType, class NonterminalSymbolType >
ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > RawRules::getRawRules ( const RightRG < TerminalSymbolType, NonterminalSymbolType > & grammar ) {
	ext::map < TerminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > res;

	for ( const auto & rule : grammar.getRules ( ) )
		for ( const auto & rhs : rule.second ) {
			if ( rhs.template is < TerminalSymbolType > ( ) ) {
				ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > tmp { ext::variant < TerminalSymbolType, NonterminalSymbolType > ( rhs.template get < TerminalSymbolType > ( ) ) };
				res[rule.first].insert ( std::move ( tmp ) );
			} else {
				const auto & rhsPair = rhs.template get < ext::pair < TerminalSymbolType, NonterminalSymbolType > > ( );
				ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > tmp { ext::variant < TerminalSymbolType, NonterminalSymbolType > ( rhsPair.first ), ext::variant < TerminalSymbolType, NonterminalSymbolType > ( rhsPair.second ) };
				res[rule.first].insert ( std::move ( tmp ) );
			}
		}

	if ( grammar.getGeneratesEpsilon ( ) )
		res [ grammar.getInitialSymbol ( ) ].insert ( ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > { } );

	return res;
}

template < class TerminalSymbolType, class NonterminalSymbolType >
ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > RawRules::getRawRules ( const RightLG < TerminalSymbolType, NonterminalSymbolType > & grammar ) {
	ext::map < NonterminalSymbolType, ext::set < ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > > > res;

	for ( const auto & rule : grammar.getRules ( ) )
		for ( const auto & rhs : rule.second ) {
			ext::vector < ext::variant < TerminalSymbolType, NonterminalSymbolType > > tmp;

			if ( rhs.template is < ext::vector < TerminalSymbolType > > ( ) ) {
				for ( const TerminalSymbolType & symbol : rhs.template get < ext::vector < TerminalSymbolType > > ( ) )
					tmp.push_back ( ext::variant < TerminalSymbolType, NonterminalSymbolType > ( symbol ) );
			} else {
				const auto & rhsTuple = rhs.template get < ext::pair < ext::vector < TerminalSymbolType >, NonterminalSymbolType > > ( );

				for ( const TerminalSymbolType & symbol : ext::make_iterator_range ( rhsTuple.first.begin ( ), rhsTuple.first.end ( ) ) )
					tmp.push_back ( ext::variant < TerminalSymbolType, NonterminalSymbolType > ( symbol ) );
				tmp.push_back ( ext::variant < TerminalSymbolType, NonterminalSymbolType > ( rhsTuple.second ) );
			}

			res[rule.first].insert ( std::move ( tmp ) );
		}

	return res;
}

} /* namespace grammar */

#endif /* _RAW_RULES_H__ */
