//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================//

#ifndef ENTITYDATAINSTANTIATOR_H
#define ENTITYDATAINSTANTIATOR_H
#ifdef _WIN32
#pragma once
#endif

#include "utlhash.h"

// This is the hash key type, but it could just as easily be and int or void *
class CBaseEntity;

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
class IEntityDataInstantiator
{
public:
	virtual ~IEntityDataInstantiator() {};

	virtual void *GetDataObject( const CBaseEntity *instance ) = 0;
	virtual void *CreateDataObject( const CBaseEntity *instance ) = 0;
	virtual void DestroyDataObject( const CBaseEntity *instance ) = 0;
};

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
template <class T>
class CEntityDataInstantiator : public IEntityDataInstantiator
{
public:
	CEntityDataInstantiator() : 
		m_HashTable( 64, 0, 0, CompareFunc, KeyFunc )
	{
	}

	virtual void *GetDataObject( const CBaseEntity *instance )
	{
		UtlHashHandle_t handle; 
		HashEntry entry;
		entry.key = instance;
		handle = m_HashTable.Find( entry );

		if ( handle != m_HashTable.InvalidHandle() )
		{
			return (void *)m_HashTable[ handle ].data;
		}

		return NULL;
	}

	virtual void *CreateDataObject( const CBaseEntity *instance )
	{
		UtlHashHandle_t handle; 
		HashEntry entry;
		entry.key = instance;
		handle = m_HashTable.Find( entry );

		// Create it if not already present
		if ( handle == m_HashTable.InvalidHandle() )
		{
			handle = m_HashTable.Insert( entry );
			Assert( handle != m_HashTable.InvalidHandle() );
			m_HashTable[ handle ].data = new T;
	
			// FIXME: We'll have to remove this if any objects we instance have vtables!!!
			Q_memset( m_HashTable[ handle ].data, 0, sizeof( T ) );
		}

		return (void *)m_HashTable[ handle ].data;
	}

	virtual void DestroyDataObject( const CBaseEntity *instance )
	{
		UtlHashHandle_t handle; 
		HashEntry entry;
		entry.key = instance;
		handle = m_HashTable.Find( entry );

		if ( handle != m_HashTable.InvalidHandle() )
		{
			delete m_HashTable[ handle ].data;
			m_HashTable.Remove( handle );
		}
	}

private:

	struct HashEntry
	{
		HashEntry()
		{
			key = NULL;
			data = NULL;
		}

		const CBaseEntity *key;
		T				*data;
	};

	static bool CompareFunc( const HashEntry &src1, const HashEntry &src2 )
	{
		return ( src1.key == src2.key );
	}


	static unsigned int KeyFunc( const HashEntry &src )
	{
		// Shift right to get rid of alignment bits and border the struct on a 16 byte boundary
		return (unsigned int)src.key;
	}

	CUtlHash< HashEntry >	m_HashTable;
};

#endif // ENTITYDATAINSTANTIATOR_H
