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

#ifndef SUFFIX_ARRAY_H_
#define SUFFIX_ARRAY_H_

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

#include <common/DefaultSymbolType.h>

#include <exception/CommonException.h>

#include <string/LinearString.h>

#include <alphabet/common/SymbolNormalize.h>

namespace indexes {

namespace stringology {

/**
 * \brief Suffix array string index. Linear representation of all suffixes ordered lexicographically. Suffixes are represented as indexes to the indexed string and alphabet is stored within the string as well. Therefore the string is stored allong with Tree like representation of all suffixes. The class does not checks whether the suffixes order is correct.
 *
 * \tparam SymbolType type of symbols of indexed string
 */
template < class SymbolType = DefaultSymbolType >
class SuffixArray final : public ext::CompareOperators < SuffixArray < SymbolType > > {
	/**
	 * Representation of lexicographically sorted indexes to the indexed string.
	 */
	ext::vector < unsigned > m_data;

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

public:
	/**
	 * Creates a new instance of the index with concrete sorted indexes and indexed string.
	 *
	 * \param data the sorted indexes to the indexed string
	 * \param string the indexed string
	 */
	explicit SuffixArray ( ext::vector < unsigned > data, string::LinearString < SymbolType > string );

	/**
	 * Getter of the sorted indexes.
	 *
	 * @return vector of indexes
	 */
	const ext::vector < unsigned > & getData ( ) const &;

	/**
	 * Getter of the sorted indexes.
	 *
	 * @return vector of indexes
	 */
	ext::vector < unsigned > && getData ( ) &&;

	/**
	 * 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 vector of sorted indexes to the indexed string.
	 *
	 * @param data new vector of indexes
	 */
	void setData ( ext::vector < unsigned > data );

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

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

	/**
	 * 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 >
SuffixArray < SymbolType >::SuffixArray ( ext::vector < unsigned > data, string::LinearString < SymbolType > string ) : m_data ( std::move ( data ) ), m_string ( std::move ( string ) ) {
}

template < class SymbolType >
const ext::vector < unsigned > & SuffixArray < SymbolType >::getData ( ) const & {
	return m_data;
}

template < class SymbolType >
ext::vector < unsigned > && SuffixArray < SymbolType >::getData ( ) && {
	return std::move ( m_data );
}

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

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

template < class SymbolType >
void SuffixArray < SymbolType >::setData ( ext::vector < unsigned > data ) {
	this->m_data = std::move ( data );
}

template < class SymbolType >
int SuffixArray < SymbolType >::compare ( const SuffixArray & other ) const {
	auto first = ext::tie ( getData ( ), getString ( ) );
	auto second = ext::tie ( other.getData ( ), other.getString ( ) );

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

	return comp ( first, second );
}

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

} /* namespace stringology */

} /* 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 >
struct normalize < indexes::stringology::SuffixArray < SymbolType > > {
	static indexes::stringology::SuffixArray < > eval ( indexes::stringology::SuffixArray < SymbolType > && value ) {
		string::LinearString < DefaultSymbolType > string = normalize < string::LinearString < SymbolType > >::eval ( std::move ( value ).getString ( ) );

		return indexes::stringology::SuffixArray < > ( std::move ( value ).getData ( ), std::move ( string ) );
	}
};

} /* namespace core */

#endif /* SUFFIX_ARRAY_H_ */
