/*
 * SuffixTrie.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 SUFFIX_TRIE_H_
#define SUFFIX_TRIE_H_

#include <alib/string>
#include <alib/set>
#include <alib/trie>
#include <alib/optional>
#include <alib/iostream>
#include <alib/algorithm>
#include <sstream>

#include <common/DefaultSymbolType.h>

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

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

namespace indexes {

namespace stringology {

class GeneralAlphabet;

/**
 * \brief Suffix trie string index. Tree like representation of all suffixes. Nodes of the trie are optionally containing index of the suffix. The parent child relationship of nodes is represented by single symbol. The class does not checks whether the trie actually is suffix trie.
 *
 * \tparam SymbolType type of symbols of indexed string
 */
template < class SymbolType = DefaultSymbolType >
class SuffixTrie final : public ext::CompareOperators < SuffixTrie < SymbolType > >, public core::Components < SuffixTrie < SymbolType >, ext::set < SymbolType >, component::Set, GeneralAlphabet > {
	/**
	 * Representation of the suffix trie.
	 */
	ext::trie < SymbolType, ext::optional < unsigned > > m_trie;

	/**
	 * \brief Checks edges of the raw suffix trie, whether they are over the specified alphabet.
	 *
	 * \throws exception::CommonException if there is a symbol on trie edges not present in the alphabet.
	 */
	void checkTrie ( const ext::trie < SymbolType, ext::optional < unsigned > > & trie );

public:
	/**
	 * Creates a new instance of the index over concrete alphabet and content initialized to empty root.
	 *
	 * \param edgeAlphabet the alphabet of symbols on node to node edges.
	 */
	explicit SuffixTrie ( ext::set < SymbolType > edgeAlphabet );

	/**
	 * Creates a new instance of the index over concrete alphabet and raw suffix trie.
	 *
	 * \param edgeAlphabet the alphabet of symbols on node to node edges.
	 * \param trie the representation of suffix trie.
	 */
	explicit SuffixTrie ( ext::set < SymbolType > edgeAlphabet, ext::trie < SymbolType, ext::optional < unsigned > > trie );

	/**
	 * Creates a new instance of the index based on raw suffix trie.
	 *
	 * \param trie the representation of suffix trie.
	 */
	explicit SuffixTrie ( ext::trie < SymbolType, ext::optional < unsigned > > trie );

	/**
	 * Getter of the raw suffix trie.
	 *
	 * \return root node of the trie
	 */
	const ext::trie < SymbolType, ext::optional < unsigned > > & getRoot ( ) const &;

	/**
	 * Getter of the raw suffix trie.
	 *
	 * \return root node of the trie
	 */
	ext::trie < SymbolType, ext::optional < unsigned > > && getRoot ( ) &&;

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

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

	/**
	 * Sets the root node of the suffix trie
	 *
	 * \param tree root node to set
	 */
	void setTree ( ext::trie < SymbolType, ext::optional < unsigned > > trie );

	/**
	 * Remover of a symbol from the alphabet.
	 *
	 * \param symbol a symbol to remove.
	 */
	bool removeSymbolFromEdgeAlphabet ( const SymbolType & symbol ) {
		return this->template accessComponent < GeneralAlphabet > ( ).remove ( symbol );
	}

	/**
	 * The actual compare method
	 *
	 * \param other the other instance
	 *
	 * \returns the actual relation between two by type same index instances
	 */
	int compare ( const SuffixTrie & 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 SuffixTrie & instance ) {
		return out << "(SuffixTrie " << instance.m_trie << ")";
	}

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

} /* namespace stringology */

} /* namespace indexes */

