/*
 * RegExpEmpty.h
 *
 *  Created on: 19. 1. 2014
 *	  Author: Tomas Pecka
 */

#ifndef REG_EXP_EMPTY_H_
#define REG_EXP_EMPTY_H_

#include <regexp/formal/FormalRegExp.h>
#include <regexp/formal/FormalRegExpElements.h>
#include <regexp/unbounded/UnboundedRegExp.h>
#include <regexp/unbounded/UnboundedRegExpElements.h>

namespace regexp {

namespace properties {

/**
 * Determines whether regular expression (or its subtree) describes an empty language (regexp == \0)
 *
 */
class RegExpEmpty {
public:
	/**
	 * Determines whether regular expression is describes an empty language (regexp == \0)
	 *
	 * \tparam SymbolType the type of symbol in the tested regular expression
	 *
	 * \param regexp the regexp to test
	 *
	 * \return true of the language described by the regular expression is empty
	 */
	template < class SymbolType >
	static bool languageIsEmpty(const regexp::FormalRegExpElement < SymbolType > & regexp);

	/**
	 * \override
	 */
	template < class SymbolType >
	static bool languageIsEmpty(const regexp::FormalRegExpStructure < SymbolType > & regexp);

	/**
	 * \override
	 */
	template < class SymbolType >
	static bool languageIsEmpty(const regexp::FormalRegExp < SymbolType > & regexp);

	/**
	 * \override
	 */
	template < class SymbolType >
	static bool languageIsEmpty(const regexp::UnboundedRegExpElement < SymbolType > & regexp);

	/**
	 * \override
	 */
	template < class SymbolType >
	static bool languageIsEmpty(const regexp::UnboundedRegExpStructure < SymbolType > & regexp);

	/**
	 * \override
	 */
	template < class SymbolType >
	static bool languageIsEmpty(const regexp::UnboundedRegExp < SymbolType > & regexp);

	template < class SymbolType >
	class Unbounded {
	public:
		static bool visit(const regexp::UnboundedRegExpAlternation < SymbolType > & alternation);
		static bool visit(const regexp::UnboundedRegExpConcatenation < SymbolType > & concatenation);
		static bool visit(const regexp::UnboundedRegExpIteration < SymbolType > & iteration);
		static bool visit(const regexp::UnboundedRegExpSymbol < SymbolType > & symbol);
		static bool visit(const regexp::UnboundedRegExpEmpty < SymbolType > & empty);
		static bool visit(const regexp::UnboundedRegExpEpsilon < SymbolType > & epsilon);
	};

