/*
 * PositionHeap.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
 */

#pragma once

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

#include <common/DefaultSymbolType.h>

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

#include <string/LinearString.h>

#include <indexes/common/IndexesNormalize.h>

namespace indexes {

namespace stringology {

class GeneralAlphabet;

/**
 * \brief Position heap string index. Tree like representation of all suffixes. The suffixes are themselves represented by their prefixes. The prefix of each suffix needs to be unique from all representants (prefixes again) of shorter suffixes. Nodes of the trie contain index of the suffix. The parent child relationship of nodes is represented by single symbol. The class does not checks whether the trie structure actually is position heap.
 *
 * Also the size of the structure is exactly linear.
 *
 * \tparam SymbolType type of symbols of indexed string
 */
template < class SymbolType = DefaultSymbolType >
class PositionHeap final {
	/**
	 * Representation of the position heap.
	 */
	ext::trie < SymbolType, unsigned > m_trie;

	/**
	 * The copy of indexed string.
	 */
	string::LinearString < SymbolType > m_string;

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

public:
	/**
	 * Creates a new instance of the index based on constructed position heap and original indexed string.
	 *
	 * \param trie the raw data of the index - the position heap
	 * \param string the indexed string
	 */
	explicit PositionHeap ( ext::trie < SymbolType, unsigned > trie, string::LinearString < SymbolType > string );

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

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

	/**
	 * Getter of the indexed string.
	 *
	 * @return the indexed string
	 */
	const string::LinearString < SymbolType > & getString ( ) const &;

	/**
	 * Getter of the indexed string.
	 *
	 * @return the indexed string
	 */
	string::LinearString < SymbolType > && getString ( ) &&;

	/**
	 * Getter of the alphabet of the indexed string.
	 *
	 * \returns the alphabet of the indexed string
	 */
	const ext::set < SymbolType > & getAlphabet ( ) const & {
		return m_string.getAlphabet ( );
	}

	/**
	 * Getter of the alphabet of the indexed string.
	 *
	 * \returns the alphabet of the indexed string
	 */
	ext::set < SymbolType > && getAlphabet ( ) && {
		return std::move ( m_string ).getAlphabet ( );
	}

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

	/**
	 * Remover of a symbol from the alphabet.
	 *
	 * \param symbol a symbol to remove.
	 */
	bool removeSymbolFromEdgeAlphabet ( const SymbolType & symbol ) {
		return m_string.removeSymbol ( symbol );
	}

	/**
	 * The three way comparison implementation
	 *
	 * \param other the other instance
	 *
	 * \returns the ordering between this object and the @p other.
	 */
	auto operator <=> ( const PositionHeap & other ) const {
		return std::tie ( getRoot ( ), getAlphabet ( ) ) <=> std::tie ( other.getRoot ( ), other.getAlphabet ( ) ); // indexed string does not need to be compared.
	}

	/**
	 * The equality comparison implementation.
	 *
	 * \param other the other object to compare with.
	 *
	 * \returns true if this and other objects are equal, false othervise
	 */
	bool operator == ( const PositionHeap & other ) const {
		return std::tie ( getRoot ( ), getAlphabet ( ) ) == std::tie ( other.getRoot ( ), other.getAlphabet ( ) ); // indexed string does not need to be compared.
	}

	/**
	 * 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 PositionHeap & instance ) {
		return out << "(PositionHeap " << 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 >
PositionHeap < SymbolType >::PositionHeap ( ext::trie < SymbolType, unsigned > trie, string::LinearString < SymbolType > string ) : m_trie ( std::move ( trie ) ), m_string ( std::move ( string ) ) {
	checkTrie ( this->m_trie );
}

template < class SymbolType >
void PositionHeap < SymbolType >::checkTrie ( const ext::trie < SymbolType, unsigned > & trie ) {
	for ( const std::pair < const SymbolType, ext::trie < SymbolType, 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, unsigned > & PositionHeap < SymbolType >::getRoot ( ) const & {
	return m_trie;
}

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

template < class SymbolType >
const string::LinearString < SymbolType > & PositionHeap < SymbolType >::getString ( ) const & {
	return m_string;
}

template < class SymbolType >
string::LinearString < SymbolType > && PositionHeap < SymbolType >::getString ( ) && {
	return std::move ( m_string );
}

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

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

} /* namespace stringology */

} /* namespace indexes */

namespace core {

template < class SymbolType >
struct normalize < indexes::stringology::PositionHeap < common::ranked_symbol < SymbolType > > > {
	static indexes::stringology::PositionHeap < common::ranked_symbol < DefaultSymbolType > > evalRanked ( indexes::stringology::PositionHeap < common::ranked_symbol < SymbolType > > && value ) {
		ext::trie < common::ranked_symbol < DefaultSymbolType >, unsigned > trie = indexes::IndexesNormalize::normalizeRankedTrie ( std::move ( value ).getRoot ( ) );
		string::LinearString < common::ranked_symbol < DefaultSymbolType > > string = normalize < string::LinearString < common::ranked_symbol < SymbolType > > >::eval ( std::move ( value ).getString ( ) );

		return indexes::stringology::PositionHeap < common::ranked_symbol < DefaultSymbolType > > ( std::move ( trie ), std::move ( string ) );
	}

	static indexes::stringology::PositionHeap < > eval ( indexes::stringology::PositionHeap < common::ranked_symbol < SymbolType > > && value ) {
		ext::trie < DefaultSymbolType, unsigned > trie = indexes::IndexesNormalize::normalizeTrie ( std::move ( value ).getRoot ( ) );
		string::LinearString < DefaultSymbolType > string = normalize < string::LinearString < SymbolType > >::eval ( std::move ( value ).getString ( ) );

		return indexes::stringology::PositionHeap < > ( std::move ( trie ), std::move ( string ) );
	}
};

/**
 * 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::PositionHeap < SymbolType > > {
	static indexes::stringology::PositionHeap < > eval ( indexes::stringology::PositionHeap < SymbolType > && value ) {
		ext::trie < DefaultSymbolType, unsigned > trie = indexes::IndexesNormalize::normalizeTrie ( std::move ( value ).getRoot ( ) );
		string::LinearString < DefaultSymbolType > string = normalize < string::LinearString < SymbolType > >::eval ( std::move ( value ).getString ( ) );

		return indexes::stringology::PositionHeap < > ( std::move ( trie ), std::move ( string ) );
	}
};

} /* namespace core */

