/******************************************************************************/
/* HASH TABLES ****************************************************************/
/******************************************************************************/

#ifndef _H_LW_LIB_HASHTABLES
#define _H_LW_LIB_HASHTABLES
#include <ebcl/Arrays.hh>
#include <ebcl/HashIndex.hh>
namespace ebcl {


// F_KeyMatch< K > - Equality check function for keys
template< typename K >
using F_KeyMatch = std::function< bool( K const& , K const& ) >;


// T_DefaultKeyMatch< K > - Default equality check for keys
template< typename K >
struct T_DefaultKeyMatch
{
	static bool keysMatch( K const& , K const& );
};


/*= KEY / VALUE TABLE ========================================================*/

/*
 * T_KeyValueTable - Hash table with separate key and value storage.
 * This class is meant to be used when data with unrelated types need to be
 * associated with each other, e.g. string -> int mapping.
 */

template<
	typename KeyType ,
	typename ValueType
	>
class T_KeyValueTable
{
   public:
	typedef F_KeyMatch< KeyType > F_Match;

   private:
	typedef T_KeyValueTable< KeyType , ValueType > MyType_;

	F_Match match_;
	T_HashIndex index_;
	T_Array< KeyType > keys_;
	T_Array< ValueType > values_;

   public:
	M_TEMPLATE_POINTERS( MyType_ );

	// ---------------------------------------------------------------------

	T_KeyValueTable( uint32_t initialSize = T_HashIndex::DEFAULT_SIZE ,
			uint32_t hashSize = T_HashIndex::DEFAULT_SIZE ,
			uint32_t growth = T_HashIndex::DEFAULT_GROWTH ,
			F_Match match = T_DefaultKeyMatch< KeyType >::keysMatch );

	template< typename K , typename T >
	friend void swap( T_KeyValueTable< K , T >& lhs , T_KeyValueTable< K , T >& rhs );

	// ---------------------------------------------------------------------

	// Add a new value (fails if key already present)
	template<
		typename A , typename B ,
		T_EnableIfTypesMatch< KeyType , A > = true ,
		T_EnableIfTypesMatch< ValueType , B > = true
	> bool add( A&& k , B&& v );

	// Update an existing value (fails if key not present)
	template<
		typename A ,
		T_EnableIfTypesMatch< ValueType , A > = true
	> bool update( KeyType const& k , A&& v );

	// Add or modify a key/value pair
	template<
		typename A , typename B ,
		T_EnableIfTypesMatch< KeyType , A > = true ,
		T_EnableIfTypesMatch< ValueType , B > = true
	> void set( A&& k , B&& v );

	// Remove a key/value pair
	bool remove( KeyType const& k );

	void clear( );
	void free( );

	// ---------------------------------------------------------------------

	uint32_t size( ) const;
	T_Array< KeyType > const& keys( ) const;
	T_Array< ValueType > const& values( ) const;
	uint32_t indexOf( KeyType const& k ) const;
	bool contains( KeyType const& k ) const;

	ValueType const * get( KeyType const& k ) const;
	ValueType * get( KeyType const& k );

	template< typename... ArgTypes >
		ValueType& getOrCreate( KeyType const& k , ArgTypes&&... args );

	ValueType& operator[] ( uint32_t index );
	ValueType const& operator[] ( uint32_t index ) const;

	// ---------------------------------------------------------------------

   private:
	uint32_t find( KeyType const& k , uint32_t hash ) const;
};

template< typename K , typename T >
void swap( T_KeyValueTable< K , T >& lhs , T_KeyValueTable< K , T >& rhs );


/*= OBJECT TABLE =============================================================*/

/*
 * This class is meant to be used to store objects that somehow contain their
 * own keys. Of course, modifying one of these objects' key after it's been
 * inserted will cause major SNAFU's.
 */

template<
	typename KeyType ,
	typename ValueType
	>
class T_ObjectTable
{
   public:
	typedef F_KeyMatch< KeyType > F_Match;
	typedef std::function< KeyType( ValueType const& ) > F_GetKey;

   private:
	typedef T_ObjectTable< KeyType , ValueType > MyType_;

	F_Match match_;
	F_GetKey keyGetter_;
	T_HashIndex index_;
	T_Array< ValueType > values_;

   public:
	M_TEMPLATE_POINTERS( MyType_ );
	T_ObjectTable( ) = delete;

	// ---------------------------------------------------------------------

	T_ObjectTable( F_GetKey keyGetter , uint32_t initialSize = T_HashIndex::DEFAULT_SIZE ,
		      uint32_t hashSize = T_HashIndex::DEFAULT_SIZE ,
		      uint32_t growth = T_HashIndex::DEFAULT_GROWTH ,
		      F_Match match = T_DefaultKeyMatch< KeyType >::keysMatch );

	template< typename K , typename T >
	friend void swap( T_ObjectTable< K , T >& lhs , T_ObjectTable< K , T >& rhs );

	// ---------------------------------------------------------------------

	template<
		typename A ,
		T_EnableIfTypesMatch< ValueType , A > = true
	> bool add( A&& v );

	template<
		typename A ,
		T_EnableIfTypesMatch< ValueType , A > = true
	> bool update( A&& v );

	template<
		typename A ,
		T_EnableIfTypesMatch< ValueType , A > = true
	> void set( A&& v );

	bool remove( KeyType const& k );

	void clear( );
	void free( );

	// ---------------------------------------------------------------------

	uint32_t size( ) const;
	uint32_t indexOf( KeyType const& k ) const;
	bool contains( KeyType const& k ) const;
	T_Array< KeyType > keys( ) const;
	T_Array< ValueType > const& values( ) const;

	ValueType const * get( KeyType const& k ) const;
	ValueType * get( KeyType const& k );

	ValueType& operator[] ( uint32_t index );
	ValueType const& operator[] ( uint32_t index ) const;

	// ---------------------------------------------------------------------

   private:
	uint32_t find( KeyType const& k , uint32_t hash ) const;
};

template< typename K , typename T >
void swap( T_ObjectTable< K , T >& lhs , T_ObjectTable< K , T >& rhs );


}
#endif // _H_LW_LIB_HASHTABLES
#include <ebcl/inline/HashTables.hh>