//////////////////////////////////////////////////////////////////////////////
//
// Asura_Platform_CPUProfile.cpp
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

/******************************************************************************/
/* Included Files                                                             */
/******************************************************************************/

#include "Core/Asura.h"

#if defined ASURA_USE_PROFILING_SYSTEM

#include "Debugging/Asura_CPUProfile.h"

// Asura Platform-independent
#include "Core/Asura_LinkList.h"
#include "Core/Asura_String.h"
#include "Debugging/Asura_Assert.h"

#if defined( ASURA_USE_PROFILING_SYSTEM_MULTITHREADED)
	#include "Debugging/Asura_CPUProfile_Multithreaded_Render.h"
#endif

/******************************************************************************/
/* Functions                                                                  */
/******************************************************************************/

#if defined( ASURA_USE_PROFILING_SYSTEM_MULTITHREADED)
static CRITICAL_SECTION g_xProfileRender_CriticalSection;
static DWORD g_uProfileRender_TLSHandle = TLS_OUT_OF_INDEXES;
static Asura_LinkList< Asura_CPUProfile_Renderer_ThreadData*> g_xList_pxProfileRenderThreadData;
#endif
#if defined( ASURA_USE_PROFILING_SYSTEM_MULTITHREADED)
void Asura_CPUProfile_Renderer_ThreadData::Platform_Initialise()
{
	Asura_Assert( TLS_OUT_OF_INDEXES == g_uProfileRender_TLSHandle, "");
	
	InitializeCriticalSectionAndSpinCount( &g_xProfileRender_CriticalSection, 1000);
	
	EnterCriticalSection( &g_xProfileRender_CriticalSection);
	{
		// DLH - 22 May 2007 14:35:47 - Create Thread Local Storage
		DWORD uTLSHandle = TlsAlloc();
		if( TLS_OUT_OF_INDEXES != uTLSHandle)
		{
			g_uProfileRender_TLSHandle = uTLSHandle;
		}
	}
	LeaveCriticalSection( &g_xProfileRender_CriticalSection);
}
#endif

#if defined( ASURA_USE_PROFILING_SYSTEM_MULTITHREADED)
void Asura_CPUProfile_Renderer_ThreadData::Platform_Shutdown()
{
	EnterCriticalSection( &g_xProfileRender_CriticalSection);
	{
		// DLH - 22 May 2007 14:36:10 - Delete Thread Local Storage
		DWORD& uTLSHandle = g_uProfileRender_TLSHandle;
		if( TLS_OUT_OF_INDEXES != uTLSHandle)
		{
			TlsFree( uTLSHandle);
			uTLSHandle = TLS_OUT_OF_INDEXES;
		}
	}
	LeaveCriticalSection( &g_xProfileRender_CriticalSection);
	
	DeleteCriticalSection( &g_xProfileRender_CriticalSection);
	
	// DLH - 22 May 2007 14:54:14 - Clear the list of thread data
	for( Asura_ListElt< Asura_CPUProfile_Renderer_ThreadData*>* pxIteratorList = g_xList_pxProfileRenderThreadData.Front(); NULL != pxIteratorList; pxIteratorList = g_xList_pxProfileRenderThreadData.Front())
	{
		g_xList_pxProfileRenderThreadData.Remove( pxIteratorList);
	}
}
#endif

#if defined( ASURA_USE_PROFILING_SYSTEM_MULTITHREADED)
Asura_CPUProfile_Renderer_ThreadData* Asura_CPUProfile_Renderer_ThreadData::Platform_GetThreadData()
{
	Asura_CPUProfile_Renderer_ThreadData* pxThreadData = NULL;
	
	{
		DWORD& uTLSHandle = g_uProfileRender_TLSHandle;
		if( TLS_OUT_OF_INDEXES != uTLSHandle)
		{
			// DLH - 22 May 2007 14:43:45 - Get Thread Data
			void* pData = TlsGetValue( uTLSHandle);
			if( NULL == pData)
			{
				EnterCriticalSection( &g_xProfileRender_CriticalSection);
			
				// DLH - 22 May 2007 14:43:55 - Thread Data does not exist so create it
				pxThreadData = new Asura_CPUProfile_Renderer_ThreadData;
				pData = pxThreadData;
				
				TlsSetValue( uTLSHandle, pData);
				
				// DLH - 22 May 2007 14:54:26 - Add to the list of thread data
				g_xList_pxProfileRenderThreadData.Push_Back( pxThreadData);
			
				LeaveCriticalSection( &g_xProfileRender_CriticalSection);
			}
			else
			{
				pxThreadData = static_cast< Asura_CPUProfile_Renderer_ThreadData* >( pData);
			}
		}
	}	
	
	return( pxThreadData);
}
#endif

