/*************************************************************************/
/*	File : XHashTable.h													 */
/*	Author :  Aymeric Bard												 */	
/*																		 */	
/*	Virtools SDK 															 */	 
/*	Copyright (c) Virtools 2000, All Rights Reserved.						 */	
/*************************************************************************/


#ifndef _XHashTable_H_
#define _XHashTable_H_

#include "XClassArray.h"
#include "XArray.h"
#include "XSArray.h"
#include "XHashFun.h"

#ifdef _WIN32
#pragma warning(disable : 4786)
#endif


const float L = 0.75f;

/************************************************
Summary: Class representation of an Hash Table 
container.

Remarks:
	T is the type of element to insert
	K is the type of the key
	H is the hash function to hash the key

	Several hash functions for basic types are 
already defined in XHashFun.h

	This implementation of the hash table uses
Linked List in each bucket for element hashed to 
the same index, so there are memory allocation
for each insertion. For a static implementation
without dynamic allocation, look at XSHashTable.

	There is a m_LoadFactor member which allow the 
user to decide at which occupation the hash table 
must be extended and rehashed.


************************************************/
template <class T,class K,class H = XHashFun<K>, class Eq = XEqual<K>/*, float L = 0.75f*/>
class XHashTable
{
	// Types
	
	
	struct Entry
	{
		K					key;
		T					data;
		Entry*				next;
	};

	typedef Entry*			pEntry;
public:

	typedef XHashTable				Table;
	typedef XHashTable*				pTable;
	typedef const XHashTable*		pConstTable;

	/************************************************
	Summary: Iterator on a hash table.

	Remarks: This iterator is the only way to iterate on
	elements in a hash table. The iteration will be in no
	specific order, not in the insertion order. Here is an example 
	of how to use it:

	Example:
		
		XHashTable<T,K,H>::Iterator it = hashtable.Begin();
		while (it != hashtable.End()) {
			// access to the key
			it.GetKey();

			// access to the element
			*it;

			// next element
			++it;
		}

	
	************************************************/
	class Iterator
	{
	public:

		/************************************************
		Summary: Default constructor of the iterator.
		************************************************/
		Iterator():m_It(0) {}

		/************************************************
		Summary: Copy constructor of the iterator.
		************************************************/
		Iterator(const Iterator& n):m_It(n.m_It) {}
		
		/************************************************
		Summary: Operator Equal of the iterator.
		************************************************/
		int operator==(const Iterator& it) const { return m_It == it.m_It; }

		/************************************************
		Summary: Operator Not Equal of the iterator.
		************************************************/
		int operator!=(const Iterator& it) const { return m_It != it.m_It; }
		
		/************************************************
		Summary: Returns a constant reference on the data 
		pointed	by the iterator.

		Remarks:
			The returned reference is constant, so you can't 
		modify its value. Use the other * operator for this 
		purpose.
		************************************************/
		const T& operator*() const { return m_It->data; }

		/************************************************
		Summary: Returns a reference on the data pointed	
		by the iterator.

		Remarks:
			The returned reference is not constant, so you
		can modify its value. 
		************************************************/
		T& operator*() { return m_It->data; }

		/************************************************
		Summary: Returns a pointer on a T object.
		************************************************/
//		operator const T*() const { return &m_It->data; }
		
		/************************************************
		Summary: Returns a pointer on a T object.
		************************************************/
//		operator T*() { return &m_It->data; }

		/************************************************
		Summary: Returns a const reference on the key of
		the pointed entry.
		************************************************/
		const K& GetKey() const {return m_It->key;}
		K& GetKey() {return m_It->key;}

		/************************************************
		Summary: Jumps to next entry in the hashtable.
		************************************************/
		Iterator& operator++() { // Prefixe
			++m_It;
			return *this;
		}

		/************************************************
		Summary: Jumps to next entry in the hashtable.
		************************************************/
		Iterator& operator--() { // Prefixe
			--m_It;
			return *this;
		}

		/************************************************
		Summary: Jumps to next entry in the hashtable.
		************************************************/
		Iterator operator++(int) { 
			Iterator tmp = *this;
			++*this;
			return tmp;
		}

		/************************************************
		Summary: Jumps to next entry in the hashtable.
		************************************************/
		Iterator operator--(int) { 
			Iterator tmp = *this;
			--*this;
			return tmp;
		}

		
		Iterator(Entry* iT):m_It(iT) {}
		
		Entry*	m_It;
	};
	friend class Iterator;

