#include "stdafx.h"

#include "MemoryUtils.h"
#include "StringExt.h"
#include "Hash.h"

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

struct THashBucket 
{
  StringExt   *seKey;
  union 
  {
    void *p;
    int   i;
  } uValue;
  THashBucket *pNext;
};

struct THashIter 
{
  int          nHashIndex;
  THashBucket *pBucket;
};

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

CHash::CHash(BOOL bDeleteKeys) 
{
	m_bDeleteKeys = bDeleteKeys;
	m_nBucketsCount = 7;
	m_ppTable = (THashBucket **)MemUtilsMallocArray( m_nBucketsCount, sizeof(THashBucket *));
	for ( int nHashIndex = 0; nHashIndex < m_nBucketsCount; ++nHashIndex ) 
		m_ppTable[ nHashIndex ] = NULL;
	m_nLength = 0;
}

CHash::~CHash() 
{
	THashBucket *pBucket = NULL;

	for ( int nHashIndex = 0; nHashIndex < m_nBucketsCount; ++nHashIndex ) 
	{
		while ( m_ppTable[ nHashIndex ] ) 
		{
			pBucket = m_ppTable[ nHashIndex ];
			m_ppTable[ nHashIndex ] = pBucket->pNext;
			if (m_bDeleteKeys) 
			{
				delete pBucket->seKey;
			}
			delete pBucket;
		}
	}
	MemUtilsFree( m_ppTable );
}

void CHash::Add(StringExt *seKey, void *pValue) 
{
	if ( m_nLength >= m_nBucketsCount )
		Expand();

	THashBucket *pBucket = new THashBucket;
	pBucket->seKey = seKey;
	pBucket->uValue.p = pValue;
	int nHashIndex = Hash( seKey );
	pBucket->pNext = m_ppTable[ nHashIndex ];
	m_ppTable[ nHashIndex ] = pBucket;
	++m_nLength;
}

void CHash::Add(StringExt *seKey, int nValue) 
{
	if ( m_nLength >= m_nBucketsCount )
		Expand();

	THashBucket *pBucket = new THashBucket;
	pBucket->seKey = seKey;
	pBucket->uValue.i = nValue;
	int nHashIndex = Hash( seKey );
	pBucket->pNext = m_ppTable[ nHashIndex ];
	m_ppTable[ nHashIndex ] = pBucket;
	++m_nLength;
}

void CHash::Replace(StringExt *seKey, void *pValue) 
{
	THashBucket *pBucket = NULL;
	int nHashIndex = 0;

	if ( ( pBucket = Find( seKey, &nHashIndex ) ) ) 
	{
		pBucket->uValue.p = pValue;
		delete seKey;
	} 
	else 
		Add( seKey, pValue);
}

void CHash::Replace(StringExt *seKey, int nValue) 
{
	THashBucket *pBucket = NULL;
	int nHashIndex = 0;

	if ( ( pBucket = Find( seKey, &nHashIndex ) ) ) 
	{
		pBucket->uValue.i = nValue;
		delete seKey;
	} 
	else
		Add( seKey, nValue);
}

void *CHash::Lookup(StringExt *seKey) 
{
	THashBucket *pBucket = NULL;
	int nHashIndex = 0;

	if ( !( pBucket = Find( seKey, &nHashIndex ) ) ) 
		return NULL;
	return pBucket->uValue.p;
}

int CHash::LookupInt(StringExt *seKey) 
{
	THashBucket *pBucket = NULL;
	int nHashIndex = 0;

	if ( !( pBucket = Find( seKey, &nHashIndex ) ) ) 
		return 0;
	
	return pBucket->uValue.i;
}

void *CHash::Lookup(char *sKey) 
{
	THashBucket *pBucket = NULL;
	int nHashIndex = 0;

	if ( !( pBucket = Find( sKey, &nHashIndex ) ) ) 
		return NULL;
	return pBucket->uValue.p;
}

int CHash::LookupInt(char *sKey) 
{
	THashBucket *pBucket = NULL;
	int nHashIndex = 0;

	if ( !( pBucket = Find( sKey, &nHashIndex ) ) ) 
		return 0;
	
	return pBucket->uValue.i;
}

void *CHash::Remove(StringExt *seKey) 
{
	THashBucket *pBucket;
	THashBucket **ppBucket;
	int nHashIndex = 0;

	if ( !( pBucket = Find( seKey, &nHashIndex ) ) ) 
		return NULL;

	ppBucket = &m_ppTable[ nHashIndex ];
	while ( *ppBucket != pBucket ) 
		ppBucket = &((*ppBucket)->pNext);

	*ppBucket = pBucket->pNext;
	if ( m_bDeleteKeys ) 
		delete pBucket->seKey;
	void *pValue = pBucket->uValue.p;
	delete pBucket;
	--m_nLength;
	return pValue;
}

int CHash::RemoveInt(StringExt *seKey) 
{
	THashBucket *pBucket;
	THashBucket **ppBucket;
	int nHashIndex = 0;

	if ( !( pBucket = Find( seKey, &nHashIndex) ) )
		return 0;

	ppBucket = &m_ppTable[ nHashIndex ];
	while ( *ppBucket != pBucket )
		ppBucket = &((*ppBucket)->pNext);

	*ppBucket = pBucket->pNext;
	if (m_bDeleteKeys)
		delete pBucket->seKey;

	int nValue = pBucket->uValue.i;
	delete pBucket;
	--m_nLength;
	return nValue;
}

