/*
 * FormalRTE.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_H_
#define FORMAL_RTE_H_

#include <alib/string>
#include <alib/set>
#include <alib/iostream>
#include <alib/algorithm>
#include <alib/compare>
#include <sstream>

#include <core/components.hpp>
#include <exception/CommonException.h>

#include <common/DefaultSymbolType.h>

#include "FormalRTEStructure.h"

#include <core/normalize.hpp>
#include <alphabet/common/SymbolNormalize.h>

namespace rte {

class GeneralAlphabet;
class ConstantAlphabet;

/**
 * \brief
 * Formal regular tree expression represents regular tree expression. It describes regular tree languages. The expression consists of the following nodes:
 *   - Alternation - the representation of + in the regular tree expression
 *   - Substitution - the representation of &sdot; □ in the regular tree expression
 *   - Iteration - the representation of *, □ in the regular tree expression
 *   - Empty - the representation of empty regular tree expression
 *   - SymbolAlphabet - the representation of a single symbol from symbol alphabet
 *   - SymbolSubst - the representation of a single symbol from the substitution alphabet
 *
 * The formal representation allows nodes of alternation to have exactly two children.
 *
 * The structure of the regular tree expression is wrapped in a structure class to allow separate the alphabets from the structure

 * \details
 * Definition is unbounded definition of regular expressions.
 * E = (T, S, C),
 * T (TerminalAlphabet) = finite set of terminal symbols
 * S (SubstitutionAlphabet) = finite set of substitution symbols
 * C (Content) = representation of the regular expression
 *
 * \tparam SymbolType used for the symbol part of the ranked symbol
 */
template < class SymbolType = DefaultSymbolType >
class FormalRTE final : public ext::CompareOperators < FormalRTE < SymbolType > >, public core::Components < FormalRTE < SymbolType >, ext::set < common::ranked_symbol < SymbolType > >, component::Set, std::tuple < GeneralAlphabet, ConstantAlphabet > > {
	/**
	 * The structure of the regular tree expression.
	 */
	FormalRTEStructure < SymbolType > m_rte;

public:
	/**
	 * \brief Creates a new instance of the expression. The default constructor creates expression describing empty language.
	 */
	explicit FormalRTE ( );

	/**
	 * \brief Creates a new instance of the expression with a concrete terminal alphabet, substitution alphabet, and initial content.
	 *
	 * \param alphabetF the initial terminal alphabet
	 * \param alphabetK the initial substitution alphabet
	 * \param rte the initial rte content
	 */
	explicit FormalRTE ( ext::set < common::ranked_symbol < SymbolType > > alphabetF, ext::set < common::ranked_symbol < SymbolType > > alphabetK, FormalRTEStructure < SymbolType > rte );

	/**
	 * \brief Creates a new instance of the expression with a concrete terminal alphabet, substitution alphabet, and initial content.
	 *
	 * \param alphabets the pair of terminal alphabet and substitution alphabet
	 * \param rte the initial rte content
	 */
	explicit FormalRTE ( std::pair < ext::set < common::ranked_symbol < SymbolType > >, ext::set < common::ranked_symbol < SymbolType > > > alphabets, FormalRTEStructure < SymbolType > rte );

	/**
	 * \brief Creates a new instance of the expression based on the initial content. Alphabets are deduced from the content.
	 *
	 * \param rte the initial rte content
	 */
	explicit FormalRTE ( FormalRTEStructure < SymbolType > rte );

	/**
	 * Getter of the terminal alphabet.
	 *
	 * \returns the terminal alphabet of the expression
	 */
	const ext::set < common::ranked_symbol < SymbolType > > & getAlphabet ( ) const & {
		return this->template accessComponent < GeneralAlphabet > ( ).get ( );
	}

	/**
	 * Getter of the terminal alphabet.
	 *
	 * \returns the terminal alphabet of the expression
	 */
	ext::set < common::ranked_symbol < SymbolType > > && getAlphabet ( ) && {
		return std::move ( this->template accessComponent < GeneralAlphabet > ( ).get ( ) );
	}

