/*
 * UnboundedRegExpElement.h
 *
 * This file is part of Algorithms library toolkit.
 * Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz)

 * Algorithms library toolkit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * Algorithms library toolkit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with Algorithms library toolkit.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  Created on: Nov 23, 2013
 *      Author: Jan Travnicek
 */

#ifndef UNBOUNDED_REG_EXP_ELEMENT_H_
#define UNBOUNDED_REG_EXP_ELEMENT_H_

#include <common/DefaultSymbolType.h>

namespace regexp {

template < class SymbolType >
class UnboundedRegExpElement;

} /* namespace regexp */

#include <alib/set>
#include <alib/tree>
#include <alib/compare>
#include <core/visitor.hpp>

#include "../formal/FormalRegExpElement.h"

namespace regexp {

template < class SymbolType >
class UnboundedRegExpAlternation;
template < class SymbolType >
class UnboundedRegExpConcatenation;
template < class SymbolType >
class UnboundedRegExpIteration;
template < class SymbolType >
class UnboundedRegExpSymbol;
template < class SymbolType >
class UnboundedRegExpEmpty;
template < class SymbolType >
class UnboundedRegExpEpsilon;

/**
 * Abstract class representing element in the unbounded regular expression. Can be an operator, special node, or a symbol.
 *
 * \tparam SymbolType used for the terminal alphabet
 */
template < class SymbolType >
class UnboundedRegExpElement : public ext::CompareOperators < UnboundedRegExpElement < SymbolType > >, public ext::BaseNode < UnboundedRegExpElement < SymbolType > > {
protected:
	/**
	 * Visitor interface of the element.
	 */
	class Visitor {
	public:
		virtual void visit ( const UnboundedRegExpAlternation < SymbolType > & ) = 0;
		virtual void visit ( const UnboundedRegExpConcatenation < SymbolType > & ) = 0;
		virtual void visit ( const UnboundedRegExpIteration < SymbolType > & ) = 0;
		virtual void visit ( const UnboundedRegExpSymbol < SymbolType > & ) = 0;
		virtual void visit ( const UnboundedRegExpEmpty < SymbolType > & ) = 0;
		virtual void visit ( const UnboundedRegExpEpsilon < SymbolType > & ) = 0;
	};

	/**
	 * Visitor interface of the element.
	 */
	class RvalueVisitor {
	public:
		virtual void visit ( UnboundedRegExpAlternation < SymbolType > && ) = 0;
		virtual void visit ( UnboundedRegExpConcatenation < SymbolType > && ) = 0;
		virtual void visit ( UnboundedRegExpIteration < SymbolType > && ) = 0;
		virtual void visit ( UnboundedRegExpSymbol < SymbolType > && ) = 0;
		virtual void visit ( UnboundedRegExpEmpty < SymbolType > && ) = 0;
		virtual void visit ( UnboundedRegExpEpsilon < SymbolType > && ) = 0;
	};

	/**
	 * Helper class interconnecting the visitor interface and visitor core logic.
	 *
	 * \tparam ReturnType the return type of the result of the visit
	 * \tparam Visitorr the type of the actuall visitor
	 * \tparam Params ... types of data passed with the visitor call
	 */
	template < class ReturnType, class Visitorr, class ... Params >
	class VisitorContext : public core::VisitorContextAux < ReturnType, Visitorr, Params ... >, public UnboundedRegExpElement::Visitor {
	public:
		/**
		 * Inherited constructor.
		 */
		using core::VisitorContextAux < ReturnType, Visitorr, Params ... >::VisitorContextAux;

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( const UnboundedRegExpAlternation < SymbolType > & inherit ) override {
			this->call ( inherit, std::make_index_sequence < sizeof ... ( Params ) > { } );
		}

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( const UnboundedRegExpConcatenation < SymbolType > & inherit ) override {
			this->call ( inherit, std::make_index_sequence < sizeof ... ( Params ) > { } );
		}

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( const UnboundedRegExpIteration < SymbolType > & inherit ) override {
			this->call ( inherit, std::make_index_sequence < sizeof ... ( Params ) > { } );
		}

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( const UnboundedRegExpSymbol < SymbolType > & inherit ) override {
			this->call ( inherit, std::make_index_sequence < sizeof ... ( Params ) > { } );
		}

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( const UnboundedRegExpEmpty < SymbolType > & inherit ) override {
			this->call ( inherit, std::make_index_sequence < sizeof ... ( Params ) > { } );
		}

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( const UnboundedRegExpEpsilon < SymbolType > & inherit ) override {
			this->call ( inherit, std::make_index_sequence < sizeof ... ( Params ) > { } );
		}
	};

