/*
 * UnrankedNonlinearPattern.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 UNRANKED_NONLINEAR_PATTERN_H_
#define UNRANKED_NONLINEAR_PATTERN_H_

#include <common/DefaultSymbolType.h>

namespace tree {

template < class SymbolType = DefaultSymbolType >
class UnrankedNonlinearPattern;

} /* namespace tree */

#include <sstream>

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

#include <core/components.hpp>

#include <tree/TreeException.h>
#include <tree/common/TreeAuxiliary.h>

#include <core/normalize.hpp>
#include <tree/common/TreeNormalize.h>

#include <tree/ranked/RankedNonlinearPattern.h>

namespace tree {

class GeneralAlphabet;
class SubtreeWildcard;
class NonlinearAlphabet;

/**
 * \brief
 * Nonlinear tree pattern represented in its natural representation. The representation is so called unranked, therefore it consists of unranked symbols. Additionally the pattern contains a special wildcard symbol representing any subtree and nonlinear variables each to represent same subtree (in the particular occurrence in a tree).
 *
 * \details
 * T = ( A, C, W \in A, V \in A ),
 * A (Alphabet) = finite set of symbols,
 * C (Content) = pattern in its natural representation
 * W (Wildcard) = special symbol representing any subtree
 * V (Variables) = finite set of special symbols each representing same subtree
 *
 * \tparam SymbolType used for the symbol of the alphabet
 */
template < class SymbolType >
class UnrankedNonlinearPattern final : public ext::CompareOperators < UnrankedNonlinearPattern < SymbolType > > , public core::Components < UnrankedNonlinearPattern < SymbolType >, ext::set < SymbolType >, component::Set, std::tuple < GeneralAlphabet, NonlinearAlphabet >, SymbolType, component::Value, SubtreeWildcard > {
	/**
	 * Natural representation of the pattern content.
	 */
	ext::tree < SymbolType > m_content;

	/**
	 * Checks that symbols of the pattern are present in the alphabet.
	 *
	 * \throws TreeException when some symbols of the pattern representation are not present in the alphabet
	 *
	 * \param data the pattern in its natural representation
	 */
	void checkAlphabet ( const ext::tree < SymbolType > & data ) const;

public:
	/**
	 * \brief Creates a new instance of the pattern with concrete alphabet, content, wildcard, and nonlinear variables.
	 *
	 * \param subtreeWildcard the wildcard symbol
	 * \param nonlinearVariables the set of nonlinear variables
	 * \param alphabet the initial alphabet of the pattern
	 * \param pattern the initial content in it's natural representation
	 */
	explicit UnrankedNonlinearPattern ( SymbolType subtreeWildcard, ext::set < SymbolType > nonlinearVariables, ext::set < SymbolType > alphabet, ext::tree < SymbolType > pattern );

	/**
	 * \brief Creates a new instance of the pattern with concrete content, wildcard, and nonlinear variables. The alphabet is deduced from the content.
	 *
	 * \param subtreeWildcard the wildcard symbol
	 * \param nonlinearVariables the set of nonlinear variables
	 * \param pattern the initial content in it's natural representation
	 */
	explicit UnrankedNonlinearPattern ( SymbolType subtreeWildcard, ext::set < SymbolType > nonlinearVariables, ext::tree < SymbolType > pattern );

	/**
	 * \brief Creates a new instance of the pattern with concrete content and wildcard. The alphabet is deduced from the content. Nonlinear variables are defaultly constructed to empty set.
	 *
	 * \param subtreeWildcard the wildcard symbol
	 * \param pattern the initial content in it's natural representation
	 */
	explicit UnrankedNonlinearPattern ( SymbolType subtreeWildcard, ext::tree < SymbolType > pattern );

	/**
	 * \brief Creates a new instance of the pattern based on RankedNonlinearPattern, the alphabet is created from the content of the RankedNonlinearPattern.
	 *
	 * \param other the pattern represented as RankedNonlinearPattern
	 */
	explicit UnrankedNonlinearPattern ( const RankedNonlinearPattern < SymbolType > & other );

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

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

	/**
	 * Adder of an alphabet symbols.
	 *
	 * \param symbols the new symbols to be added to the alphabet
	 */
	void extendAlphabet ( const ext::set < SymbolType > & symbols ) {
		this->template accessComponent < GeneralAlphabet > ( ).add ( symbols );
	}