namespace indexes {

namespace stringology {

template < class SymbolType >
SuffixTrie < SymbolType >::SuffixTrie ( ext::set < SymbolType > edgeAlphabet ) : SuffixTrie ( std::move ( edgeAlphabet ), ext::trie < SymbolType, ext::optional < unsigned > > ( ext::optional < unsigned > ( ) ) ) {
}

template < class SymbolType >
SuffixTrie < SymbolType >::SuffixTrie ( ext::set < SymbolType > edgeAlphabet, ext::trie < SymbolType, ext::optional < unsigned > > trie ) : core::Components < SuffixTrie, ext::set < SymbolType >, component::Set, GeneralAlphabet > ( std::move ( edgeAlphabet ) ), m_trie ( std::move ( trie ) ) {
	checkTrie ( this->m_trie );
}

template < class SymbolType >
SuffixTrie < SymbolType >::SuffixTrie ( ext::trie < SymbolType, ext::optional < unsigned > > trie ) : SuffixTrie ( computeMinimalEdgeAlphabet ( trie ), trie ) {
}

template < class SymbolType >
void SuffixTrie < SymbolType >::checkTrie ( const ext::trie < SymbolType, ext::optional < unsigned > > & trie ) {
	for ( const std::pair < const SymbolType, ext::trie < SymbolType, ext::optional < unsigned > > > & child : trie.getChildren ( ) ) {
		if ( ! getAlphabet ( ).count ( child.first ) )
			throw exception::CommonException ( "Symbol " + ext::to_string ( child.first ) + "not in the alphabet." );
		checkTrie ( child.second );
	}
}

template < class SymbolType >
const ext::trie < SymbolType, ext::optional < unsigned > > & SuffixTrie < SymbolType >::getRoot ( ) const & {
	return m_trie;
}

template < class SymbolType >
ext::trie < SymbolType, ext::optional < unsigned > > && SuffixTrie < SymbolType >::getRoot ( ) && {
	return std::move ( m_trie );
}

template < class SymbolType >
void SuffixTrie < SymbolType >::setTree ( ext::trie < SymbolType, ext::optional < unsigned > > trie ) {
	checkTrie ( trie );
	this->m_trie = std::move ( trie ).clone ( );
}

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

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

	return comp ( first, second );
}

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

} /* namespace stringology */

} /* namespace indexes */

namespace core {

/**
 * Helper class specifying constraints for the internal alphabet component of the index.
 *
 * \tparam SymbolType type of symbols of indexed string
 */
template < class SymbolType >
class SetConstraint < indexes::stringology::SuffixTrie < SymbolType >, SymbolType, indexes::stringology::GeneralAlphabet > {

	/**
	 * Recursive implementation of the used constraint checker. Returns true if the symbol is still used on edges of the suffix trie bellow the node specified by parameter.
	 *
	 * \param trie the tested subtrie
	 * \param symbol the tested symbol
	 *
	 * \returns true if the symbol is used, false othervise
	 */
	static bool used ( const ext::trie < SymbolType, ext::optional < unsigned > > & trie, const SymbolType & symbol ) {
		for ( const std::pair < const SymbolType, ext::trie < SymbolType, ext::optional < unsigned > > > & child : trie.getChildren ( ) ) {
			if ( symbol == child.first || checkTrie ( trie, child.second ) )
				return true;
		}
		return false;
	}

public:
	/**
	 * Returns true if the symbol is still used on edges of the suffix trie.
	 *
	 * \param index the tested index
	 * \param symbol the tested symbol
	 *
	 * \returns true if the symbol is used, false othervise
	 */
	static bool used ( const indexes::stringology::SuffixTrie < SymbolType > & index, const SymbolType & symbol ) {
		return used ( index.getRoot ( ), symbol );
	}

	/**
	 * Returns true as all symbols are possibly available to be elements of the alphabet.
	 *
	 * \param index the tested index
	 * \param symbol the tested symbol
	 *
	 * \returns true
	 */
	static bool available ( const indexes::stringology::SuffixTrie < SymbolType > &, const SymbolType & ) {
		return true;
	}

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

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols.
 *
 * \returns new instance of the index with default template parameters or unmodified instance if the template parameters were already the default ones
 */
template < class SymbolType >
struct normalize < indexes::stringology::SuffixTrie < SymbolType > > {
	static indexes::stringology::SuffixTrie < > eval ( indexes::stringology::SuffixTrie < SymbolType > && value ) {
		ext::set < DefaultSymbolType > alphabet = alphabet::SymbolNormalize::normalizeAlphabet ( std::move ( value ).getAlpahet ( ) );
		ext::trie < DefaultSymbolType, ext::optional < unsigned > > trie = indexes::IndexesNormalize::normalizeTrie ( std::move ( value ).getRoot ( ) );

		return indexes::stringology::SuffixTrie < > ( std::move ( alphabet ), std::move ( trie ) );
	}
};

} /* namespace core */

#endif /* SUFFIX_TRIE_H_ */
