/*
 * GrammarNormalize.h
 *
 *  Created on: Apr 7, 2017
 *      Author: Jan Travnicek
 */

#ifndef GRAMMAR_NORMALIZE_H_
#define GRAMMAR_NORMALIZE_H_

#include <alib/vector>
#include <alib/tuple>
#include <alib/set>
#include <alib/variant>

#include <alphabet/common/SymbolNormalize.h>

namespace grammar {

/**
 * This class contains methods to print XML representation of automata to the output stream.
 */
class GrammarNormalize {
public:
	template < class FirstSymbolType, class SecondSymbolType >
	static ext::pair < DefaultSymbolType, ext::vector < DefaultSymbolType > > normalizeRHS ( ext::pair < FirstSymbolType, ext::vector < SecondSymbolType > > && symbol );

	template < class FirstSymbolType, class SecondSymbolType, class ThirdSymbolType >
	static ext::variant < DefaultSymbolType, ext::pair < DefaultSymbolType, DefaultSymbolType > > normalizeRHS ( ext::variant < FirstSymbolType, ext::pair < SecondSymbolType, ThirdSymbolType > > && symbol );

	template < class FirstSymbolType, class SecondSymbolType, class ThirdSymbolType >
	static ext::variant < ext::vector < DefaultSymbolType >, ext::pair < DefaultSymbolType, ext::vector < DefaultSymbolType > > > normalizeRHS ( ext::variant < ext::vector < FirstSymbolType >, ext::pair < SecondSymbolType, ext::vector < ThirdSymbolType > > > && symbol );

	template < class FirstSymbolType, class SecondSymbolType, class ThirdSymbolType >
	static ext::variant < ext::vector < DefaultSymbolType >, ext::pair < ext::vector < DefaultSymbolType >, DefaultSymbolType > > normalizeRHS ( ext::variant < ext::vector < FirstSymbolType >, ext::pair < ext::vector < SecondSymbolType >, ThirdSymbolType > > && symbol );

