/*!****************************************************************************

 @file         PVRTHash.h
 @copyright    Copyright (c) Imagination Technologies Limited.
 @brief        A simple hash class which uses TEA to hash a string or given data
               into a 32-bit unsigned int.

******************************************************************************/

#ifndef PVRTHASH_H
#define PVRTHASH_H

#include "PVRTString.h"
#include "PVRTGlobal.h"

/*!****************************************************************************
 @class         CPVRTHash
 @brief         A simple hash class which uses TEA to hash a string or other given 
                data into a 32-bit unsigned int.
******************************************************************************/
class CPVRTHash
{
public:
	/*!***************************************************************************
	@brief      	Constructor
	*****************************************************************************/
	CPVRTHash() : m_uiHash(0) {}

	/*!***************************************************************************
	@brief      	Copy Constructor
	@param[in]		rhs         CPVRTHash to copy.
	*****************************************************************************/
	CPVRTHash(const CPVRTHash& rhs) : m_uiHash(rhs.m_uiHash) {}

	/*!***************************************************************************
	@brief      	Overloaded constructor
	@param[in]		String      CPVRTString to create the CPVRTHash with.
	*****************************************************************************/
	CPVRTHash(const CPVRTString& String) : m_uiHash(0)
	{
		if(String.length() > 0)		// Empty string. Don't set.
		{
			m_uiHash = MakeHash(String);
		}
	}

	/*!***************************************************************************
	@brief      	Overloaded constructor
	@param[in]		c_pszString String to create the CPVRTHash with.
	*****************************************************************************/
	CPVRTHash(const char* c_pszString) : m_uiHash(0)
	{
		_ASSERT(c_pszString);
		if(c_pszString[0] != 0)		// Empty string. Don't set.
		{
			m_uiHash = MakeHash(c_pszString);	
		}
	}

	/*!***************************************************************************
	@brief      	Overloaded constructor
	@param[in]		pData
	@param[in]		dataSize
	@param[in]		dataCount
	*****************************************************************************/
	CPVRTHash(const void* pData, unsigned int dataSize, unsigned int dataCount) : m_uiHash(0)
	{
		_ASSERT(pData);
		_ASSERT(dataSize > 0);

		if(dataCount > 0)
		{
			m_uiHash = MakeHash(pData, dataSize, dataCount);
		}
	}

	/*!***************************************************************************
	@brief      	Overloaded assignment.
	@param[in]		rhs
	@return			CPVRTHash &	
	*****************************************************************************/
	CPVRTHash& operator=(const CPVRTHash& rhs)
	{
		if(this != &rhs)
		{
			m_uiHash = rhs.m_uiHash;
		}

		return *this;
	}

	/*!***************************************************************************
	@brief      	Converts to unsigned int.
	@return			int	
	*****************************************************************************/
	operator unsigned int() const
	{
		return m_uiHash;
	}

	/*!***************************************************************************
	@brief      	Generates a hash from a CPVRTString.
	@param[in]		String
	@return			The hash.
	*****************************************************************************/
	static CPVRTHash MakeHash(const CPVRTString& String)
	{
		if(String.length() > 0)
			return MakeHash(String.c_str(), sizeof(char), (unsigned int) String.length());

		return CPVRTHash();
	}

	/*!***************************************************************************
	@brief      	Generates a hash from a null terminated char array.
	@param[in]		c_pszString
	@return         The hash.
	*****************************************************************************/
	static CPVRTHash MakeHash(const char* c_pszString)
	{
		_ASSERT(c_pszString);

		if(c_pszString[0] == 0)
			return CPVRTHash();

		const char* pCursor = c_pszString;
		while(*pCursor) pCursor++;
		return MakeHash(c_pszString, sizeof(char), (unsigned int) (pCursor - c_pszString));
	}
		
	/*!***************************************************************************
	@brief      	Generates a hash from generic data. This function uses the
					32-bit Fowler/Noll/Vo algorithm which trades efficiency for
					slightly increased risk of collisions. This algorithm is
					public domain. More information can be found at:
					http://www.isthe.com/chongo/tech/comp/fnv/.
	@param[in]		pData
	@param[in]		dataSize
	@param[in]		dataCount
	@return			unsigned int			The hash.
	*****************************************************************************/
	static CPVRTHash MakeHash(const void* pData, unsigned int dataSize, unsigned int dataCount)
	{
		_ASSERT(pData);
		_ASSERT(dataSize > 0);

#define FNV_PRIME		16777619U
#define FNV_OFFSETBIAS	2166136261U

		if(dataCount == 0)
			return CPVRTHash();

		CPVRTHash pvrHash;
		unsigned char* p = (unsigned char*)pData;
		pvrHash.m_uiHash = FNV_OFFSETBIAS;
		for(unsigned int i = 0; i < dataSize * dataCount; ++i)
		{
			pvrHash.m_uiHash = (pvrHash.m_uiHash * FNV_PRIME) ^ p[i];
		}
		
		return pvrHash;
	}

private:
	unsigned int		m_uiHash;		/// The hashed data.
};

#endif