/*
 * RegExpIntegral.h
 *
 *  Created on: 24. 2. 2014
 *	  Author: Tomas Pecka
 */

#ifndef _REGEXP_INTEGRAL_H_
#define _REGEXP_INTEGRAL_H_

#include <regexp/formal/FormalRegExp.h>
#include <regexp/formal/FormalRegExpElement.h>
#include <regexp/unbounded/UnboundedRegExp.h>
#include <regexp/unbounded/UnboundedRegExpElement.h>

#include <string/LinearString.h>

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

namespace regexp {

namespace transform {

/**
 * Calculates integral of regular expression. Result does not include the integration constant.
 * Source: Melichar definition 2.93 in chapter 2.4.4
 */
class RegExpIntegral {
public:
	/**
	 * Implements integration of regular expression by a symbol sequence. Result does not include the integration constant.
	 *
	 * \tparam SymbolType the type of symbols in the regular expression
	 *
	 * \param regexp the regexp to derivate
	 * \param string the sequence of symbols to integrate by
	 *
	 * \return resulting regexp
	 */
	template < class SymbolType >
	static regexp::FormalRegExp < SymbolType > integral(const regexp::FormalRegExp < SymbolType > & regexp, const string::LinearString < SymbolType >& string);

	/**
	 * \override
	 */
	template < class SymbolType >
	static regexp::UnboundedRegExp < SymbolType > integral(const regexp::UnboundedRegExp < SymbolType > & regexp, const string::LinearString < SymbolType >& string);

	/**
	 * Implements integration of regular expression by a symbol. Result does not include the integration constant.
	 *
	 * \tparam SymbolType the type of symbols in the regular expression
	 *
	 * \param regexp the regexp to derivate
	 * \param symbol the symbol to integrate by
	 *
	 * \return resulting regexp
	 */
	template < class SymbolType >
	static regexp::FormalRegExp < SymbolType > integral(const regexp::FormalRegExp < SymbolType > & regexp, const SymbolType & symbol);

