//*******************************************************************
//
// File: Asura_Direct3D_Carpet_Game_Render_Batch.cpp
//
// Created: 3 Feb 2006
//
// Description: 
//
//*******************************************************************

// system libraries
#include "Core/Asura.h"

#if defined( ASURA_USE_CARPET)

// main header
#include "Asura_Direct3D_Carpet_Game_Render.h"

// other libraries

// internal dependencies

//*******************************************************************
// Constants
//*******************************************************************


//*******************************************************************
// Local Classes
//*******************************************************************


//*******************************************************************
// Static Data
//*******************************************************************


//*******************************************************************
// Function Definitions
//*******************************************************************

bool Asura_Direct3D_Carpet_Game_Render::BatchGetRequiredBufferSize( const Asura_Direct3D_CGR_Mesh& xCGRMesh, const Asura_Direct3D_CGR_PolygonBatch& xCGRBatch, uint32& uIndexCount, uint32& uVertexCount)
{
	uIndexCount = 0;	
	uVertexCount = 0;	
	
	const uint32 uPolygonFinish = xCGRBatch.m_uDataOffset + xCGRBatch.m_uDataSize;
	for( uint32 uPolygonIndex = xCGRBatch.m_uDataOffset; uPolygonIndex < uPolygonFinish; uPolygonIndex += 1)
	{
		Asura_Assert( uPolygonIndex <= xCGRMesh.m_uPolygonCount, "CGRMesh Polygon Index not in range");
		const Asura_Direct3D_CGR_Polygon& xPolygon = xCGRMesh.m_pxPolygons[ uPolygonIndex];
		
		const uint32 uInstanceCount = xPolygon.m_uInstanceCount;
		uIndexCount += 6 * uInstanceCount;
		uVertexCount += 4 * uInstanceCount;
	}
	
	return( true);
}

bool Asura_Direct3D_Carpet_Game_Render::BatchCopyToBuffers( Asura_Direct3D_CGR_Mesh& xCGRMesh, const Asura_Direct3D_CGR_PolygonBatch& xCGRBatch)
{
	HRESULT xResult;
	
	const uint32 uLockSize = xCGRBatch.m_uVertexBufferSize * sizeof( Asura_Direct3D_CGR_StreamVertexSimple);
	if( 0 == uLockSize)
	{
		return( true);
	}
	
	if( NULL == xCGRMesh.m_pxBatchVertexBuffer)
	{
		CreateVertexBufferMesh( xCGRMesh);
	}
	
	Direct3D_VertexBuffer* const pxVertexBuffer = xCGRMesh.m_pxBatchVertexBuffer;
	const uint32 uLockOffset = xCGRBatch.m_uVertexBufferOffset * sizeof( Asura_Direct3D_CGR_StreamVertexSimple);
	Asura_Direct3D_CGR_StreamVertexSimple* pxVertex0 = NULL;
	xResult = pxVertexBuffer->Lock( uLockOffset, uLockSize, (void**)&pxVertex0, D3DLOCK_NOOVERWRITE);
	if( ASURA_D3D_FAILED( xResult))
	{
		return( false);
	}
	
	uint32 uVertexSpace = xCGRBatch.m_uVertexBufferSize;
	Asura_Direct3D_CGR_StreamVertexSimple* pxVertex = pxVertex0;
	const uint32 uPolygonCount = xCGRBatch.m_uDataSize;
	Asura_Direct3D_CGR_Polygon* pxCGRPolygon = &xCGRMesh.m_pxPolygons[ xCGRBatch.m_uDataOffset];
	for( uint32 uPolygonIndex = 0; uPolygonIndex < uPolygonCount; uPolygonIndex += 1, pxCGRPolygon += 1)
	{
		uint32 uVertexCount = uVertexSpace;
		
		CopyMeshPolygonToVertex( xCGRMesh, *pxCGRPolygon, pxVertex, uVertexCount);
		
		Asura_Assert( uVertexCount <= uVertexSpace, "CGR Too many vertices have been used");
		pxVertex += uVertexCount;
		uVertexSpace -= uVertexCount;
	}
	Asura_Assert( (pxVertex0 + xCGRBatch.m_uVertexBufferSize) == pxVertex, "CGR Vertex pointer anommily");
	Asura_Assert( 0 == uVertexSpace, "CGR Vertex count anommily!");
	
	xResult = pxVertexBuffer->Unlock();
	if( ASURA_D3D_FAILED( xResult))
	{
		return( false);
	}
	
	return( true);
}