	/**
	 * Getter of the wildcard symbol.
	 *
	 * \returns the wildcard symbol of the pattern
	 */
	const SymbolType & getSubtreeWildcard ( ) const & {
		return this->template accessComponent < SubtreeWildcard > ( ).get ( );
	}

	/**
	 * Getter of the wildcard symbol.
	 *
	 * \returns the wildcard symbol of the pattern
	 */
	SymbolType && getSubtreeWildcard ( ) && {
		return std::move ( this->template accessComponent < SubtreeWildcard > ( ).get ( ) );
	}

	/**
	 * Getter of the nonlinear variables.
	 *
	 * \returns the nonlinear variables of the pattern
	 */
	const ext::set < SymbolType > & getNonlinearVariables ( ) const & {
		return this->template accessComponent < NonlinearAlphabet > ( ).get ( );
	}

	/**
	 * Getter of the nonlinear variables.
	 *
	 * \returns the nonlinear variables of the pattern
	 */
	ext::set < SymbolType > && getNonlinearVariables ( ) && {
		return std::move ( this->template accessComponent < NonlinearAlphabet > ( ).get ( ) );
	}

	/**
	 * Getter of the pattern representation.
	 *
	 * \return the natural representation of the pattern.
	 */
	const ext::tree < SymbolType > & getContent ( ) const &;

	/**
	 * Getter of the pattern representation.
	 *
	 * \return the natural representation of the pattern.
	 */
	ext::tree < SymbolType > && getContent ( ) &&;

	/**
	 * Setter of the representation of the pattern.
	 *
	 * \throws TreeException in same situations as checkAlphabet
	 *
	 * \param pattern new representation of the pattern.
	 */
	void setTree ( ext::tree < SymbolType > pattern );

	/**
	 * The actual compare method
	 *
	 * \param other the other instance
	 *
	 * \returns the actual relation between two by type same pattern instances
	 */
	int compare ( const UnrankedNonlinearPattern & 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 UnrankedNonlinearPattern & instance ) {
		out << "(UnrankedNonlinearPattern ";
		out << "alphabet = " << instance.getAlphabet ( );
		out << "content = " << instance.getContent ( );
		out << "nonlinearVariables = " << instance.getNonlinearVariables ( );
		out << "subtreeWildcard = " << instance.getSubtreeWildcard ( );
		out << ")";
		return out;
	}

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