#if defined( ASURA_USE_PROFILING_SYSTEM_MULTITHREADED)
Asura_LinkList< Asura_CPUProfile_Renderer_ThreadData*>& Asura_CPUProfile_Renderer_ThreadData::Platform_GetAndLockThreadList()
{
	EnterCriticalSection( &g_xProfileRender_CriticalSection);

	return( g_xList_pxProfileRenderThreadData);
}
#endif

#if defined( ASURA_USE_PROFILING_SYSTEM_MULTITHREADED)
void Asura_CPUProfile_Renderer_ThreadData::Platform_ReturnAndUnlockThreadList()
{
	LeaveCriticalSection( &g_xProfileRender_CriticalSection);
}
#endif

#if defined( ASURA_USE_PROFILING_SYSTEM_MULTITHREADED)
void Asura_CPUProfile_Renderer::Platform_StartThread( const char* pszThreadName)
{
	EnterCriticalSection( &g_xProfileRender_CriticalSection);
	{
		DWORD& uTLSHandle = g_uProfileRender_TLSHandle;
		Asura_Assert( TLS_OUT_OF_INDEXES != uTLSHandle, "Multithreaded Profiling not initialised");
		if( TLS_OUT_OF_INDEXES != uTLSHandle)
		{
			// DLH - 22 May 2007 14:43:45 - Get Thread Data
			void* pData = TlsGetValue( uTLSHandle);
			Asura_Assert( NULL == pData, "Multithreaded Profiling already setup for this thread");
			if( NULL == pData)
			{
				// DLH - 22 May 2007 14:43:55 - Thread Data does not exist so create it
				Asura_CPUProfile_Renderer_ThreadData* pxThreadData = new Asura_CPUProfile_Renderer_ThreadData;
				pData = pxThreadData;
				
				if( NULL != pszThreadName)
				{
					Asura_PrintfToBfr( pxThreadData->m_szThreadName, "%s", sizeof( pxThreadData->m_szThreadName), true, pszThreadName);
				}
				
				TlsSetValue( uTLSHandle, pData);
				
				// DLH - 22 May 2007 14:54:26 - Add to the list of thread data
				g_xList_pxProfileRenderThreadData.Push_Back( pxThreadData);
			}
		}
	}	
	LeaveCriticalSection( &g_xProfileRender_CriticalSection);
}
#endif

#if defined( ASURA_USE_PROFILING_SYSTEM_MULTITHREADED)
void Asura_CPUProfile_Renderer::Platform_StopThread( void)
{
	EnterCriticalSection( &g_xProfileRender_CriticalSection);
	{
		DWORD& uTLSHandle = g_uProfileRender_TLSHandle;
		Asura_Assert( TLS_OUT_OF_INDEXES != uTLSHandle, "Multithreaded Profiling not initialised");
		if( TLS_OUT_OF_INDEXES != uTLSHandle)
		{
			// DLH - 22 May 2007 14:43:45 - Get Thread Data
			void* pData = TlsGetValue( uTLSHandle);
			Asura_Assert( NULL != pData, "Multithreaded Profiling not setup for this thread");
			if( NULL != pData)
			{
				Asura_CPUProfile_Renderer_ThreadData* pxThreadData = static_cast<Asura_CPUProfile_Renderer_ThreadData*>( pData);
				
				g_xList_pxProfileRenderThreadData.Remove( pxThreadData);
				
				TlsSetValue( uTLSHandle, NULL);
				
				delete pxThreadData;
			}
		}
	}	
	LeaveCriticalSection( &g_xProfileRender_CriticalSection);
}
#endif




#endif // defined ASURA_USE_PROFILING_SYSTEM