bool Asura_Direct3D_Carpet_Game_Render::BatchCopyAllToBuffers()
{
	const uint32 uMeshCount = s_uMeshesCount;
	for( uint32 uMeshIndex = 0; uMeshIndex < uMeshCount; uMeshIndex += 1)
	{
		Asura_Direct3D_CGR_Mesh& xCGRMesh	= s_pxMeshes[ uMeshIndex];
		const uint32 uBatchCount = xCGRMesh.m_uPolygonBatchCount;
		for( uint32 uBatchIndex = 0; uBatchIndex < uBatchCount; uBatchIndex += 1)	
		{
			const Asura_Direct3D_CGR_PolygonBatch& xCGRBatch = xCGRMesh.m_pxPolygonBatches[ uBatchIndex];
			
			BatchCopyToBuffers( xCGRMesh, xCGRBatch);
		}
	}
	return( true);
}

bool Asura_Direct3D_Carpet_Game_Render::BatchAddToBuffers( Asura_Direct3D_CGR_Mesh& xCGRMesh, Asura_Direct3D_CGR_PolygonBatch& xCGRBatch)
{
	xCGRBatch.m_uTimeStampLastUsed = s_uTimeStampLast;
	if( 0 != xCGRBatch.m_uVertexBufferSize)
	{
		return( true);
	}
	
	// DLH - 03 February 2006 12:38:54 - Find Required Buffer Sizes
	if( false == xCGRMesh.m_xVBRangeManager.AddBatch( s_uTimeStampLast, xCGRMesh, xCGRBatch))
	{
		return( false);
	}
	
	// DLH - 03 February 2006 12:39:16 - Copy Data To Buffers

	BatchCopyToBuffers( xCGRMesh, xCGRBatch);
	
	return( true);
}

bool Asura_Direct3D_Carpet_Game_Render::BatchRemoveFromBuffers( Asura_Direct3D_CGR_Mesh& xCGRMesh, Asura_Direct3D_CGR_PolygonBatch& xCGRBatch)
{
	xCGRMesh.m_xVBRangeManager.RemoveBatch( xCGRBatch);
	
	return( true);
}

bool Asura_Direct3D_CGR_RangeManager::AddBatch( const uint32 uTimeStamp, Asura_Direct3D_CGR_Mesh& xCGRMesh, Asura_Direct3D_CGR_PolygonBatch& xCGRBatch)
{
	// DLH - 03 February 2006 12:38:54 - Find Required Buffer Sizes
	uint32 uIndexCount = 0;
	uint32 uVertexCount = 0;
	
	Asura_Direct3D_Carpet_Game_Render::BatchGetRequiredBufferSize( xCGRMesh, xCGRBatch, uIndexCount, uVertexCount);
	
	if( 0 == uVertexCount)
	{
		return( true);
	}
	
	// DLH - 03 February 2006 12:39:07 - Allocate Space
	uint32 uVertexOffset;
	
	const bool bFoundSpace = FindFreeRange( xCGRMesh, &xCGRBatch, uVertexCount, uVertexOffset);
	if( false == bFoundSpace)
	{
		DeleteOldBatches( uTimeStamp);
		
		const bool bFoundSpace = FindFreeRange( xCGRMesh, &xCGRBatch, uVertexCount, uVertexOffset);
		if( false == bFoundSpace)
		{
			Asura_Assert( false, "Failed to find room for Carpet Batch in Vertex Buffer. This will cause slown down and render problems. Increase the Size of ASURA_CGR_MESH_VB_SIZE.");
			return( false);
		}
	}
	
	xCGRBatch.m_uVertexBufferSize = uVertexCount;
	xCGRBatch.m_uVertexBufferOffset = uVertexOffset;
	xCGRBatch.m_uTimeStampLastUsed = uTimeStamp;
	
	this->m_uRangeUsedCount += xCGRBatch.m_uVertexBufferSize;
	this->m_uBlockUsedCount += 1;
	
	return( true);
}

bool Asura_Direct3D_CGR_RangeManager::RemoveBatch( Asura_Direct3D_CGR_PolygonBatch& xCGRBatch)
{
	if( 0 != xCGRBatch.m_uVertexBufferSize)
	{
		Asura_ListElt< Asura_Direct3D_CGR_PolygonBatch* >* pxIteratorBatch = this->m_xListBatches.Find( &xCGRBatch);
		Asura_Assert( NULL != pxIteratorBatch, "Missing Batch from Range Manager");
		if( NULL != pxIteratorBatch)
		{
			this->m_xListBatches.Remove( pxIteratorBatch);
		}
		
		Asura_Assert( 0 < this->m_uBlockUsedCount, "Trying to delete too many blocks");
		Asura_Assert( xCGRBatch.m_uVertexBufferSize <= this->m_uRangeUsedCount, "Trying to delete too many ranges");
		this->m_uBlockUsedCount -= 1;
		this->m_uRangeUsedCount -= xCGRBatch.m_uVertexBufferSize;
		
	}
	
	xCGRBatch.m_uVertexBufferSize = 0;
	xCGRBatch.m_uVertexBufferOffset = 0;
	
	
	return( true);
}