	template < class FirstSymbolType, class SecondSymbolType, class ThirdSymbolType, class FourthSymbolType >
	static ext::variant < ext::vector < DefaultSymbolType >, ext::tuple < ext::vector < DefaultSymbolType >, DefaultSymbolType, ext::vector < DefaultSymbolType > > > normalizeRHS ( ext::variant < ext::vector < FirstSymbolType >, ext::tuple < ext::vector < SecondSymbolType >, ThirdSymbolType, ext::vector < FourthSymbolType > > > && symbols );

};

template < class FirstSymbolType, class SecondSymbolType >
ext::pair < DefaultSymbolType, ext::vector < DefaultSymbolType > > GrammarNormalize::normalizeRHS ( ext::pair < FirstSymbolType, ext::vector < SecondSymbolType > > && symbol ) {
	return ext::make_pair ( alphabet::SymbolNormalize::normalizeSymbol ( std::move ( symbol.first ) ), alphabet::SymbolNormalize::normalizeSymbols ( std::move ( symbol.second ) ) );
}

template < class FirstSymbolType, class SecondSymbolType, class ThirdSymbolType >
ext::variant < DefaultSymbolType, ext::pair < DefaultSymbolType, DefaultSymbolType > > GrammarNormalize::normalizeRHS ( ext::variant < FirstSymbolType, ext::pair < SecondSymbolType, ThirdSymbolType > > && symbol ) {
	if ( symbol.template is < FirstSymbolType > ( ) ) {
		return ext::variant < DefaultSymbolType, ext::pair < DefaultSymbolType, DefaultSymbolType > > ( alphabet::SymbolNormalize::normalizeSymbol ( std::move ( symbol.template get < FirstSymbolType > ( ) ) ) );
	} else {
		ext::pair < SecondSymbolType, ThirdSymbolType > & inner = symbol.template get < ext::pair < SecondSymbolType, ThirdSymbolType > > ( );
		return ext::variant < DefaultSymbolType, ext::pair < DefaultSymbolType, DefaultSymbolType > > ( ext::make_pair ( alphabet::SymbolNormalize::normalizeSymbol ( std::move ( inner.first ) ), alphabet::SymbolNormalize::normalizeSymbol ( std::move ( inner.second ) ) ) );
	}
}

template < class FirstSymbolType, class SecondSymbolType, class ThirdSymbolType >
ext::variant < ext::vector < DefaultSymbolType >, ext::pair < DefaultSymbolType, ext::vector < DefaultSymbolType > > > GrammarNormalize::normalizeRHS ( ext::variant < ext::vector < FirstSymbolType >, ext::pair < SecondSymbolType, ext::vector < ThirdSymbolType > > > && symbol ) {
	if ( symbol.template is < ext::vector < FirstSymbolType > > ( ) ) {
		return ext::variant < ext::vector < DefaultSymbolType >, ext::pair < DefaultSymbolType, ext::vector < DefaultSymbolType > > > ( alphabet::SymbolNormalize::normalizeSymbols ( std::move ( symbol.template get < ext::vector < FirstSymbolType > > ( ) ) ) );
	} else {
		ext::pair < SecondSymbolType, ext::vector < ThirdSymbolType > > & inner = symbol.template get < ext::pair < SecondSymbolType, ext::vector < ThirdSymbolType > > > ( );
		return ext::variant < ext::vector < DefaultSymbolType >, ext::pair < DefaultSymbolType, ext::vector < DefaultSymbolType > > > ( ext::make_pair ( alphabet::SymbolNormalize::normalizeSymbol ( std::move ( inner.first ) ), alphabet::SymbolNormalize::normalizeSymbols ( std::move ( inner.second ) ) ) );
	}
}

template < class FirstSymbolType, class SecondSymbolType, class ThirdSymbolType >
ext::variant < ext::vector < DefaultSymbolType >, ext::pair < ext::vector < DefaultSymbolType >, DefaultSymbolType > > GrammarNormalize::normalizeRHS ( ext::variant < ext::vector < FirstSymbolType >, ext::pair < ext::vector < SecondSymbolType >, ThirdSymbolType > > && symbol ) {
	if ( symbol.template is < ext::vector < FirstSymbolType > > ( ) ) {
		return ext::variant < ext::vector < DefaultSymbolType >, ext::pair < ext::vector < DefaultSymbolType >, DefaultSymbolType > > ( alphabet::SymbolNormalize::normalizeSymbols ( std::move ( symbol.template get < ext::vector < FirstSymbolType > > ( ) ) ) );
	} else {
		ext::pair < ext::vector < SecondSymbolType >, ThirdSymbolType > & inner = symbol.template get < ext::pair < ext::vector < SecondSymbolType >, ThirdSymbolType > > ( );
		return ext::variant < ext::vector < DefaultSymbolType >, ext::pair < ext::vector < DefaultSymbolType >, DefaultSymbolType > > ( ext::make_pair ( alphabet::SymbolNormalize::normalizeSymbols ( std::move ( inner.first ) ), alphabet::SymbolNormalize::normalizeSymbol ( std::move ( inner.second ) ) ) );
	}
}

template < class FirstSymbolType, class SecondSymbolType, class ThirdSymbolType, class FourthSymbolType >
ext::variant < ext::vector < DefaultSymbolType >, ext::tuple < ext::vector < DefaultSymbolType >, DefaultSymbolType, ext::vector < DefaultSymbolType > > > GrammarNormalize::normalizeRHS ( ext::variant < ext::vector < FirstSymbolType >, ext::tuple < ext::vector < SecondSymbolType >, ThirdSymbolType, ext::vector < FourthSymbolType > > > && symbols ) {
	if ( symbols.template is < ext::vector < FirstSymbolType > > ( ) ) {
		return ext::variant < ext::vector < DefaultSymbolType >, ext::tuple < ext::vector < DefaultSymbolType >, DefaultSymbolType, ext::vector < DefaultSymbolType > > > ( alphabet::SymbolNormalize::normalizeSymbols ( std::move ( symbols.template get < ext::vector < FirstSymbolType > > ( ) ) ) );
	} else {
		ext::tuple < ext::vector < SecondSymbolType >, ThirdSymbolType, ext::vector < FourthSymbolType > > & inner = symbols.template get < ext::tuple < ext::vector < FirstSymbolType >, SecondSymbolType, ext::vector < ThirdSymbolType > > > ( );

		ext::vector < DefaultSymbolType > first = alphabet::SymbolNormalize::normalizeSymbols ( std::move ( std::get < 0 > ( inner ) ) );
		DefaultSymbolType second = alphabet::SymbolNormalize::normalizeSymbol ( std::move ( std::get < 1 > ( inner ) ) );
		ext::vector < DefaultSymbolType > third = alphabet::SymbolNormalize::normalizeSymbols ( std::move ( std::get < 2 > ( inner ) ) );

		return ext::variant < ext::vector < DefaultSymbolType >, ext::tuple < ext::vector < DefaultSymbolType >, DefaultSymbolType, ext::vector < DefaultSymbolType > > > ( ext::make_tuple ( std::move ( first ), std::move ( second ), std::move ( third ) ) );
	}
}

} /* namespace grammar */

#endif /* GRAMMAR_NORMALIZE_H_ */