	/************************************************
	Summary: Constant Iterator on a hash table.

	Remarks: This iterator is the only way to iterate on
	elements in a constant hash table. The iteration will be in no
	specific order, not in the insertion order. Here is an example 
	of how to use it:

	Example:
		
		void MyClass::MyMethod() const
		{
			XHashTable<T,K,H>::ConstIterator it = m_Hashtable.Begin();
			while (it != m_Hashtable.End()) {
				// access to the key
				it.GetKey();

				// access to the element
				*it;

				// next element
				++it;
			}
		}

	
	************************************************/
	class ConstIterator
	{
		friend class XHashTable;
	public:
		/************************************************
		Summary: Default constructor of the iterator.
		************************************************/
		ConstIterator():m_It(0) {}

		/************************************************
		Summary: Copy constructor of the iterator.
		************************************************/
		ConstIterator(const ConstIterator& n):m_It(n.m_It){}
		
		/************************************************
		Summary: Operator Equal of the iterator.
		************************************************/
		int operator==(const ConstIterator& it) const { return m_It == it.m_It; }

		/************************************************
		Summary: Operator Not Equal of the iterator.
		************************************************/
		int operator!=(const ConstIterator& it) const { return m_It != it.m_It; }
		
		/************************************************
		Summary: Returns a constant reference on the data 
		pointed	by the iterator.

		Remarks:
			The returned reference is constant, so you can't 
		modify its value. Use the other * operator for this 
		purpose.
		************************************************/
		const T& operator*() const { return m_It->data; }

		/************************************************
		Summary: Returns a pointer on a T object.
		************************************************/
//		operator const T*() const { return &(m_It->data); }
		
		/************************************************
		Summary: Returns a const reference on the key of
		the pointed entry.
		************************************************/
		const K& GetKey() const {return m_It->key;}

		/************************************************
		Summary: Jumps to next entry in the hashtable.
		************************************************/
		ConstIterator& operator++() { // Prefixe
			++m_It;
			return *this;
		}

		/************************************************
		Summary: Jumps to next entry in the hashtable.
		************************************************/
		ConstIterator operator++(int) { 
			ConstIterator tmp = *this;
			++*this;
			return tmp;
		}

		
		ConstIterator(Entry* iT):m_It(iT) {}
		
		Entry* m_It;
	};
	friend class ConstIterator;

	/************************************************
	Summary: Struct containing an iterator on an object
	inserted and a BOOL determining if it were really 
	inserted (TRUE) or already there (FALSE).

	
	************************************************/
	struct Pair
	{
		public:
			Pair(Iterator it,int n) : m_Iterator(it),m_New(n) {};

			Iterator				m_Iterator;
			XBOOL					m_New;
	};

	/************************************************
	Summary: Default Constructor.

	Input Arguments: 
		initialsize: The default number of buckets 
		(should be a power of 2, otherwise will be 
		converted.)
		l: Load Factor (see Class Description).
		a: hash table to copy.

	************************************************/
	XHashTable(int initialsize = 16)
	{
		initialsize = Near2Power(initialsize);
		if (initialsize < 4) initialsize = 4;

		m_Table.Resize(initialsize);
		m_Table.Fill(0);
		m_Pool.Reserve((int)(initialsize*L));
	}

	/************************************************
	Summary: Copy Constructor.
	************************************************/
	XHashTable(const XHashTable& a)
	{
		XCopy(a);
	}

	/************************************************
	Summary: Destructor.

	Remarks:
		Release the elements contained in the hash table. If
	you were storing pointers, you need first to iterate 
	on the table and call delete on each pointer.
	************************************************/
	~XHashTable()
	{
	}

	/************************************************
	Summary: Removes all the elements from the table.

	Remarks:
		The hash table remains with the same number 
	of buckets after a clear.
	************************************************/
	void Clear()
	{
		// we clear all the allocated entries
		m_Pool.Resize(0);
		// we clear the table
		m_Table.Fill(0);
	}

	/************************************************
	Summary: Calculates the average occupation for the 
	buckets by filling an array with the population for
	different bucket size (represented by the index of 
	the array)

	************************************************/
	void GetOccupation(XArray<int>& iBucketOccupation) const
	{
		iBucketOccupation.Resize(1);
		iBucketOccupation[0] = 0;

		for(pEntry* it = m_Table.Begin();it != m_Table.End();it++) {
			if (!*it) { // there is someone there
				iBucketOccupation[0]++;
			} else {
				// count the number of occupant
				int count = 1;
				pEntry e = *it;
				while (e->next) {
					e = e->next;
					count++;
				}
				
				int oldsize = iBucketOccupation.Size();
				if (oldsize <= count) { // we need to resize
					iBucketOccupation.Resize(count+1);

					// and we init to 0
					for (int i=oldsize; i<=count; ++i)
						iBucketOccupation[i] = 0;
				}
		
				// the recensing
				iBucketOccupation[count]++;

			}
		}

	
	}

