/*
 * GrammarToXMLComposer.h
 *
 *  Created on: Nov 11, 2013
 *      Author: Jan Travnicek
 */

#pragma once

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

#include <sax/Token.h>
#include <core/xmlApi.hpp>

namespace grammar {

/**
 * This class contains methods to print XML representation of automata to the output stream.
 */
class GrammarToXMLComposer {
public:
	template < class SymbolType >
	static void composeNonterminalAlphabet(ext::deque<sax::Token>& out, const ext::set<SymbolType>& symbols);
	template < class SymbolType >
	static void composeTerminalAlphabet(ext::deque<sax::Token>& out, const ext::set<SymbolType>& symbols);
	template < class SymbolType >
	static void composeInitialSymbol(ext::deque<sax::Token>& out, const SymbolType& symbol);

	static void composeGeneratesEpsilon(ext::deque<sax::Token>& out, bool generatesEpsilon) {
		out.emplace_back("generatesEpsilon", sax::Token::TokenType::START_ELEMENT);
		if(generatesEpsilon) {
			out.emplace_back("true", sax::Token::TokenType::START_ELEMENT);
			out.emplace_back("true", sax::Token::TokenType::END_ELEMENT);
		} else {
			out.emplace_back("false", sax::Token::TokenType::START_ELEMENT);
			out.emplace_back("false", sax::Token::TokenType::END_ELEMENT);
		}
		out.emplace_back("generatesEpsilon", sax::Token::TokenType::END_ELEMENT);
	}