	/**
	 * Helper class interconnecting the visitor interface and visitor core logic.
	 *
	 * \tparam ReturnType the return type of the result of the visit
	 * \tparam Visitorr the type of the actuall visitor
	 * \tparam Params ... types of data passed with the visitor call
	 */
	template < class ReturnType, class Visitorr, class ... Params >
	class RvalueVisitorContext : public core::VisitorContextAux < ReturnType, Visitorr, Params ... >, public UnboundedRegExpElement::RvalueVisitor {
	public:
		/**
		 * Inherited constructor.
		 */
		using core::VisitorContextAux < ReturnType, Visitorr, Params ... >::VisitorContextAux;

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( UnboundedRegExpAlternation < SymbolType > && inherit ) override {
			this->call ( std::move ( inherit ), std::make_index_sequence < sizeof ... ( Params ) > { } );
		}

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( UnboundedRegExpConcatenation < SymbolType > && inherit ) override {
			this->call ( std::move ( inherit ), std::make_index_sequence < sizeof ... ( Params ) > { } );
		}

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( UnboundedRegExpIteration < SymbolType > && inherit ) override {
			this->call ( std::move ( inherit ), std::make_index_sequence < sizeof ... ( Params ) > { } );
		}

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( UnboundedRegExpSymbol < SymbolType > && inherit ) override {
			this->call ( std::move ( inherit ), std::make_index_sequence < sizeof ... ( Params ) > { } );
		}

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( UnboundedRegExpEmpty < SymbolType > && inherit ) override {
			this->call ( std::move ( inherit ), std::make_index_sequence < sizeof ... ( Params ) > { } );
		}

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( UnboundedRegExpEpsilon < SymbolType > && inherit ) override {
			this->call ( std::move ( inherit ), std::make_index_sequence < sizeof ... ( Params ) > { } );
		}
	};

	/**
	 * \brief Accept method of the visitor pattern. This is where the actual type of this object is evaluated.
	 *
	 * \param visitor the accepted visitor.
	 */
	virtual void accept ( UnboundedRegExpElement::Visitor & visitor ) const & = 0;

	/**
	 * \brief Accept method of the visitor pattern. This is where the actual type of this object is evaluated.
	 *
	 * \param visitor the accepted visitor.
	 */
	virtual void accept ( UnboundedRegExpElement::RvalueVisitor & visitor ) && = 0;

public:
	virtual UnboundedRegExpElement < SymbolType > * clone ( ) const & = 0;

	virtual UnboundedRegExpElement < SymbolType > * clone ( ) && = 0;

	/**
	 * Visitor interface method.
	 *
	 * \tparam ReturnType the return type of the result of the visit
	 * \tparam Visitorr the type of the actuall visitor
	 * \tparam Params ... types of data passed with the visitor call
	 *
	 * \params params ... Additional params passed to visited nodes
	 *
	 * \return result of the visit
	 */
	template < class ReturnType, class Visitorr, class ... Params >
	ReturnType accept ( Params && ... params ) const & {
		VisitorContext < ReturnType, Visitorr, Params ... > context ( std::forward < Params > ( params ) ... );
		accept ( context );
		return context.getResult ( );
	}

