//*******************************************************************
//
// File: Asura_DIPCache.cpp         
//
// Created: 7 Apr 2006
//
// Description: 
//
//*******************************************************************

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

// main header
#include "Rendering/Asura_DIPCache.h"

// other libraries

// internal dependencies
#include "Core/Asura_Maths.h"

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


//*******************************************************************
// Local Classes
//*******************************************************************
//*******************************************************************
// Function Definitions
//*******************************************************************

Asura_DIPCache::Asura_DIPCache() :
	m_pfnCallback( NULL),
	m_uCallbackData( 0)
{
	this->m_uBufferMaxCount = 0;
	this->m_uBufferUsedCount = 0;
}

Asura_DIPCache::Asura_DIPCache( const Render_Callback pfnCallback, const uint32 uCallbackData) :
	m_pfnCallback( pfnCallback),
	m_uCallbackData( uCallbackData)
{
	this->m_uBufferMaxCount = 0;
	this->m_uBufferUsedCount = 0;
}

bool Asura_DIPCache::SetCallback( const Render_Callback pfnCallback, const uint32 uCallbackData)
{
	this->m_pfnCallback = pfnCallback;
	this->m_uCallbackData = uCallbackData;
	return( true);
}

bool Asura_DIPCache::AddBufferPointers( Asura_DIPCache_Batch* pxBuffers, Asura_DIPCache_Batch** ppxBuffers, const uint32 uBufferSize)
{
	this->m_pxBufferStart = NULL;
	this->m_pxBatchBuffers = pxBuffers;
	this->m_ppxBatchBuffers = ppxBuffers;
	this->m_uBufferMaxCount = uBufferSize;
	
	return( true);
}

void Asura_DIPCache::Clear()
{
	this->m_uBufferUsedCount = 0;
	this->m_pxBufferStart = NULL;
}

Asura_DIPCache_Batch* Asura_DIPCache::GetNextBatch()
{
	if( this->m_uBufferMaxCount <= this->m_uBufferUsedCount)
	{
		Asura_Assertf( false, ("DIP Cache Buffer is not big enough. Present size of %d Batches", this->m_uBufferMaxCount));
		return( NULL);
	}

	Asura_DIPCache_Batch* pxBatch = &this->m_pxBatchBuffers[ this->m_uBufferUsedCount];
	this->m_uBufferUsedCount += 1;
	
	return( pxBatch);
}

bool Asura_DIPCache::AddDIP( const Asura_DIPCache_DIPData& xData)
{
	if( xData.m_uIndexBufferStop == xData.m_uIndexBufferStart)
	{
		return( true);
	}
	Asura_DIPCache_Batch** ppxBatchInsert = &this->m_pxBufferStart;
	
	for( ; NULL != *ppxBatchInsert; )
	{
		bool bFound = false;
		Asura_DIPCache_Batch& xBatch = **ppxBatchInsert;
		
		// DLH - 06 February 2006 17:06:13 - Add to Lower side
		if( xData.m_uVertexBufferOffset < xBatch.m_xData.m_uVertexBufferOffset)
		{
			ppxBatchInsert = &(xBatch.m_pxLower);
			continue;
		}
		if( xData.m_uIndexBufferStop < xBatch.m_xData.m_uIndexBufferStart)
		{
			ppxBatchInsert = &(xBatch.m_pxLower);
			continue;
		}
		
		// DLH - 06 February 2006 17:06:25 - Add to Higher side
		if( xBatch.m_xData.m_uVertexBufferOffset < xData.m_uVertexBufferOffset)
		{
			ppxBatchInsert = &(xBatch.m_pxHigher);
			continue;
		}
		if( xBatch.m_xData.m_uIndexBufferStop < xData.m_uIndexBufferStart)
		{
			ppxBatchInsert = &(xBatch.m_pxHigher);
			continue;
		}
		
		if( xBatch.m_xData.m_uIndexBufferStart == xData.m_uIndexBufferStop)
		{
			xBatch.m_xData.m_uIndexBufferStart = xData.m_uIndexBufferStart;
			bFound = true;
		}
		else
		{
			if( xData.m_uIndexBufferStart == xBatch.m_xData.m_uIndexBufferStop)
			{
				xBatch.m_xData.m_uIndexBufferStop = xData.m_uIndexBufferStop;
				bFound = true;
			}
		}
		
		if( true == bFound)
		{
			Asura_Assert( xBatch.m_xData.m_uVertexBufferOffset == xData.m_uVertexBufferOffset, "Cannot merge these to Batches as the offsets are different");
			
			xBatch.m_xData.m_uVertexBufferStart = Asura_Maths::Min( xBatch.m_xData.m_uVertexBufferStart, xData.m_uVertexBufferStart);
			xBatch.m_xData.m_uVertexBufferStop = Asura_Maths::Max( xBatch.m_xData.m_uVertexBufferStop, xData.m_uVertexBufferStop);
			return( true);
		}
		
		Asura_Assert( false, "Asura_DIPCache overlapping draws, this is not supported");
		return( false);
	}
	
	{
		Asura_DIPCache_Batch* pxBatch = this->GetNextBatch();
		if( NULL == pxBatch)
		{
			return( false);
		}
		
		pxBatch->m_xData = xData;
		pxBatch->m_pxLower = NULL;
		pxBatch->m_pxHigher = NULL;
		
		*(ppxBatchInsert) = pxBatch;
	}
	
	return( true);
}

