/*
 * FormalRTEElement.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: Feb 20, 2016
 *      Author: Tomas Pecka
 */

#ifndef FORMAL_RTE_ELEMENT_H_
#define FORMAL_RTE_ELEMENT_H_

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

namespace rte {

template < class SymbolType >
class FormalRTEElement;
template < class SymbolType >
class FormalRTEAlternation;
template < class SymbolType >
class FormalRTEIteration;
template < class SymbolType >
class FormalRTESubstitution;
template < class SymbolType >
class FormalRTESymbolSubst;
template < class SymbolType >
class FormalRTESymbolAlphabet;
template < class SymbolType >
class FormalRTEEmpty;

/**
 * Abstract class representing element in the formal regular tree expression. Can be an operator, special node, or a symbol.
 *
 * \tparam SymbolType used for the symbol part of the ranked symbol
 */
template < class SymbolType >
class FormalRTEElement : public ext::CompareOperators < FormalRTEElement < SymbolType > >, public ext::BaseNode < FormalRTEElement < SymbolType > > {
protected:
	/**
	 * Visitor interface of the element.
	 */
	class ConstVisitor {
	public:
		virtual void visit ( const FormalRTEAlternation < SymbolType > & alternation ) = 0;
		virtual void visit ( const FormalRTEIteration < SymbolType > & iteration ) = 0;
		virtual void visit ( const FormalRTESubstitution < SymbolType > & substitution ) = 0;
		virtual void visit ( const FormalRTESymbolAlphabet < SymbolType > & symbol ) = 0;
		virtual void visit ( const FormalRTESymbolSubst < SymbolType > & symbol ) = 0;
		virtual void visit ( const FormalRTEEmpty < SymbolType > & empty ) = 0;
	};

	class Visitor {
	public:
		virtual void visit ( FormalRTEAlternation < SymbolType > & alternation ) = 0;
		virtual void visit ( FormalRTEIteration < SymbolType > & iteration ) = 0;
		virtual void visit ( FormalRTESubstitution < SymbolType > & substitution ) = 0;
		virtual void visit ( FormalRTESymbolAlphabet < SymbolType > & symbol ) = 0;
		virtual void visit ( FormalRTESymbolSubst < SymbolType > & symbol ) = 0;
		virtual void visit ( FormalRTEEmpty < SymbolType > & empty ) = 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 ConstVisitorContext : public core::VisitorContextAux < ReturnType, Visitorr, Params ... >, public FormalRTEElement::ConstVisitor {
	public:
		/**
		 * Inherited constructor.
		 */
		using core::VisitorContextAux < ReturnType, Visitorr, Params ... >::VisitorContextAux;

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( const FormalRTEAlternation < 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 FormalRTEIteration < 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 FormalRTESubstitution < 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 FormalRTESymbolAlphabet < 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 FormalRTESymbolSubst < 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 FormalRTEEmpty < 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 VisitorContext : public core::VisitorContextAux < ReturnType, Visitorr, Params ... >, public FormalRTEElement::Visitor {
	public:
		/**
		 * Inherited constructor.
		 */
		using core::VisitorContextAux < ReturnType, Visitorr, Params ... >::VisitorContextAux;

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

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

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

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

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

		/**
		 * Method passing the visit call to the visitor core logic.
		 */
		void visit ( FormalRTEEmpty < SymbolType > & inherit ) override {
			this->call ( 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 ( FormalRTEElement::ConstVisitor & 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 ( FormalRTEElement::Visitor & visitor ) = 0;

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

	virtual FormalRTEElement < 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 {
		ConstVisitorContext < 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 ) {
		VisitorContext < ReturnType, Visitorr, Params ... > context ( std::forward < Params > ( params ) ... );
		accept ( context );
		return context.getResult ( );
	}

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

	/**
	 * Traverses the rte tree computing minimal alphabet needed by rte
	 *
	 * \param alphabetF All terminal alphabet symbols encountered are added into this set
	 * \param alphabetK All substitution alphabet symbols encountered are added into this set
	 */
	virtual void computeMinimalAlphabet ( ext::set < common::ranked_symbol < SymbolType > > & alphabetF, ext::set < common::ranked_symbol < SymbolType > > & alphabetK ) const = 0;

	/**
	 * Traverses the rte tree computing minimal symbol alphabet and substitution alphabet needed by rte
	 *
	 * \return the minimal symbol and substitution alphabet needed by the rte
	 */
	std::pair < ext::set < common::ranked_symbol < SymbolType > >, ext::set < common::ranked_symbol < SymbolType > > > computeMinimalAlphabets ( ) const;

	/**
	 * Traverses the rte tree and checks whether all symbols in the rte tree are in the alphabet
	 *
	 * \param alphabetF All terminal alphabet symbols encountered are added into this set
	 * \param alphabetK All substitution alphabet symbols encountered are added into this set
	 * \return true if symbols in the rte are in the alphabet, false otherwise
	 */
	virtual bool checkAlphabet ( const ext::set < common::ranked_symbol < SymbolType > > & alphabetF, const ext::set < common::ranked_symbol < SymbolType > > & alphabetK ) const = 0;

	/**
	 * \brief Traverses the rte 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 < FormalRTEElement < 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 FormalRTEElement < 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 FormalRTEElement < SymbolType > & other ) const = 0;
};

template < class SymbolType >
std::pair < ext::set < common::ranked_symbol < SymbolType > >, ext::set < common::ranked_symbol < SymbolType > > > FormalRTEElement < SymbolType >::computeMinimalAlphabets ( ) const {
	ext::set < common::ranked_symbol < SymbolType > > alphabetF;
	ext::set < common::ranked_symbol < SymbolType > > alphabetK;

	computeMinimalAlphabet ( alphabetF, alphabetK );
	return std::make_pair ( std::move ( alphabetF ), std::move ( alphabetK ) );
}

} /* namespace rte */

extern template class rte::FormalRTEElement < DefaultSymbolType >;

#endif /* FORMAL_RTE_ELEMENT_H_ */
