/*------------------------------------------------------------------------------
	FILE	:	Hash.c
	CREATED	:	98/05/12
	AUTHOR	:	Catalin Cocos
	CONTENTS:	variable size hash table implementation
------------------------------------------------------------------------------*/


#include "StdInc.h"
#include "Hash.h"



/*creation, initialization, deletion
--------------------------------------*/

/*------------------------------------------------------------------------------
	DESC.	:	creates a new hash
	INPUT	:	A) the dispersion function, with the following characteristics:
						- input	: pointer to a hash element, or NULL;
						- output:	> if input is NULL, the maximum index possible + 1
									> else, the hash index.
				B) the comparison function - similar with the one used for the LDT_tdst_DSArray
				C) the element deletion function - used to delete the elements
	OUTPUT	:	the newly created hash or NULL
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
LDT_tdst_DSHash* LDT_DSHs_new(int (* _pfnDispersion ) (void*), 
						int (* _pfnCompare)(const void**, const void** ),
						void (* _pfnDelete) (void*) )
{
	LDT_tdst_DSHash* pObj = (LDT_tdst_DSHash*) malloc( sizeof(LDT_tdst_DSHash) );
	if( LDT_DSHs_Init( pObj, _pfnDispersion, _pfnCompare, _pfnDelete) )
		return pObj;	/* success */
	free( pObj );
	return NULL;		/* failure */
}


/*------------------------------------------------------------------------------
	DESC.	:	initializes an existing hash
	INPUT	:	see above
	OUTPUT	:	nonzero if successful
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_DSHs_Init(LDT_tdst_DSHash* pObj,
				int (* _pfnDispersion ) (void*),
				int (* _pfnCompare)(const void**, const void** ),
				void (* _pfnDelete) (void*) )
{
	int HashDimension;
	
	if(!pObj || !_pfnDispersion || !_pfnCompare || !_pfnDelete ) return 0;	/* invalid input */
	
	HashDimension = _pfnDispersion(NULL);
	if( !HashDimension ) return 0;	/* bad dispersion function */

	pObj->pfnDelete = _pfnDelete;				/* set the deletion function */
	pObj->pfnDispersion = _pfnDispersion;		/* set the dispersion function */
	pObj->pData = (LDT_tdst_DSArray*) malloc( HashDimension * sizeof(LDT_tdst_DSArray) );	/* allocate the needed memory */
	for(HashDimension --; HashDimension>=0; HashDimension--)
	{
		LDT_DSAr_Init( pObj->pData + HashDimension );	/* initialize the dynamic arrays */
		LDT_DSAr_SetSortMethod(  pObj->pData + HashDimension, _pfnCompare );
	}
	return 1;	/* success */
}

/*------------------------------------------------------------------------------
	DESC.	:	deletes an existing array
	INPUT	:	- the object
				- an integer: DelElements; if non-zero, all elements are freed
				(the pfnDelete is called for each element)
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
void LDT_DSHs_delete( LDT_tdst_DSHash* pObj, int DelElements)
{
	if( pObj )
	{
		LDT_DSHs_Close( pObj, DelElements );	/* clean-up */
		free( pObj );			/* deletion */
	}
}								


/*------------------------------------------------------------------------------
	DESC.	:	sets the dynamic aray elements granularity
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
void LDT_DSHs_SetGranularity( LDT_tdst_DSHash* pObj, int _nGrowBy)
{
	int HashDimension = pObj->pfnDispersion( NULL ) - 1 ;
	for( ; HashDimension>=0; HashDimension--)
		LDT_DSAr_SetGranularity( pObj->pData + HashDimension, _nGrowBy );	/* set the granularity for each array */
}


/* Clean up 
-------------*/