	/**
	 * Adder of a terminal symbol.
	 *
	 * \param symbol the new symbol to be added to a terminal alphabet
	 *
	 * \returns true if the symbol was indeed added
	 */
	void addAlphabetSymbols ( common::ranked_symbol < SymbolType > symbol ) {
		this->template accessComponent < GeneralAlphabet > ( ).add ( std::move ( symbol ) );
	}

	/**
	 * Adder of terminal symbols.
	 *
	 * \param symbols new symbols to be added to a terminal alphabet
	 */
	void addAlphabetSymbols ( ext::set < common::ranked_symbol < SymbolType > > symbols ) {
		this->template accessComponent < GeneralAlphabet > ( ).add ( std::move ( symbols ) );
	}

	/**
	 * Setter of terminal alphabet.
	 *
	 * \param symbols completely new terminal alphabet
	 */
	void setAlphabetSymbols ( ext::set < common::ranked_symbol < SymbolType > > symbols ) {
		this->template accessComponent < GeneralAlphabet > ( ).set ( std::move ( symbols ) );
	}

	/**
	 * Remover of a terminal symbol.
	 *
	 * \param symbol a symbol to be removed from a terminal alphabet
	 *
	 * \returns true if the symbol was indeed removed
	 */
	void removeAlphabetSymbol ( const common::ranked_symbol < SymbolType > & symbol ) {
		this->template accessComponent < GeneralAlphabet > ( ).remove ( symbol );
	}

	/**
	 * Getter of the substitution alphabet.
	 *
	 * \returns the substitution alphabet of the expression
	 */
	const ext::set < common::ranked_symbol < SymbolType > > & getSubstitutionAlphabet ( ) const & {
		return this->template accessComponent < ConstantAlphabet > ( ).get ( );
	}

	/**
	 * Getter of the substitution alphabet.
	 *
	 * \returns the substitution alphabet of the expression
	 */
	ext::set < common::ranked_symbol < SymbolType > > && getSubstitutionAlphabet ( ) && {
		return std::move ( this->template accessComponent < ConstantAlphabet > ( ).get ( ) );
	}

	/**
	 * Adder of a substitution symbol.
	 *
	 * \param symbol the new symbol to be added to a substitution alphabet
	 *
	 * \returns true if the symbol was indeed added
	 */
	void addConstantSymbol ( common::ranked_symbol < SymbolType > symbol ) {
		this->template accessComponent < ConstantAlphabet > ( ).add ( std::move ( symbol ) );
	}

	/**
	 * Adder of substitution symbols.
	 *
	 * \param symbols new symbols to be added to a substitution alphabet
	 */
	void addConstantSymbols ( ext::set < common::ranked_symbol < SymbolType > > symbols ) {
		this->template accessComponent < ConstantAlphabet > ( ).add ( std::move ( symbols ) );
	}

	/**
	 * Setter of substitution alphabet.
	 *
	 * \param symbols completely new substitution alphabet
	 */
	void setConstantSymbols ( ext::set < common::ranked_symbol < SymbolType > > symbols ) {
		this->template accessComponent < ConstantAlphabet > ( ).set ( std::move ( symbols ) );
	}

	/**
	 * Remover of a substitution symbol.
	 *
	 * \param symbol a symbol to be removed from a substitution alphabet
	 *
	 * \returns true if the symbol was indeed removed
	 */
	void removeConstantSymbol ( const common::ranked_symbol < SymbolType > & symbol ) {
		this->template accessComponent < ConstantAlphabet > ( ).remove ( symbol );
	}

	/**
	 * Get the structure of the expression.
	 *
	 * \returns the structure of the expression.
	 */
	const FormalRTEStructure < SymbolType > & getRTE ( ) const &;

	/**
	 * Get the structure of the expression.
	 *
	 * \returns the structure of the expression.
	 */
	FormalRTEStructure < SymbolType > && getRTE ( ) &&;

