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

#pragma once

#include <alib/deque>
#include <alib/vector>
#include <alib/multiset>
#include <alib/set>
#include <alib/map>

#include <core/xmlApi.hpp>

#include <regexp/xml/UnboundedRegExpStructure.h>
#include <alphabet/xml/RankedSymbol.h>
#include <automaton/common/Shift.h>
#include <common/xml/SymbolOrEpsilon.h>

namespace automaton {

/**
 * This class contains methods to print XML representation of automata to the output stream.
 */
class AutomatonToXMLComposer {
public:
	template<class StateType>
	static void composeStates(ext::deque<sax::Token>&, const ext::set<StateType>& states);
	template<class SymbolType>
	static void composeInputAlphabet(ext::deque<sax::Token>&, const ext::set<SymbolType>& symbols);
	template<class SymbolType>
	static void composeCallInputAlphabet(ext::deque<sax::Token>&, const ext::set<SymbolType>& symbols);
	template<class SymbolType>
	static void composeReturnInputAlphabet(ext::deque<sax::Token>&, const ext::set<SymbolType>& symbols);
	template<class SymbolType>
	static void composeLocalInputAlphabet(ext::deque<sax::Token>&, const ext::set<SymbolType>& symbols);
	template<class SymbolType>
	static void composeRankedInputAlphabet(ext::deque<sax::Token>&, const ext::set<SymbolType>& symbols);
	template<class StateType>
	static void composeInitialStates(ext::deque<sax::Token>&, const ext::set<StateType>& states);
	template<class StateType>
	static void composeInitialState(ext::deque<sax::Token>&, const StateType& state);
	template<class StateType>
	static void composeFinalStates(ext::deque<sax::Token>&, const ext::set<StateType>& states);
	template<class SymbolType>
	static void composePushdownStoreAlphabet(ext::deque<sax::Token>&, const ext::set<SymbolType>& symbols);
	template<class SymbolType>
	static void composeInitialPushdownStoreSymbols(ext::deque<sax::Token>&, const ext::set<SymbolType>& symbols);
	template<class SymbolType>
	static void composeInitialPushdownStoreSymbol(ext::deque<sax::Token>&, const SymbolType& symbol);
	template<class SymbolType>
	static void composeOutputAlphabet(ext::deque<sax::Token>&, const ext::set<SymbolType>& symbols);
	template<class SymbolType>
	static void composeTapeAlphabet(ext::deque<sax::Token>&, const ext::set<SymbolType>& symbols);
	template<class SymbolType>
	static void composeBlankSymbol(ext::deque<sax::Token>&, const SymbolType& symbol);
	template<class SymbolType>
	static void composeBottomOfTheStackSymbol(ext::deque<sax::Token>&, const SymbolType& symbol);
	template<class InputSymbolType, class PushdownStoreSymbolType >
	static void composeInputToPushdownStoreOperation(ext::deque<sax::Token>&, const ext::map<InputSymbolType, ext::pair<ext::vector<PushdownStoreSymbolType>, ext::vector<PushdownStoreSymbolType> > >& operations);

	template<class StateType>
	static void composeTransitionTo(ext::deque<sax::Token>&, const StateType& state);
	template<class StateType>
	static void composeTransitionFrom(ext::deque<sax::Token>&, const StateType& state);
	template<class StateType>
	static void composeTransitionFrom(ext::deque<sax::Token>&, const ext::vector<StateType> & states);
	template<class StateType>
	static void composeTransitionFrom(ext::deque<sax::Token>&, const ext::multiset<StateType> & states);
	template<class SymbolType>
	static void composeTransitionPop(ext::deque<sax::Token>&, const ext::vector<SymbolType>& symbols);
	template<class SymbolType>
	static void composeTransitionSinglePop(ext::deque<sax::Token>&, const SymbolType& symbol);
	template<class SymbolType>
	static void composeTransitionPush(ext::deque<sax::Token>&, const ext::vector<SymbolType>& symbols);
	template<class SymbolType>
	static void composeTransitionSinglePush(ext::deque<sax::Token>&, const SymbolType& symbol);
	template<class SymbolType>
	static void composeTransitionInputSymbol(ext::deque<sax::Token>&, const SymbolType& symbol);
	template<class SymbolType>
	static void composeTransitionInputSymbolMultiple(ext::deque<sax::Token>&, const ext::vector<SymbolType>& symbols);
	template<class SymbolType>
	static void composeTransitionOutputSymbol(ext::deque<sax::Token>&, const SymbolType& symbol);
	template<class SymbolType>
	static void composeTransitionOutputSymbolMultiple(ext::deque<sax::Token>&, const ext::vector<SymbolType>& symbols);

	template < class SymbolType >
	static void composeTransitionInputRegexp(ext::deque<sax::Token>&, const regexp::UnboundedRegExpStructure < SymbolType > & regexp);