	/**
	 * Nice printer of the tree natural representation
	 *
	 * \param out the output stream to print to
	 */
	void nicePrint ( std::ostream & out ) const;
};

template < class SymbolType >
UnrankedNonlinearPattern < SymbolType >::UnrankedNonlinearPattern ( SymbolType subtreeWildcard, ext::set < SymbolType > nonlinearVariables, ext::set < SymbolType > alphabet, ext::tree < SymbolType > pattern ) : core::Components < UnrankedNonlinearPattern, ext::set < SymbolType >, component::Set, std::tuple < GeneralAlphabet, NonlinearAlphabet >, SymbolType, component::Value, SubtreeWildcard > ( std::move ( alphabet ), std::move ( nonlinearVariables ), std::move ( subtreeWildcard ) ), m_content ( std::move ( pattern ) ) {
	checkAlphabet ( m_content );
}

template < class SymbolType >
UnrankedNonlinearPattern < SymbolType >::UnrankedNonlinearPattern ( SymbolType subtreeWildcard, ext::set < SymbolType > nonlinearVariables, ext::tree < SymbolType > pattern ) : UnrankedNonlinearPattern ( subtreeWildcard, nonlinearVariables, ext::set < SymbolType > ( pattern.prefix_begin ( ), pattern.prefix_end ( ) ) + nonlinearVariables + ext::set < SymbolType > { subtreeWildcard }, pattern ) {
}

template < class SymbolType >
UnrankedNonlinearPattern < SymbolType >::UnrankedNonlinearPattern ( SymbolType subtreeWildcard, ext::tree < SymbolType > pattern ) : UnrankedNonlinearPattern ( subtreeWildcard, ext::set < SymbolType > { }, pattern ) {
}

template < class SymbolType >
UnrankedNonlinearPattern < SymbolType >::UnrankedNonlinearPattern ( const RankedNonlinearPattern < SymbolType > & other ) : UnrankedNonlinearPattern ( other.getSubtreeWildcard ( ).getSymbol ( ), TreeAuxiliary::unrankSymbols ( other.getAlphabet ( ) ), TreeAuxiliary::rankedToUnranked ( other.getContent ( ) ) ) {
}

template < class SymbolType >
const ext::tree < SymbolType > & UnrankedNonlinearPattern < SymbolType >::getContent ( ) const & {
	return m_content;
}

template < class SymbolType >
ext::tree < SymbolType > && UnrankedNonlinearPattern < SymbolType >::getContent ( ) && {
	return std::move ( m_content );
}

template < class SymbolType >
void UnrankedNonlinearPattern < SymbolType >::checkAlphabet ( const ext::tree < SymbolType > & data ) const {
	ext::set < SymbolType > minimalAlphabet ( data.prefix_begin ( ), data.prefix_end ( ) );
	ext::set < SymbolType > unknownSymbols;
	std::set_difference ( minimalAlphabet.begin ( ), minimalAlphabet.end ( ), getAlphabet().begin ( ), getAlphabet().end ( ), std::inserter ( unknownSymbols, unknownSymbols.end ( ) ) );

	if ( !unknownSymbols.empty ( ) )
		throw exception::CommonException ( "Input symbols not in the alphabet." );
}

template < class SymbolType >
void UnrankedNonlinearPattern < SymbolType >::setTree ( ext::tree < SymbolType > pattern ) {
	checkAlphabet ( pattern );

	this->m_content = std::move ( pattern );
}

template < class SymbolType >
int UnrankedNonlinearPattern < SymbolType >::compare ( const UnrankedNonlinearPattern & other ) const {
	auto first = ext::tie ( m_content, getAlphabet(), getSubtreeWildcard(), getNonlinearVariables() );
	auto second = ext::tie ( other.m_content, other.getAlphabet(), other.getSubtreeWildcard(), getNonlinearVariables() );

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

	return comp ( first, second );
}

template < class SymbolType >
void UnrankedNonlinearPattern < SymbolType >::nicePrint ( std::ostream & out ) const {
	m_content.nicePrint ( out );
}

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

} /* namespace tree */

namespace core {

/**
 * Helper class specifying constraints for the pattern's internal alphabet component.
 *
 * \tparam SymbolType used for the symbol of the alphabet
 */
template < class SymbolType >
class SetConstraint< tree::UnrankedNonlinearPattern < SymbolType >, SymbolType, tree::GeneralAlphabet > {
public:
	/**
	 * Returns true if the symbol is still used in the pattern.
	 *
	 * \param pattern the tested pattern
	 * \param symbol the tested symbol
	 *
	 * \returns true if the symbol is used, false othervise
	 */
	static bool used ( const tree::UnrankedNonlinearPattern < SymbolType > & pattern, const SymbolType & symbol ) {
		const ext::tree<SymbolType>& m_content = pattern.getContent ( );
		return std::find(m_content.prefix_begin(), m_content.prefix_end(), symbol) != m_content.prefix_end() || pattern.template accessComponent < tree::SubtreeWildcard > ( ).get ( ) == symbol || pattern.template accessComponent < tree::NonlinearAlphabet > ( ).get ( ).count ( symbol );
	}

	/**
	 * Returns true as all symbols are possibly available to be in an alphabet.
	 *
	 * \param pattern the tested pattern
	 * \param symbol the tested symbol
	 *
	 * \returns true
	 */
	static bool available ( const tree::UnrankedNonlinearPattern < SymbolType > &, const SymbolType & ) {
		return true;
	}

	/**
	 * All symbols are valid as symbols of an alphabet.
	 *
	 * \param pattern the tested pattern
	 * \param symbol the tested symbol
	 */
	static void valid ( const tree::UnrankedNonlinearPattern < SymbolType > &, const SymbolType & ) {
	}
};

/**
 * Helper class specifying constraints for the pattern's internal nonlinear variables component.
 *
 * \tparam SymbolType used for the symbol of the alphabet
 */
template < class SymbolType >
class SetConstraint< tree::UnrankedNonlinearPattern < SymbolType >, SymbolType, tree::NonlinearAlphabet > {
public:
	/**
	 * Returns false. Nonlinear symbol is only a mark that the pattern itself does require further.
	 *
	 * \param pattern the tested pattern
	 * \param symbol the tested symbol
	 *
	 * \returns true if the symbol is used, false othervise
	 */
	static bool used ( const tree::UnrankedNonlinearPattern < SymbolType > &, const SymbolType & ) {
		return false;
	}

