/*

The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is a portion of the Path Development API developed
by CygnaCom Solutions, Inc.

Portions created by CygnaCom Solutions, Inc. are Copyright (C) CygnaCom 
Solutions, Inc. All Rights Reserved.

Contributor(s): ______________________________________.

http://www.cygnacom.com

*/
/*------------------------------------------------------------------------*\
|  Project Name  :   Path Development API
|  File Name     :   Cache.h
|  Creation Date :   May 17, 1999
|  Author        :   Matt Cooper (mcooper@cygnacom.com)
|  Last Mode Date:   May 17, 1999
|  Modification History:
|--------------------------------------------------------------------------
|
|     Known Bugs:   None
|                   
|--------------------------------------------------------------------------
| Cryptographic devices and technical data regarding them are subject to
| Federal Government export controls as specified in Title 22, Code of
| Federal Regulations, Parts 121 through 128.  Cryptographic devices
| implementing the Data Encryption Standard (DES) and technical data
| regarding them must comply with these Federal regulations
|--------------------------------------------------------------------------
| Copyright (c) 1999, CygnaCom Solutions, Inc.
| All Rights Reserved.
\*------------------------------------------------------------------------*/

#ifndef CACHE_CLASS_INCL
#define CACHE_CLASS_INCL

/* The following are actions for ProbeTable(...) */
#define DELETE_REC 1
#define INSERT_REC 2
#define FIND_REC   3


/* define the following to false to speed up searching 
if you will not ever need to delete members */
#define DELETE_ENABLED true

#include "Defines.h"

template <class CacheRec> class CMemCache
{
	friend class CPathCache;
	friend class CCertCache;
	public:
		/* vectorSize indicates how big your table should be  */
		CMemCache(long vectorSize);	
		~CMemCache( );
		bool Insert(const CacheRec &new_rec);
		bool Delete(const CacheRec &old_rec);
		bool DeleteFromVector( long VectorPos );
		CacheRec* Search(const CacheRec &search_rec);
		void DumpTable(char*** entry_list);
		long hash_function(char *s ) const;
	protected:
		long m_vectorSize;
		CacheRec** HashVector;
		CacheRec* ProbeTable( long VectorPos, const CacheRec *rec, long Action );
};


////////////////////////////////////////

template <class CacheRec>
bool CMemCache<CacheRec>::DeleteFromVector( long VectorPos )
{

	/* Let's try not to delete from 0x00 */
	if ( HashVector[VectorPos] == NULL )
		return false;

	CacheRec *tmp = HashVector[VectorPos];
	HashVector[VectorPos] = HashVector[VectorPos]->NextRec;
	tmp->NextRec = NULL;
	delete tmp;

	
	return true;
}


template <class CacheRec>
CacheRec* CMemCache<CacheRec>::ProbeTable( long VectorPos, const CacheRec *rec, long Action )
{

	if ( HashVector[VectorPos] == NULL )
		return NULL;

	bool Found;
	CacheRec *ChainedRec = HashVector[VectorPos];
	CacheRec *Prev = NULL;

	switch (Action)
	{
		case INSERT_REC:
		
			
			while ( ChainedRec->NextRec != NULL)
			{
				#ifdef MONITOR_PERFORMANCE
					InsertIterations++;
				#endif
				ChainedRec = ChainedRec->NextRec;	
			}

			ChainedRec->NextRec = new CacheRec;
			if ( ChainedRec->NextRec == NULL )
				return NULL;

			ChainedRec = ChainedRec->NextRec;

			ChainedRec->CopyFrom((CacheRec*)rec);

			return ChainedRec;

		case FIND_REC:
			Found = false;
			while ( !Found )
			{
				#ifdef MONITOR_PERFORMANCE
					SearchIterations++;
				#endif
				ChainedRec = ChainedRec->NextRec;

				if ( ChainedRec == NULL )
				{
					/* the table does not contain the record */
					#ifdef MONITOR_PERFORMANCE
						FailedSearches++;
					#endif
					return NULL;
				}

				if (*ChainedRec == *rec)
					Found = true;

			}

			return ChainedRec;

		case DELETE_REC:
			if( !DELETE_ENABLED )
				return NULL;

			Found = false;

			while ( !Found )
			{
				Prev = ChainedRec;
				ChainedRec = ChainedRec->NextRec;

				if ( ChainedRec == NULL )
				{
					/* the table does not contain the record */
					return NULL;
				}

				if (*ChainedRec == *rec)
					Found = true;

			}

			Prev->NextRec = ChainedRec->NextRec;
			ChainedRec->NextRec = NULL;
			delete ChainedRec;

			return (CacheRec*)0xFFFFFFFF;  

			/* Must return NULL on failure, and anything but NULL on success */
			/* Note that we don't return the original record any longer 'cause the
			programmer may pass in a pointer to the record in the table they
			are deleting, on some systems this may return a NULL after deleting
			it.. */


		default:
			return NULL;

	}
	/* Unreachable */
	return NULL;
}