	/************************************************
	Summary: Affectation operator.

	Remarks:
		The content of the table is enterely overwritten
	by the given table.
	************************************************/
	Table& operator = (const Table& a)
	{
		if(this != &a) {
			// We clear the current table 
			Clear();
			// we then copy the content of a
			XCopy(a);
		}

		return *this;
	}

	/************************************************
	Summary: Inserts an element in the table.

	Input Arguments: 
		key: key of the element to insert.
		o: element to insert.
		override: if the key is already present, should
	the old element be overriden ?

	Remarks:
		Insert will automatically override the old value
	and InsertUnique will not replace the old value.
	TestInsert returns a XHashPair, which allow you to know
	if the element was already present.
	************************************************/
	XBOOL Insert(const K& key,const T& o,XBOOL override)
	{
		int index = Index(key);

		// we look for existing key
		pEntry e = XFind(index,key);
		if (e == m_Pool.End()) {
			if (m_Pool.Size() == m_Pool.Allocated()) { // Need Rehash
				Rehash(m_Table.Size()*2);
				return Insert(key,o,override);
			} else { // No
				XInsert(index,key,o);
			}
		} else {
			if (!override) return FALSE;
			e->data = o;
		}

		return TRUE;
	}

	Iterator Insert(const K& key,const T& o)
	{
		int index = Index(key);
		Eq equalFunc;

		// we look for existing key
		for(pEntry e=m_Table[index];e != 0;e = e->next) {
			if (equalFunc(e->key,key)) {
				e->data = o;
				return Iterator(e);
			}
		}
		
		if (m_Pool.Size() == m_Pool.Allocated()) { // Need Rehash
			Rehash(m_Table.Size()*2);
			return Insert(key,o);
		} else { // No
			return Iterator(XInsert(index,key,o));
		}
	}

	
	Pair
	TestInsert(const K& key,const T& o)
	{
		int index = Index(key);
		Eq equalFunc;

		// we look for existing key
		for(pEntry e=m_Table[index];e != 0;e = e->next) {
			if (equalFunc(e->key,key)) {
				return Pair(Iterator(e),0);
			}
		}
		
		// Need Rehash
		if (m_Pool.Size() == m_Pool.Allocated()) { // Need Rehash
			Rehash(m_Table.Size()*2);
			return TestInsert(key,o);
		} else { // No
			return Pair(Iterator(XInsert(index,key,o)),1);
		}
	}

	
	Iterator InsertUnique(const K& key,const T& o)
	{
		int index = Index(key);
		Eq equalFunc;

		// we look for existing key
		for(pEntry e=m_Table[index];e != 0;e = e->next) {
			if (equalFunc(e->key,key)) {
				return Iterator(e);
			}
		}
		
		// Need Rehash
		if (m_Pool.Size() == m_Pool.Allocated()) { // Need Rehash
			Rehash(m_Table.Size()*2);
			return InsertUnique(key,o);
		} else { // No
			return Iterator(XInsert(index,key,o));
		}
	}

	/************************************************
	Summary: Removes an element.

	Input Arguments: 
		key: key of the element to remove.
		it: iterator on the object to remove.

	Return Value: iterator on the lement next to 
	the one just removed.
	************************************************/
	void Remove(const K& key)
	{
		int index = Index(key);
		Eq equalFunc;

		// we look for existing key
		pEntry old = NULL;
		for(pEntry e=m_Table[index];e != 0;e = e->next) {
			if (equalFunc(e->key,key)) {
				// This is the element to remove
				
				// change the pointers to it
				if (old) {
					old->next = e->next;
				} else {
					m_Table[index] = e->next;
				}

				// then removed it from the pool
				m_Pool.FastRemove(e);
				if (e != m_Pool.End()) { // wasn't the last one... we need to remap
					RemapEntry(m_Pool.End(), e);
				}


				break;
			}
			old = e;
		}
	}

