/******************************************************************************/
/* HASH INDEX *****************************************************************/
/******************************************************************************/

#ifndef _H_EBCL_HASHINDEX
#define _H_EBCL_HASHINDEX
#include <ebcl/Externals.hh>
#include <ebcl/Pointers.hh>
namespace ebcl {


/*
 * This is based on http://glampert.com/2016/05-04/dissecting-idhashindex,
 * which explains how the hash tables in idTech4 work. Not looking at the code
 * to avoid GPL-related issues, so my implementation will probably suck.
 *
 * Unlike the original thing, this version considers that:
 *	- all additions are made at the end of the array (and therefore at the
 * end of the index),
 *	- erasing is done by swapping the element being erased and the last
 * element of the index.
 *
 * It also keeps track of the hash key for a given index in order to make
 * erasing easier.
 */

class T_HashIndex
{
   public:
	static constexpr uint32_t INVALID_INDEX = 0xffffffff;
	static constexpr uint32_t DEFAULT_SIZE = 1024;
	static constexpr uint32_t DEFAULT_GROWTH = 1024;

   private:
	static uint32_t invalidIndex_;

	uint32_t hashSize_;
	uint32_t* hash_;

	uint32_t indexSize_;
	uint32_t indexGrowth_;
	uint32_t indexUsed_;
	uint32_t* index_;
	uint32_t* indexReverse_;

	uint32_t hashMask_;
	uint32_t lookupMask_;

	void enlargeIndex( uint32_t needed );
	void allocateIfNecessary( );

   public:
	T_HashIndex( ) noexcept;
	T_HashIndex( uint32_t hashSize , uint32_t indexSize , uint32_t growth = DEFAULT_GROWTH ) noexcept;

	T_HashIndex( T_HashIndex const& source );
	T_HashIndex( T_HashIndex&& source ) noexcept;

	~T_HashIndex( );

	T_HashIndex& operator =( T_HashIndex const& other );
	T_HashIndex& operator =( T_HashIndex&& other ) noexcept;

	friend void swap( T_HashIndex& lhs , T_HashIndex& rhs ) noexcept;

	void free( );
	void clear( );

	void add( uint32_t key );
	void remove( uint32_t index );

	uint32_t first( uint32_t key ) const;
	uint32_t next( uint32_t index ) const;
};
M_CLASS_POINTERS( HashIndex );
void swap( T_HashIndex& lhs , T_HashIndex& rhs ) noexcept;


} // namespace
#include <ebcl/inline/HashIndex.hh>
#endif // _H_EBCL_HASHINDEX