template <class CacheRec>
bool CMemCache<CacheRec>::Insert(const CacheRec &new_rec)
{
	char *key;

	key = new_rec.get_key();

	if ( key == NULL )
		return false;

	if ( strcmp( key, "" ) == 0 )
		return false;


	/*
		long hash_function(char *s ) const;
	*/
	
	long VectorPos = hash_function(key);

	if ( HashVector[VectorPos] == NULL )
	{
		/* We can safely insert in the table */
		HashVector[VectorPos] = new CacheRec;
		HashVector[VectorPos]->CopyFrom((CacheRec*)&new_rec);
		return true;
	}
		/* resolve collision */
	if ( ProbeTable( VectorPos, &new_rec, INSERT_REC ) != NULL )
		return true;
	
	return false;

}

template <class CacheRec>
bool CMemCache<CacheRec>::Delete(const CacheRec &old_rec)
{
	/* Check to make sure deletion is enabled */
	if ( DELETE_ENABLED != true )
	{
		return false;
	}

	char *key = NULL;
	long VectorPos = 0;

	key = old_rec.get_key();

	/* Check for stupid programmers.. */
	if ( key == NULL )
		return NULL;

	/* Check for stupid programmers.. */
	if ( strcmp( key, "" ) == 0 )
		return NULL;

	VectorPos = hash_function(key); /* get the key */

	if ( HashVector[VectorPos] != NULL )
	{
		if ( *HashVector[VectorPos] == old_rec )
		{
			/* Delete the record */
			if (DeleteFromVector( VectorPos ))
				return true;
			/* else */
			return false;
		}
	}

	if ( ProbeTable( VectorPos, &old_rec, DELETE_REC ) != NULL )
	{
		/* delete was successful */
		return true;
	}
	/* else */
	return false;

}


template <class CacheRec>
CacheRec* CMemCache<CacheRec>::Search(const CacheRec &search_rec)
{
	char *key = NULL;
	long VectorPos = 0;

	key = search_rec.get_key();

	/* Check for stupid programmers.. */
	if ( key == NULL )
		return NULL;

	/* Check for stupid programmers.. */
	if ( strcmp( key, "" ) == 0 )
		return NULL;


	VectorPos = hash_function(key);

	if ( HashVector[VectorPos] == NULL )
	{
		if( DELETE_ENABLED == false)
		{
			/* the record can not exist in the table */
			#ifdef MONITOR_PERFORMANCE
				FailedSearches++;
			#endif
			return NULL;
		}
		/* The value may still be in the table, if it was placed with ProbeTable.. */
	}
	else
	{
		if ( *HashVector[VectorPos]==search_rec )
			return HashVector[VectorPos];
	}



	/* The desired record is not at the current position so we must use the
	collision resolution method to determine if it exists in the table. */
	return ProbeTable( VectorPos, &search_rec, FIND_REC );
	
}

template <class CacheRec>
CMemCache<CacheRec>::CMemCache( long vectorSize )
{
	#ifdef _WIN32
		#ifdef _DEBUG
			/* char *_itoa( long value, char *string, int radix );  */
			char tmp[25];
			afxDump << "\nHashTable constructor reached.  the_size == " << ltoa( vectorSize, tmp, 10) << "\n";
		#endif
	#endif

	m_vectorSize = vectorSize;

	if ( m_vectorSize < 5 )
	{
		m_vectorSize = 5;
	}
		/* the "HashVector" as it were holds pointers to the records,
		Adding five members for safety sake.
		The 5 extra members also prevent needing to check for a zero
		passed into this constructor */

	HashVector = new CacheRec*[m_vectorSize + 5];
	
	/* initialize the vector to all NULL, including the pad */
	/* void *memset( void *dest, long c, size_t count ); */
	memset( (void*)HashVector, 0, (sizeof(HashVector)*(m_vectorSize+5)) );

}