	/**
	 * Determines whether the symbol is available in the pattern's alphabet.
	 *
	 * \param pattern the tested pattern
	 * \param symbol the tested symbol
	 *
	 * \returns true
	 */
	static bool available ( const tree::UnrankedNonlinearPattern < SymbolType > & pattern, const SymbolType & symbol ) {
		return pattern.template accessComponent < tree::GeneralAlphabet > ( ).get ( ).count ( symbol );
	}

	/**
	 * Nonlinear variable needs to have zero arity and needs to be different from subtree wildcard.
	 *
	 * \param pattern the tested pattern
	 * \param symbol the tested symbol
	 *
	 * \throws TreeException if the symbol does not have zero arity
	 */
	static void valid ( const tree::UnrankedNonlinearPattern < SymbolType > & pattern, const SymbolType & symbol) {
		if ( pattern.template accessComponent < tree::SubtreeWildcard > ( ).get ( ) == symbol )
			throw tree::TreeException ( "Symbol " + ext::to_string ( symbol ) + "cannot be set as nonlinear variable since it is already subtree wildcard" );
	}
};

/**
 * Helper class specifying constraints for the pattern's internal subtree wildcard element.
 *
 * \tparam SymbolType used for the symbol of the alphabet
 */
template < class SymbolType >
class ElementConstraint< tree::UnrankedNonlinearPattern < SymbolType >, SymbolType, tree::SubtreeWildcard > {
public:
	/**
	 * Determines whether the symbol is available in the pattern's alphabet.
	 *
	 * \param pattern the tested pattern
	 * \param symbol the tested symbol
	 *
	 * \returns true if the symbol is already in the alphabet of the pattern
	 */
	static bool available ( const tree::UnrankedNonlinearPattern < SymbolType > & pattern, const SymbolType & symbol ) {
		return pattern.template accessComponent < tree::GeneralAlphabet > ( ).get ( ).count ( symbol );
	}

	/**
	 * Subtree wildcard needs to have zero arity and it needs to be different from any nonlinear variable of the tree.
	 *
	 * \param pattern the tested pattern
	 * \param symbol the tested symbol
	 *
	 * \throws TreeException if the symbol does not have zero arity
	 */
	static void valid ( const tree::UnrankedNonlinearPattern < SymbolType > & pattern, const SymbolType & symbol) {
		if ( pattern.template accessComponent < tree::NonlinearAlphabet > ( ).get ( ).count ( symbol ) )
			throw tree::TreeException ( "Symbol " + ext::to_string ( symbol ) + "cannot be set as subtree wildcard since it is already nonlinear variable" );
	}
};

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols.
 *
 * \returns new instance of the tree with default template parameters or unmodified instance if the template parameters were already default ones
 */
template < class SymbolType >
struct normalize < tree::UnrankedNonlinearPattern < SymbolType > > {
	static tree::UnrankedNonlinearPattern < > eval ( tree::UnrankedNonlinearPattern < SymbolType > && value ) {
		DefaultSymbolType wildcard = alphabet::SymbolNormalize::normalizeSymbol ( std::move ( value ).getSubtreeWildcard ( ) );
		ext::set < DefaultSymbolType > nonlinearAlphabet = alphabet::SymbolNormalize::normalizeAlphabet ( std::move ( value ).getNonlinearVariables ( ) );
		ext::set < DefaultSymbolType > alphabet = alphabet::SymbolNormalize::normalizeAlphabet ( std::move ( value ).getAlphabet ( ) );
		ext::tree < DefaultSymbolType > content = tree::TreeNormalize::normalizeTree ( std::move ( value ).getContent ( ) );

		return tree::UnrankedNonlinearPattern < > ( std::move ( wildcard ), std::move ( nonlinearAlphabet ), std::move ( alphabet ), std::move ( content ) );
	}
};

} /* namespace core */

extern template class tree::UnrankedNonlinearPattern < >;

#endif /* UNRANKED_NONLINEAR_PATTERN_H_ */
