/*
===========================================================================
Copyright (C) 2000 - 2013, Raven Software, Inc.
Copyright (C) 2001 - 2013, Activision, Inc.
Copyright (C) 2013 - 2015, OpenJK contributors

This file is part of the OpenJK source code.

OpenJK is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

////////////////////////////////////////////////////////////////////////////////////////
// RAVEN STANDARD TEMPLATE LIBRARY
//  (c) 2002 Activision
//
//
// Hash Pool
// ---------
// The hash pool stores raw data of variable size.  It uses a hash table to check for
// redundant data, and upon finding any, will return the existing handle.  Otherwise
// it copies the data to memory and returns a new handle.
//
//
// NOTES:
//
//
//
////////////////////////////////////////////////////////////////////////////////////////
#if !defined(RATL_HASH_POOL_VS_INC)
#define RATL_HASH_POOL_VS_INC


////////////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////////////
#if !defined(RATL_COMMON_INC)
	#include "ratl_common.h"
#endif
namespace ratl
{





////////////////////////////////////////////////////////////////////////////////////////
// The Hash Pool
////////////////////////////////////////////////////////////////////////////////////////
template <int SIZE, int SIZE_HANDLES>
class	hash_pool
{
	int		mHandles[SIZE_HANDLES];					// each handle holds the start index of it's data

	int		mDataAlloc;								// where the next chunck of data will go
	char	mData[SIZE];

	#ifdef _DEBUG
	int		mFinds;									// counts how many total finds have run
	int		mCurrentCollisions;						// counts how many collisions on the last find
	int		mTotalCollisions;						// counts the total number of collisions
	int		mTotalAllocs;
	#endif


	////////////////////////////////////////////////////////////////////////////////////
	// This function searches for a handle which already stores the data (assuming the
	// handle is a hash within range SIZE_HANDLES).
	//
	// If it failes, it returns false, and the handle passed in points to the next
	// free slot.
	////////////////////////////////////////////////////////////////////////////////////
	bool		find_existing(int& handle, const void* data, int datasize)
	{
		#ifdef _DEBUG
		mFinds++;
		mCurrentCollisions = 0;
		#endif

		while (mHandles[handle])					// So long as a handle exists there
		{
			if (mem::eql((void*)(&mData[mHandles[handle]]), data, datasize))
			{
				return true;						// found
			}
			handle=(handle+1)&(SIZE_HANDLES-1);		// incriment the handle

			#ifdef _DEBUG
			mCurrentCollisions ++;
			mTotalCollisions ++;

			//assert(mCurrentCollisions < 16);		// If We Had 16+ Collisions, Hash May Be Inefficient.
													// Evaluate SIZE and SIZEHANDLES
			#endif
		}
		return false;								// failed to find
	}



	////////////////////////////////////////////////////////////////////////////////////
	// A simple hash function for the range of [0, SIZE_HANDLES]
	////////////////////////////////////////////////////////////////////////////////////
	int			hash(const void* data, int datasize)
	{
		int	 h=0;
		for (int i=0; i<datasize; i++)
		{
			h += ((const char*)(data))[i] * (i + 119);		// 119.  Prime Number?
		}
		h &= SIZE_HANDLES - 1;						// zero out bits beyoned SIZE_HANDLES
		return h;
	}

public:
	hash_pool()
	{
		clear();
	}

    ////////////////////////////////////////////////////////////////////////////////////
	// The Number Of Bytes Allocated
    ////////////////////////////////////////////////////////////////////////////////////
	int			size()	 const
	{
		return mDataAlloc;
	}

    ////////////////////////////////////////////////////////////////////////////////////
	// Check To See If This Memory Pool Is Empty
    ////////////////////////////////////////////////////////////////////////////////////
	bool		empty()	 const
	{
		return (mDataAlloc==1);
	}

    ////////////////////////////////////////////////////////////////////////////////////
	// Check To See If This Memory Pool Has Enough Space Left For (minimum) Bytes
    ////////////////////////////////////////////////////////////////////////////////////
	bool		full(int minimum)	 const
	{
		return ((SIZE - mDataAlloc)<minimum);
	}

    ////////////////////////////////////////////////////////////////////////////////////
	// Clear - Removes all allocation information - Note!  DOES NOT CLEAR MEMORY
    ////////////////////////////////////////////////////////////////////////////////////
	void		clear()
	{
		mData[0] = 0;
		mDataAlloc = 1;
		for (int i=0; i<SIZE_HANDLES; i++)
		{
			mHandles[i] = 0;
		}

		#ifdef _DEBUG
		mFinds = 0;
		mCurrentCollisions = 0;
		mTotalCollisions = 0;
		mTotalAllocs = 0;
		#endif
	}

	////////////////////////////////////////////////////////////////////////////////////
	// This is the primary functionality of the hash pool.  It will search for existing
	// data of the same size, and failing to find any, it will append the data to the
	// memory.
	//
	// In both cases, it gives you a handle to look up the data later.
	////////////////////////////////////////////////////////////////////////////////////
	int			get_handle(const void* data, int datasize)
	{
		int	handle = hash(data, datasize);				// Initialize Our Handle By Hash Fcn
		if (!find_existing(handle, data, datasize))
		{
			assert(mDataAlloc+datasize < SIZE);			// Is There Enough Memory?

			#ifdef _DEBUG
			mTotalAllocs++;
			#endif

			mem::cpy((void*)(&mData[mDataAlloc]), data, datasize);// Copy Data To Memory
			mHandles[handle] = mDataAlloc;				// Mark Memory In Hash Tbl
			mDataAlloc += datasize;						// Adjust Next Alloc Location
		}
		return handle;									// Return The Hash Tbl handleess
	}

	////////////////////////////////////////////////////////////////////////////////////
	// Constant Access Operator
	////////////////////////////////////////////////////////////////////////////////////
	const void*	operator[](int handle) const
	{
		assert(handle>=0 && handle<SIZE_HANDLES);

		return &(mData[mHandles[handle]]);
	}


#ifdef _DEBUG
	float		average_collisions()	{return ((float)mTotalCollisions / (float)mFinds);}
	int			total_allocs()			{return mTotalAllocs;}
	int			total_finds()			{return mFinds;}
	int			total_collisions()		{return mTotalCollisions;}
#endif

};


}
#endif