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

#ifndef _STRING_FORMAL_REG_EXP_H_
#define _STRING_FORMAL_REG_EXP_H_

#include <regexp/formal/FormalRegExpElements.h>
#include <regexp/formal/FormalRegExpStructure.h>
#include <regexp/formal/FormalRegExp.h>
#include <core/stringApi.hpp>

#include "UnboundedRegExp.h"

namespace core {

template<class SymbolType >
struct stringApi < regexp::FormalRegExpStructure < SymbolType > > {
	static regexp::FormalRegExpStructure < SymbolType > parse ( std::istream & input );
	static bool first ( std::istream & input );
	static void compose ( std::ostream & output, const regexp::FormalRegExpStructure < SymbolType > & regexp );
private:
	enum class Priority {
		ALTERNATION,
		CONCATENATION,
		FACTOR
	};

	class Formal {
	public:
		static void visit( const regexp::FormalRegExpAlternation < SymbolType > & alternation, ext::tuple < Priority &, std::ostream & > & output);
		static void visit( const regexp::FormalRegExpConcatenation < SymbolType > & concatenation, ext::tuple < Priority &, std::ostream & > & output);
		static void visit( const regexp::FormalRegExpIteration < SymbolType > & iteration, ext::tuple < Priority &, std::ostream & > & output);
		static void visit( const regexp::FormalRegExpSymbol < SymbolType > & symbol, ext::tuple < Priority &, std::ostream & > & output);
		static void visit( const regexp::FormalRegExpEpsilon < SymbolType > & epsilon, ext::tuple < Priority &, std::ostream & > & output);
		static void visit( const regexp::FormalRegExpEmpty < SymbolType > & empty, ext::tuple < Priority &, std::ostream & > & output);
	};
};

template<class SymbolType >
regexp::FormalRegExpStructure < SymbolType > stringApi < regexp::FormalRegExpStructure < SymbolType > >::parse ( std::istream & input ) {
	return regexp::FormalRegExpStructure < SymbolType > ( core::stringApi < regexp::UnboundedRegExpStructure < SymbolType > >::parse ( input ) );
}

template<class SymbolType >
bool stringApi < regexp::FormalRegExpStructure < SymbolType > >::first ( std::istream & input ) {
	return core::stringApi < regexp::UnboundedRegExpStructure < SymbolType > >::first ( input );
}

template<class SymbolType >
void stringApi < regexp::FormalRegExpStructure < SymbolType > >::compose ( std::ostream & output, const regexp::FormalRegExpStructure < SymbolType > & regexp ) {
	Priority tmp = Priority::ALTERNATION;
	ext::tuple < Priority &, std::ostream & > out = ext::tie ( tmp, output );
	regexp.getStructure ( ).template accept < void, stringApi < regexp::FormalRegExpStructure < SymbolType > >::Formal > ( out );
}

template < class SymbolType >
void stringApi < regexp::FormalRegExpStructure < SymbolType > >::Formal::visit( const regexp::FormalRegExpAlternation < SymbolType > & alternation, ext::tuple < Priority &, std::ostream & > & output ) {
	Priority outerPriorityMinimum = std::get < 0 > ( output );
	if ( outerPriorityMinimum == ext::any_of ( Priority::CONCATENATION, Priority::FACTOR ) ) std::get < 1 > ( output ) << '(';

	std::get < 0 > ( output ) = Priority::ALTERNATION;
	alternation.getLeftElement ( ).template accept < void, stringApi < regexp::FormalRegExpStructure < SymbolType > >::Formal > ( output );
	std::get < 1 > ( output ) << '+';
	alternation.getRightElement ( ).template accept < void, stringApi < regexp::FormalRegExpStructure < SymbolType > >::Formal > ( output );

	if ( outerPriorityMinimum == ext::any_of ( Priority::CONCATENATION, Priority::FACTOR ) ) std::get < 1 > ( output ) << ')';
}

template < class SymbolType >
void stringApi < regexp::FormalRegExpStructure < SymbolType > >::Formal::visit( const regexp::FormalRegExpConcatenation < SymbolType > & concatenation, ext::tuple < Priority &, std::ostream & > & output ) {
	Priority outerPriorityMinimum = std::get < 0 > ( output );
	if ( outerPriorityMinimum == Priority::FACTOR ) std::get < 1 > ( output ) << '(';

	std::get < 0 > ( output ) = Priority::CONCATENATION;
	concatenation.getLeftElement ( ).template accept < void, stringApi < regexp::FormalRegExpStructure < SymbolType > >::Formal > ( output );
	std::get < 1 > ( output ) << ' ';
	std::get < 0 > ( output ) = Priority::CONCATENATION;
	concatenation.getRightElement ( ).template accept < void, stringApi < regexp::FormalRegExpStructure < SymbolType > >::Formal > ( output );

	if ( outerPriorityMinimum == Priority::FACTOR ) std::get < 1 > ( output ) << ')';
}

template < class SymbolType >
void stringApi < regexp::FormalRegExpStructure < SymbolType > >::Formal::visit( const regexp::FormalRegExpIteration < SymbolType > & iteration, ext::tuple < Priority &, std::ostream & > & output ) {
	std::get < 0 > ( output ) = Priority::FACTOR;
	iteration.getElement ( ).template accept < void, stringApi < regexp::FormalRegExpStructure < SymbolType > >::Formal > ( output );
	std::get < 1 > ( output ) << "*";
}

template < class SymbolType >
void stringApi < regexp::FormalRegExpStructure < SymbolType > >::Formal::visit( const regexp::FormalRegExpSymbol < SymbolType > & symbol, ext::tuple < Priority &, std::ostream & > & output ) {
	core::stringApi < SymbolType >::compose ( std::get < 1 > ( output ), symbol.getSymbol ( ) );
}

template < class SymbolType >
void stringApi < regexp::FormalRegExpStructure < SymbolType > >::Formal::visit( const regexp::FormalRegExpEpsilon < SymbolType > &, ext::tuple < Priority &, std::ostream & > & output ) {
	std::get < 1 > ( output ) << "#E";
}

template < class SymbolType >
void stringApi < regexp::FormalRegExpStructure < SymbolType > >::Formal::visit( const regexp::FormalRegExpEmpty < SymbolType > &, ext::tuple < Priority &, std::ostream & > & output ) {
	std::get < 1 > ( output ) << "#0";
}

template<class SymbolType >
struct stringApi < regexp::FormalRegExp < SymbolType > > {
	static regexp::FormalRegExp < SymbolType > parse ( std::istream & input );
	static bool first ( std::istream & input );
	static void compose ( std::ostream & output, const regexp::FormalRegExp < SymbolType > & regexp );
};

template<class SymbolType >
regexp::FormalRegExp < SymbolType > stringApi < regexp::FormalRegExp < SymbolType > >::parse ( std::istream & input ) {
	return regexp::FormalRegExp < SymbolType > ( core::stringApi < regexp::FormalRegExpStructure < SymbolType > >::parse ( input ) );
}

template<class SymbolType >
bool stringApi < regexp::FormalRegExp < SymbolType > >::first ( std::istream & input ) {
	return core::stringApi < regexp::FormalRegExpStructure < SymbolType > >::first ( input );
}

template<class SymbolType >
void stringApi < regexp::FormalRegExp < SymbolType > >::compose ( std::ostream & output, const regexp::FormalRegExp < SymbolType > & regexp ) {
	core::stringApi < regexp::FormalRegExpStructure < SymbolType > >::compose ( output, regexp.getRegExp ( ) );
}

} /* namespace core */

#endif /* _STRING_FORMAL_REG_EXP_H_ */
