/*
 * FullAndLinearIndex.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: Jan 8, 2017
 *      Author: Jan Travnicek
 */

#pragma once

#include <alib/string>
#include <sstream>

#include <common/ranked_symbol.hpp>

#include <indexes/stringology/PositionHeap.h>

#include <alphabet/common/SymbolNormalize.h>

namespace indexes {

namespace arbology {

class GeneralAlphabet;

/**
 * \brief Full and linear tree index. The index serves as a adaptor of string index so that searching for tree patterns is possible. The representation of the index stores a string index and a subtree jump table. The implementation since parametrized with the string index represents a family of indexes.
 *
 * The actual notation of used tree is irelevant. The index, as fas as the data structure is concerned, is not different. Of course tree in postfix notation must be queried with patterns in postfix notation, etc.
 *
 * \tparam SymbolType used for the symbol part of the ranked symbol
 * \tparam StringIndex a template parameter representing a type of the string index
 */
template < class SymbolType = DefaultSymbolType, template < typename > class StringIndex = indexes::stringology::PositionHeap >
class FullAndLinearIndex final {
	/**
	 * Representation of the string index.
	 */
	StringIndex < common::ranked_symbol < SymbolType > > m_StringIndex;

	/**
	 * Representation of the subtree jump table.
	 */
	ext::vector < int > m_JumpTable;

public:
	/**
	 * Creates a new instance of the index with concrete alphabet, bit vectors, and subtree jump table.
	 *
	 * \param stringIndex the concrete underlying string index.
	 * \param jumpTable the subtree jump table
	 */
	explicit FullAndLinearIndex ( StringIndex < common::ranked_symbol < SymbolType > > stringIndex, ext::vector < int > jumpTable );

	/**
	 * Getter of the underlying string index.
	 *
	 * @return underlying string index
	 */
	const StringIndex < common::ranked_symbol < SymbolType > > & getStringIndex ( ) const &;

	/**
	 * Getter of the underlying string index.
	 *
	 * @return underlying string index
	 */
	StringIndex < common::ranked_symbol < SymbolType > > && getStringIndex ( ) &&;

	/**
	 * Getter of the subtree jump table
	 *
	 * @return the subtree jump table
	 */
	const ext::vector < int > & getJumps ( ) const &;

	/**
	 * Getter of the subtree jump table
	 *
	 * @return the subtree jump table
	 */
	ext::vector < int > && getJumps ( ) &&;

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

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

	/**
	 * Changes the underlying string index.
	 *
	 * \param stringIndex new string index
	 */
	void setStringIndex ( StringIndex < common::ranked_symbol < SymbolType > > stringIndex );

	/**
	 * Remover of a symbol from the alphabet. The symbol can be removed if it is not used in any of bit vector keys.
	 *
	 * \param symbol a symbol to remove.
	 */
	bool removeSymbolFromAlphabet ( const common::ranked_symbol < SymbolType > & symbol ) {
		return m_StringIndex.removeSymbolFromAlphabet ( symbol );
	}

	/**
	 * The three way comparison implementation
	 *
	 * \param other the other instance
	 *
	 * \returns the ordering between this object and the @p other.
	 */
	auto operator <=> ( const FullAndLinearIndex & other ) const {
		return std::tie ( getStringIndex ( ), getJumps ( ) ) <=> std::tie ( other.getStringIndex ( ), other.getJumps ( ) );
	}

	/**
	 * 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 FullAndLinearIndex & other ) const {
		return std::tie ( getStringIndex ( ), getJumps ( ) ) == std::tie ( other.getStringIndex ( ), other.getJumps ( ) );
	}

	/**
	 * 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 FullAndLinearIndex & instance ) {
		return out << "(FullAndLinearIndex " << instance.m_StringIndex << ", " << instance.m_JumpTable << ")";
	}

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

} /* namespace arbology */

} /* namespace indexes */

namespace indexes {

namespace arbology {

template < class SymbolType, template < typename > class StringIndex >
FullAndLinearIndex < SymbolType, StringIndex >::FullAndLinearIndex ( StringIndex < common::ranked_symbol < SymbolType > > stringIndex, ext::vector < int > jumpTable ) : m_StringIndex ( std::move ( stringIndex ) ), m_JumpTable ( std::move ( jumpTable ) ) {
}

template < class SymbolType, template < typename > class StringIndex >
const StringIndex < common::ranked_symbol < SymbolType > > & FullAndLinearIndex < SymbolType, StringIndex >::getStringIndex ( ) const & {
	return m_StringIndex;
}

template < class SymbolType, template < typename > class StringIndex >
StringIndex < common::ranked_symbol < SymbolType > > && FullAndLinearIndex < SymbolType, StringIndex >::getStringIndex ( ) && {
	return std::move ( m_StringIndex );
}

template < class SymbolType, template < typename > class StringIndex >
const ext::vector < int > & FullAndLinearIndex < SymbolType, StringIndex >::getJumps ( ) const & {
	return m_JumpTable;
}

template < class SymbolType, template < typename > class StringIndex >
ext::vector < int > && FullAndLinearIndex < SymbolType, StringIndex >::getJumps ( ) && {
	return std::move ( m_JumpTable );
}

template < class SymbolType, template < typename > class StringIndex >
void FullAndLinearIndex < SymbolType, StringIndex >::setStringIndex ( StringIndex < common::ranked_symbol < SymbolType > > stringIndex ) {
	this->m_StringIndex = std::move ( stringIndex );
}

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

} /* namespace arbology */

} /* namespace indexes */

namespace core {

/**
 * 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, template < typename > class StringIndex >
struct normalize < indexes::arbology::FullAndLinearIndex < SymbolType, StringIndex > > {
	static indexes::arbology::FullAndLinearIndex < > eval ( indexes::arbology::FullAndLinearIndex < SymbolType, StringIndex > && value ) {
		StringIndex < common::ranked_symbol < > > stringIndex = core::normalize < StringIndex < common::ranked_symbol < SymbolType > > >::eval ( std::move ( value ).getStringIndex ( ) );

		return indexes::arbology::FullAndLinearIndex < > ( std::move ( stringIndex ), std::move ( value ).getJumps ( ) );
	}
};

} /* namespace core */

