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

#ifndef GLUSHKOV_FOLLOW_H_
#define GLUSHKOV_FOLLOW_H_

#include <alib/set>
#include <alib/iterator>

#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>

#include "GlushkovFirst.h"
#include "GlushkovLast.h"
#include "GlushkovPos.h"

#include <regexp/properties/RegExpEpsilon.h>

namespace regexp {

/**
 * RegExp tree traversal utils for Glushkov algorithm.
 *
 * Thanks to http://www.sciencedirect.com/science/article/pii/S030439759700296X for better follow() solution.
 */
class GlushkovFollow {
public:
	/**
	 * @param re RegExp to probe
	 * @param symbol GlushkovSymbol for which we need the follow()
	 * @return all symbols that can follow specific symbol in word
	 */
	template < class SymbolType >
	static ext::set < UnboundedRegExpSymbol < SymbolType > > follow ( const regexp::UnboundedRegExp < SymbolType > & re, const UnboundedRegExpSymbol < SymbolType > & symbol );

	template < class SymbolType >
	class Unbounded {
	public:
		static ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > visit ( const regexp::UnboundedRegExpAlternation < SymbolType > & node, const regexp::UnboundedRegExpSymbol < SymbolType > & symbolptr );
		static ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > visit ( const regexp::UnboundedRegExpConcatenation < SymbolType > & node, const regexp::UnboundedRegExpSymbol < SymbolType > & symbolptr );
		static ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > visit ( const regexp::UnboundedRegExpIteration < SymbolType > & node, const regexp::UnboundedRegExpSymbol < SymbolType > & symbolptr );
		static ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > visit ( const regexp::UnboundedRegExpSymbol < SymbolType > & node, const regexp::UnboundedRegExpSymbol < SymbolType > & symbFollow );
		static ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > visit ( const regexp::UnboundedRegExpEmpty < SymbolType > & node, const regexp::UnboundedRegExpSymbol < SymbolType > & symbFollow );
		static ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > visit ( const regexp::UnboundedRegExpEpsilon < SymbolType > & node, const regexp::UnboundedRegExpSymbol < SymbolType > & symbFollow );
	};

	/**
	 * @param re RegExp to probe
	 * @param symbol GlushkovSymbol for which we need the follow()
	 * @return all symbols that can follow specific symbol in word
	 */
	template < class SymbolType >
	static ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > follow ( const regexp::FormalRegExp < SymbolType > & re );