	Iterator Remove(const Iterator& it)
	{
		int index = Index(it.m_It->key);
		if(index >= m_Table.Size()) 
			return Iterator(m_Pool.End());

		if (!m_Pool.Size())
			return End();

		// we look for existing key
		pEntry old = NULL;
		for (pEntry e=m_Table[index];e != 0;e = e->next) {
			if (e == (it.m_It)) {
				// This is the element to remove
				if (old) {
					old->next = e->next;
					old = old->next;
				} else {
					m_Table[index] = e->next;
					old = m_Table[index];
				}

				// then removed it from the pool
				m_Pool.FastRemove(e);
				if (e != m_Pool.End()) { // wasn't the last one... we need to remap
					RemapEntry(m_Pool.End(), e);
					if (old == m_Pool.End())
						old = e;
				}

				break;
			}
			old = e;
		}

		return it;
	}

	/************************************************
	Summary: Access to an hash table element.

	Input Arguments: 
		key: key of the element to access.

	Return Value: a copy of the element found.

	Remarks:
		If no element correspond to the key, an element
	constructed with 0.
	************************************************/
	T& operator [] (const K& key)
	{
		int index = Index(key);

		// we look for existing key
		pEntry e = XFind(index,key);
		if (e == m_Pool.End()) {
			if (m_Pool.Size() == m_Pool.Allocated()) { // Need Rehash
				Rehash(m_Table.Size()*2);
				return operator [] (key);
			} else { // No
				e = XInsert(index,key,T());
			}
		}

		return e->data;
	}

	/************************************************
	Summary: Access to an hash table element.

	Input Arguments: 
		key: key of the element to access.

	Return Value: an iterator of the element found. End()
	if not found.

	************************************************/
	Iterator Find(const K& key)
	{
		return Iterator(XFindIndex(key));
	}

	/************************************************
	Summary: Access to a constant hash table element.

	Input Arguments: 
		key: key of the element to access.

	Return Value: a constant iterator of the element found. End()
	if not found.

	************************************************/
	ConstIterator Find(const K& key) const
	{
		return ConstIterator(XFindIndex(key));
	}

	/************************************************
	Summary: Access to an hash table element.

	Input Arguments: 
		key: key of the element to access.

	Return Value: a pointer on the element found. NULL
	if not found.

	************************************************/
	T *FindPtr(const K& key) const
	{
		pEntry e = XFindIndex(key);
		if (e != m_Pool.End())	
			return &e->data;
		else	
			return 0;
	}

	/************************************************
	Summary: search for an hash table element.

	Input Arguments: 
		key: key of the element to access.
		value: value to receive the element found value.

	Return Value: TRUE if the key was found, FALSE 
	otherwise..

	************************************************/
	XBOOL LookUp(const K& key,T& value) const
	{
		pEntry e = XFindIndex(key);
		if (e != m_Pool.End()) {
			value = e->data;
			return TRUE;
		} else 
			return FALSE;
	}

	/************************************************
	Summary: test for the presence of a key.

	Input Arguments: 
		key: key of the element to access.

	Return Value: TRUE if the key was found, FALSE 
	otherwise..

	************************************************/
	XBOOL IsHere(const K& key) const
	{
		return (XFindIndex(key) != m_Pool.End());
	}

	/************************************************
	Summary: Returns an iterator on the first element.

	Example:
		Typically, an algorithm iterating on an hash table 
	looks like:

		XHashTable<T,K,H>::Iterator it = h.Begin();
		XHashTable<T,K,H>::Iterator itend = h.End();

		for(; it != itend; ++it) {
			// do something with *t
		}

	************************************************/
	Iterator Begin()
	{
		return Iterator(m_Pool.Begin());
	}

	ConstIterator Begin() const
	{
		return ConstIterator(m_Pool.Begin());
	}

	/************************************************
	Summary: Returns an iterator out of the hash table.
	************************************************/
	Iterator End()
	{
		return Iterator(m_Pool.End());
	}

	ConstIterator End() const
	{
		return ConstIterator(m_Pool.End());
	}

	/************************************************
	Summary: Returns the index of the given key.

	Input Arguments: 
		key: key of the element to find the index.
	************************************************/
	int Index(const K& key) const
	{
		H hashfun;
		return XIndex(hashfun(key),m_Table.Size());
	}

	/************************************************
	Summary: Returns the index of the given key.

	Input Arguments: 
		key: key of the element to find the index.
	************************************************/
	Iterator GetIteratorByIndex(int index) const
	{
		XASSERT(index < m_Pool.Size());
		return Iterator(m_Pool.Begin()+index);
	}

