/*
 * BitParallelIndex.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/set>
#include <alib/string>
#include <alib/iostream>
#include <sstream>

#include <common/DefaultSymbolType.h>

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

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

namespace indexes {

namespace stringology {

class GeneralAlphabet;

/**
 * \brief Bit parallel string index. Stores a bit vector for each symbol of the alphabet. The bit vector of symbol a contains true on index i if symbol a is on i-th position in the indexed string. The class does not check whether the bit vectors actually represent valid index.
 *
 * \tparam SymbolType type of symbols of indexed string
 */
template < class SymbolType = DefaultSymbolType >
class BitParallelIndex final : public core::Components < BitParallelIndex < SymbolType >, ext::set < SymbolType >, component::Set, GeneralAlphabet > {
	/**
	 * Representation of bit vectors for each symbol of the alphabet.
	 */
	ext::map < SymbolType, ext::vector < bool > > m_vectors;

public:
	/**
	 * Creates a new instance of the index with concrete alphabet and bit vectors.
	 *
	 * \param alphabet the alphabet of indexed string
	 * \param vectors the bit vectors
	 */
	explicit BitParallelIndex ( ext::set < SymbolType > alphabet, ext::map < SymbolType, ext::vector < bool > > vectors );

	/**
	 * Getter of the bit vectors.
	 *
	 * @return bit vectors
	 */
	const ext::map < SymbolType, ext::vector < bool > > & getData ( ) const &;

	/**
	 * Getter of the bit vectors.
	 *
	 * @return bit vectors
	 */
	ext::map < SymbolType, ext::vector < bool > > && getData ( ) &&;

	/**
	 * Reconstructs the indexed string from bit vectors.
	 *
	 * @return the original indexed string
	 */
	ext::vector < SymbolType > getString ( ) const;

	/**
	 * 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 ( ) );
	}

	/**
	 * Changes the bit vector for concrete symbol.
	 *
	 * \param symbol the changed symbol
	 * \param data the new bit vector
	 */
	void setBitVectorForSymbol ( SymbolType symbol, ext::vector < bool > data );

	/**
	 * 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 SymbolType & symbol ) {
		return this->template accessComponent < GeneralAlphabet > ( ).remove ( symbol );
	}

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

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

	/**
	 * 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 BitParallelIndex & instance ) {
		return out << "(BitParallelIndex " << instance.m_vectors << ")";
	}

	/**
	 * 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 >
BitParallelIndex < SymbolType >::BitParallelIndex ( ext::set < SymbolType > alphabet, ext::map < SymbolType, ext::vector < bool > > vectors ) : core::Components < BitParallelIndex, ext::set < SymbolType >, component::Set, GeneralAlphabet > ( std::move ( alphabet ) ), m_vectors ( std::move ( vectors ) ) {
}

template < class SymbolType >
const ext::map < SymbolType, ext::vector < bool > > & BitParallelIndex < SymbolType >::getData ( ) const & {
	return m_vectors;
}

template < class SymbolType >
ext::map < SymbolType, ext::vector < bool > > && BitParallelIndex < SymbolType >::getData ( ) && {
	return std::move ( m_vectors );
}

template < class SymbolType >
ext::vector < SymbolType > BitParallelIndex < SymbolType >::getString ( ) const {
	ext::vector < SymbolType > res;

	unsigned index = 0;
	do {
		for ( const std::pair < const SymbolType, ext::vector < bool > > & bitVector : m_vectors )
			if ( bitVector.second.size ( ) > index && bitVector.second [ index ] ) {
				res.push_back ( bitVector.first );
				continue;
			}

	} while ( res.size ( ) == index ++ + 1 );

	return res;
}

template < class SymbolType >
void BitParallelIndex < SymbolType >::setBitVectorForSymbol ( SymbolType symbol, ext::vector < bool > data ) {
	this->m_vectors [ symbol ] = std::move ( data );
}

template < class SymbolType >
BitParallelIndex < 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::BitParallelIndex < SymbolType >, SymbolType, indexes::stringology::GeneralAlphabet > {
public:
	/**
	 * Returns true if the symbol is still used as key in mapping symbol to bit vector.
	 *
	 * \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::BitParallelIndex < SymbolType > & index, const SymbolType & symbol ) {
		const ext::map < SymbolType, ext::vector < bool > > & content = index.getData ( );
		return content.find( symbol ) != content.end();
	}

	/**
	 * 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::BitParallelIndex < 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::BitParallelIndex < 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::BitParallelIndex < SymbolType > > {
	static indexes::stringology::BitParallelIndex < > eval ( indexes::stringology::BitParallelIndex < SymbolType > && value ) {
		ext::set < DefaultSymbolType > alphabet = alphabet::SymbolNormalize::normalizeAlphabet ( std::move ( value ).getAlphabet ( ) );

		ext::map < DefaultSymbolType, ext::vector < bool > > vectors;
		for ( std::pair < SymbolType, ext::vector < bool > > && vector : ext::make_mover ( std::move ( value ).getData ( ) ) )
			vectors.insert ( std::make_pair ( alphabet::SymbolNormalize::normalizeSymbol ( std::move ( vector.first ) ), std::move ( vector.second ) ) );

		return indexes::stringology::BitParallelIndex < > ( std::move ( alphabet ), std::move ( vectors ) );
	}
};

} /* namespace core */