	/**
	 * Visitor interface method.
	 *
	 * \tparam ReturnType the return type of the result of the visit
	 * \tparam Visitorr the type of the actuall visitor
	 * \tparam Params ... types of data passed with the visitor call
	 *
	 * \params params ... Additional params passed to visited nodes
	 *
	 * \return result of the visit
	 */
	template < class ReturnType, class Visitorr, class ... Params >
	ReturnType accept ( Params && ... params ) && {
		RvalueVisitorContext < ReturnType, Visitorr, Params ... > context ( std::forward < Params > ( params ) ... );
		std::move ( * this ).accept ( context );
		return context.getResult ( );
	}

	/**
	 * Creates copy of the element.
	 *
	 * \return copy of the element
	 */
	virtual ext::smart_ptr < FormalRegExpElement < SymbolType > > asFormal ( ) const = 0;

	/**
	 * Traverses the regexp tree looking if particular Symbol is used in the regexp.
	 *
	 * \param symbol to test if used in regexp element
	 * \return true if symbol is used by the element and its successor
	 */
	virtual bool testSymbol ( const SymbolType & symbol ) const = 0;

	/**
	 * Traverses the regexp tree computing minimal alphabet needed by regexp
	 *
	 * \param alphabet All alphabet symbols encountered are added into this set
	 */
	virtual void computeMinimalAlphabet ( ext::set < SymbolType > & alphabet ) const = 0;

	/**
	 * Traverses the regexp tree and checks whether all symbols in the regexp tree are in the alphabet
	 *
	 * \param alphabet alphabet to check against
	 * \return true if symbols in the regexp are in the alphabet, false otherwise
	 */
	virtual bool checkAlphabet ( const ext::set < SymbolType > & alphabet ) const = 0;

	/**
	 * Traverses the regexp tree and constructs the minimal alphabet of symbols used in the expression
	 *
	 * \return the minimal alphabet
	 */
	ext::set < SymbolType > computeMinimalAlphabet ( ) const;

	/**
	 * \brief Traverses the regexp tree and normalizes the symbols to DefaultSymbolType
	 *
	 * \return the cloned node including children representing the same content but with type of symbols normalized to DefaultSymbolType
	 */
	virtual ext::smart_ptr < UnboundedRegExpElement < DefaultSymbolType > > normalize ( ) && = 0;

	/**
	 * Print this object as raw representation to ostream.
	 *
	 * \param os ostream where to print
	 * \param instance object to print
	 *
	 * \returns modified output stream
	 */
	friend std::ostream & operator <<( std::ostream & os, const UnboundedRegExpElement < SymbolType > & instance ) {
		instance >> os;
		return os;
	}

	/**
	 * Print this instance as raw representation to ostream.
	 *
	 * \param os ostream where to print
	 */
	virtual void operator >>( std::ostream & ) const = 0;

	/**
	 * Casts this instance to as compact as possible string representation.
	 *
	 * \returns string representation of the object
	 */
	virtual explicit operator std::string ( ) const = 0;

	/**
	 * \brief Comparison helper method evaluating allowing possibly deeper comparison of this with other class of the same hierarchy.
	 *
	 * \details If the other class is of different type the relative order is computer by means of type_index.
	 *
	 * \param other the other class to compare with
	 *
	 * \returns result of actual comparison if type of this class and other class is the same, result of difference of type indexes othervise.
	 */
	virtual int compare ( const UnboundedRegExpElement < SymbolType > & other ) const = 0;
};

template < class SymbolType >
ext::set < SymbolType > UnboundedRegExpElement < SymbolType >::computeMinimalAlphabet ( ) const {
	ext::set < SymbolType > res;

	computeMinimalAlphabet ( res );
	return res;
}

} /* namespace regexp */

extern template class regexp::UnboundedRegExpElement < DefaultSymbolType >;

#endif /* UNBOUNDED_REG_EXP_ELEMENT_H_ */