	template < class SymbolType >
	class Formal {
	public:
		static bool visit(const regexp::FormalRegExpAlternation < SymbolType > & alternation);
		static bool visit(const regexp::FormalRegExpConcatenation < SymbolType > & concatenation);
		static bool visit(const regexp::FormalRegExpIteration < SymbolType > & iteration);
		static bool visit(const regexp::FormalRegExpSymbol < SymbolType > & symbol);
		static bool visit(const regexp::FormalRegExpEmpty < SymbolType > & empty);
		static bool visit(const regexp::FormalRegExpEpsilon < SymbolType > & epsilon);
	};

};

// ----------------------------------------------------------------------------

template < class SymbolType >
bool RegExpEmpty::languageIsEmpty(const regexp::FormalRegExpElement < SymbolType > & regexp) {
	return regexp.template accept < bool, RegExpEmpty::Formal < SymbolType > > ( );
}

template < class SymbolType >
bool RegExpEmpty::languageIsEmpty(const regexp::FormalRegExpStructure < SymbolType > & regexp) {
	return languageIsEmpty(regexp.getStructure());
}

template < class SymbolType >
bool RegExpEmpty::languageIsEmpty(const regexp::FormalRegExp < SymbolType > & regexp) {
	return languageIsEmpty(regexp.getRegExp());
}

// ----------------------------------------------------------------------------

template < class SymbolType >
bool RegExpEmpty::languageIsEmpty(const regexp::UnboundedRegExpElement < SymbolType > & regexp) {
	return regexp.template accept < bool, RegExpEmpty::Unbounded < SymbolType > > ( );
}

template < class SymbolType >
bool RegExpEmpty::languageIsEmpty(const regexp::UnboundedRegExpStructure < SymbolType > & regexp) {
	return languageIsEmpty(regexp.getStructure());
}

template < class SymbolType >
bool RegExpEmpty::languageIsEmpty(const regexp::UnboundedRegExp < SymbolType > & regexp) {
	return languageIsEmpty(regexp.getRegExp());
}

// ----------------------------------------------------------------------------

template < class SymbolType >
bool RegExpEmpty::Unbounded< SymbolType >::visit(const regexp::UnboundedRegExpAlternation < SymbolType > & alternation) {
	return std::all_of ( alternation.getElements ( ).begin ( ), alternation.getElements ( ).end ( ), [ ] ( const UnboundedRegExpElement < SymbolType > & element ) {
		return element.template accept < bool, RegExpEmpty::Unbounded < SymbolType > > ( );
	} );
}

template < class SymbolType >
bool RegExpEmpty::Unbounded< SymbolType >::visit(const regexp::UnboundedRegExpConcatenation < SymbolType > & concatenation) {
	return std::any_of ( concatenation.getElements ( ).begin ( ), concatenation.getElements ( ).end ( ), [ ] ( const UnboundedRegExpElement < SymbolType > & element ) {
		return element.template accept < bool, RegExpEmpty::Unbounded < SymbolType > > ( );
	} );
}

template < class SymbolType >
bool RegExpEmpty::Unbounded< SymbolType >::visit(const regexp::UnboundedRegExpIteration < SymbolType > &) {
	return false;
}

template < class SymbolType >
bool RegExpEmpty::Unbounded< SymbolType >::visit(const regexp::UnboundedRegExpSymbol < SymbolType > &) {
	return false;
}

template < class SymbolType >
bool RegExpEmpty::Unbounded< SymbolType >::visit(const regexp::UnboundedRegExpEmpty < SymbolType > &) {
	return true;
}

template < class SymbolType >
bool RegExpEmpty::Unbounded< SymbolType >::visit(const regexp::UnboundedRegExpEpsilon < SymbolType > &) {
	return false;
}

// ----------------------------------------------------------------------------

template < class SymbolType >
bool RegExpEmpty::Formal< SymbolType >::visit(const regexp::FormalRegExpAlternation < SymbolType > & alternation) {
	return alternation.getLeftElement().template accept < bool, RegExpEmpty::Formal < SymbolType > > ( ) && alternation.getRightElement().template accept < bool, RegExpEmpty::Formal < SymbolType > > ( );
}

template < class SymbolType >
bool RegExpEmpty::Formal< SymbolType >::visit(const regexp::FormalRegExpConcatenation < SymbolType > & concatenation) {
	return concatenation.getLeftElement().template accept < bool, RegExpEmpty::Formal < SymbolType > > ( ) || concatenation.getRightElement().template accept < bool, RegExpEmpty::Formal < SymbolType > > ( );
}

template < class SymbolType >
bool RegExpEmpty::Formal< SymbolType >::visit(const regexp::FormalRegExpIteration < SymbolType > &) {
	return false;
}

template < class SymbolType >
bool RegExpEmpty::Formal< SymbolType >::visit(const regexp::FormalRegExpSymbol < SymbolType > &) {
	return false;
}

template < class SymbolType >
bool RegExpEmpty::Formal< SymbolType >::visit(const regexp::FormalRegExpEmpty < SymbolType > &) {
	return true;
}

template < class SymbolType >
bool RegExpEmpty::Formal< SymbolType >::visit(const regexp::FormalRegExpEpsilon < SymbolType > &) {
	return false;
}

} /* namespace properties */

} /* namespace regexp */

#endif /* REG_EXP_EMPTY_H_ */
