/*
 * ToGrammarRightRG.h
 *
 *  Created on: 8. 3. 2014
 *	  Author: Tomas Pecka
 */

#ifndef TO_GRAMMAR_RIGHT_RG_H_
#define TO_GRAMMAR_RIGHT_RG_H_

#include <grammar/Regular/LeftRG.h>
#include <grammar/Regular/RightRG.h>

#include <common/createUnique.hpp>

namespace grammar {

namespace convert {

/**
 * Converts left regular grammar to right regular grammar.
 */
class ToGrammarRightRG {
public:
	/**
	 * Transforms a left regular grammar to right regular grammar.
	 *
	 * \tparam TerminalSymbolType used for the terminal alphabet of the grammar.
	 * \tparam NonterminalSymbolType used for the nonterminal alphabet, and the initial symbol of the grammar.
	 *
	 * \param grammar left regular grammar to convert
	 *
	 * \return right regular grammar which is equivalent to source left regular grammar.
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static grammar::RightRG < TerminalSymbolType, NonterminalSymbolType > convert(const grammar::LeftRG < TerminalSymbolType, NonterminalSymbolType > & grammar);
};

template < class TerminalSymbolType, class NonterminalSymbolType >
grammar::RightRG < TerminalSymbolType, NonterminalSymbolType > ToGrammarRightRG::convert(const grammar::LeftRG < TerminalSymbolType, NonterminalSymbolType > & grammar) {
	// 1.
	NonterminalSymbolType s = common::createUnique( grammar.getInitialSymbol( ), grammar.getNonterminalAlphabet(), grammar.getTerminalAlphabet() );

	grammar::RightRG < TerminalSymbolType, NonterminalSymbolType > rrg( s );

	for(const auto & nonterminalSymbol : grammar.getNonterminalAlphabet() ) {
		rrg.addNonterminalSymbol( nonterminalSymbol );
	}

	rrg.setTerminalAlphabet( grammar.getTerminalAlphabet( ) );
	rrg.setGeneratesEpsilon( grammar.getGeneratesEpsilon( ) );

	// 2
	for( const auto & rule : grammar.getRules( ) ) {
		const NonterminalSymbolType& lhs = rule.first;

		for(const auto & ruleRHS : rule.second ) {
		        if( ruleRHS.template is<ext::pair<NonterminalSymbolType, TerminalSymbolType>>( ) ) {
				const ext::pair<NonterminalSymbolType, TerminalSymbolType>& rhs = ruleRHS.template get<ext::pair<NonterminalSymbolType, TerminalSymbolType>>();

				NonterminalSymbolType leftSide = ( rhs.first );
				ext::pair<TerminalSymbolType, NonterminalSymbolType> rightSide = ext::make_pair( rhs.second, lhs );
				rrg.addRule( leftSide, std::move ( rightSide ) );

				if( lhs == grammar.getInitialSymbol( ) ) {
					leftSide = rhs.first;
					TerminalSymbolType rightSide2 = rhs.second;
					rrg.addRule( leftSide, rightSide2 );
				}
			} else {
				const TerminalSymbolType& rhs = ruleRHS.template get<TerminalSymbolType>();

				NonterminalSymbolType leftSide = rrg.getInitialSymbol( );
				ext::pair<TerminalSymbolType, NonterminalSymbolType> rightSide = ext::make_pair( rhs, lhs );
				rrg.addRule( leftSide, std::move ( rightSide ) );

				if( lhs == grammar.getInitialSymbol( ) ) {
					leftSide = rrg.getInitialSymbol( );
					TerminalSymbolType rightSide2 = rhs;
					rrg.addRule( leftSide, rightSide2 );
				}
			}
		}
	}
	return rrg;
}

} /* namespace convert */

} /* namespace grammar */

#endif /* TO_GRAMMAR_RIGHT_RG_H_ */