bool Asura_Direct3D_CGR_RangeManager::FindFreeRange( const Asura_Direct3D_CGR_Mesh& xCGRMesh, Asura_Direct3D_CGR_PolygonBatch* pxCGRBatch, const uint32 uRequiredSize, uint32& uFounfOffset)
{
	uint32 uOffsetLast = 0;
	
    for (Asura_ListElt< Asura_Direct3D_CGR_PolygonBatch*>* pxIteratorBatch = this->m_xListBatches.Front(); pxIteratorBatch; pxIteratorBatch = pxIteratorBatch->Next())
    {
		Asura_Direct3D_CGR_PolygonBatch* pxBatchSearch = pxIteratorBatch->Data();		
		const uint32 uOffsetPresent = pxBatchSearch->m_uVertexBufferOffset;
		
		const uint32 uPresentSized = uOffsetPresent - uOffsetLast;
		if( uRequiredSize <= uPresentSized)
		{
			uFounfOffset = uOffsetLast;
			this->m_xListBatches.Insert_Before( pxIteratorBatch, pxCGRBatch);
			return( true);
		}
		
		uOffsetLast = uOffsetPresent + pxBatchSearch->m_uVertexBufferSize;
	}
	
	{
		const uint32 uPresentSized = this->m_uRangeMaxCount - uOffsetLast;
		if( uRequiredSize <= uPresentSized)
		{
			uFounfOffset = uOffsetLast;
			this->m_xListBatches.Push_Back( pxCGRBatch);
			return( true);
		}
	}
	
	return( false);
}

bool Asura_Direct3D_CGR_RangeManager::DeleteOldBatches( const uint32 uTimeStampLast)
{
	uint32 const uTimeStampDelete = uTimeStampLast - 2;
	
	Asura_LinkList< Asura_Direct3D_CGR_PolygonBatch*> xListDeleteBatches;
	
	{
		for (Asura_ListElt< Asura_Direct3D_CGR_PolygonBatch*>* pxIteratorBatch = this->m_xListBatches.Front(); pxIteratorBatch; pxIteratorBatch = pxIteratorBatch->Next())
		{
			Asura_Direct3D_CGR_PolygonBatch* pxBatchSearch = pxIteratorBatch->Data();		
			
			if( 0 == pxBatchSearch->m_uVertexBufferSize)
			{
				continue;
			}
			
			const int32 uTimeStampDelta = pxBatchSearch->m_uTimeStampLastUsed - uTimeStampDelete;
			if( uTimeStampDelta < 0)
			{
				xListDeleteBatches.Push_Front( pxBatchSearch);
			}
		}
	}
	
	{
		for (Asura_ListElt< Asura_Direct3D_CGR_PolygonBatch*>* pxIteratorBatch = xListDeleteBatches.Front(); pxIteratorBatch; pxIteratorBatch = pxIteratorBatch->Next())
		{
			Asura_Direct3D_CGR_PolygonBatch* pxBatchSearch = pxIteratorBatch->Data();		
			
			RemoveBatch( *pxBatchSearch);
		}
	}
	
	return( true);
}

bool Asura_Direct3D_CGR_RangeManager::DeleteAllBatches()
{
	for (Asura_ListElt< Asura_Direct3D_CGR_PolygonBatch*>* pxIteratorBatch = this->m_xListBatches.Front(); NULL != pxIteratorBatch; pxIteratorBatch = this->m_xListBatches.Front())
	{
		Asura_Direct3D_CGR_PolygonBatch* pxBatch = pxIteratorBatch->Data();
		RemoveBatch( *pxBatch);
	}
	
	return( true);
}

#endif //defined( ASURA_USE_CARPET)

//*******************************************************************
//
// (c) 2006 Rebellion (Derby)
//
// Warning: This computer program is protected by copyright law and 
// international treaties. Unauthorized reproduction or distribution
// of this program, or any portion of it, may result in severe civil and
// criminal penalties, and will be prosecuted to the maximum extent 
// possible under the law.
//
//*******************************************************************