/*------------------------------------------------------------------------------
	DESC.	:	frees the unused memory
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
void LDT_DSHs_FreeExtra( LDT_tdst_DSHash* pObj )	
{
	int HashDimension = pObj->pfnDispersion( NULL ) - 1 ;
	for( ; HashDimension>=0; HashDimension--)
		LDT_DSAr_FreeExtra( pObj->pData + HashDimension );
}

/*------------------------------------------------------------------------------
	DESC.	:	clears the hash, removing all the elements
	INPUT	:	- the object
				- an integer: DelElements; if non-zero, all elements are freed
				(the pfnDelete is called for each element)
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
void LDT_DSHs_RemoveAll( LDT_tdst_DSHash* pObj, int DelElements)
{
	LDT_tdst_DSArray* pCrtAr;
	void**	pData;
	void (* Delete ) (void*) = pObj->pfnDelete;
	int i;
	int HashDimension = pObj->pfnDispersion( NULL ) - 1 ;
	for( ; HashDimension>=0; HashDimension--)
	{
		pCrtAr = pObj->pData + HashDimension;
		if(DelElements)
			for(i = LDT_DSAr_GetUpperBound( pCrtAr ), pData = pCrtAr->pData; i>=0; i--)
				Delete( pData[i]);
		LDT_DSAr_RemoveAll( pCrtAr );
	}
}

/*------------------------------------------------------------------------------
	DESC.	:	clears the hash memory
	INPUT	:	- the object
				- an integer: DelElements; if non-zero, all elements are freed
				(the pfnDelete is called for each element)
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
void LDT_DSHs_Close( LDT_tdst_DSHash* pObj,  int DelElements )
{
	LDT_DSHs_RemoveAll( pObj, DelElements );
	free( pObj->pData );
	pObj->pData = NULL;
}


/* Accessing elements
-----------------------*/

/*------------------------------------------------------------------------------
	DESC.	:	gets the hash element matching the parameter
	OUTPUT	:	the equivalent element in the hash, or NULL if none found
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
void* LDT_DSHs_GetEqOf( LDT_tdst_DSHash* pHash, void* pTgt)
{
	LDT_tdst_DSArray* pArr;
	if( !pTgt ) return NULL;
	pArr = pHash->pData + pHash->pfnDispersion( pTgt );			/* get the array */ 
	return LDT_DSAr_GetAt( pArr, LDT_DSAr_Index( pArr, pTgt, NULL ));	/* get the element */
}


/*------------------------------------------------------------------------------
	DESC.	:	removes the hash element matching the parameter
	OUTPUT	:	the equivalent element removed, or NULL if none found
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
void* LDT_DSHs_RemoveEqOf( LDT_tdst_DSHash* pHash, void* pTgt )
{
	LDT_tdst_DSArray* pArr;
	if( !pTgt ) return NULL;
	pArr = pHash->pData + pHash->pfnDispersion( pTgt );			/* get the array */ 
	return LDT_DSAr_RemoveAt( pArr, LDT_DSAr_Index( pArr, pTgt, NULL ));/* remove the element */
}

/*------------------------------------------------------------------------------
	DESC.	:	inserts the element in the hash table
	INPUT	:	the hash;
				the element to insert;
				an integer specifying the action to perform if an equivalent element is found in the hash:
					0	->	no action
					1	->	delete the element if it is not needed (an equivalent is found)
	OUTPUT	:	the element in the hash: the inserted element or the found equivalent.
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
void* LDT_DSHs_Insert( LDT_tdst_DSHash* pHash, void* pTgt, int del)
{
	int position;
	LDT_tdst_DSArray* pArr;
	if( !pTgt ) return NULL;
	pArr = pHash->pData + pHash->pfnDispersion( pTgt );			/* get the array */ 
	if(LDT_DSAr_SAdd( pArr, pTgt, 0, &position ) < 0)				/* try to insert the element */
	{
		/* insertion failed */
		void* existing = LDT_DSAr_GetAt( pArr, position);
		if( del && (existing != pTgt) )
			pHash->pfnDelete (pTgt);	/* delete the non-inserted element */
		return existing;
	}
	return pTgt;	
}