	static void composeTransitionShift(ext::deque<sax::Token>& out, Shift shift) {
		out.emplace_back("shift", sax::Token::TokenType::START_ELEMENT);
		out.emplace_back(SHIFT_NAMES [shiftToInt(shift)], sax::Token::TokenType::CHARACTER);
		out.emplace_back("shift", sax::Token::TokenType::END_ELEMENT);
	}

};

template < class StateType >
void AutomatonToXMLComposer::composeStates(ext::deque<sax::Token>& out, const ext::set<StateType>& states) {
	out.emplace_back("states", sax::Token::TokenType::START_ELEMENT);
	for (const auto& state : states) {
		core::xmlApi<StateType>::compose(out, state);
	}
	out.emplace_back("states", sax::Token::TokenType::END_ELEMENT);
}

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

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

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

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

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

template < class StateType >
void AutomatonToXMLComposer::composeInitialStates(ext::deque<sax::Token>& out, const ext::set<StateType>& states) {
	out.emplace_back("initialStates", sax::Token::TokenType::START_ELEMENT);
	for (const auto& state : states) {
		core::xmlApi<StateType>::compose(out, state);
	}
	out.emplace_back("initialStates", sax::Token::TokenType::END_ELEMENT);
}

template < class StateType >
void AutomatonToXMLComposer::composeInitialState(ext::deque<sax::Token>& out, const StateType& state) {
	out.emplace_back("initialState", sax::Token::TokenType::START_ELEMENT);
	core::xmlApi<StateType>::compose(out, state);
	out.emplace_back("initialState", sax::Token::TokenType::END_ELEMENT);
}

template < class StateType >
void AutomatonToXMLComposer::composeFinalStates(ext::deque<sax::Token>& out, const ext::set<StateType>& states) {
	out.emplace_back("finalStates", sax::Token::TokenType::START_ELEMENT);
	for (const auto& state : states) {
		core::xmlApi<StateType>::compose(out, state);
	}
	out.emplace_back("finalStates", sax::Token::TokenType::END_ELEMENT);
}

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

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

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

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

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

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

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

template < class InputSymbolType, class PushdownStoreSymbolType >
void AutomatonToXMLComposer::composeInputToPushdownStoreOperation(ext::deque<sax::Token>& out, const ext::map<InputSymbolType, ext::pair<ext::vector<PushdownStoreSymbolType>, ext::vector<PushdownStoreSymbolType> > >& operations) {
	out.emplace_back("inputToPushdownStoreOperations", sax::Token::TokenType::START_ELEMENT);
	for(const auto& pushdownStoreOperation : operations) {
		out.emplace_back("operation", sax::Token::TokenType::START_ELEMENT);

		core::xmlApi<InputSymbolType>::compose(out, pushdownStoreOperation.first);

		composeTransitionPop(out, pushdownStoreOperation.second.first);
		composeTransitionPush(out, pushdownStoreOperation.second.second);

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

template < class StateType >
void AutomatonToXMLComposer::composeTransitionTo(ext::deque<sax::Token>& out, const StateType& state) {
	out.emplace_back("to", sax::Token::TokenType::START_ELEMENT);
	core::xmlApi<StateType>::compose(out, state);
	out.emplace_back("to", sax::Token::TokenType::END_ELEMENT);
}

template < class StateType >
void AutomatonToXMLComposer::composeTransitionFrom(ext::deque<sax::Token>& out, const StateType& state) {
	out.emplace_back("from", sax::Token::TokenType::START_ELEMENT);
	core::xmlApi<StateType>::compose(out, state);
	out.emplace_back("from", sax::Token::TokenType::END_ELEMENT);
}

template < class StateType >
void AutomatonToXMLComposer::composeTransitionFrom(ext::deque<sax::Token>& out, const ext::vector<StateType> & states) {
	out.emplace_back("from", sax::Token::TokenType::START_ELEMENT);
	for (const auto& state : states) {
		core::xmlApi<StateType>::compose(out, state);
	}
	out.emplace_back("from", sax::Token::TokenType::END_ELEMENT);
}

template < class StateType >
void AutomatonToXMLComposer::composeTransitionFrom(ext::deque<sax::Token>& out, const ext::multiset<StateType> & states) {
	out.emplace_back("from", sax::Token::TokenType::START_ELEMENT);
	for (const auto& state : states) {
		core::xmlApi<StateType>::compose(out, state);
	}
	out.emplace_back("from", sax::Token::TokenType::END_ELEMENT);
}

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

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

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

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

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

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

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

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

template < class SymbolType >
void AutomatonToXMLComposer::composeTransitionInputRegexp(ext::deque<sax::Token>& out, const regexp::UnboundedRegExpStructure < SymbolType > & regexp) {
	out.emplace_back("input", sax::Token::TokenType::START_ELEMENT);
	core::xmlApi<regexp::UnboundedRegExpStructure < SymbolType > >::compose(out, regexp);
	out.emplace_back("input", sax::Token::TokenType::END_ELEMENT);
}

} /* namespace automaton */