	/**
	 * Set the structure of the expression.
	 *
	 * \param regExp the new structure of the expression.
	 */
	void setRTE ( FormalRTEStructure < SymbolType > param );

	/**
	 * The actual compare method
	 *
	 * \param other the other instance
	 *
	 * \returns the actual relation between two by type same expression instances
	 */
	int compare ( const FormalRTE & other ) const;

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

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

template < class SymbolType >
FormalRTE < SymbolType >::FormalRTE ( ext::set < common::ranked_symbol < SymbolType > > alphabetF, ext::set < common::ranked_symbol < SymbolType > > alphabetK, FormalRTEStructure < SymbolType > rte ) : core::Components < FormalRTE, ext::set < common::ranked_symbol < SymbolType > >, component::Set, std::tuple < GeneralAlphabet, ConstantAlphabet > > ( std::move ( alphabetF ), std::move ( alphabetK ) ), m_rte ( std::move ( rte ) ) {
	if ( !this->m_rte.getStructure ( ).checkAlphabet ( getAlphabet ( ), getSubstitutionAlphabet ( ) ) )
		throw exception::CommonException ( "Input symbols not in the alphabet." );
}

template < class SymbolType >
FormalRTE < SymbolType >::FormalRTE ( std::pair < ext::set < common::ranked_symbol < SymbolType > >, ext::set < common::ranked_symbol < SymbolType > > > alphabets, FormalRTEStructure < SymbolType > rte ) : FormalRTE ( std::move ( alphabets.first ), std::move ( alphabets.second ), std::move ( rte ) ) {
}

template < class SymbolType >
FormalRTE < SymbolType >::FormalRTE ( ) : FormalRTE ( ext::set < common::ranked_symbol < SymbolType > > ( ), ext::set < common::ranked_symbol < SymbolType > > ( ), FormalRTEStructure < SymbolType > ( ) ) {
}

template < class SymbolType >
FormalRTE < SymbolType >::FormalRTE ( FormalRTEStructure < SymbolType > rte ) : FormalRTE ( rte.getStructure ( ).computeMinimalAlphabets ( ), rte ) {
}

template < class SymbolType >
const FormalRTEStructure < SymbolType > & FormalRTE < SymbolType >::getRTE ( ) const & {
	return m_rte;
}

template < class SymbolType >
FormalRTEStructure < SymbolType > && FormalRTE < SymbolType >::getRTE ( ) && {
	return std::move ( m_rte );
}

template < class SymbolType >
void FormalRTE < SymbolType >::setRTE ( FormalRTEStructure < SymbolType > param ) {
	if ( !param.getStructure ( ).checkAlphabet ( getAlphabet ( ), getSubstitutionAlphabet ( ) ) )
		throw exception::CommonException ( "Symbols not matching alphabets." );

	this->m_rte = std::move ( param );
}

template < class SymbolType >
int FormalRTE < SymbolType >::compare ( const FormalRTE & other ) const {
	auto first = ext::tie ( m_rte, getAlphabet ( ), getSubstitutionAlphabet ( ) );
	auto second = ext::tie ( m_rte, other.getAlphabet ( ), getSubstitutionAlphabet ( ) );

	static ext::compare < decltype ( first ) > comp;

	return comp ( first, second );
}

template < class SymbolType >
FormalRTE < SymbolType >::operator std::string ( ) const {
	std::stringstream ss;
	ss << * this;
	return ss.str ( );
}

} /* namespace rte */

namespace core {

/**
 * Helper class specifying constraints for the expression's internal terminal alphabet component.
 *
 * \tparam SymbolType used for the symbol part of the ranked symbol
 */
template < class SymbolType >
class SetConstraint< rte::FormalRTE < SymbolType >, common::ranked_symbol < SymbolType >, rte::GeneralAlphabet > {
public:
	/**
	 * Returns true if the symbol is still used somewhere in the structure of the expression.
	 *
	 * \param rte the tested expresion
	 * \param symbol the tested symbol
	 *
	 * \returns true if the symbol is used, false othervise
	 */
	static bool used ( const rte::FormalRTE < SymbolType > & rte, const common::ranked_symbol < SymbolType > & symbol ) {
		return rte.getRTE ( ).getStructure ( ).testSymbol ( symbol );
	}

