#pragma once

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

#include <regexp/unbounded/UnboundedRegExpStructure.h>
#include <alphabet/common/SymbolNormalize.h>
#include <regexp/common/RegExpNormalize.h>
#include <rte/common/RTENormalizeVariant.h>

#include <common/DefaultStateType.h>
#include <common/DefaultSymbolType.h>
#include <common/symbol_or_epsilon.hpp>

namespace automaton {

/**
 * This class contains methods to print XML representation of automata to the output stream.
 */
class AutomatonNormalize {
public:
	template < class StateType >
	static ext::multiset < DefaultStateType > normalizeStates ( ext::multiset < StateType > && states );

	template < class StateType >
	static ext::set < DefaultStateType > normalizeStates ( ext::set < StateType > && states );

	template < class StateType >
	static ext::vector < DefaultStateType > normalizeStates ( ext::vector < StateType > && states );

	template < class StateType >
	static DefaultStateType normalizeState ( StateType && state);

	template < class SymbolType >
	static common::symbol_or_epsilon < DefaultSymbolType > normalizeSymbolEpsilon ( common::symbol_or_epsilon < SymbolType > && symbol );

	template < class SymbolType >
	static regexp::UnboundedRegExpStructure < DefaultSymbolType > normalizeRegExp ( regexp::UnboundedRegExpStructure < SymbolType > && regexp );

	template < class SymbolType, class StateType >
	static rte::FormalRTEStructure < ext::variant < DefaultSymbolType, DefaultStateType > > normalizeRTE ( rte::FormalRTEStructure < ext::variant < SymbolType, StateType > > && rte );
};

template < class StateType >
ext::multiset < DefaultStateType > AutomatonNormalize::normalizeStates ( ext::multiset < StateType > && states ) {
	ext::multiset < DefaultStateType > res;
	for ( StateType && state : ext::make_mover ( states ) )
		res.insert ( normalizeState ( std::move ( state ) ) );

	return res;
}

template < class StateType >
ext::set < DefaultStateType > AutomatonNormalize::normalizeStates ( ext::set < StateType > && states ) {
	ext::set < DefaultStateType > res;
	for ( StateType && state : ext::make_mover ( states ) )
		res.insert ( normalizeState ( std::move ( state ) ) );

	return res;
}

template < class StateType >
ext::vector < DefaultStateType > AutomatonNormalize::normalizeStates ( ext::vector < StateType > && states ) {
	ext::vector < DefaultStateType > res;
	for ( StateType & state : states )
		res.push_back ( normalizeState ( std::move ( state ) ) );

	return res;
}

template < class StateType >
DefaultStateType AutomatonNormalize::normalizeState ( StateType && state) {
	return factory::NormalizeFactory::normalize < StateType > ( std::forward < StateType > ( state ) );
}

template < class SymbolType >
common::symbol_or_epsilon < DefaultSymbolType > AutomatonNormalize::normalizeSymbolEpsilon ( common::symbol_or_epsilon < SymbolType > && symbol ) {
	if ( symbol.is_epsilon ( ) )
		return common::symbol_or_epsilon < DefaultSymbolType > ( );
	else
		return common::symbol_or_epsilon < DefaultSymbolType > ( alphabet::SymbolNormalize::normalizeSymbol ( std::move ( symbol ).getSymbol ( ) ) );
}

template < class SymbolType >
regexp::UnboundedRegExpStructure < DefaultSymbolType > AutomatonNormalize::normalizeRegExp ( regexp::UnboundedRegExpStructure < SymbolType > && regexp ) {
	return regexp::UnboundedRegExpStructure < DefaultSymbolType > ( std::move ( * regexp::RegExpNormalize::normalize < SymbolType > ( std::move ( regexp ).getStructure ( ) ) ) );
}

template < class SymbolType, class StateType >
rte::FormalRTEStructure < ext::variant < DefaultSymbolType, DefaultStateType > > AutomatonNormalize::normalizeRTE ( rte::FormalRTEStructure < ext::variant < SymbolType, StateType > > && rte ) {
	return rte::FormalRTEStructure < ext::variant < DefaultSymbolType, DefaultStateType > > ( std::move ( * rte::RTENormalizeVariant::normalize < SymbolType, StateType > ( std::move ( rte ).getStructure ( ) ) ) );
}

} /* namespace automaton */