void *CHash::Remove(char *sKey) 
{
	THashBucket *pBucket;
	THashBucket **ppBucket;
	int nHashIndex = 0;

	if ( !( pBucket = Find( sKey, &nHashIndex ) ) )
		return NULL;
	ppBucket = &m_ppTable[ nHashIndex ];
	while ( *ppBucket != pBucket )
		ppBucket = &((*ppBucket)->pNext);

	*ppBucket = pBucket->pNext;

	if (m_bDeleteKeys)
		delete pBucket->pNext;

	void *pValue = pBucket->uValue.p;
	delete pBucket;
	--m_nLength;
	return pValue;
}

int CHash::RemoveInt(char *sKey) 
{
	THashBucket *pBucket;
	THashBucket **ppBucket;
	int nHashIndex = 0;

	if ( !( pBucket = Find( sKey, &nHashIndex ) ) )
		return 0;

	ppBucket = &m_ppTable[ nHashIndex ];
	while (*ppBucket != pBucket )
		ppBucket = &((*ppBucket)->pNext);

	*ppBucket = pBucket->pNext;
	if ( m_bDeleteKeys )
		delete pBucket->seKey;

	int nValue = pBucket->uValue.i;
	delete pBucket;
	--m_nLength;
	return nValue;
}

void CHash::StartIter(THashIter **ppIter) 
{
	*ppIter = new THashIter;
	(*ppIter)->nHashIndex = -1;
	(*ppIter)->pBucket = NULL;
}

BOOL CHash::GetNext(THashIter **ppIter, StringExt **pseKey, void **ppValue) 
{
	if (!*ppIter) 
		return FALSE;

	if ((*ppIter)->pBucket)
		(*ppIter)->pBucket = (*ppIter)->pBucket->pNext;

	while (!(*ppIter)->pBucket) 
	{
		if (++(*ppIter)->nHashIndex == m_nBucketsCount) 
		{
			delete *ppIter;
			*ppIter = NULL;
			return FALSE;
		}
		(*ppIter)->pBucket = m_ppTable[(*ppIter)->nHashIndex];
	}
	*pseKey = (*ppIter)->pBucket->seKey;
	*ppValue = (*ppIter)->pBucket->uValue.p;
	return TRUE;
}

BOOL CHash::GetNext(THashIter **ppIter, StringExt **pseKey, int   *pnValue) 
{
	if (!*ppIter) 
		return FALSE;

	if ((*ppIter)->pBucket)
		(*ppIter)->pBucket = (*ppIter)->pBucket->pNext;

	while (!(*ppIter)->pBucket) 
	{
		if (++(*ppIter)->nHashIndex == m_nBucketsCount) 
		{
			delete *ppIter;
			*ppIter = NULL;
			return FALSE;
		}
		(*ppIter)->pBucket = m_ppTable[(*ppIter)->nHashIndex ];
	}
	*pseKey = (*ppIter)->pBucket->seKey;
	*pnValue = (*ppIter)->pBucket->uValue.i;
	return TRUE;
}

void CHash::DeleteIter(THashIter **ppIter) 
{
	delete *ppIter;
	*ppIter = NULL;
}

void CHash::Expand() 
{
	int nHashIndex = 0;

	int nOldSize = m_nBucketsCount;
	THashBucket **ppOldTab = m_ppTable;
	m_nBucketsCount = 2 * m_nBucketsCount + 1;
	m_ppTable = (THashBucket **)MemUtilsMallocArray( m_nBucketsCount, sizeof(THashBucket *) );
	for ( nHashIndex = 0; nHashIndex < m_nBucketsCount; ++nHashIndex ) 
		m_ppTable[ nHashIndex ] = NULL;

	for (int nIndex = 0; nIndex < nOldSize; ++nIndex ) 
	{
		while ( ppOldTab[nIndex] ) 
		{
			THashBucket *pBucket = ppOldTab[ nIndex ];
			ppOldTab[ nIndex ] = ppOldTab[ nIndex ]->pNext;
			nHashIndex = Hash( pBucket->seKey );
			pBucket->pNext = m_ppTable[ nHashIndex ];
			m_ppTable[ nHashIndex ] = pBucket;
		}
	}
	MemUtilsFree( ppOldTab );
}

THashBucket *CHash::Find(StringExt *seKey, int *pnHashIndex) 
{
	THashBucket *pBucket;

	*pnHashIndex = Hash( seKey );
	for ( pBucket = m_ppTable[ *pnHashIndex ]; pBucket; pBucket = pBucket->pNext ) 
	{
		if ( !pBucket->seKey->Compare( seKey ) ) 
			return pBucket;
	}
	return NULL;
}
THashBucket *CHash::Find(char       *sKey, int *pnHashIndex) 
{
	THashBucket *pBucket;

	*pnHashIndex = Hash( sKey );
	for ( pBucket = m_ppTable[ *pnHashIndex ]; pBucket; pBucket = pBucket->pNext ) 
	{
		if ( !pBucket->seKey->Compare( sKey ) )
			return pBucket;
	}
	return NULL;
}

int CHash::Hash(StringExt *seKey) 
{
	char *sTemp;
	unsigned int nHash = 0;
	int nIndex;

	for ( sTemp = seKey->GetBuffer(), nIndex = 0; nIndex < seKey->GetLength(); ++sTemp, ++nIndex ) 
		nHash = 17 * nHash + (int)(*sTemp & 0xff);

	return (int)(nHash % m_nBucketsCount);
}

int CHash::Hash(char *sKey) 
{
	char *sTemp;
	unsigned int nHash = 0;

	for ( sTemp = sKey; *sTemp; ++sTemp ) 
		nHash = 17 * nHash + (int)(*sTemp & 0xff);

	return (int)(nHash % m_nBucketsCount);
}
