/*
 * SymbolNormalize.h
 *
 *  Created on: Apr 8, 2017
 *      Author: Jan Travnicek
 */

#ifndef SYMBOL_NORMALIZE_H_
#define SYMBOL_NORMALIZE_H_

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

#include <common/ranked_symbol.hpp>

namespace alphabet {

/**
 * This class contains methods to print XML representation of automata to the output stream.
 */
class SymbolNormalize {
public:
	template < class SymbolType >
	static ext::set < DefaultSymbolType > normalizeAlphabet ( ext::set < SymbolType > && symbols );

	template < class SymbolType >
	static ext::set < common::ranked_symbol < DefaultSymbolType > > normalizeRankedAlphabet ( ext::set < common::ranked_symbol < SymbolType > > && symbols );

	template < class SymbolType >
	static DefaultSymbolType normalizeSymbol ( SymbolType && symbol );

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

	template < class SymbolType >
	static common::ranked_symbol < DefaultSymbolType > normalizeRankedSymbol ( common::ranked_symbol < SymbolType > && symbol );

	template < class SymbolType >
	static ext::vector < DefaultSymbolType > normalizeSymbols ( ext::vector < SymbolType > && symbols );

	template < class FirstSymbolType, class SecondSymbolType >
	static ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > normalizeVariantSymbols ( ext::vector < ext::variant < FirstSymbolType, SecondSymbolType > > && symbols );

	template < class SymbolType >
	static ext::vector < common::ranked_symbol < DefaultSymbolType > > normalizeRankedSymbols ( ext::vector < common::ranked_symbol < SymbolType > > && symbols );
};

template < class SymbolType >
ext::set < DefaultSymbolType > SymbolNormalize::normalizeAlphabet ( ext::set < SymbolType > && symbols ) {
	ext::set < DefaultSymbolType > res;
	for ( SymbolType && symbol : ext::make_mover ( symbols ) ) {
		res.insert ( normalizeSymbol ( std::move ( symbol ) ) );
	}
	return res;
}

template < class SymbolType >
ext::set < common::ranked_symbol < DefaultSymbolType > > SymbolNormalize::normalizeRankedAlphabet ( ext::set < common::ranked_symbol < SymbolType > > && symbols ) {
	ext::set < common::ranked_symbol < DefaultSymbolType > > res;
	for ( common::ranked_symbol < SymbolType > && symbol : ext::make_mover ( symbols ) ) {
		res.insert ( normalizeRankedSymbol ( std::move ( symbol ) ) );
	}
	return res;
}

template < class SymbolType >
DefaultSymbolType SymbolNormalize::normalizeSymbol ( SymbolType && symbol ) {
	return object::Object ( std::forward < SymbolType > ( symbol ) );
}

template < class FirstSymbolType, class SecondSymbolType >
ext::variant < DefaultSymbolType, DefaultSymbolType > SymbolNormalize::normalizeVariantSymbol ( ext::variant < FirstSymbolType, SecondSymbolType > && symbol ) {
	if ( symbol.template is < FirstSymbolType > ( ) )
		return ext::variant < DefaultSymbolType, DefaultSymbolType > ( object::Object ( std::move ( symbol.template get < FirstSymbolType > ( ) ) ) );
	else
		return ext::variant < DefaultSymbolType, DefaultSymbolType > ( object::Object ( std::move ( symbol.template get < SecondSymbolType > ( ) ) ) );
}

template < class SymbolType >
common::ranked_symbol < DefaultSymbolType > SymbolNormalize::normalizeRankedSymbol ( common::ranked_symbol < SymbolType > && symbol ) {
	return common::ranked_symbol < DefaultSymbolType > ( normalizeSymbol ( std::move ( symbol ).getSymbol ( ) ), symbol.getRank ( ) );
}

template < class SymbolType >
ext::vector < DefaultSymbolType > SymbolNormalize::normalizeSymbols ( ext::vector < SymbolType > && symbols ) {
	ext::vector < DefaultSymbolType > res;
	for ( SymbolType & symbol : symbols ) {
		res.push_back ( normalizeSymbol ( std::move ( symbol ) ) );
	}
	return res;
}

template < class FirstSymbolType, class SecondSymbolType >
ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > SymbolNormalize::normalizeVariantSymbols ( ext::vector < ext::variant < FirstSymbolType, SecondSymbolType > > && symbols ) {
	ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > res;
	for ( ext::variant < FirstSymbolType, SecondSymbolType > & symbol : symbols ) {
		res.push_back ( normalizeVariantSymbol ( std::move ( symbol ) ) );
	}
	return res;
}

template < class SymbolType >
ext::vector < common::ranked_symbol < DefaultSymbolType > > SymbolNormalize::normalizeRankedSymbols ( ext::vector < common::ranked_symbol < SymbolType > > && symbols ) {
	ext::vector < common::ranked_symbol < DefaultSymbolType > > res;
	for ( common::ranked_symbol < SymbolType > & symbol : symbols ) {
		res.push_back ( normalizeRankedSymbol ( std::move ( symbol ) ) );
	}
	return res;
}

} /* namespace alphabet */

namespace core {

template < class SymbolType >
struct normalize < common::ranked_symbol < SymbolType > > {
	static common::ranked_symbol < > eval ( common::ranked_symbol < SymbolType > && value ) {
		return alphabet::SymbolNormalize::normalizeRankedSymbol ( std::move ( value ) );
	}
};

} /* namespace core */

#endif /* SYMBOL_NORMALIZE_H_ */