	/**
	 * \override
	 */
	template < class SymbolType >
	static regexp::UnboundedRegExp < SymbolType > integral(const regexp::UnboundedRegExp < SymbolType > & regexp, const SymbolType & symbol);

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

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

template < class SymbolType >
regexp::FormalRegExp < SymbolType > RegExpIntegral::integral(const regexp::FormalRegExp < SymbolType > & regexp, const string::LinearString < SymbolType >& string) {
	std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > newRegExp ( regexp.getRegExp().getStructure().clone() );

	for(const auto& symbol : string.getContent())
		newRegExp = newRegExp->template accept < std::unique_ptr < regexp::FormalRegExpElement < SymbolType > >, RegExpIntegral::Formal < SymbolType > > ( symbol );

	return regexp::FormalRegExp < SymbolType > ( regexp::FormalRegExpStructure < SymbolType > ( * newRegExp ) );
}

template < class SymbolType >
regexp::UnboundedRegExp < SymbolType > RegExpIntegral::integral(const regexp::UnboundedRegExp < SymbolType > & regexp, const string::LinearString < SymbolType >& string) {
	std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > > newRegExp ( regexp.getRegExp().getStructure().clone() );

	for(const auto& symbol : string.getContent())
		newRegExp = newRegExp->template accept < std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > >, RegExpIntegral::Unbounded < SymbolType > > ( symbol );

	return regexp::UnboundedRegExp < SymbolType > ( regexp::UnboundedRegExpStructure < SymbolType > ( * newRegExp ) );
}

template < class SymbolType >
regexp::FormalRegExp < SymbolType > RegExpIntegral::integral(const regexp::FormalRegExp < SymbolType > & regexp, const SymbolType & symbol) {
	std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > newRegExp = regexp.getRegExp().getStructure().template accept < std::unique_ptr < regexp::FormalRegExpElement < SymbolType > >, RegExpIntegral::Formal < SymbolType > > ( symbol );

	return regexp::FormalRegExp < SymbolType > ( regexp::FormalRegExpStructure < SymbolType > ( * newRegExp ) );
}

template < class SymbolType >
regexp::UnboundedRegExp < SymbolType > RegExpIntegral::integral(const regexp::UnboundedRegExp < SymbolType > & regexp, const SymbolType & symbol) {
	std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > > newRegExp = regexp.getRegExp().getStructure().template accept < std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > >, RegExpIntegral::Unbounded < SymbolType > > ( symbol );

	return regexp::UnboundedRegExp < SymbolType > ( regexp::UnboundedRegExpStructure < SymbolType > ( * newRegExp ) );
}

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

template < class SymbolType >
std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > RegExpIntegral::Formal < SymbolType >::visit(const regexp::FormalRegExpAlternation < SymbolType > & alternation, const SymbolType& argument) {
	std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > leftIntegral = alternation.getLeftElement().template accept < std::unique_ptr < regexp::FormalRegExpElement < SymbolType > >, RegExpIntegral::Formal < SymbolType > > ( argument );
	std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > rightIntegral = alternation.getRightElement().template accept < std::unique_ptr < regexp::FormalRegExpElement < SymbolType > >, RegExpIntegral::Formal < SymbolType > > ( argument );

	return std::unique_ptr < FormalRegExpElement < SymbolType > > ( new regexp::FormalRegExpAlternation < SymbolType > ( std::move ( * leftIntegral ), std::move ( * rightIntegral ) ) );
}

template < class SymbolType >
std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > RegExpIntegral::Formal < SymbolType >::visit(const regexp::FormalRegExpConcatenation < SymbolType > & concatenation, const SymbolType& argument) {
	return std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > ( new regexp::FormalRegExpConcatenation < SymbolType > ( regexp::FormalRegExpSymbol < SymbolType > ( argument ), concatenation ) );
}

template < class SymbolType >
std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > RegExpIntegral::Formal < SymbolType >::visit(const regexp::FormalRegExpIteration < SymbolType > & iteration, const SymbolType& argument) {
	return std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > ( new regexp::FormalRegExpConcatenation < SymbolType > ( regexp::FormalRegExpSymbol < SymbolType > ( argument ), iteration ) );
}

template < class SymbolType >
std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > RegExpIntegral::Formal < SymbolType >::visit(const regexp::FormalRegExpSymbol < SymbolType > & symbol, const SymbolType& argument) {
	return std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > ( new regexp::FormalRegExpConcatenation < SymbolType > ( regexp::FormalRegExpSymbol < SymbolType > ( argument ), symbol ) );
}

template < class SymbolType >
std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > RegExpIntegral::Formal < SymbolType >::visit(const regexp::FormalRegExpEpsilon < SymbolType > &, const SymbolType& argument) {
	return std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > ( new regexp::FormalRegExpSymbol < SymbolType > ( argument ) );
}

template < class SymbolType >
std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > RegExpIntegral::Formal < SymbolType >::visit(const regexp::FormalRegExpEmpty < SymbolType > &, const SymbolType&) {
	return std::unique_ptr < regexp::FormalRegExpElement < SymbolType > > ( new regexp::FormalRegExpEmpty < SymbolType > ( ) );
}

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

template < class SymbolType >
std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > > RegExpIntegral::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpAlternation < SymbolType > & alternation, const SymbolType& argument) {
	regexp::UnboundedRegExpAlternation < SymbolType > * alt = new regexp::UnboundedRegExpAlternation < SymbolType > ();

	for(const UnboundedRegExpElement < SymbolType > & child : alternation.getElements())
		alt->appendElement(* ( child.template accept < std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > >, RegExpIntegral::Unbounded < SymbolType > > (argument) ) );

	return std::unique_ptr < UnboundedRegExpElement < SymbolType > > ( alt );
}

template < class SymbolType >
std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > > RegExpIntegral::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpConcatenation < SymbolType > & concatenation, const SymbolType& argument) {
	regexp::UnboundedRegExpConcatenation < SymbolType > * con = new regexp::UnboundedRegExpConcatenation < SymbolType > ( );

	con->appendElement ( regexp::UnboundedRegExpSymbol < SymbolType > ( argument ) );
	for ( const UnboundedRegExpElement < SymbolType > & element : concatenation.getElements() )
		con->appendElement ( element );

	return std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > > ( con );
}

template < class SymbolType >
std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > > RegExpIntegral::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpIteration < SymbolType > & iteration, const SymbolType& argument) {
	regexp::UnboundedRegExpConcatenation < SymbolType > * con = new regexp::UnboundedRegExpConcatenation < SymbolType > ( );
	con->appendElement ( regexp::UnboundedRegExpSymbol < SymbolType > ( argument ) );
	con->appendElement ( iteration );

	return std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > > ( con );
}

template < class SymbolType >
std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > > RegExpIntegral::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpSymbol < SymbolType > & symbol, const SymbolType& argument) {
	regexp::UnboundedRegExpConcatenation < SymbolType > * con = new regexp::UnboundedRegExpConcatenation < SymbolType > ( );
	con->appendElement ( regexp::UnboundedRegExpSymbol < SymbolType > ( argument ) );
	con->appendElement ( symbol );

	return std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > > ( con );
}

template < class SymbolType >
std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > > RegExpIntegral::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpEpsilon < SymbolType > &, const SymbolType& argument) {
	return std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > > ( new regexp::UnboundedRegExpSymbol < SymbolType > ( argument ) );
}

template < class SymbolType >
std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > > RegExpIntegral::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpEmpty < SymbolType > &, const SymbolType&) {
	return std::unique_ptr < regexp::UnboundedRegExpElement < SymbolType > > ( new regexp::UnboundedRegExpEmpty < SymbolType > ( ) );
}

} /* namespace transform */

} /* namespace regexp */

#endif /* _REGEXP_INTEGRAL_H_ */
