/*
 * GlushkovIndexate.h
 *
 *  Created on: 13. 3. 2014
 *	  Author: Tomas Pecka
 */

#ifndef GLUSHKOV_INDEXATE_H_
#define GLUSHKOV_INDEXATE_H_

#include <regexp/unbounded/UnboundedRegExp.h>

#include <regexp/unbounded/UnboundedRegExpAlternation.h>
#include <regexp/unbounded/UnboundedRegExpConcatenation.h>
#include <regexp/unbounded/UnboundedRegExpElement.h>
#include <regexp/unbounded/UnboundedRegExpEmpty.h>
#include <regexp/unbounded/UnboundedRegExpEpsilon.h>
#include <regexp/unbounded/UnboundedRegExpIteration.h>
#include <regexp/unbounded/UnboundedRegExpSymbol.h>

namespace regexp {

/**
 * RegExp tree traversal utils for Glushkov algorithm.
 *
 */
class GlushkovIndexate {
public:
	/**
	 * @param re RegExp to index
	 * @return UnboundedRegExp with indexed elements
	 */
	template < class SymbolType >
	static regexp::UnboundedRegExp < ext::pair < SymbolType, unsigned > > index ( const regexp::UnboundedRegExp < SymbolType > & re );

	template < class SymbolType >
	class Unbounded {
	public:
		static ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > visit(const regexp::UnboundedRegExpAlternation < SymbolType > & alternation, unsigned & i);
		static ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > visit(const regexp::UnboundedRegExpConcatenation < SymbolType > & concatenation, unsigned & i);
		static ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > visit(const regexp::UnboundedRegExpIteration < SymbolType > & iteration, unsigned & i);
		static ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > visit(const regexp::UnboundedRegExpSymbol < SymbolType > & symbol, unsigned & i);
		static ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > visit(const regexp::UnboundedRegExpEpsilon < SymbolType > & epsilon, unsigned & i);
		static ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > visit(const regexp::UnboundedRegExpEmpty < SymbolType > & empty, unsigned & i);
	};

	/**
	 * @param re RegExp to index
	 * @return FormalRegExp with indexed elements
	 */
	template < class SymbolType >
	static regexp::FormalRegExp < ext::pair < SymbolType, unsigned > > index ( const regexp::FormalRegExp < SymbolType > & re );

	template < class SymbolType >
	class Formal {
	public:
		static ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > visit(const regexp::FormalRegExpAlternation < SymbolType > & alternation, unsigned & i);
		static ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > visit(const regexp::FormalRegExpConcatenation < SymbolType > & concatenation, unsigned & i);
		static ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > visit(const regexp::FormalRegExpIteration < SymbolType > & iteration, unsigned & i);
		static ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > visit(const regexp::FormalRegExpSymbol < SymbolType > & symbol, unsigned & i);
		static ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > visit(const regexp::FormalRegExpEpsilon < SymbolType > & epsilon, unsigned & i);
		static ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > visit(const regexp::FormalRegExpEmpty < SymbolType > & empty, unsigned & i);
	};
};

template < class SymbolType >
UnboundedRegExp < ext::pair < SymbolType, unsigned > > GlushkovIndexate::index ( const regexp::UnboundedRegExp < SymbolType > & re ) {
	unsigned i = 1;

	return UnboundedRegExp < ext::pair < SymbolType, unsigned > > ( regexp::UnboundedRegExpStructure < ext::pair < SymbolType, unsigned > > ( re.getRegExp ( ).getStructure ( ).template accept < ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > >, GlushkovIndexate::Unbounded < SymbolType > > ( i ) ) );
}

template < class SymbolType >
ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > GlushkovIndexate::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpAlternation < SymbolType > & alternation, unsigned & i) {
	UnboundedRegExpAlternation < ext::pair < SymbolType, unsigned > > alt;

	for ( const UnboundedRegExpElement < SymbolType > & element : alternation.getElements ( ) )
		alt.appendElement ( element.template accept < ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > >, GlushkovIndexate::Unbounded < SymbolType > > ( i ) );

	return ext::ptr_value < UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > ( std::move ( alt ) );
}

template < class SymbolType >
ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > GlushkovIndexate::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpConcatenation < SymbolType > & concatenation, unsigned & i) {
	UnboundedRegExpConcatenation < ext::pair < SymbolType, unsigned > > con;

	for ( const UnboundedRegExpElement < SymbolType > & element : concatenation.getElements ( ) )
		con.appendElement ( element.template accept < ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > >, GlushkovIndexate::Unbounded < SymbolType > > ( i ) );

	return ext::ptr_value < UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > ( std::move ( con ) );
}

template < class SymbolType >
ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > GlushkovIndexate::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpIteration < SymbolType > & iteration, unsigned & i) {
	return ext::ptr_value < UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > ( UnboundedRegExpIteration < ext::pair < SymbolType, unsigned > > ( iteration.getElement ( ).template accept < ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < DefaultSymbolType, unsigned > > >, GlushkovIndexate::Unbounded < SymbolType > > ( i ) ) );
}

