/*
 * PrefixBarTree.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 PREFIX_BAR_TREE_H_
#define PREFIX_BAR_TREE_H_

#include <common/DefaultSymbolType.h>

namespace tree {

template < class SymbolType = DefaultSymbolType >
class PrefixBarTree;

} /* namespace tree */

#include <sstream>

#include <alib/algorithm>
#include <alib/deque>
#include <alib/set>
#include <alib/vector>
#include <alib/tree>

#include <core/components.hpp>

#include <alphabet/BarSymbol.h>

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

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

#include "UnrankedTree.h"

#include <string/LinearString.h>

namespace tree {

class GeneralAlphabet;
class BarSymbol;

/**
 * \brief
 * Tree structure represented as linear sequece as result of preorder traversal with additional bar symbols. The representation is so called unranked, therefore it consists of unranked symbols bars are unranked as well.
 *
 * The bars represent end mark of all subtrees in the notation.
 *
 * \details
 * T = ( A, B \in A, C ),
 * A (Alphabet) = finite set of unranked symbols,
 * B (Bar) = unranked symbol representing bar,
 * C (Content) = linear representation of the tree content
 *
 * \tparam SymbolType used for the symbol of the alphabet
 */
template < class SymbolType >
class PrefixBarTree final : public ext::CompareOperators < PrefixBarTree < SymbolType > >, public core::Components < PrefixBarTree < SymbolType >, ext::set < SymbolType >, component::Set, GeneralAlphabet, SymbolType, component::Value, BarSymbol > {
	/**
	 * Linear representation of the tree content.
	 */
	ext::vector < SymbolType > m_Data;

	/**
	 * Checker of validity of the representation of the tree
	 *
	 * \throws TreeException when new tree representation is not valid
	 */
	void arityChecksum ( const ext::vector < SymbolType > & data );

public:
	/**
	 * \brief Creates a new instance of the tree with concrete alphabet, bar, and content.
	 *
	 * \param bars the bar symbol
	 * \param alphabet the initial alphabet of the tree
	 * \param data the initial tree in linear representation
	 */
	explicit PrefixBarTree ( SymbolType bar, ext::set < SymbolType > alphabet, ext::vector < SymbolType > data );

	/**
	 * \brief Creates a new instance of the tree based on the content and bar, the alphabet is implicitly created from the content.
	 *
	 * \param bar the bar symbol
	 * \param data the initial tree in linear representation
	 */
	explicit PrefixBarTree ( SymbolType bar, ext::vector < SymbolType > data );

	/**
	 * \brief Creates a new instance of the tree based on the UnrankedTree. The linear representation is constructed (including bars) by preorder traversal on the tree parameter. Bar symbol is provided as a parameter.
	 *
	 * \param bar the bar symbol
	 * \param tree RankedTree representation of a tree.
	 */
	explicit PrefixBarTree ( SymbolType bar, const UnrankedTree < SymbolType > & tree );

	/**
	 * \brief Creates a new instance of the tree based on the UnrankedTree. The linear representation is constructed (including bars) by preorder traversal on the tree parameter. Bar symbol is created using some default value.
	 *
	 * \param tree UnrankedTree representation of a tree.
	 */
	explicit PrefixBarTree ( const UnrankedTree < SymbolType > & tree );

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

	/**
	 * Getter of the alphabet.
	 *
	 * \returns the alphabet of the tree
	 */
	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 bar symbol.
	 *
	 * \returns the bar symbol of the tree
	 */
	const SymbolType & getBar ( ) const & {
		return this->template accessComponent < BarSymbol > ( ).get ( );
	}

	/**
	 * Getter of the bar symbol.
	 *
	 * \returns the bar symbol of the tree
	 */
	SymbolType && getBar ( ) && {
		return std::move ( this->template accessComponent < BarSymbol > ( ).get ( ) );
	}

	/**
	 * Getter of the tree representation.
	 *
	 * \return List of symbols forming the linear representation of the tree.
	 */
	const ext::vector < SymbolType > & getContent ( ) const &;

	/**
	 * Getter of the tree representation.
	 *
	 * \return List of symbols forming the linear representation of the tree.
	 */
	ext::vector < SymbolType > && getContent ( ) &&;

	/**
	 * Setter of the representation of the tree.
	 *
	 * \throws TreeException when new tree representation is not valid or when symbol of the representation are not present in the alphabet
	 *
	 * \param data new List of symbols forming the representation of the tree.
	 */
	void setContent ( ext::vector < SymbolType > data );

	/**
	 * @return true if tree is an empty word (vector length is 0). The method is present to allow compatibility with strings. Tree is never empty in this datatype.
	 */
	bool isEmpty ( ) const;

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

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