	template < class SymbolType >
	class Formal {
	public:
		static void visit ( const regexp::FormalRegExpAlternation < SymbolType > & node, ext::set < regexp::FormalRegExpSymbol < SymbolType > > & currentFollow, ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > & res );
		static void visit ( const regexp::FormalRegExpConcatenation < SymbolType > & node, ext::set < regexp::FormalRegExpSymbol < SymbolType > > & currentFollow, ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > & res );
		static void visit ( const regexp::FormalRegExpIteration < SymbolType > & node, ext::set < regexp::FormalRegExpSymbol < SymbolType > > & currentFollow, ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > & res );
		static void visit ( const regexp::FormalRegExpSymbol < SymbolType > & node, ext::set < regexp::FormalRegExpSymbol < SymbolType > > & currentFollow, ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > & res );
		static void visit ( const regexp::FormalRegExpEmpty < SymbolType > & node, ext::set < regexp::FormalRegExpSymbol < SymbolType > > & currentFollow, ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > & res );
		static void visit ( const regexp::FormalRegExpEpsilon < SymbolType > & node, ext::set < regexp::FormalRegExpSymbol < SymbolType > > & currentFollow, ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > & res );
	};
};

template < class SymbolType >
ext::set < UnboundedRegExpSymbol < SymbolType > > GlushkovFollow::follow ( const regexp::UnboundedRegExp < SymbolType > & re, const UnboundedRegExpSymbol < SymbolType > & symbol ) {
	return re.getRegExp ( ).getStructure ( ).template accept < ext::set < regexp::UnboundedRegExpSymbol < SymbolType > >, GlushkovFollow::Unbounded < SymbolType > > ( symbol );
}

template < class SymbolType >
ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > GlushkovFollow::Unbounded < SymbolType >::visit ( const regexp::UnboundedRegExpAlternation < SymbolType > & node, const regexp::UnboundedRegExpSymbol < SymbolType > & symbolptr ) {
	auto iter = std::find_if ( node.getElements ( ).begin ( ), node.getElements ( ).end ( ), [ & ] ( const UnboundedRegExpElement < SymbolType > & element ) {
		return element.template accept < bool, GlushkovPos::Unbounded < SymbolType > > ( symbolptr );
	} );

	if ( iter == node.getElements ( ).end ( ) )
		throw exception::CommonException ( "GlushkovFollow::Unbounded < SymbolType >::visit(Alt)" );

	return iter->template accept < ext::set < regexp::UnboundedRegExpSymbol < SymbolType > >, GlushkovFollow::Unbounded < SymbolType > > ( symbolptr );
}

template < class SymbolType >
ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > GlushkovFollow::Unbounded < SymbolType >::visit ( const regexp::UnboundedRegExpConcatenation < SymbolType > & node, const regexp::UnboundedRegExpSymbol < SymbolType > & symbolptr ) {
	ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > ret;
	ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > tmp;
	ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > lastSet;

	for ( auto e = node.getElements ( ).begin ( ); e != node.getElements ( ).end ( ); e++ ) {
		if ( ! e->template accept < bool, GlushkovPos::Unbounded < SymbolType > > ( symbolptr ) )
			continue;

		tmp = e->template accept < ext::set < regexp::UnboundedRegExpSymbol < SymbolType > >, GlushkovFollow::Unbounded < SymbolType > > ( symbolptr );
		ret.insert ( tmp.begin ( ), tmp.end ( ) );

		lastSet = e->template accept < ext::set < regexp::UnboundedRegExpSymbol < SymbolType > >, GlushkovLast::Unbounded < SymbolType > > ( );

		if ( lastSet.find ( symbolptr ) != lastSet.end ( ) )
			for ( auto f = next ( e ); f != node.getElements ( ).end ( ); f++ ) {
				tmp = f->template accept < ext::set < regexp::UnboundedRegExpSymbol < SymbolType > >, GlushkovFirst::Unbounded < SymbolType > > ( );
				ret.insert ( tmp.begin ( ), tmp.end ( ) );

				if ( ! regexp::properties::RegExpEpsilon::languageContainsEpsilon ( * f ) )
					break;
			}

	}

	return ret;
}

template < class SymbolType >
ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > GlushkovFollow::Unbounded < SymbolType >::visit ( const regexp::UnboundedRegExpIteration < SymbolType > & node, const regexp::UnboundedRegExpSymbol < SymbolType > & symbolptr ) {
	ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > ret = node.getElement ( ).template accept < ext::set < regexp::UnboundedRegExpSymbol < SymbolType > >, GlushkovFollow::Unbounded < SymbolType > > ( symbolptr );
	ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > lastSet = node.getElement ( ).template accept < ext::set < regexp::UnboundedRegExpSymbol < SymbolType > >, GlushkovLast::Unbounded < SymbolType > > ( );

	if ( lastSet.find ( symbolptr ) != lastSet.end ( ) ) {
		ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > firstSet = node.getElement ( ).template accept < ext::set < regexp::UnboundedRegExpSymbol < SymbolType > >, GlushkovFirst::Unbounded < SymbolType > > ( );
		ret.insert ( firstSet.begin ( ), firstSet.end ( ) );
	}

	return ret;
}

template < class SymbolType >
ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > GlushkovFollow::Unbounded < SymbolType >::visit ( const regexp::UnboundedRegExpSymbol < SymbolType > & /* node */, const regexp::UnboundedRegExpSymbol < SymbolType > & /* symbolptr */ ) {
	return ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > ( );
}

template < class SymbolType >
ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > GlushkovFollow::Unbounded < SymbolType >::visit ( const regexp::UnboundedRegExpEmpty < SymbolType > & /* node */, const regexp::UnboundedRegExpSymbol < SymbolType > & /* symbolptr */ ) {
	return ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > ( );
}

template < class SymbolType >
ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > GlushkovFollow::Unbounded < SymbolType >::visit ( const regexp::UnboundedRegExpEpsilon < SymbolType > & /* node */, const regexp::UnboundedRegExpSymbol < SymbolType > & /* symbolptr */ ) {
	return ext::set < regexp::UnboundedRegExpSymbol < SymbolType > > ( );
}

template < class SymbolType >
ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > GlushkovFollow::follow ( const regexp::FormalRegExp < SymbolType > & re ) {
	ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > res;
	ext::set < regexp::FormalRegExpSymbol < SymbolType > > currentFollow;
	re.getRegExp ( ).getStructure ( ).template accept < void, GlushkovFollow::Formal < SymbolType > > ( currentFollow, res );
	return res;
}

template < class SymbolType >
void GlushkovFollow::Formal < SymbolType >::visit ( const regexp::FormalRegExpAlternation < SymbolType > & node, ext::set < regexp::FormalRegExpSymbol < SymbolType > > & currentFollow, ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > & res ) {
	node.getLeftElement ( ).template accept < void, GlushkovFollow::Formal < SymbolType > > ( currentFollow, res );
	node.getRightElement ( ).template accept < void, GlushkovFollow::Formal < SymbolType > > ( currentFollow, res );
}

template < class SymbolType >
void GlushkovFollow::Formal < SymbolType >::visit ( const regexp::FormalRegExpConcatenation < SymbolType > & node, ext::set < regexp::FormalRegExpSymbol < SymbolType > > & currentFollow, ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > & res ) {
	ext::set < regexp::FormalRegExpSymbol < SymbolType > > leftFollow = node.getRightElement ( ).template accept < ext::set < regexp::FormalRegExpSymbol < SymbolType > >, GlushkovFirst::Formal < SymbolType > > ( );
	if ( regexp::properties::RegExpEpsilon::languageContainsEpsilon ( node.getRightElement ( ) ) )
		leftFollow.insert ( currentFollow.begin ( ), currentFollow.end ( ) );
	node.getLeftElement ( ).template accept < void, GlushkovFollow::Formal < SymbolType > > ( leftFollow, res );
	node.getRightElement ( ).template accept < void, GlushkovFollow::Formal < SymbolType > > ( currentFollow, res );
}

template < class SymbolType >
void GlushkovFollow::Formal < SymbolType >::visit ( const regexp::FormalRegExpIteration < SymbolType > & node, ext::set < regexp::FormalRegExpSymbol < SymbolType > > & currentFollow, ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > & res ) {
	ext::set < regexp::FormalRegExpSymbol < SymbolType > > newFollow = node.getElement ( ).template accept < ext::set < regexp::FormalRegExpSymbol < SymbolType > >, GlushkovFirst::Formal < SymbolType > > ( );
	newFollow.insert ( currentFollow.begin ( ), currentFollow.end ( ) );
	node.getElement ( ).template accept < void, GlushkovFollow::Formal < SymbolType > > ( newFollow, res );
}

template < class SymbolType >
void GlushkovFollow::Formal < SymbolType >::visit ( const regexp::FormalRegExpSymbol < SymbolType > & node, ext::set < regexp::FormalRegExpSymbol < SymbolType > > & currentFollow, ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > & res ) {
	res.insert ( std::make_pair ( node, currentFollow ) );
}

template < class SymbolType >
void GlushkovFollow::Formal < SymbolType >::visit ( const regexp::FormalRegExpEmpty < SymbolType > & /* node */, ext::set < regexp::FormalRegExpSymbol < SymbolType > > & /* currentFollow */, ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > & /* res */ ) {
}

template < class SymbolType >
void GlushkovFollow::Formal < SymbolType >::visit ( const regexp::FormalRegExpEpsilon < SymbolType > & /* node */, ext::set < regexp::FormalRegExpSymbol < SymbolType > > & /* currentFollow */, ext::map < regexp::FormalRegExpSymbol < SymbolType >, ext::set < regexp::FormalRegExpSymbol < SymbolType > > > & /* res */ ) {
}

} /* namespace regexp */

#endif /* GLUSHKOV_FOLLOW_H_ */