template < class SymbolType >
ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > GlushkovIndexate::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpSymbol < SymbolType > & symbol, unsigned & i) {
	return ext::ptr_value < UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > ( UnboundedRegExpSymbol < ext::pair < SymbolType, unsigned > > ( ext::make_pair ( symbol.getSymbol ( ), i++ ) ) );
}

template < class SymbolType >
ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > GlushkovIndexate::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpEpsilon < SymbolType > &, unsigned &) {
	return ext::ptr_value < UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > ( UnboundedRegExpEpsilon < ext::pair < SymbolType, unsigned > > ( ) );
}

template < class SymbolType >
ext::ptr_value < regexp::UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > GlushkovIndexate::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpEmpty < SymbolType > &, unsigned &) {
	return ext::ptr_value < UnboundedRegExpElement < ext::pair < SymbolType, unsigned > > > ( UnboundedRegExpEmpty < ext::pair < SymbolType, unsigned > > ( ) );
}

template < class SymbolType >
FormalRegExp < ext::pair < SymbolType, unsigned > > GlushkovIndexate::index ( const regexp::FormalRegExp < SymbolType > & re ) {
	unsigned i = 1;

	return FormalRegExp < ext::pair < SymbolType, unsigned > > ( regexp::FormalRegExpStructure < ext::pair < SymbolType, unsigned > > ( re.getRegExp ( ).getStructure ( ).template accept < ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > >, GlushkovIndexate::Formal < SymbolType > > ( i ) ) );
}

template < class SymbolType >
ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > GlushkovIndexate::Formal < SymbolType >::visit(const regexp::FormalRegExpAlternation < SymbolType > & alternation, unsigned & i) {
	ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > left = alternation.getLeftElement ( ).template accept < ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > >, GlushkovIndexate::Formal < SymbolType > > ( i );
	ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > right = alternation.getRightElement ( ).template accept < ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > >, GlushkovIndexate::Formal < SymbolType > > ( i );
	return ext::ptr_value < FormalRegExpElement < ext::pair < SymbolType, unsigned > > > ( FormalRegExpAlternation < ext::pair < SymbolType, unsigned > > ( left, right ) );
}

template < class SymbolType >
ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > GlushkovIndexate::Formal < SymbolType >::visit(const regexp::FormalRegExpConcatenation < SymbolType > & concatenation, unsigned & i) {
ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > left = concatenation.getLeftElement ( ).template accept < ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > >, GlushkovIndexate::Formal < SymbolType > > ( i );
ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > right = concatenation.getRightElement ( ).template accept < ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > >, GlushkovIndexate::Formal < SymbolType > > ( i );
	return ext::ptr_value < FormalRegExpElement < ext::pair < SymbolType, unsigned > > > ( FormalRegExpConcatenation < ext::pair < SymbolType, unsigned > > ( left, right ) );
}

template < class SymbolType >
ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > GlushkovIndexate::Formal < SymbolType >::visit(const regexp::FormalRegExpIteration < SymbolType > & iteration, unsigned & i) {
	return ext::ptr_value < FormalRegExpElement < ext::pair < SymbolType, unsigned > > > ( FormalRegExpIteration < ext::pair < SymbolType, unsigned > > ( iteration.getElement ( ).template accept < ext::ptr_value < regexp::FormalRegExpElement < ext::pair < DefaultSymbolType, unsigned > > >, GlushkovIndexate::Formal < SymbolType > > ( i ) ) );
}

template < class SymbolType >
ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > GlushkovIndexate::Formal < SymbolType >::visit(const regexp::FormalRegExpSymbol < SymbolType > & symbol, unsigned & i) {
	return ext::ptr_value < FormalRegExpElement < ext::pair < SymbolType, unsigned > > > ( FormalRegExpSymbol < ext::pair < SymbolType, unsigned > > ( ext::make_pair ( symbol.getSymbol ( ), i++ ) ) );
}

template < class SymbolType >
ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > GlushkovIndexate::Formal < SymbolType >::visit(const regexp::FormalRegExpEpsilon < SymbolType > &, unsigned &) {
	return ext::ptr_value < FormalRegExpElement < ext::pair < SymbolType, unsigned > > > ( FormalRegExpEpsilon < ext::pair < SymbolType, unsigned > > ( ) );
}

template < class SymbolType >
ext::ptr_value < regexp::FormalRegExpElement < ext::pair < SymbolType, unsigned > > > GlushkovIndexate::Formal < SymbolType >::visit(const regexp::FormalRegExpEmpty < SymbolType > &, unsigned &) {
	return ext::ptr_value < FormalRegExpElement < ext::pair < SymbolType, unsigned > > > ( FormalRegExpEmpty < ext::pair < SymbolType, unsigned > > ( ) );
}

} /* namespace regexp */

#endif /* GLUSHKOV_INDEXATE_H_ */