	/************************************************
	Summary: Returns the elements number.
	************************************************/
	int Size() const
	{
		return m_Pool.Size();
	}

	/************************************************
	Summary: Return the occupied size in bytes.

	Parameters:
		addstatic: TRUE if you want to add the size occupied
	by the class itself.
	************************************************/
	int GetMemoryOccupation(XBOOL addstatic=FALSE) const 
	{
		return m_Table.GetMemoryOccupation(addstatic) +
			m_Pool.Allocated()*sizeof(Entry) +
			(addstatic?sizeof(*this):0);
	}

	/************************************************
	Summary: Reserve an estimation of future hash occupation.

	Parameters:
		iCount: count of elements to reserve
	Remarks:
		you need to call this function before populating 
	the hash table.
	************************************************/
	void Reserve(const int iCount) 
	{
		// Reserve the elements
		m_Pool.Reserve(iCount);

		int tableSize = Near2Power((int)(iCount/L));
		m_Table.Resize(tableSize);
		m_Table.Fill(0);
	}
	
	Iterator GetByIndex(const int iIndex) 
	{
		return(m_Pool.Begin() + iIndex);
	}
	
	

private:
	// Types
	
	///
	// Methods

	
	pEntry* GetFirstBucket() const {return m_Table.Begin();}

	
	void
	Rehash(int iSize) {
		int oldsize = m_Table.Size();

		// we create a new pool
		XClassArray<Entry> pool((int)(iSize*L));
		pool = m_Pool;

		// Temporary table
		XSArray<pEntry> tmp;
		tmp.Resize(iSize);
		tmp.Fill(0);
		
		for (int index = 0; index < oldsize; ++index) {
			Entry* first = m_Table[index];
			while (first) {
				H hashfun;
				int newindex	= XIndex(hashfun(first->key),iSize);

				Entry* newe		= pool.Begin() + (first - m_Pool.Begin());

				// insert new entry in new table
				newe->next		= tmp[newindex];
				tmp[newindex]	= newe;
				
				first			= first->next;          
			}
		}
		m_Table.Swap(tmp);
		m_Pool.Swap(pool);
	}

	
	int XIndex(int key,int size) const
	{
		return key&(size-1);
	}
	
	
	void XCopy(const XHashTable& a)
	{
		int size = a.m_Table.Size();
		m_Table.Resize(size);
		m_Table.Fill(0);

		m_Pool.Reserve(a.m_Pool.Allocated());
		m_Pool = a.m_Pool;

		// remap the address in the table
		for (int i=0; i<size; ++i) {
			if (a.m_Table[i])
				m_Table[i] = m_Pool.Begin() + (a.m_Table[i] - a.m_Pool.Begin());
		}

		// remap the adresses in the entries
		for (Entry* e = m_Pool.Begin(); e != m_Pool.End(); ++e) {
			if (e->next) {
				e->next = m_Pool.Begin() + (e->next - a.m_Pool.Begin());
			}
		}
	}

	
	pEntry XFindIndex(const K& key) const
	{
		int index = Index(key);
		return XFind(index,key);
	}

	
	pEntry XFind(int index,const K& key) const
	{
		Eq equalFunc;

		// we look for existing key
		for(pEntry e=m_Table[index];e != 0;e = e->next) {
			if (equalFunc(e->key,key)) {
				return e;
			}
		}
		return m_Pool.End();
	}

	
	pEntry XInsert(int index,const K& key,const T& o)
	{
		Entry* newe		= GetFreeEntry();
		newe->key		= key; 
		newe->data		= o;
		newe->next		= m_Table[index];
		m_Table[index]	= newe;
		return newe;
	}

	
	pEntry GetFreeEntry()
	{
		// We consider when we arrive here that we have space
		m_Pool.Resize(m_Pool.Size()+1);
		return (m_Pool.End()-1);
	}

	void	RemapEntry(pEntry iOld, pEntry iNew)
	{
		int index = Index(iNew->key);
		XASSERT(m_Table[index]);

		if (m_Table[index] == iOld) { // It was the first of the bucket
			m_Table[index] = iNew;
		} else {
			for (pEntry n = m_Table[index]; n->next != NULL; n = n->next) {
				if (n->next == iOld) { // found one
					n->next = iNew;
					break; // only one can match
				}
			}
		}
		
	}

	///
	// Members
	
	// the hash table data {secret}
	XSArray<pEntry>		m_Table;
	// the entry pool {secret}
	XClassArray<Entry>	m_Pool;
};

#endif
