//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//
#include "cbase.h"
#include "interface.h"
#include "vphysics/object_hash.h"
#include "vphysics/collision_set.h"
#include "tier1/tier1.h"
#include "ivu_vhash.hxx"



#if defined(_WIN32) && !defined(_X360)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif	// _WIN32 && !_X360

#include "vphysics_interfaceV30.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

static void ivu_string_print_function( const char *str )
{
	Msg("%s", str);
}

#if defined(_WIN32) && !defined(_XBOX)
//HMODULE	gPhysicsDLLHandle;

#pragma warning (disable:4100)

BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
{
 	if ( fdwReason == DLL_PROCESS_ATTACH )
	{
//		ivp_set_message_print_function( ivu_string_print_function );

		MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
		// store out module handle
		//gPhysicsDLLHandle = (HMODULE)hinstDLL;
	}
	else if ( fdwReason == DLL_PROCESS_DETACH )
	{
	}
	return TRUE;
}

#endif		// _WIN32

#ifdef POSIX
void __attribute__ ((constructor)) vphysics_init(void);
void vphysics_init(void)
{
//	ivp_set_message_print_function( ivu_string_print_function );

	MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
}
#endif


// simple 32x32 bit array
class CPhysicsCollisionSet : public IPhysicsCollisionSet
{
public:
	~CPhysicsCollisionSet() {}
	CPhysicsCollisionSet()
	{
		memset( m_bits, 0, sizeof(m_bits) );
	}
	void EnableCollisions( int index0, int index1 )
	{
		Assert(index0<32&&index1<32);
		m_bits[index0] |= 1<<index1;
		m_bits[index1] |= 1<<index0;
	}
	void DisableCollisions( int index0, int index1 )
	{
		Assert(index0<32&&index1<32);
		m_bits[index0] &= ~(1<<index1);
		m_bits[index1] &= ~(1<<index0);
	}

	bool ShouldCollide( int index0, int index1 )
	{
		Assert(index0<32&&index1<32);
		return (m_bits[index0] & (1<<index1)) ? true : false;
	}
private:
	unsigned int m_bits[32];
};


//-----------------------------------------------------------------------------
// Main physics interface
//-----------------------------------------------------------------------------
class CPhysicsInterface : public CTier1AppSystem<IPhysics>
{
public:
	CPhysicsInterface() : m_pCollisionSetHash(NULL) {}
	virtual void *QueryInterface( const char *pInterfaceName );
	virtual	IPhysicsEnvironment *CreateEnvironment( void );
	virtual void DestroyEnvironment( IPhysicsEnvironment *pEnvironment );
	virtual IPhysicsEnvironment *GetActiveEnvironmentByIndex( int index );
	virtual IPhysicsObjectPairHash *CreateObjectPairHash();
	virtual void DestroyObjectPairHash( IPhysicsObjectPairHash *pHash );
	virtual IPhysicsCollisionSet *FindOrCreateCollisionSet( unsigned int id, int maxElementCount );
	virtual IPhysicsCollisionSet *FindCollisionSet( unsigned int id );
	virtual void DestroyAllCollisionSets();

private:
	CUtlVector<IPhysicsEnvironment *>	m_envList;
	CUtlVector<CPhysicsCollisionSet>	m_collisionSets;
	IVP_VHash_Store						*m_pCollisionSetHash;
};


//-----------------------------------------------------------------------------
// Expose singleton
//-----------------------------------------------------------------------------
static CPhysicsInterface g_MainDLLInterface;
IPhysics *g_PhysicsInternal = &g_MainDLLInterface;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CPhysicsInterface, IPhysics, VPHYSICS_INTERFACE_VERSION, g_MainDLLInterface );


//-----------------------------------------------------------------------------
// Query interface
//-----------------------------------------------------------------------------
void *CPhysicsInterface::QueryInterface( const char *pInterfaceName )
{
	// Loading the datacache DLL mounts *all* interfaces
	// This includes the backward-compatible interfaces + other vphysics interfaces
	CreateInterfaceFn factory = Sys_GetFactoryThis();	// This silly construction is necessary
	return factory( pInterfaceName, NULL );				// to prevent the LTCG compiler from crashing.
}


//-----------------------------------------------------------------------------
// Implementation of IPhysics
//-----------------------------------------------------------------------------
IPhysicsEnvironment *CPhysicsInterface::CreateEnvironment( void )
{
	IPhysicsEnvironment *pEnvironment = CreatePhysicsEnvironment();
	m_envList.AddToTail( pEnvironment );
	return pEnvironment;
}

void CPhysicsInterface::DestroyEnvironment( IPhysicsEnvironment *pEnvironment )
{
	m_envList.FindAndRemove( pEnvironment );
	delete pEnvironment;
}

IPhysicsEnvironment	*CPhysicsInterface::GetActiveEnvironmentByIndex( int index )
{
	if ( index < 0 || index >= m_envList.Count() )
		return NULL;

	return m_envList[index];
}

IPhysicsObjectPairHash *CPhysicsInterface::CreateObjectPairHash()
{
	return ::CreateObjectPairHash();
}

void CPhysicsInterface::DestroyObjectPairHash( IPhysicsObjectPairHash *pHash )
{
	delete pHash;
}
// holds a cache of these by id.
// NOTE: This is stuffed into vphysics.dll as a sneaky way of sharing the memory between
// client and server in single player.  So you can't have different client/server rules.
IPhysicsCollisionSet *CPhysicsInterface::FindOrCreateCollisionSet( unsigned int id, int maxElementCount )
{
	if ( !m_pCollisionSetHash )
	{
		m_pCollisionSetHash = new IVP_VHash_Store(256);
	}
	Assert( id != 0 );
	Assert( maxElementCount <= 32 );
	if ( maxElementCount > 32 )
		return NULL;

	IPhysicsCollisionSet *pSet = FindCollisionSet( id );
	if ( pSet )
		return pSet;
	intp index = m_collisionSets.AddToTail();
	m_pCollisionSetHash->add_elem( (void *)(intp)id, (void *)(intp)(index+1) );
	return &m_collisionSets[index];
}

IPhysicsCollisionSet *CPhysicsInterface::FindCollisionSet( unsigned int id )
{
	if ( m_pCollisionSetHash )
	{
		intp index = (intp)m_pCollisionSetHash->find_elem( (void *)(intp)id );
		if ( index > 0 )
		{
			Assert( index <= m_collisionSets.Count() );
			if ( index <= m_collisionSets.Count() )
			{
				return &m_collisionSets[index-1];
			}
		}
	}
	return NULL;
}

void CPhysicsInterface::DestroyAllCollisionSets()
{
	m_collisionSets.Purge();
	delete m_pCollisionSetHash;
	m_pCollisionSetHash = NULL;
}

	

// In release build, each of these libraries must contain a symbol that indicates it is also a release build
// You MUST disable this in order to run a release vphysics.dll with a debug library.
// This should not usually be necessary
// #if !defined(_DEBUG) && defined(_WIN32)
// extern int ivp_physics_lib_is_a_release_build;
// extern int ivp_compactbuilder_lib_is_a_release_build;
// extern int hk_base_lib_is_a_release_build;
// extern int hk_math_lib_is_a_release_build;
// extern int havana_constraints_lib_is_a_release_build;

// void DebugTestFunction()
// {
// 	ivp_physics_lib_is_a_release_build = 0;
// 	ivp_compactbuilder_lib_is_a_release_build = 0;
// 	hk_base_lib_is_a_release_build = 0;
// 	hk_math_lib_is_a_release_build = 0;
// 	havana_constraints_lib_is_a_release_build = 0;
// }
// #endif