/*
 * Rename.h
 *
 *  Created on: Sep 27, 2016
 *      Author: Tomas Pecka
 */

#ifndef RENAME_GRAMMAR_H_
#define RENAME_GRAMMAR_H_

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

#include <alib/algorithm>
#include <alib/map>
#include <alib/vector>

namespace grammar {

namespace simplify {

class Rename {
public:
	/**
	 * Renames nonterminal symbols in given grammar
	 *
	 * \tparam TerminalSymbolType the type of terminal symbol in the given grammar
	 * \tparam NonterminalSymbolType the type of nonterminal symbol in the given grammar
	 *
	 * \param grammar the renamed grammar
	 *
	 * \return grammar with nonterminal symbols renamed
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static grammar::RightRG < TerminalSymbolType, unsigned > rename ( const grammar::RightRG < TerminalSymbolType, NonterminalSymbolType > & rrg );

	/**
	 * \override
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static grammar::LeftRG < TerminalSymbolType, unsigned > rename ( const grammar::LeftRG < TerminalSymbolType, NonterminalSymbolType > & lrg );
};

template < class TerminalSymbolType, class NonterminalSymbolType >
grammar::RightRG < TerminalSymbolType, unsigned > Rename::rename ( const grammar::RightRG < TerminalSymbolType, NonterminalSymbolType > & rrg ) {
	unsigned counter = 0;

	ext::map < NonterminalSymbolType, unsigned > renamingData;

	for ( const NonterminalSymbolType & symbol : rrg.getNonterminalAlphabet ( ) )
		renamingData.insert ( std::make_pair ( symbol, counter++ ) );

	grammar::RightRG < TerminalSymbolType, unsigned > result ( renamingData.at ( rrg.getInitialSymbol ( ) ) );
	result.setGeneratesEpsilon ( rrg.getGeneratesEpsilon ( ) );
	result.setTerminalAlphabet ( rrg.getTerminalAlphabet ( ) );

	for ( const NonterminalSymbolType & symbol : rrg.getNonterminalAlphabet ( ) )
		result.addNonterminalSymbol ( renamingData.at ( symbol ) );

	for ( const std::pair < const NonterminalSymbolType, ext::set < ext::variant < TerminalSymbolType, ext::pair < TerminalSymbolType, NonterminalSymbolType > > > > & rule : rrg.getRules ( ) )
		for ( const ext::variant < TerminalSymbolType, ext::pair < TerminalSymbolType, NonterminalSymbolType > > & rhs : rule.second )
			if ( rhs.template is < TerminalSymbolType > ( ) ) {
				result.addRule ( renamingData.at ( rule.first ), rhs.template get < TerminalSymbolType > ( ) );
			} else {
				const ext::pair < TerminalSymbolType, NonterminalSymbolType > & realRhs = rhs.template get < ext::pair < TerminalSymbolType, NonterminalSymbolType > > ( );
				result.addRule ( renamingData.at ( rule.first ), ext::make_pair ( realRhs.first, renamingData.at ( realRhs.second ) ) );
			}

	return result;
}

template < class TerminalSymbolType, class NonterminalSymbolType >
grammar::LeftRG < TerminalSymbolType, unsigned > Rename::rename ( const grammar::LeftRG < TerminalSymbolType, NonterminalSymbolType > & lrg ) {
	unsigned counter = 0;

	ext::map < NonterminalSymbolType, unsigned > renamingData;

	for ( const NonterminalSymbolType & symbol : lrg.getNonterminalAlphabet ( ) )
		renamingData.insert ( std::make_pair ( symbol, counter++ ) );

	grammar::LeftRG < TerminalSymbolType, unsigned > result ( renamingData.at ( lrg.getInitialSymbol ( ) ) );
	result.setGeneratesEpsilon ( lrg.getGeneratesEpsilon ( ) );
	result.setTerminalAlphabet ( lrg.getTerminalAlphabet ( ) );

	for ( const NonterminalSymbolType & symbol : lrg.getNonterminalAlphabet ( ) )
		result.addNonterminalSymbol ( renamingData.at ( symbol ) );

	for ( const std::pair < const NonterminalSymbolType, ext::set < ext::variant < TerminalSymbolType, ext::pair < NonterminalSymbolType, TerminalSymbolType > > > > & rule : lrg.getRules ( ) )
		for ( const ext::variant < TerminalSymbolType, ext::pair < NonterminalSymbolType, TerminalSymbolType > > & rhs : rule.second )
			if ( rhs.template is < TerminalSymbolType > ( ) ) {
				result.addRule ( renamingData.at ( rule.first ), rhs.template get < TerminalSymbolType > ( ) );
			} else {
				const ext::pair < NonterminalSymbolType, TerminalSymbolType > & realRhs = rhs.template get < ext::pair < NonterminalSymbolType, TerminalSymbolType > > ( );
				result.addRule ( renamingData.at ( rule.first ), ext::make_pair ( renamingData.at ( realRhs.first ), realRhs.second ) );
			}

	return result;
}

} /* namespace simplify */

} /* namespace grammar */

#endif /* RENAME_GRAMMAR_H_ */
