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

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

#include "Core/Asura.h"

#if defined ASURA_USE_VISIBILITY_SYSTEM

#include "Visibility/Asura_Visibility_System.h"

// Asura Xbox 360
#include "Rendering/Asura_Xbox360_EnvironmentRenderer.h"
#include "Rendering/Asura_Xbox360_Graphics.h"

// Misc
#include <xtl.h>

/******************************************************************************/
/* Classes                                                                    */
/******************************************************************************/

class Asura_Xbox360_OcclusionQuery
{
public:

	// Constructor / destructor
	Asura_Xbox360_OcclusionQuery();
	~Asura_Xbox360_OcclusionQuery();

	// Functions
	void Start();
	void End();
	int GetResult( const bool bForceFlush );

	// Static Functions
	static Asura_Xbox360_OcclusionQuery* Create( const u_int uNodeIndex );

	// Data
	Direct3D_Query* m_pxQuery;
	u_int m_uNodeIndex;
	Asura_Xbox360_OcclusionQuery* m_pxNext;
};

/******************************************************************************/
/* Global Variables                                                           */
/******************************************************************************/

static Asura_Xbox360_OcclusionQuery* g_pxActiveQueries = NULL;
static Asura_Xbox360_OcclusionQuery* g_pxEndOfQueue = NULL;
static u_int g_uNumberOfActiveQueries = 0;

/******************************************************************************/
/* Asura_Xbox360_OcclusionQuery Class Functions                               */
/******************************************************************************/

Asura_Xbox360_OcclusionQuery::Asura_Xbox360_OcclusionQuery()
	: m_pxQuery( NULL )
	, m_uNodeIndex(0)
	, m_pxNext( NULL ) 
{
}

Asura_Xbox360_OcclusionQuery::~Asura_Xbox360_OcclusionQuery()
{
	m_pxQuery->Release();
}

void Asura_Xbox360_OcclusionQuery::Start()
{
	m_pxQuery->Issue( D3DISSUE_BEGIN );
}

void Asura_Xbox360_OcclusionQuery::End()
{
	m_pxQuery->Issue( D3DISSUE_END );
}

int Asura_Xbox360_OcclusionQuery::GetResult( const bool bForceFlush )
{
	int iPixelsDrawn = -1;

	const HRESULT xRes = m_pxQuery->GetData( &iPixelsDrawn, sizeof( DWORD ), bForceFlush ? D3DGETDATA_FLUSH : 0 );

	return xRes == S_FALSE ? -1
		 : xRes == S_OK ? iPixelsDrawn
		 : 0;
}

/******************************************************************************/
/* Asura_Xbox360_OcclusionQuery Static Class Functions                        */
/******************************************************************************/

Asura_Xbox360_OcclusionQuery* Asura_Xbox360_OcclusionQuery::Create( const u_int uNodeIndex )
{
	Direct3D_Query* pxQuery = NULL;
	Asura_Xbox360_Graphics::GetDevice()->CreateQueryTiled( D3DQUERYTYPE_OCCLUSION, 4, &pxQuery );

	if( pxQuery )
	{
		Asura_Xbox360_OcclusionQuery* const pxOccQuery = new Asura_Xbox360_OcclusionQuery;

		pxOccQuery->m_pxQuery = pxQuery;
		pxOccQuery->m_uNodeIndex = uNodeIndex;
		pxOccQuery->m_pxNext = NULL;

		return pxOccQuery;
	}
	else
	{
		return NULL;
	}
}

/******************************************************************************/
/* Asura_Visibility_System Static Class Functions                             */
/******************************************************************************/

void Asura_Visibility_System::Platform_RenderStripZOnly( u_int uStripID )
{
	Asura_Xbox360_EnvironmentRenderer::RenderStripZOnly( uStripID );
}

void Asura_Visibility_System::Platform_IssueOcclusionQuery( u_int uNodeIndex, const Asura_Bounding_Box& xBB )
{
	Asura_Xbox360_OcclusionQuery* const pxQuery = Asura_Xbox360_OcclusionQuery::Create( uNodeIndex );
    if( !pxQuery ) return;

	pxQuery->Start();
	Asura_Xbox360_EnvironmentRenderer::RenderAABB( xBB );
	pxQuery->End();

	g_pxEndOfQueue = g_pxActiveQueries;
	if( g_pxEndOfQueue )
	{
		while( g_pxEndOfQueue->m_pxNext )
		{
			g_pxEndOfQueue = g_pxEndOfQueue->m_pxNext;
		}

		g_pxEndOfQueue->m_pxNext = pxQuery;
		g_pxEndOfQueue = pxQuery;
	}
	else
	{
		g_pxActiveQueries = pxQuery;
	}

	++g_uNumberOfActiveQueries;
}

void Asura_Visibility_System::Platform_IssueOcclusionQuery( u_int uNodeIndex, u_int uStripID )
{
	Asura_Xbox360_OcclusionQuery* const pxQuery = Asura_Xbox360_OcclusionQuery::Create( uNodeIndex );
    if( !pxQuery ) return;

	pxQuery->Start();
	Asura_Xbox360_EnvironmentRenderer::RenderStripZOnly( uStripID );
	pxQuery->End();

	g_pxEndOfQueue = g_pxActiveQueries;
	if( g_pxEndOfQueue )
	{
		while( g_pxEndOfQueue->m_pxNext )
		{
			g_pxEndOfQueue = g_pxEndOfQueue->m_pxNext;
		}

		g_pxEndOfQueue->m_pxNext = pxQuery;
		g_pxEndOfQueue = pxQuery;
	}
	else
	{
		g_pxActiveQueries = pxQuery;
	}

	++g_uNumberOfActiveQueries;
}

u_int Asura_Visibility_System::Platform_GetNumberOfOutstandingOcclusionQueries()
{
	return g_uNumberOfActiveQueries;
}

bool Asura_Visibility_System::Platform_GetOcclusionResults( u_int& uNodeIndex, bool bForceFlush )
{
	Asura_Xbox360_OcclusionQuery** ppxActiveQueries = &g_pxActiveQueries;

	while( *ppxActiveQueries )
	{
		Asura_Xbox360_OcclusionQuery* pxThisQuery = *ppxActiveQueries;

		const int iReturn = pxThisQuery->GetResult( bForceFlush );

		if( iReturn >= 0 )
		{
			bForceFlush = false;
			uNodeIndex = pxThisQuery->m_uNodeIndex;
			*ppxActiveQueries = pxThisQuery->m_pxNext;

			delete pxThisQuery;

			--g_uNumberOfActiveQueries;

			if( iReturn > 0 ) return true;
		}

		if( *ppxActiveQueries )
		{
			ppxActiveQueries = &(*ppxActiveQueries)->m_pxNext;
		}
	}

	return false;
}

#endif // defined ASURA_USE_VISIBILITY_SYSTEM