bool Asura_DIPCache::Render()
{
	Asura_Assert( NULL != this->m_pfnCallback, "The callback function pointer is not setup");
	
	Asura_DIPCache_DIPData xDIPData;
	
	xDIPData.m_uIndexBufferStart = 0;
	xDIPData.m_uIndexBufferStop = 0;
	xDIPData.m_uVertexBufferOffset = 0;
	xDIPData.m_uVertexBufferStart = 0;
	xDIPData.m_uVertexBufferStop = 0;
	
	uint32 uBufferSize = 1;
	this->m_ppxBatchBuffers[ 0] = this->m_pxBufferStart;
	
	while( 0 < uBufferSize)
	{
		const uint32 uBufferIndex = uBufferSize - 1;
		Asura_DIPCache_Batch* pxBatch = this->m_ppxBatchBuffers[ uBufferIndex];
		
		if( NULL == pxBatch)
		{
			uBufferSize -= 1;
			continue;
		}
	
		if( NULL != pxBatch->m_pxLower)
		{		
			Asura_Assert( uBufferSize < this->m_uBufferMaxCount, "DIPCache has over flown");
			this->m_ppxBatchBuffers[ uBufferSize] = pxBatch->m_pxLower;
			pxBatch->m_pxLower = NULL;
			uBufferSize += 1;
			continue;
		}
		
		bool bExtend = false;
		
		if( xDIPData.m_uVertexBufferOffset == pxBatch->m_xData.m_uVertexBufferOffset)
		{
			if( xDIPData.m_uIndexBufferStop == pxBatch->m_xData.m_uIndexBufferStart)
			{
				xDIPData.m_uIndexBufferStop = pxBatch->m_xData.m_uIndexBufferStop;
				bExtend = true;
			}
			else
			{
				if( pxBatch->m_xData.m_uIndexBufferStop == xDIPData.m_uIndexBufferStart)
				{
					xDIPData.m_uIndexBufferStart = pxBatch->m_xData.m_uIndexBufferStart;
					bExtend = true;
				}
			}
		}
		if( true == bExtend)
		{
			xDIPData.m_uVertexBufferStart = Asura_Maths::Min( xDIPData.m_uVertexBufferStart, pxBatch->m_xData.m_uVertexBufferStart);
			xDIPData.m_uVertexBufferStop = Asura_Maths::Max( xDIPData.m_uVertexBufferStop, pxBatch->m_xData.m_uVertexBufferStop);
		}
		else
		{
			if( xDIPData.m_uIndexBufferStart != xDIPData.m_uIndexBufferStop)
			{
				this->m_pfnCallback( xDIPData, this->m_uCallbackData);
			}
			xDIPData = pxBatch->m_xData;
		}
	
		uBufferSize -= 1;
		
		if( NULL != pxBatch->m_pxHigher)
		{		
			Asura_Assert( uBufferSize < this->m_uBufferMaxCount, "DIPCache has over flown");
			this->m_ppxBatchBuffers[ uBufferSize] = pxBatch->m_pxHigher;
			pxBatch->m_pxHigher = NULL;
			uBufferSize += 1;
			continue;
		}
	}
	
	if( xDIPData.m_uIndexBufferStart != xDIPData.m_uIndexBufferStop)
	{
		this->m_pfnCallback( xDIPData, this->m_uCallbackData);
	}

	return( true);
}

bool Asura_DIPCache::IsEmpty() const
{
	return( (NULL == this->m_pxBufferStart) || (this->m_pxBufferStart->m_xData.m_uIndexBufferStart == this->m_pxBufferStart->m_xData.m_uIndexBufferStop));
}



//*******************************************************************
//
// (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.
//
//*******************************************************************