template <class CacheRec>
CMemCache<CacheRec>::~CMemCache(void)//:~HashTable<CacheRec>(void)
{
	for ( long XX = 0; XX < m_vectorSize; XX++)
	{
		if ( HashVector[XX] != NULL )
		{
			delete HashVector[XX];
		}

	}

	delete []HashVector;
}

template <class CacheRec>
void CMemCache<CacheRec>::DumpTable(char*** entry_list)
{
#ifdef _WIN32
#ifdef _DEBUG
	afxDump << "DumpTable() reached\n";
#endif
#endif

	CacheRec* ChainedRec = NULL;
	ULONG rec_count = 0;
	int len = 0;
	int tlen = 0;
	char **pArr = NULL;
	char *pkey = NULL;

	#ifdef _DEBUG
		afxDump << "Dumping Table of size " << m_vectorSize << "\n--------------------------------------------------------\n";   
	#endif

		// Determine Table Size
	for ( long POS = 0; POS < m_vectorSize; POS++ )
	{
		if ( HashVector[POS] != NULL )
		{
			rec_count++;

			ChainedRec = HashVector[POS];

			#ifdef _DEBUG
				afxDump << HashVector[POS]->get_key() << " - " << ChainedRec->m_genTime << "\n";
			#endif

			while( ChainedRec->NextRec != NULL )
			{
				rec_count++;
				ChainedRec = ChainedRec->NextRec;

				#ifdef _DEBUG
					afxDump << ChainedRec->get_key() << " - " << ChainedRec->m_genTime << "\n";
				#endif
			}
		}
	}

	#ifdef _DEBUG
		afxDump << "--------------------------------------------------------\nEnd of Table\n\n";
	#endif

	//rec_count now == entry count
	//allocate array of char*'s
	*entry_list = new char*[rec_count+3];
	// init them to all null
	memset(*entry_list,0,(sizeof(char*) * (rec_count+3)) );

	pArr = *entry_list;

	rec_count = 0;

	// place all entry pm id's in list
	for ( long XX = 0; XX < m_vectorSize; XX++ )
	{
		if ( HashVector[XX] != NULL )
		{
			ChainedRec = HashVector[XX];
			pkey = ChainedRec->get_key();
			len = strlen(pkey);
			//len += 16;
			pArr[rec_count] = new char[len+8];
			//strcpy(pArr[rec_count],ChainedRec->m_genTime);
			strcpy(pArr[rec_count],pkey);

			rec_count++;

			while( ChainedRec->NextRec != NULL )
			{
				ChainedRec = ChainedRec->NextRec;
				pkey = ChainedRec->get_key();
				len = strlen(pkey) + 1;
				//len += 16;
				pArr[rec_count] = new char[len+8];
				//strcpy(pArr[rec_count],ChainedRec->m_genTime);
				strcpy(pArr[rec_count],pkey);
				rec_count++;
			}
		}
	}
	


}


template <class CacheRec>
long CMemCache<CacheRec>::hash_function(char *s ) const 
{ 

	long hash_val=0;

	double temp_val = 0;

	char c1;

	for (unsigned long i=0;i < strlen(s);i++)
	{		
		c1 = (char)(s[i]);

		if( (c1 >= 'A') && (c1 <= 'Z') )
		{
			c1 += ('a' - 'A');
		}

		temp_val += ( c1 * i );
	}

	/* float FibsNum =  0.6180339887499; */
	temp_val = temp_val * 0.6180339887499;
	temp_val -= (unsigned long)temp_val;
	hash_val = (long)(m_vectorSize * temp_val);

	if ( hash_val < 0 || hash_val >= m_vectorSize )
	{
		hash_val = 0;
	}

	return hash_val;
}


#endif //CACHE_CLASS_INCL