	template < class SymbolType >
	static void composeRuleLContext(ext::deque<sax::Token>& out, const ext::vector<SymbolType>& symbols);
	template < class SymbolType >
	static void composeRuleLHS(ext::deque<sax::Token>& out, const ext::vector<SymbolType>& symbols);
	template < class SymbolType >
	static void composeRuleRContext(ext::deque<sax::Token>& out, const ext::vector<SymbolType>& symbols);
	template < class SymbolType >
	static void composeRuleSingleSymbolLHS(ext::deque<sax::Token>& out, const SymbolType& symbol);
	template < class SymbolType >
	static void composeRuleRHS(ext::deque<sax::Token>& out, const ext::vector<SymbolType>& symbols);
	template < class T, class R, class S >
	static void composeRuleOneOrTwoSymbolsRHS ( ext::deque < sax::Token > & out, const ext::variant < T, ext::pair < R, S > > & symbols );
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static void composeRuleGNFRHS(ext::deque<sax::Token>& out, const ext::pair<TerminalSymbolType, ext::vector<NonterminalSymbolType>>& symbols);
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static void composeRuleLeftLGRHS(ext::deque<sax::Token>& out, const ext::variant<ext::vector<TerminalSymbolType>, ext::pair<NonterminalSymbolType, ext::vector<TerminalSymbolType>>>& symbols);
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static void composeRuleRightLGRHS(ext::deque<sax::Token>& out, const ext::variant<ext::vector<TerminalSymbolType>, ext::pair<ext::vector<TerminalSymbolType>, NonterminalSymbolType>>& symbols);
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static void composeRuleLGRHS(ext::deque<sax::Token>& out, const ext::variant<ext::vector<TerminalSymbolType>, ext::tuple<ext::vector<TerminalSymbolType>, NonterminalSymbolType, ext::vector<TerminalSymbolType>>>& symbols);
};

template < class SymbolType >
void GrammarToXMLComposer::composeNonterminalAlphabet(ext::deque<sax::Token>& out, const ext::set<SymbolType>& symbols) {
	out.emplace_back("nonterminalAlphabet", sax::Token::TokenType::START_ELEMENT);
	for (const auto& symbol : symbols) {
		core::xmlApi<SymbolType>::compose(out, symbol);
	}
	out.emplace_back("nonterminalAlphabet", sax::Token::TokenType::END_ELEMENT);
}

template < class SymbolType >
void GrammarToXMLComposer::composeTerminalAlphabet(ext::deque<sax::Token>& out, const ext::set<SymbolType>& symbols) {
	out.emplace_back("terminalAlphabet", sax::Token::TokenType::START_ELEMENT);
	for (const auto& symbol : symbols) {
		core::xmlApi<SymbolType>::compose(out, symbol);
	}
	out.emplace_back("terminalAlphabet", sax::Token::TokenType::END_ELEMENT);
}

template < class SymbolType >
void GrammarToXMLComposer::composeInitialSymbol(ext::deque<sax::Token>& out, const SymbolType& symbol) {
	out.emplace_back("initialSymbol", sax::Token::TokenType::START_ELEMENT);
	core::xmlApi<SymbolType>::compose(out, symbol);
	out.emplace_back("initialSymbol", sax::Token::TokenType::END_ELEMENT);
}

template < class SymbolType >
void GrammarToXMLComposer::composeRuleLContext(ext::deque<sax::Token>& out, const ext::vector<SymbolType>& symbols) {
	out.emplace_back("lContext", sax::Token::TokenType::START_ELEMENT);
	if(symbols.empty ( )) {
		out.emplace_back("epsilon", sax::Token::TokenType::START_ELEMENT);
		out.emplace_back("epsilon", sax::Token::TokenType::END_ELEMENT);
	} else for (const auto& symbol : symbols) {
		core::xmlApi<SymbolType>::compose(out, symbol);
	}
	out.emplace_back("lContext", sax::Token::TokenType::END_ELEMENT);
}

template < class SymbolType >
void GrammarToXMLComposer::composeRuleLHS(ext::deque<sax::Token>& out, const ext::vector<SymbolType>& symbols) {
	out.emplace_back("lhs", sax::Token::TokenType::START_ELEMENT);
	if(symbols.empty ( )) {
		out.emplace_back("epsilon", sax::Token::TokenType::START_ELEMENT);
		out.emplace_back("epsilon", sax::Token::TokenType::END_ELEMENT);
	} else for (const auto& symbol : symbols) {
		core::xmlApi<SymbolType>::compose(out, symbol);
	}
	out.emplace_back("lhs", sax::Token::TokenType::END_ELEMENT);
}

template < class SymbolType >
void GrammarToXMLComposer::composeRuleRContext(ext::deque<sax::Token>& out, const ext::vector<SymbolType>& symbols) {
	out.emplace_back("rContext", sax::Token::TokenType::START_ELEMENT);
	if(symbols.empty ( )) {
		out.emplace_back("epsilon", sax::Token::TokenType::START_ELEMENT);
		out.emplace_back("epsilon", sax::Token::TokenType::END_ELEMENT);
	} else for (const auto& symbol : symbols) {
		core::xmlApi<SymbolType>::compose(out, symbol);
	}
	out.emplace_back("rContext", sax::Token::TokenType::END_ELEMENT);
}

template < class SymbolType >
void GrammarToXMLComposer::composeRuleSingleSymbolLHS(ext::deque<sax::Token>& out, const SymbolType& symbol) {
	out.emplace_back("lhs", sax::Token::TokenType::START_ELEMENT);
	core::xmlApi<SymbolType>::compose(out, symbol);
	out.emplace_back("lhs", sax::Token::TokenType::END_ELEMENT);
}

template < class SymbolType >
void GrammarToXMLComposer::composeRuleRHS(ext::deque<sax::Token>& out, const ext::vector<SymbolType>& symbols) {
	out.emplace_back("rhs", sax::Token::TokenType::START_ELEMENT);
	if(symbols.empty ( )) {
		out.emplace_back("epsilon", sax::Token::TokenType::START_ELEMENT);
		out.emplace_back("epsilon", sax::Token::TokenType::END_ELEMENT);
	} else for (const auto& symbol : symbols) {
		core::xmlApi<SymbolType>::compose(out, symbol);
	}
	out.emplace_back("rhs", sax::Token::TokenType::END_ELEMENT);
}

template < class T, class R, class S >
void GrammarToXMLComposer::composeRuleOneOrTwoSymbolsRHS ( ext::deque < sax::Token > & out, const ext::variant < T, ext::pair < R, S > > & symbols ) {
	out.emplace_back("rhs", sax::Token::TokenType::START_ELEMENT);
	if ( symbols.template is < T > ( ) ) {
		core::xmlApi < T >::compose ( out, symbols.template get < T > ( ) );
	} else {
		const ext::pair < R, S > & rhs = symbols.template get < ext::pair < R, S > > ( );
		core::xmlApi < R >::compose ( out, rhs.first );
		core::xmlApi < S >::compose ( out, rhs.second );
	}
	out.emplace_back("rhs", sax::Token::TokenType::END_ELEMENT);
}

template < class TerminalSymbolType, class NonterminalSymbolType >
void GrammarToXMLComposer::composeRuleGNFRHS(ext::deque<sax::Token>& out, const ext::pair<TerminalSymbolType, ext::vector<NonterminalSymbolType>>& symbols) {
	out.emplace_back("rhs", sax::Token::TokenType::START_ELEMENT);

	core::xmlApi<TerminalSymbolType>::compose(out, symbols.first);

	for (const auto& symbol : symbols.second) {
		core::xmlApi<NonterminalSymbolType>::compose(out, symbol);
	}

	out.emplace_back("rhs", sax::Token::TokenType::END_ELEMENT);
}

template < class TerminalSymbolType, class NonterminalSymbolType >
void GrammarToXMLComposer::composeRuleLGRHS(ext::deque<sax::Token>& out, const ext::variant<ext::vector<TerminalSymbolType>, ext::tuple<ext::vector<TerminalSymbolType>, NonterminalSymbolType, ext::vector<TerminalSymbolType>>>& symbols) {
	out.emplace_back("rhs", sax::Token::TokenType::START_ELEMENT);
	if(symbols.template is<ext::vector<TerminalSymbolType>>()) {
		const ext::vector<TerminalSymbolType>& rhs = symbols.template get<ext::vector<TerminalSymbolType>>();
		if(rhs.empty ( )) {
			out.emplace_back("epsilon", sax::Token::TokenType::START_ELEMENT);
			out.emplace_back("epsilon", sax::Token::TokenType::END_ELEMENT);
		} else for (const auto& symbol : symbols.template get<ext::vector<TerminalSymbolType>>()) {
			core::xmlApi<TerminalSymbolType>::compose(out, symbol);
		}
	} else {
		const ext::tuple<ext::vector<TerminalSymbolType>, NonterminalSymbolType, ext::vector<TerminalSymbolType>>& rhs = symbols.template get<ext::tuple<ext::vector<TerminalSymbolType>, NonterminalSymbolType, ext::vector<TerminalSymbolType>>>();
		for (const auto& symbol : std::get<0>(rhs)) {
			core::xmlApi<TerminalSymbolType>::compose(out, symbol);
		}
		core::xmlApi<NonterminalSymbolType>::compose(out, std::get<1>(rhs));
		for (const auto& symbol : std::get<2>(rhs)) {
			core::xmlApi<TerminalSymbolType>::compose(out, symbol);
		}
	}
	out.emplace_back("rhs", sax::Token::TokenType::END_ELEMENT);
}

template < class TerminalSymbolType, class NonterminalSymbolType >
void GrammarToXMLComposer::composeRuleLeftLGRHS(ext::deque<sax::Token>& out, const ext::variant<ext::vector<TerminalSymbolType>, ext::pair<NonterminalSymbolType, ext::vector<TerminalSymbolType>>>& symbols) {
	out.emplace_back("rhs", sax::Token::TokenType::START_ELEMENT);
	if(symbols.template is<ext::vector<TerminalSymbolType>>()) {
		const ext::vector<TerminalSymbolType>& rhs = symbols.template get<ext::vector<TerminalSymbolType>>();
		if(rhs.empty ( )) {
			out.emplace_back("epsilon", sax::Token::TokenType::START_ELEMENT);
			out.emplace_back("epsilon", sax::Token::TokenType::END_ELEMENT);
		} else for (const auto& symbol : symbols.template get<ext::vector<TerminalSymbolType>>()) {
			core::xmlApi<TerminalSymbolType>::compose(out, symbol);
		}
	} else {
		const ext::pair<NonterminalSymbolType, ext::vector<TerminalSymbolType>>& rhs = symbols.template get<ext::pair<NonterminalSymbolType, ext::vector<TerminalSymbolType>>>();
		core::xmlApi<NonterminalSymbolType>::compose(out, rhs.first);
		for (const auto& symbol : rhs.second) {
			core::xmlApi<TerminalSymbolType>::compose(out, symbol);
		}
	}
	out.emplace_back("rhs", sax::Token::TokenType::END_ELEMENT);
}

template < class TerminalSymbolType, class NonterminalSymbolType >
void GrammarToXMLComposer::composeRuleRightLGRHS(ext::deque<sax::Token>& out, const ext::variant<ext::vector<TerminalSymbolType>, ext::pair<ext::vector<TerminalSymbolType>, NonterminalSymbolType>>& symbols) {
	out.emplace_back("rhs", sax::Token::TokenType::START_ELEMENT);
	if(symbols.template is<ext::vector<TerminalSymbolType>>()) {
		const ext::vector<TerminalSymbolType>& rhs = symbols.template get<ext::vector<TerminalSymbolType>>();
		if(rhs.empty ( )) {
			out.emplace_back("epsilon", sax::Token::TokenType::START_ELEMENT);
			out.emplace_back("epsilon", sax::Token::TokenType::END_ELEMENT);
		} else {
			for (const auto& symbol : symbols.template get<ext::vector<TerminalSymbolType>>()) {
				core::xmlApi<TerminalSymbolType>::compose(out, symbol);
			}
		}
	} else {
		const ext::pair<ext::vector<TerminalSymbolType>, NonterminalSymbolType>& rhs = symbols.template get<ext::pair<ext::vector<TerminalSymbolType>, NonterminalSymbolType>>();
		for (const auto& symbol : rhs.first) {
			core::xmlApi<TerminalSymbolType>::compose(out, symbol);
		}
		core::xmlApi<NonterminalSymbolType>::compose(out, rhs.second);
	}
	out.emplace_back("rhs", sax::Token::TokenType::END_ELEMENT);
}

} /* namespace grammar */

