/*
 * GlushkovFirst.h
 *
 *  Created on: 14. 4. 2016
 *	  Author: Tomas Pecka
 */

#ifndef RTE_GLUSHKOV_FIRST_H_
#define RTE_GLUSHKOV_FIRST_H_

#include <alib/set>
#include <common/ranked_symbol.hpp>

#include <rte/formal/FormalRTE.h>

namespace rte {

class GlushkovFirst {
public:
	/**
	 * @param re rte to probe
	 * @return all rteSymbols which can be root of the tree.
	 */
	template < class SymbolType >
	static ext::set < common::ranked_symbol < SymbolType > > first ( const rte::FormalRTE < SymbolType > & rte );

	template < class SymbolType >
	class Formal {
	public:
		static ext::set < common::ranked_symbol < SymbolType > > visit ( const rte::FormalRTEAlternation < SymbolType > & node );
		static ext::set < common::ranked_symbol < SymbolType > > visit ( const rte::FormalRTESubstitution < SymbolType > & node );
		static ext::set < common::ranked_symbol < SymbolType > > visit ( const rte::FormalRTEIteration < SymbolType > & node );
		static ext::set < common::ranked_symbol < SymbolType > > visit ( const rte::FormalRTESymbolAlphabet < SymbolType > & node );
		static ext::set < common::ranked_symbol < SymbolType > > visit ( const rte::FormalRTESymbolSubst < SymbolType > & node );
		static ext::set < common::ranked_symbol < SymbolType > > visit ( const rte::FormalRTEEmpty < SymbolType > & node );
	};
};

template < class SymbolType >
ext::set < common::ranked_symbol < SymbolType > > GlushkovFirst::first ( const rte::FormalRTE < SymbolType > & rte ) {
	return rte.getRTE ( ).getStructure ( ).template accept < ext::set < common::ranked_symbol < SymbolType > >, GlushkovFirst::Formal < SymbolType > > ( );
}

template < class SymbolType >
ext::set < common::ranked_symbol < SymbolType > > GlushkovFirst::Formal < SymbolType >::visit ( const rte::FormalRTEAlternation < SymbolType > & node ) {
	ext::set < common::ranked_symbol < SymbolType > > ret;
	ext::set < common::ranked_symbol < SymbolType > > tmp;

	tmp = node.getLeftElement ( ).template accept < ext::set < common::ranked_symbol < SymbolType > >, GlushkovFirst::Formal < SymbolType > > ( );
	ret.insert ( tmp.begin ( ), tmp.end ( ) );

	tmp = node.getRightElement ( ).template accept < ext::set < common::ranked_symbol < SymbolType > >, GlushkovFirst::Formal < SymbolType > > ( );
	ret.insert ( tmp.begin ( ), tmp.end ( ) );

	return ret;
}

template < class SymbolType >
ext::set < common::ranked_symbol < SymbolType > > GlushkovFirst::Formal < SymbolType >::visit ( const rte::FormalRTESubstitution < SymbolType > & node ) {
	ext::set < common::ranked_symbol < SymbolType > > ret;
	ext::set < common::ranked_symbol < SymbolType > > tmp;

	tmp = node.getLeftElement ( ).template accept < ext::set < common::ranked_symbol < SymbolType > >, GlushkovFirst::Formal < SymbolType > > ( );
	ret.insert ( tmp.begin ( ), tmp.end ( ) );

	 // First returns a set. hence only one occurrence.
	auto it = std::find_if ( ret.begin ( ), ret.end ( ), [node] ( const common::ranked_symbol < SymbolType > & a ) {
			return a == node.getSubstitutionSymbol ( ).getSymbol ( );
		} );

	if ( it != ret.end ( ) ) {
		ret.erase ( it );
		tmp = node.getRightElement ( ).template accept < ext::set < common::ranked_symbol < SymbolType > >, GlushkovFirst::Formal < SymbolType > > ( );
		ret.insert ( tmp.begin ( ), tmp.end ( ) );
	}

	return ret;
}

template < class SymbolType >
ext::set < common::ranked_symbol < SymbolType > > GlushkovFirst::Formal < SymbolType >::visit ( const rte::FormalRTEIteration < SymbolType > & node ) {
	ext::set < common::ranked_symbol < SymbolType > > ret;

	ret = node.getElement ( ).template accept < ext::set < common::ranked_symbol < SymbolType > >, GlushkovFirst::Formal < SymbolType > > ( );
	ret.insert ( node.getSubstitutionSymbol ( ).getSymbol ( ) );
	return ret;
}

template < class SymbolType >
ext::set < common::ranked_symbol < SymbolType > > GlushkovFirst::Formal < SymbolType >::visit ( const rte::FormalRTESymbolAlphabet < SymbolType > & node ) {
	return ext::set < common::ranked_symbol < SymbolType > > { node.getSymbol ( ) };
}

template < class SymbolType >
ext::set < common::ranked_symbol < SymbolType > > GlushkovFirst::Formal < SymbolType >::visit ( const rte::FormalRTESymbolSubst < SymbolType > & node ) {
	return ext::set < common::ranked_symbol < SymbolType > > { node.getSymbol ( ) };
}

template < class SymbolType >
ext::set < common::ranked_symbol < SymbolType > > GlushkovFirst::Formal < SymbolType >::visit ( const rte::FormalRTEEmpty < SymbolType > & /* node */ ) {
	return ext::set < common::ranked_symbol < SymbolType > > ( );
}

} /* namespace rte */

#endif /* RTE_GLUSHKOV_FIRST_H_ */
