/*
 * GrammarToStringComposerCommon.h
 *
 * Created on: Sep 27, 2017
 * Author: Jan Travnicek
 */

#pragma once

#include <ostream>

#include <core/stringApi.hpp>

#include <grammar/RawRules.h>
#include <container/string/ObjectsVariant.h>

namespace grammar {

class GrammarToStringComposerCommon {
public:
	template < class T, class TerminalSymbolType = typename grammar::TerminalSymbolTypeOfGrammar < T >, class NonterminalSymbolType = typename grammar::NonterminalSymbolTypeOfGrammar < T > >
	static void composeCFLikeGrammar(std::ostream& output, const T& grammar);
	template < class T, class SymbolType = typename grammar::TerminalSymbolTypeOfGrammar < T > >
	static void composeCSLikeGrammar(std::ostream& output, const T& grammar);
	template < class T, class SymbolType = typename grammar::TerminalSymbolTypeOfGrammar < T > >
	static void composePreservingCSLikeGrammar(std::ostream& output, const T& grammar);
};

template < class T, class TerminalSymbolType, class NonterminalSymbolType >
void GrammarToStringComposerCommon::composeCFLikeGrammar(std::ostream& output, const T& grammar) {
	bool first;

	output << " (" << std::endl;

	output << "{";
	first = false;
	for(const auto& symbol : grammar.getNonterminalAlphabet() ) {
		if(first)
			output << ", ";
		else
			first = true;
		core::stringApi < NonterminalSymbolType >::compose(output, symbol);
	}
	output << "}," << std::endl;
	output << "{";
	first = false;
	for(const auto& symbol : grammar.getTerminalAlphabet() ) {
		if(first)
			output << ", ";
		else
			first = true;
		core::stringApi < TerminalSymbolType >::compose(output, symbol);
	}
	output << "}," << std::endl;
	output << "{ ";
	first = true;
	auto rawRules = grammar::RawRules::getRawRules ( grammar );
	for(const auto& rule : rawRules ) {
		if(first)
			first = false;
		else
			output << "," << std::endl << "  ";
		core::stringApi < NonterminalSymbolType >::compose(output, rule.first);
		output << " ->";
		bool innerFirst = true;
		for(const auto& rhs : rule.second) {
			if(innerFirst)
				innerFirst = false;
			else
				output << " |";
			for(const auto& symbol : rhs) {
				output << " ";
				core::stringApi < ext::variant < TerminalSymbolType, NonterminalSymbolType > >::compose(output, symbol);
			}
		}
	}
	output << "}," << std::endl;
	core::stringApi < NonterminalSymbolType >::compose(output, grammar.getInitialSymbol());
	output << ")" << std::endl;
}

template < class T, class SymbolType >
void GrammarToStringComposerCommon::composeCSLikeGrammar(std::ostream& output, const T& grammar) {
	bool first;

	output << " (" << std::endl;

	output << "{";
	first = false;
	for(const auto& symbol : grammar.getNonterminalAlphabet() ) {
		if(first)
			output << ", ";
		else
			first = true;
		core::stringApi < SymbolType >::compose(output, symbol);
	}
	output << "}," << std::endl;
	output << "{";
	first = false;
	for(const auto& symbol : grammar.getTerminalAlphabet() ) {
		if(first)
			output << ", ";
		else
			first = true;
		core::stringApi < SymbolType >::compose(output, symbol);
	}
	output << "}," << std::endl;
	output << "{";
	first = true;
	for(const auto& rule : grammar.getRules() ) {
		if(first)
			first = false;
		else
			output << "," << std::endl << " ";
		for(const auto& symbol : rule.first) {
			output << " ";
			core::stringApi < SymbolType >::compose(output, symbol);
		}
		output << " ->";
		bool innerFirst = true;
		for(const auto& rhs : rule.second) {
			if(innerFirst)
				innerFirst = false;
			else
				output << " |";
			for(const auto& symbol : rhs) {
				output << " ";
				core::stringApi < SymbolType >::compose(output, symbol);
			}
		}
	}
	output << "}," << std::endl;
	core::stringApi < SymbolType >::compose(output, grammar.getInitialSymbol());
	output << ")" << std::endl;
}

template < class T, class SymbolType >
void GrammarToStringComposerCommon::composePreservingCSLikeGrammar(std::ostream& output, const T& grammar) {
	bool first;

	output << " (" << std::endl;

	output << "{";
	first = false;
	for(const auto& symbol : grammar.getNonterminalAlphabet() ) {
		if(first)
			output << ", ";
		else
			first = true;
		core::stringApi < SymbolType >::compose(output, symbol);
	}
	output << "}," << std::endl;
	output << "{";
	first = false;
	for(const auto& symbol : grammar.getTerminalAlphabet() ) {
		if(first)
			output << ", ";
		else
			first = true;
		core::stringApi < SymbolType >::compose(output, symbol);
	}
	output << "}," << std::endl;
	output << "{";
	first = true;
	for(const auto& rule : grammar.getRules() ) {
		if(first)
			first = false;
		else
			output << "," << std::endl << " ";
		for(const auto& symbol : std::get<0>(rule.first)) {
			output << " ";
			core::stringApi < SymbolType >::compose(output, symbol);
		}
		output << " | ";
		core::stringApi < SymbolType >::compose(output, std::get<1>(rule.first));
		output << " |";
		for(const auto& symbol : std::get<2>(rule.first)) {
			output << " ";
			core::stringApi < SymbolType >::compose(output, symbol);
		}
		output << " ->";
		bool innerFirst = true;
		for(const auto& rhs : rule.second) {
			if(innerFirst)
				innerFirst = false;
			else
				output << " |";
			for(const auto& symbol : rhs) {
				output << " ";
				core::stringApi < SymbolType >::compose(output, symbol);
			}
		}
	}
	output << "}," << std::endl;
	core::stringApi < SymbolType >::compose(output, grammar.getInitialSymbol());
	output << ")" << std::endl;
}

} /* namespace grammar */