	/**
	 * Returns true as all symbols are possibly available to be elements of the terminal alphabet.
	 *
	 * \param rte the tested expresion
	 * \param symbol the tested state
	 *
	 * \returns true
	 */
	static bool available ( const rte::FormalRTE < SymbolType > &, const common::ranked_symbol < SymbolType > & ) {
		return true;
	}

	/**
	 * Throws exception if the symbol is already a part of the substitution alphabet.
	 *
	 * \param rte the tested expresion
	 * \param symbol the tested state
	 */
	static void valid ( const rte::FormalRTE < SymbolType > & rte, const common::ranked_symbol < SymbolType > & symbol ) {
		if ( rte.template accessComponent < rte::ConstantAlphabet > ( ).get ( ).count ( symbol ) )
			throw ::exception::CommonException ( "Symbol " + ext::to_string ( symbol ) + " cannot be in general alphabet since it is already in constant alphabet" );
	}
};

/**
 * Helper class specifying constraints for the expression's internal substitution alphabet component.
 *
 * \tparam SymbolType used for the symbol part of the ranked symbol
 */
template < class SymbolType >
class SetConstraint< rte::FormalRTE < SymbolType >, common::ranked_symbol < SymbolType >, rte::ConstantAlphabet > {
public:
	/**
	 * Returns true if the symbol is still used somewhere in the structure of the expression.
	 *
	 * \param rte the tested expresion
	 * \param symbol the tested symbol
	 *
	 * \returns true if the symbol is used, false othervise
	 */
	static bool used ( const rte::FormalRTE < SymbolType > & rte, const common::ranked_symbol < SymbolType > & symbol ) {
		return rte.getRTE ( ).getStructure ( ).testSymbol ( symbol );
	}

	/**
	 * Returns true as all symbols are possibly available to be elements of the substitution alphabet.
	 *
	 * \param rte the tested expresion
	 * \param symbol the tested state
	 *
	 * \returns true
	 */
	static bool available ( const rte::FormalRTE < SymbolType > &, const common::ranked_symbol < SymbolType > & ) {
		return true;
	}

	/**
	 * Throws exception if the symbol is already a part of the terminal alphabet.
	 *
	 * \param rte the tested expresion
	 * \param symbol the tested state
	 */
	static void valid ( const rte::FormalRTE < SymbolType > & rte, const common::ranked_symbol < SymbolType > & symbol ) {
		if ( rte.template accessComponent < rte::GeneralAlphabet > ( ).get ( ).count ( symbol ) )
			throw ::exception::CommonException ( "Symbol " + ext::to_string ( symbol ) + " cannot be in constant alphabet since it is already in general alphabet" );
	}
};

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols.
 *
 * \returns new instance of the expression with default template parameters or unmodified instance if the template parameters were already the default ones
 */
template < class SymbolType >
struct normalize < rte::FormalRTE < SymbolType > > {
	static rte::FormalRTE < > eval ( rte::FormalRTE < SymbolType > && value ) {
		ext::set < common::ranked_symbol < DefaultSymbolType > > alphabet = alphabet::SymbolNormalize::normalizeRankedAlphabet ( std::move ( value ).getAlphabet ( ) );
		ext::set < common::ranked_symbol < DefaultSymbolType > > constants = alphabet::SymbolNormalize::normalizeRankedAlphabet ( std::move ( value ).getSubstitutionAlphabet ( ) );

		return rte::FormalRTE < > ( alphabet, constants, std::move ( value ).getRTE ( ).normalize ( ) );
	}
};

} /* namespace core */

extern template class rte::FormalRTE < >;

#endif /* FORMAL_RTE_H_ */
