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

#pragma once

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

#include <common/createUnique.hpp>

namespace grammar {

namespace convert {

/**
 * Converts right regular grammar to left regular grammar.
 */
class ToGrammarLeftRG {
public:
	/**
	 * Transforms a right regular grammar to left 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 the right regular grammar to convert
	 *
	 * \return left regular grammar which is equivalent to source right regular grammar.
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static grammar::LeftRG < TerminalSymbolType, NonterminalSymbolType > convert(const grammar::RightRG < TerminalSymbolType, NonterminalSymbolType > & grammar);
};

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

	grammar::LeftRG < TerminalSymbolType, NonterminalSymbolType > lrg(s);

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

	lrg.setTerminalAlphabet( grammar.getTerminalAlphabet( ) );
	lrg.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<TerminalSymbolType, NonterminalSymbolType>>( ) ) {
				const ext::pair<TerminalSymbolType, NonterminalSymbolType>& rhs = ruleRHS.template get<ext::pair<TerminalSymbolType, NonterminalSymbolType>>();

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

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

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

				if( lhs == grammar.getInitialSymbol( ) ) {
					leftSide = lrg.getInitialSymbol( );
					TerminalSymbolType rightSide2 = rhs;
					lrg.addRule( leftSide, std::move ( rightSide2 ) );
				}
			}
		}
	}

	return lrg;
}

} /* namespace convert */

} /* namespace grammar */