	/**
	 * \brief Creates a new instance of the string from a linear representation of a tree
	 *
	 * \returns tree casted to string
	 */
	explicit operator string::LinearString < SymbolType > ( ) const {
		return string::LinearString < SymbolType > ( getAlphabet ( ), getContent ( ) );
	}
};

template < class SymbolType >
PrefixBarTree < SymbolType >::PrefixBarTree ( SymbolType bar, ext::set < SymbolType > alphabet, ext::vector < SymbolType > data ) : core::Components < PrefixBarTree, ext::set < SymbolType >, component::Set, GeneralAlphabet, SymbolType, component::Value, BarSymbol > ( std::move ( alphabet ), std::move ( bar ) ) {
	setContent ( std::move ( data ) );
}

template < class SymbolType >
PrefixBarTree < SymbolType >::PrefixBarTree ( SymbolType bar, ext::vector < SymbolType > data ) : PrefixBarTree ( bar, ext::set < SymbolType > ( data.begin ( ), data.end ( ) ) + ext::set < SymbolType > { bar }, data ) {
}

template < class SymbolType >
PrefixBarTree < SymbolType >::PrefixBarTree ( SymbolType bar, const UnrankedTree < SymbolType > & tree ) : PrefixBarTree ( bar, tree.getAlphabet ( ) + ext::set < SymbolType > { bar }, TreeAuxiliary::treeToPrefix ( tree.getContent ( ), bar ) ) {
}

template < class SymbolType >
PrefixBarTree < SymbolType >::PrefixBarTree ( const UnrankedTree < SymbolType > & tree ) : PrefixBarTree ( alphabet::BarSymbol::instance < SymbolType > ( ), tree ) {
}

template < class SymbolType >
const ext::vector < SymbolType > & PrefixBarTree < SymbolType >::getContent ( ) const & {
	return this->m_Data;
}

template < class SymbolType >
ext::vector < SymbolType > && PrefixBarTree < SymbolType >::getContent ( ) && {
	return std::move ( this->m_Data );
}

template < class SymbolType >
void PrefixBarTree < SymbolType >::setContent ( ext::vector < SymbolType > data ) {
	arityChecksum ( data );

	ext::set < SymbolType > minimalAlphabet ( data.begin ( ), data.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 TreeException ( "Input symbols not in the alphabet." );

	this->m_Data = std::move ( data );
}

template < class SymbolType >
void PrefixBarTree < SymbolType >::arityChecksum ( const ext::vector < SymbolType > & data ) {
	int arityChecksumTypes = 0;

	for ( const SymbolType & symbol : data ) {
		if ( symbol == getBar ( ) )
			arityChecksumTypes -= 1;
		else
			arityChecksumTypes += 1;
	}

	if ( arityChecksumTypes != 0 ) throw TreeException ( "The string does not form a tree" );
}

template < class SymbolType >
bool PrefixBarTree < SymbolType >::isEmpty ( ) const {
	return this->m_Data.empty ( );
}

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

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

	return comp ( first, second );
}

template < class SymbolType >
PrefixBarTree < SymbolType >::operator std::string ( ) const {
	std::stringstream ss;
	ss << "\"";

	for ( const SymbolType & symbol : this->m_Data )
		ss << symbol;

	ss << "\"";
	return std::move ( ss ).str ( );
}

} /* namespace tree */

namespace core {

/**
 * Helper class specifying constraints for the tree's internal alphabet component.
 *
 * \tparam SymbolType used for the symbol of the alphabet
 */
template < class SymbolType >
class SetConstraint< tree::PrefixBarTree < SymbolType >, SymbolType, tree::GeneralAlphabet > {
public:
	/**
	 * Returns true if the symbol is still used in the tree.
	 *
	 * \param tree the tested tree
	 * \param symbol the tested symbol
	 *
	 * \returns true if the symbol is used, false othervise
	 */
	static bool used ( const tree::PrefixBarTree < SymbolType > & tree, const SymbolType & symbol ) {
		const ext::vector < SymbolType > & content = tree.getContent ( );

		return std::find ( content.begin ( ), content.end ( ), symbol ) != content.end ( );
	}

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

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

/**
 * Helper class specifying constraints for the pattern's internal subtree bar element.
 *
 * \tparam SymbolType used for the symbol of the alphabet
 */
template < class SymbolType >
class ElementConstraint< tree::PrefixBarTree < SymbolType >, SymbolType, tree::BarSymbol > {
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::PrefixBarTree < SymbolType > & tree, const SymbolType & symbol ) {
		return tree.template accessComponent < tree::GeneralAlphabet > ( ).get ( ).count ( symbol );
	}

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

/**
 * 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::PrefixBarTree < SymbolType > > {
	static tree::PrefixBarTree < > eval ( tree::PrefixBarTree < SymbolType > && value ) {
		DefaultSymbolType bar = alphabet::SymbolNormalize::normalizeSymbol ( std::move ( value ).getBar ( ) );
		ext::set < DefaultSymbolType > alphabet = alphabet::SymbolNormalize::normalizeAlphabet ( std::move ( value ).getAlphabet ( ) );
		ext::vector < DefaultSymbolType > content = alphabet::SymbolNormalize::normalizeSymbols ( std::move ( value ).getContent ( ) );

		return tree::PrefixBarTree < > ( std::move ( bar ), std::move ( alphabet ), std::move ( content ) );
	}
};

} /* namespace core */

extern template class tree::PrefixBarTree < >;

#endif /* PREFIX_BAR_TREE_H_ */
