///////////////////////////////////////////////////////////////////////////////
/// @file VertexBuffer.cpp
///
/// @brief Class for vertex buffers
///
/// @author Lightning, Assassin
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Aug 2001
/// @author Lightning
/// @remarks Creation
///
/// @date Oct 2001
/// @author Lightning
/// @remarks Complete rewrite to do vertex shaders
///
/// @date Apr 2002
/// @author Assassin
/// @remarks Vertex buffer mods
///
///////////////////////////////////////////////////////////////////////////////

#include "deVBuffer_priv.hpp"
#include "deWorld_Helper.hpp"

#define ALLOW_RESIZE (deTRUE)

IdeVertexBuffer* IdeVertexBuffer_CreateVBuffer()
{
	return new deVertexBuffer;
}

IdeVertexBuffer* IdeVertexBuffer_CreateSubVBuffer(IdeVertexBuffer * Composite)
{
	if (!Composite)
		return NULL;
	if (Composite->GetVBufferType() != IdeVertexBuffer::BUFFERTYPE_REAL)
		return NULL;
	return new deSubVertexBuffer((deVertexBuffer*)Composite);
}

long IdeVertexBuffer_EntrySize(IdeVertexBuffer::BufferType Type)
{
	long Size;

	//return the size a buffer is
	IdeVertexBuffer_FillBufferDataType(Type, &Size);
	return Size;
}

IdeVertexBuffer::BufferDataType IdeVertexBuffer_FillBufferDataType(IdeVertexBuffer::BufferType Type, long *Size)
{
	long								BufferDataSize = 0;
	IdeVertexBuffer::BufferDataType		BufferData = IdeVertexBuffer::BUFFER_DATAFORCE_32BIT;

	//figure out the type and size for the buffer entry
	
	//a function to return the type a buffer is, and if specified, the size
	switch(Type)
	{
		case IdeVertexBuffer::BUFFER_POSITION:
		case IdeVertexBuffer::BUFFER_NORMAL:
			BufferData = IdeVertexBuffer::BUFFER_DATAFLOAT3;
			BufferDataSize = sizeof(deVertex);
			break;

		case IdeVertexBuffer::BUFFER_DIFFUSE:
		case IdeVertexBuffer::BUFFER_SPECULAR:
		case IdeVertexBuffer::BUFFER_INDICES32:
			BufferData = IdeVertexBuffer::BUFFER_DATALONG;
			BufferDataSize = sizeof(long);
			break;

		case IdeVertexBuffer::BUFFER_TEXTURECOORDS0:
		case IdeVertexBuffer::BUFFER_TEXTURECOORDS1:
		case IdeVertexBuffer::BUFFER_TEXTURECOORDS2:
		case IdeVertexBuffer::BUFFER_TEXTURECOORDS3:
		case IdeVertexBuffer::BUFFER_TEXTURECOORDS4:
		case IdeVertexBuffer::BUFFER_TEXTURECOORDS5:
		case IdeVertexBuffer::BUFFER_TEXTURECOORDS6:
		case IdeVertexBuffer::BUFFER_TEXTURECOORDS7:
			BufferData = IdeVertexBuffer::BUFFER_DATAFLOAT2;
			BufferDataSize = sizeof(float) * 2;
			break;

		case IdeVertexBuffer::BUFFER_INDICES16:
			BufferData = IdeVertexBuffer::BUFFER_DATASHORT;
			BufferDataSize = sizeof(short);
			break;
	};

	//if they want a size returned, return it
	if(Size)
		*Size = BufferDataSize;

	//return the type
	return BufferData;
}

deVertexBuffer::deVertexBuffer()
{
	//make sure everything is blank
	m_Locked = 0;
	m_VertexCount = 0;
	m_IndexCount = 0;

	m_TriCount = 0;
	m_RenderType = RENDER_POINTLIST;
	m_DynamicVertex = deFALSE;
	m_DynamicIndex = deFALSE;

	//blank the buffer pointers out
	memset(m_Buffer, 0, sizeof(m_Buffer));
	memset(m_WantsBuffer, 0, sizeof(m_WantsBuffer));
	m_DataBuffer = NULL;

	// sub-vbuffer variables
	m_UnusedVertices = 0;
	m_UnusedIndices = 0;
	m_HasChildren = deFALSE;

	return;
}

deVertexBuffer::~deVertexBuffer()
{
	long				Counter;
	DriverBufferEntry *	Entry;
	DriverBufferEntry *	Entry2;
	void *				ptr;
	
	//search thru the list of driver-buffers and tell em to get lost
	ptr = m_DriverBuffer.GetNext(NULL, Entry2);
	while(ptr)
	{
		Entry = Entry2;

		//next
		ptr = m_DriverBuffer.GetNext(ptr, Entry2);

		//if found, exit
		if(Entry->Driver && Entry->Buffer)
		{
			Entry->Driver->RemoveVertexBuffer(this);
		}
	}
	
	//free any memory we were using
	for(Counter=0; Counter < BUFFER_COUNT; Counter++)
	{
		//if the buffer exists and we own it, free it
		if(m_Buffer[Counter])
		{
			free(m_Buffer[Counter]);
			m_Buffer[Counter] = NULL;
		}
	}

	if (m_DataBuffer)
		m_DataBuffer->Release();

	if (m_Locked)
		DEBUGMSG0("~deVertexBuffer : destruction of locked vertex buffer");

	//clear everything
	m_TriCount = 0;
	m_Locked = 1;
	m_VertexCount = 0;
	m_IndexCount = 0;

}

deVertexBuffer::deVertexBuffer(const IdeVertexBuffer &S)
{
	if (S.GetVBufferType() == BUFFERTYPE_REAL)
	{
		CopyInternalData(S);
	}
	else
	{
		DEBUGMSG0("bad buffer type");
		throw;
	}
}

const IdeVertexBuffer& deVertexBuffer::operator=(const IdeVertexBuffer &S)
{
	if (S.GetVBufferType() == BUFFERTYPE_REAL)
	{
		CopyInternalData((deVertexBuffer&)S);
	}
	else
	{
		// for now we'll throw an exception, 
		// should allow sub-buffer to be assigned to real buffers eventually
		throw;
	}
	return *this;
}

void* deVertexBuffer::GetBuffer(BufferType Type) const
{
	//if we aren't locked, fail
	if(!m_Locked)
		DEBUGRETURN(NULL, "GetBuffer: Not Locked");

	//a pointer to the buffer
	return m_Buffer[Type];
}

long deVertexBuffer::GetVertexCount() const
{
	//how many entries exist
	if (m_DataBuffer)
		return m_DataBuffer->GetVertexCount();
	if (m_HasChildren)
		return m_VertexCount - m_UnusedVertices;
	return m_VertexCount;
}

long deVertexBuffer::GetIndexCount() const
{
	//return the number of indices
	if (m_HasChildren)
		return m_IndexCount - m_UnusedIndices;
	return m_IndexCount;
}

long deVertexBuffer::GetVertexStart() const
{
	return 0;
}

long deVertexBuffer::GetIndexStart() const
{
	return 0;
}

long deVertexBuffer::GetTriCount() const
{
	//how many tris exist
	return m_TriCount;
}

deBoolean deVertexBuffer::CalcTriCount()
{
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "CalcTriCount: Not Locked");

	long UseCount;

	m_TriCount = 0;	// in case something goes awry
	if(m_Buffer[BUFFER_INDICES16] || m_Buffer[BUFFER_INDICES32])
	{
		UseCount = m_IndexCount;
	}
	else
	{
		UseCount = m_VertexCount;
	}

	switch(m_RenderType)
	{
		case RENDER_POINTLIST:
			m_TriCount = UseCount;
			break;

		case RENDER_LINELIST:
			m_TriCount = UseCount / 2;
			break;

		case RENDER_LINESTRIP:
			m_TriCount = UseCount - 1;
			break;

		case RENDER_TRIANGLELIST:
			//3 points make a triangle
			m_TriCount = UseCount / 3;
			break;

		case RENDER_TRIANGLESTRIP:
		case RENDER_TRIANGLEFAN:
			//pointcount - 2 = triangle count when triangles use the same points
			m_TriCount = UseCount - 2;
			break;

	default:
		m_TriCount = 0;	//shouldn't hit this but just incase
		break;
	}
	return deTRUE;
}

deBoolean deVertexBuffer::GetDynamicStatusVertex() const
{
	return m_DynamicVertex;
}

deBoolean deVertexBuffer::GetDynamicStatusIndex() const
{
	return m_DynamicIndex;
}

void deVertexBuffer::SetDynamicStatus(deBoolean Vertex, deBoolean Index)
{
	m_DynamicVertex = Vertex;
	m_DynamicIndex = Index;
}

deVertexBuffer::RenderType deVertexBuffer::GetRenderType() const
{
	//return the render type
	return m_RenderType;
}

deBoolean deVertexBuffer::SetRenderType(RenderType Type)
{
	//set the type of rendering to do

	//if we aren't locked, fail
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "SetRenderType: Not Locked");

	//make sure it is valid
	if(Type < RENDER_POINTLIST || Type >= RENDER_COUNT)
		DEBUGRETURN(deFALSE, "SetRenderType: Invalid Type");

	//set the value
	m_RenderType = Type;

	CalcTriCount();

	//say we passed
	return deTRUE;
}

deBoolean deVertexBuffer::SetBufferSize(long EntryCount, deBoolean IndicesNotVertices)
{
	//resize an existing buffer and possibly copy new data to it

	if (!IndicesNotVertices && m_DataBuffer)
		DEBUGRETURN(deFALSE, "SetBufferSize: Cannot set buffer size when using a DataBuffer");
	
	long	BufferEntrySize;
	void *	TempPtr;
	long *	LocalCount;
	long StartEntry, StopEntry, Iterator, SizeChange;

	//if we aren't locked, fail
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "SetBufferSize: Not Locked");

	//make sure the entry count is valid
	if(EntryCount <= 0)
		return deFALSE;
	
	if (IndicesNotVertices)
	{
		LocalCount = &m_IndexCount;
		StartEntry = BUFFER_INDICES16;
		StopEntry = BUFFER_INDICES32;
		SizeChange = EntryCount - *LocalCount;
		m_UnusedIndices += SizeChange;
	}
	else
	{
		LocalCount = &m_VertexCount;
		StartEntry = BUFFER_POSITION;
		StopEntry = BUFFER_TEXTURECOORDS7;
		SizeChange = EntryCount - *LocalCount;
		m_UnusedVertices += SizeChange;
	}

	if (EntryCount == *LocalCount)
		return deTRUE;


	for (Iterator = StartEntry; Iterator <= StopEntry; Iterator++)
	{
		//figure out the size of a new point
		BufferEntrySize = IdeVertexBuffer_EntrySize((BufferType)Iterator);
		if(!BufferEntrySize)
			return deFALSE;

		//reallocate the memory only if we already have a buffer
		if (m_WantsBuffer[Iterator])
		{
			TempPtr = realloc(m_Buffer[Iterator], EntryCount * BufferEntrySize);
			if(!TempPtr)
				return deFALSE;
			m_Buffer[Iterator] = TempPtr;

			//update the driver buffer list
			DirtyBuffer((BufferType)Iterator);
		}
	}

	*LocalCount = EntryCount;
	CalcTriCount();

	//say everything is fine
	return deTRUE;
}

deBoolean deVertexBuffer::CreateBuffer(BufferType Type, deBoolean Clear)
{
	if (m_DataBuffer && Type != BUFFER_INDICES16 && Type != BUFFER_INDICES32)
		DEBUGRETURN(deFALSE, "CreateBuffer: Cannot create buffer when using a DataBuffer");
	
	long	BufferEntrySize;
	long	EntryCount;

	//create a buffer of data

	//make sure we know of the type
	if(Type >= BUFFER_COUNT || Type < BUFFER_POSITION)
		DEBUGRETURN(deFALSE, "CreateBuffer: Bad Type");

	//if we aren't locked, fail
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "CreateBuffer: Not Locked");
	
	//if the buffer already exists, just return
	if(m_Buffer[Type])
		return deTRUE;

	// fail if we are using a different index format already
	if (Type == BUFFER_INDICES32 && m_Buffer[BUFFER_INDICES16])
		DEBUGRETURN(deFALSE, "CreateBuffer: Conflicting index format");
	if (Type == BUFFER_INDICES16 && m_Buffer[BUFFER_INDICES32])
		DEBUGRETURN(deFALSE, "CreateBuffer: Conflicting index format");
	
	// grab the right count
	if (Type == BUFFER_INDICES32 || Type == BUFFER_INDICES16)
		EntryCount = m_IndexCount;
	else
		EntryCount = m_VertexCount;

	// record that we want some entries of this type if we have any
	m_WantsBuffer[Type] = deTRUE;

	//if no entries, we have recorded that we want some of this type
	if(!EntryCount)
		return deTRUE;

	//figure out the size of the buffer
	BufferEntrySize = IdeVertexBuffer_EntrySize(Type);
	if(!BufferEntrySize)
		return deFALSE;

	//allocate some memory for the buffer
	m_Buffer[Type] = malloc(EntryCount * BufferEntrySize);
	if(!m_Buffer[Type])
		return deFALSE;
	
	//if we should clear the buffer's memory, do it
	if(Clear)
		memset(m_Buffer[Type], 0, EntryCount * BufferEntrySize);

	//update the driver buffer list
	DirtyBuffer(Type);

	//return
	return deTRUE;
}

deBoolean deVertexBuffer::FillBuffer(BufferType Type, const void *Buffer, long StartEntry, long EntryCount)
{
	if (m_DataBuffer && Type != BUFFER_INDICES16 && Type != BUFFER_INDICES32)
		DEBUGRETURN(deFALSE, "CreateBuffer: Cannot fill buffer when using a DataBuffer");
	
	//fill a buffer with data
	long	NumElements;
	long	EntrySize;

	//make sure we know of the type
	if(Type >= BUFFER_COUNT || Type < BUFFER_POSITION)
		DEBUGRETURN(deFALSE, "FillBuffer: Bad Type");
	
	//if we aren't locked, fail
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "FillBuffer: Not Locked");
	
	if (Type == BUFFER_INDICES32 || Type == BUFFER_INDICES16)
		NumElements = m_IndexCount;
	else
		NumElements = m_VertexCount;

	//make sure there is enough room in the buffer
	if(StartEntry + EntryCount > NumElements)
		DEBUGRETURN(deFALSE, "FillBuffer: Buffer not big enough");
	
	//make sure the buffers  are valid
	if(!m_Buffer[Type] || !Buffer || EntryCount == 0)
		DEBUGRETURN(deFALSE, "FillBuffer: Buffer not valid");
	
	EntrySize = IdeVertexBuffer_EntrySize(Type);
	//copy it
	memcpy((void *)((long)m_Buffer[Type] + (StartEntry * EntrySize)), Buffer, EntryCount * EntrySize);

	//update the driver buffer list
	DirtyBuffer(Type);
	
	return deTRUE;
}

deBoolean deVertexBuffer::DestroyBuffer(BufferType Type)
{
	if (m_DataBuffer && Type != BUFFER_INDICES16 && Type != BUFFER_INDICES32)
		DEBUGRETURN(deFALSE, "CreateBuffer: Cannot destroy buffer when using a DataBuffer");

	//free the buffer we own
	
	//make sure we know of the type
	if(Type >= BUFFER_COUNT || Type < BUFFER_POSITION)
		DEBUGRETURN(deFALSE, "DestroyBuffer: Bad Type");
	
	//make sure they aren't destroying a destroyed buffer or a buffer we don't own
	if(!m_Buffer[Type])
		return deTRUE;

	//free the buffer
	free(m_Buffer[Type]);

	//reset all values for it
	m_Buffer[Type] = NULL;
	m_WantsBuffer[Type] = deFALSE;

	//update the driver buffer list
	DirtyBuffer(Type);
	
	return deTRUE;
}

deBoolean deVertexBuffer::Lock()
{
	//lock the buffer so only 1 person can access it
	if(m_Locked)
		DEBUGRETURN(deFALSE, "Lock: Already Locked");

	m_Locked = ~m_Locked;
	return deTRUE;	
}

deBoolean deVertexBuffer::Unlock()
{
	//a way so only 1 person will access us at a time for modifcations
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "Unlock: Not Locked");

	m_Locked = ~m_Locked;
	return deTRUE;
}

deBoolean deVertexBuffer::SetDriverBuffer(long ID, void *Buffer, IdeDriver * Driver)
{
	DriverBufferEntry *		Entry;
	void *					ptr;

	//gotta make sure we're locked for modifying
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "SetDriverBuffer: Not Locked");

	//search thru the list for the value, if non, insert it, otherwise, update
	//note, if the buffer value is NULL, wipe it
	ptr = m_DriverBuffer.GetNext(NULL, Entry);
	while(ptr)
	{
		//if found, exit
		if(Entry->ID == ID)
			break;

		//next
		ptr = m_DriverBuffer.GetNext(ptr, Entry);
	}

	if(!Entry)
	{
		//no entry, add it
		DriverBufferEntry NewEntry;
		NewEntry.ID = ID;
		NewEntry.Buffer = Buffer;
		NewEntry.UpdateList = 0;
		NewEntry.Driver = Driver;
		if (!m_DriverBuffer.AddElement(NewEntry))
			return deFALSE;
		return deTRUE;
	}

	//if the buffer is null, delete it, otherwise, update it
	if(!Buffer)
		return m_DriverBuffer.RemoveElement(ptr);
	else
	{
		Entry->Buffer = Buffer;
		Entry->UpdateList = 0;
	}

	return deTRUE;
}

void *deVertexBuffer::GetDriverBuffer(long ID, long *BufferUpdateList) const
{
	DriverBufferEntry *		Entry;
	void *					ptr;

	//return the driver buffer set

	//search thru the list for the value
	ptr = m_DriverBuffer.GetNext(NULL, Entry);
	while(ptr)
	{
		//if found, exit
		if(Entry->ID == ID)
			break;

		//next
		ptr = m_DriverBuffer.GetNext(ptr, Entry);
	}

	//if no entry, return NULL
	if(!Entry)
		return NULL;

	//return the stored buffer if they want it
	if(BufferUpdateList)
		*BufferUpdateList = Entry->UpdateList;

	return Entry->Buffer;
}

void deVertexBuffer::DirtyBuffer(BufferType Type)
{
	DirtyBuffer(Type, NULL);
}

void deVertexBuffer::DirtyBuffer(BufferType Type, deSubVertexBuffer* SubVBCaller)
{
	deSubVertexBuffer *		pSubVB;
	DriverBufferEntry *		Entry;
	long					NewFlag;
	void *					ptr;
	
	//update the driver buffers to say a buffer changed
	
	//figure out the bit we are to set
	NewFlag = 1 << (long)Type;
	
	//go thru the list, updating each entry
	ptr = m_DriverBuffer.GetNext(NULL, Entry);
	while(ptr)
	{
		//update the bit
		Entry->UpdateList = Entry->UpdateList | NewFlag;
		
		//next
		ptr = m_DriverBuffer.GetNext(ptr, Entry);
	}
	
	ptr = m_ChildBuffers.GetNext(NULL, pSubVB);
	while(ptr)
	{
		//update the child
		if (pSubVB != SubVBCaller)
			pSubVB->DirtyBuffer(Type);
		
		//next
		ptr = m_DriverBuffer.GetNext(ptr, Entry);
	}
	
	//exit
	return;
}

deBoolean deVertexBuffer::BindDataBuffer(IdeVertexBuffer* DataBuffer)
{
	if (!DataBuffer)
		DEBUGRETURN(deFALSE, "BindDataBuffer: Parameter 'DataBuffer' was NULL");
	
	//gotta make sure we're locked for modifying
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "BindDataBuffer: Not Locked");

	if (m_DataBuffer)
		m_DataBuffer->Release();
	
	m_DataBuffer = DataBuffer;
	if (m_DataBuffer)
	{
		m_DataBuffer->Claim();
		DEBUG_ONLY({
			BufferType count;
			for (count = BUFFER_POSITION; count < BUFFER_INDICES16; ((long&)count)++)
			{
				if (m_WantsBuffer[count])
					DEBUGMSG1("Binding Data Buffer when data already present. stream ID: %d", count);
			}
		})
	}
	
	return deTRUE;
}

IdeVertexBuffer* deVertexBuffer::GetBoundDataBuffer() const
{
	return m_DataBuffer;
}

deBoolean deVertexBuffer::BufferExists(BufferType Type) const
{
	if(Type >= BUFFER_COUNT || Type < BUFFER_POSITION)
		DEBUGRETURN(deFALSE, "BufferExists: Bad Type");

	if (m_DataBuffer && Type != BUFFER_INDICES16 && Type != BUFFER_INDICES32)
		return m_DataBuffer->BufferExists(Type);

	if(m_WantsBuffer[Type])
		return deTRUE;

	return deFALSE;
}

deVertexBuffer::BufferDataType deVertexBuffer::GetBufferDataType(BufferType Type, long *Size) const
{
	return IdeVertexBuffer_FillBufferDataType(Type, Size);
}

IdeVertexBuffer::VBufferType deVertexBuffer::GetVBufferType() const
{
	return BUFFERTYPE_REAL;
}

IdeVertexBuffer* deVertexBuffer::GetParentBuffer() const
{
	return NULL;
}

void deVertexBuffer::CopyInternalData(const deVertexBuffer &SourceVertexBuffer)
{
	long	Counter;
	long	BufferSize;

	//copy constructor

	//set some stuff
	m_Locked = 0;
	m_TriCount = SourceVertexBuffer.m_TriCount;
	m_RenderType = SourceVertexBuffer.m_RenderType;

	//create the dynamic list for driver buffers
	m_DriverBuffer.EmptyList();

	//copy the sizes of the buffers
	m_VertexCount = SourceVertexBuffer.m_VertexCount;
	m_IndexCount = SourceVertexBuffer.m_IndexCount;

	//set all entries in the buffer to NULL
	memset(m_Buffer, 0, sizeof(m_Buffer));
	
	//go thru the buffers, if one exists, copy it
	for(Counter = 0; Counter < BUFFER_COUNT; Counter++)
	{
		if(SourceVertexBuffer.m_Buffer[Counter])
		{
			//allocate the memory
			if (Counter < BUFFER_INDICES16)
				BufferSize = m_VertexCount * IdeVertexBuffer_EntrySize((BufferType)Counter);
			else
				BufferSize = m_IndexCount * IdeVertexBuffer_EntrySize((BufferType)Counter);
			m_Buffer[Counter] = malloc(BufferSize);
			if(!m_Buffer)
				return;

			//copy the data
			memcpy(m_Buffer[Counter], SourceVertexBuffer.m_Buffer[Counter], BufferSize);
		}
	}

	return;
}

deBoolean deVertexBuffer::AddSubVBuffer(deSubVertexBuffer * SubVB)
{
	void * Entry = m_ChildBuffers.AddElement(SubVB);
	if (!Entry)
		DEBUGRETURN(deFALSE, "AddSubVBuffer: Error adding SubVB");
	SubVB->SetListEntry(Entry);
	SubVB->SetVertexEntry(NULL);
	SubVB->SetIndexEntry(NULL);
	m_HasChildren = deTRUE;
	return deTRUE;
}

deBoolean deVertexBuffer::RemoveSubVBuffer(deSubVertexBuffer * SubVB)
{
	deSubVertexBuffer * temp = NULL;

	void * Entry;

	Entry = SubVB->GetIndexEntry();
	if (!m_IndexUsers.RemoveElement(Entry))
		return deFALSE;

	Entry = SubVB->GetVertexEntry();
	if (!m_VertexUsers.RemoveElement(Entry))
		return deFALSE;

	Entry = SubVB->GetListEntry();
	m_ChildBuffers.GetData(Entry, temp);
	if (temp == SubVB)
	{
		if (m_ChildBuffers.RemoveElement(Entry))
		{
			if (m_ChildBuffers.Length() == 0)
			{
				m_HasChildren = deFALSE;
			}
			return deTRUE;
		}
		DEBUGRETURN(deFALSE, "RemoveSubVBuffer: could not find sub-VB");
	}

	return deFALSE;
}

// next largest power of two
static inline unsigned int
nlpo2(register unsigned int x)
{
	x |= (x >> 1);
	x |= (x >> 2);
	x |= (x >> 4);
	x |= (x >> 8);
	x |= (x >> 16);
	return(x+1);
}

deBoolean deVertexBuffer::AllocateVertexSpace(deSubVertexBuffer* SubVB, long Length, long &StartVertex, deBoolean resize)
{
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "AllocateVertexSpace: Not Locked");

	if (!SubVB)
		return deFALSE;
	
	// grab the entry from the sub-VB and remove it from our list
	void * Entry = SubVB->GetVertexEntry();
	if (!m_VertexUsers.RemoveElement(Entry))
		return deFALSE;
	
	// the order of these methods is meant to be from least time-intensive to most
	
	// if it's de-allocating space, just remove it
	if (Length == 0)
	{
		SubVB->SetVertexEntry(NULL);
		StartVertex = -1;
		return deTRUE;
	}
	
	// if there are no more sub-vb's inside this, we know how many unused we have
	if (m_VertexUsers.Length() == 0)
	{
		m_UnusedVertices = m_VertexCount;
	}
	
	// check the end first, as that requires O(1) time
	if (Length <= m_UnusedVertices)
	{
		Entry = m_VertexUsers.AddElement(SubVB);
		if (!Entry)
			return deFALSE;
		SubVB->SetVertexEntry(Entry);
		
		StartVertex = m_VertexCount - m_UnusedVertices;
		m_UnusedVertices -= Length;
		return deTRUE;
	}

	// now we'll search for an empty spot between already-added buffers
	deSubVertexBuffer * CurrVB;
	void * ptr;
	int count, start, end, prevmax = 0;
	int totalused = 0;

	// start at the beginning, since the end is already checked
	ptr = m_VertexUsers.GetNext(NULL, CurrVB);
	while (ptr)
	{
		start = CurrVB->GetVertexStart();
		count = CurrVB->GetVertexCount();
		end = start + count;

		// if there's space between the start of this one and the end of the previous
		if ((start - prevmax) > Length)
		{
			// add between these two
			Entry = m_VertexUsers.AddElementBefore(SubVB, ptr);
			if (!Entry)
				return deFALSE;
			SubVB->SetVertexEntry(Entry);
			
			StartVertex = prevmax;
			return deTRUE;
		}

		// record where the last one finished
		prevmax = end;
		totalused += count;

		ptr = m_VertexUsers.GetNext(ptr, CurrVB);
	}

	// prevmax by now should be set to whatever the highest end was
	if (m_VertexCount - prevmax > Length)
	{
		Entry = m_VertexUsers.AddElement(SubVB);
		if (!Entry)
			return deFALSE;
		SubVB->SetVertexEntry(Entry);
		
		m_UnusedVertices = m_VertexCount - (prevmax + Length);
		StartVertex = prevmax;
		return deTRUE;
	}

	// if we're allowed to resize we can resize the buffer and tack this new stuff on the end
	if (resize)
	{
		// do some funky math to round to a power of 2
		count = m_VertexCount - m_UnusedVertices;
		unsigned int x = nlpo2(count + Length);
		if (x > 32768)
			x = ((count + Length) | 32767) + 1; // don't over-allocate too much on big buffers, save some memory
		if (SetBufferSize(x, deFALSE))
		{
			// add to the end
			Entry = m_VertexUsers.AddElement(SubVB);
			if (!Entry)
				return deFALSE;
			SubVB->SetVertexEntry(Entry);
			
			m_UnusedVertices -= Length;
			StartVertex = count;
			return deTRUE;
		}
		return deFALSE;
	}

	// only get here if we aren't allowed to resize
	// since we've kept track of amt of space total, we can try juggling stuff
	// this involves moving all the data for all the things til we find a spot
	if ((m_UnusedVertices + totalused - m_VertexCount) > Length)
	{
	}

	// by this point, nothing has worked
	return deFALSE;
}

deBoolean deVertexBuffer::AllocateIndexSpace(deSubVertexBuffer* SubVB, long Length, long &StartIndex, deBoolean resize)
{
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "AllocateIndexSpace: Not Locked");

	if (!SubVB)
		return deFALSE;

	// grab the entry from the sub-VB and remove it from our list
	void * Entry = SubVB->GetIndexEntry();
	if (!m_IndexUsers.RemoveElement(Entry))
		return deFALSE;
	
	// the order of these methods is meant to be from least time-intensive to most

	// if it's de-allocating space, just remove it
	if (Length == 0)
	{
		SubVB->SetIndexEntry(NULL);
		StartIndex = -1;
		return deTRUE;
	}

	// if there are no more sub-vb's inside this, we know how many unused we have
	if (m_IndexUsers.Length() == 0)
	{
		m_UnusedIndices = m_IndexCount;
	}
	
	// check the end first, as that requires O(1) time
	if (Length <= m_UnusedIndices)
	{
		// add to the end
		Entry = m_IndexUsers.AddElement(SubVB);
		if (!Entry)
			return deFALSE;
		SubVB->SetIndexEntry(Entry);
		
		StartIndex = m_IndexCount - m_UnusedIndices;
		m_UnusedIndices -= Length;
		return deTRUE;
	}

	// now we'll search for an empty spot between already-added buffers
	deSubVertexBuffer *CurrVB;
	void *ptr;
	int count, start, end, prevmax = 0;
	int totalused = 0;

	// start at the beginning, since the end is already checked
	ptr = m_IndexUsers.GetNext(NULL, CurrVB);
	while (ptr)
	{
		start = CurrVB->GetIndexStart();
		count = CurrVB->GetIndexCount();
		end = start + count;

		// if there's space between the start of this one and the end of the previous
		if ((start - prevmax) >= Length)
		{
			// add between these two
			Entry = m_IndexUsers.AddElementBefore(SubVB, ptr);
			if (!Entry)
				return deFALSE;
			SubVB->SetIndexEntry(Entry);

			StartIndex = prevmax;
			return deTRUE;
		}

		// record where the last one finished
		prevmax = end;
		totalused += count;

		ptr = m_IndexUsers.GetNext(ptr, CurrVB);
	}

	// prevmax by now should be set to whatever the highest end was
	if (m_IndexCount - prevmax > Length)
	{
		Entry = m_IndexUsers.AddElement(SubVB);
		if (!Entry)
			return deFALSE;
		SubVB->SetIndexEntry(Entry);
		
		m_UnusedIndices = m_IndexCount - (prevmax + Length);
		StartIndex = prevmax;
		return deTRUE;
	}

	// if we're allowed to resize we can resize the buffer and tack this new stuff on the end
	if (resize)
	{
		// do some funky math to round to a power of 2
		count = m_IndexCount - m_UnusedIndices;
		unsigned int x = nlpo2(count+Length);
		if (SetBufferSize(x, deTRUE))
		{
			// add to the end
			Entry = m_IndexUsers.AddElement(SubVB);
			if (!Entry)
				return deFALSE;
			SubVB->SetIndexEntry(Entry);
			
			m_UnusedIndices -= Length;
			StartIndex = count;
			return deTRUE;
		}
		return deFALSE;
	}

	// only get here if we aren't allowed to resize
	// since we've kept track of amt of space total, we can try juggling stuff
	// this involves moving all the data for all the things til we find a spot
	if ((m_IndexCount - (m_UnusedIndices + totalused)) > Length)
	{
	}
	
	// by this point, nothing has worked
	return deFALSE;
}


//=================================================================================
//
// deSubVertexBuffer
//
//=================================================================================


deSubVertexBuffer::deSubVertexBuffer(deVertexBuffer * Composite)
{
	if (!Composite || Composite->GetVBufferType() != BUFFERTYPE_REAL)
		throw;
	m_CompositeBuffer = Composite;
	m_CompositeVertexStart = -1;
	m_CompositeIndexStart = -1;

	m_Locked = 0;
	m_VertexCount = 0;
	m_IndexCount = 0;
	m_TriCount = 0;
	m_RenderType = RENDER_POINTLIST;
	m_ListEntry = NULL;
	m_IndexEntry = NULL;
	m_VertexEntry = NULL;
	m_DataBuffer = NULL;

//	BufferType BufferCount;
//	for (BufferCount = BUFFER_POSITION; BufferCount < BUFFER_COUNT; ((int&)(BufferCount))++)
//	{
//		m_WantsBuffer[BufferCount] = m_CompositeBuffer->BufferExists(BufferCount);
//	}

	m_CompositeBuffer->Claim();

	m_CompositeBuffer->AddSubVBuffer(this);
}

deSubVertexBuffer::~deSubVertexBuffer()
{
	DriverBufferEntry *	Entry;
	DriverBufferEntry *	Entry2;
	void *				ptr;
	
	//search thru the list of driver-buffers and tell em to get lost
	ptr = m_DriverBuffer.GetNext(NULL, Entry2);
	while(ptr)
	{
		Entry = Entry2;
		
		//next
		ptr = m_DriverBuffer.GetNext(ptr, Entry2);
		
		//if found, exit
		if(Entry->Driver && Entry->Buffer)
		{
			Entry->Driver->RemoveVertexBuffer(this);
		}
	}

	if (m_CompositeBuffer)
	{
		m_CompositeBuffer->RemoveSubVBuffer(this);
		m_CompositeBuffer->Release();
	}
	if (m_DataBuffer)
		m_DataBuffer->Release();

	if (m_Locked)
		DEBUGMSG0("~deSubVertexBuffer : destruction of locked vertex buffer");
}

deSubVertexBuffer::deSubVertexBuffer(const IdeVertexBuffer &S)
{
	if ((&S) != NULL || (&S) == NULL)
		throw "no copies!";
}

const IdeVertexBuffer& deSubVertexBuffer::operator=(const IdeVertexBuffer &S)
{
	if ((&S) != NULL || (&S) == NULL)
		throw "no copies!";
	return *this;
}

IdeVertexBuffer::VBufferType deSubVertexBuffer::GetVBufferType() const
{
	return BUFFERTYPE_SUB;
}

void* deSubVertexBuffer::GetBuffer(BufferType Type) const
{
	if(!m_Locked)
		DEBUGRETURN(NULL, "GetBuffer: Not Locked");

	char * ptr;
	long Offset;

	if (Type == BUFFER_INDICES16 || Type == BUFFER_INDICES32)
		Offset = m_CompositeIndexStart;
	else
		Offset = m_CompositeVertexStart;

	Offset *= IdeVertexBuffer_EntrySize(Type);

	if (!m_CompositeBuffer->Lock())
		DEBUGRETURN(NULL, "deSubVertexBuffer::GetBuffer - could not lock composite");
	ptr = (char*)m_CompositeBuffer->GetBuffer(Type);
	m_CompositeBuffer->Unlock();
	if (ptr == NULL)
		return NULL;
	ptr += Offset;

	return ptr;
}

long deSubVertexBuffer::GetVertexCount() const
{
	return m_VertexCount;
}

long deSubVertexBuffer::GetIndexCount() const
{
	return m_IndexCount;
}

long deSubVertexBuffer::GetVertexStart() const
{
	return m_CompositeVertexStart;
}

long deSubVertexBuffer::GetIndexStart() const
{
	return m_CompositeIndexStart;
}

long deSubVertexBuffer::GetTriCount() const
{
	return m_TriCount;
}

deBoolean deSubVertexBuffer::CalcTriCount()
{
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "CalcTriCount: Not Locked");

	long UseCount;

	m_TriCount = 0;	// in case something goes awry
	if (m_CompositeBuffer->BufferExists(BUFFER_INDICES16) || 
		m_CompositeBuffer->BufferExists(BUFFER_INDICES32))
	{
		UseCount = m_IndexCount;
	}
	else
	{
		UseCount = m_VertexCount;
	}

	switch(m_RenderType)
	{
		case RENDER_POINTLIST:
			m_TriCount = UseCount;
			break;

		case RENDER_LINELIST:
			m_TriCount = UseCount / 2;
			break;

		case RENDER_LINESTRIP:
			m_TriCount = UseCount - 1;
			break;

		case RENDER_TRIANGLELIST:
			//3 points make a triangle
			m_TriCount = UseCount / 3;
			break;

		case RENDER_TRIANGLESTRIP:
		case RENDER_TRIANGLEFAN:
			//pointcount - 2 = triangle count when triangles use the same points
			m_TriCount = UseCount - 2;
			break;

	default:
		m_TriCount = 0;	//shouldn't hit this but just incase
		break;
	}
	return deTRUE;
}

deBoolean deSubVertexBuffer::GetDynamicStatusVertex() const
{
	if (!m_CompositeBuffer)
		DEBUGRETURN(deFALSE, "GetDynamicStatusVertex: No Composite");
	return m_CompositeBuffer->GetDynamicStatusVertex();
}

deBoolean deSubVertexBuffer::GetDynamicStatusIndex() const
{
	if (!m_CompositeBuffer)
		DEBUGRETURN(deFALSE, "GetDynamicStatusIndex: No Composite");
	return m_CompositeBuffer->GetDynamicStatusIndex();
}

void deSubVertexBuffer::SetDynamicStatus(deBoolean Vertex, deBoolean Index)
{
	// don't do nothing here, it's for the real-buffer only
	Vertex = Index = deFALSE;
}

IdeVertexBuffer::RenderType deSubVertexBuffer::GetRenderType() const
{
	return m_RenderType;
}

deBoolean deSubVertexBuffer::SetRenderType(RenderType Type)
{
	//set the type of rendering to do

	//if we aren't locked, fail
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "SetRenderType: Not Locked");

	//make sure it is valid
	if(Type < RENDER_POINTLIST || Type >= RENDER_COUNT)
		DEBUGRETURN(deFALSE, "SetRenderType: Bad Type");

	//set the value
	m_RenderType = Type;

	CalcTriCount();

	//say we passed
	return deTRUE;
}

deBoolean deSubVertexBuffer::SetBufferSize(long EntryCount, deBoolean IndicesNotVertices)
{
	if (!IndicesNotVertices && m_DataBuffer)
		DEBUGRETURN(deFALSE, "SetBufferSize: Cannot set buffer size when using a DataBuffer");
	
	// this is the big-un
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "SetBufferSize: Not Locked");

	if (!m_CompositeBuffer)
		DEBUGRETURN(deFALSE, "SetRenderType: No Composite");
	
	deBoolean Result;

	m_CompositeBuffer->Lock();
	if (IndicesNotVertices)
	{
		Result = m_CompositeBuffer->AllocateIndexSpace(this, EntryCount, m_CompositeIndexStart, ALLOW_RESIZE);
		if (Result)
			m_IndexCount = EntryCount;
	}
	else
	{
		Result = m_CompositeBuffer->AllocateVertexSpace(this, EntryCount, m_CompositeVertexStart, ALLOW_RESIZE);
		if (Result)
			m_VertexCount = EntryCount;
	}
	m_CompositeBuffer->Unlock();
	
	CalcTriCount();

	return Result;
}

deBoolean deSubVertexBuffer::CreateBuffer(BufferType Type, deBoolean Clear)
{
	if (m_DataBuffer && Type != BUFFER_INDICES16 && Type != BUFFER_INDICES32)
		DEBUGRETURN(deFALSE, "CreateBuffer: Cannot create buffer when using a DataBuffer");

	// can only use the buffers the composite has
	// when manager available, can hop to a different buffer
	if(!m_Locked || Clear)
		DEBUGRETURN(deFALSE, "CreateBuffer: Not Locked");

	if (!m_CompositeBuffer->Lock())
		return deFALSE;

	deBoolean ret = m_CompositeBuffer->CreateBuffer(Type, Clear);
	m_CompositeBuffer->Unlock();

	return ret;
}

deBoolean deSubVertexBuffer::DestroyBuffer(BufferType Type)
{
	if (m_DataBuffer && Type != BUFFER_INDICES16 && Type != BUFFER_INDICES32)
		DEBUGRETURN(deFALSE, "DestroyBuffer: Cannot destroy buffer when using a DataBuffer");

	// can only use the buffers the composite has
	// when manager available, can hop to a different buffer
	if(!m_Locked || Type)
		DEBUGRETURN(deFALSE, "DestroyBuffer: Not Locked");

	return deFALSE;
}

deBoolean deSubVertexBuffer::FillBuffer(BufferType Type, const void *Buffer, long StartEntry, long EntryCount)
{
	if (m_DataBuffer && Type != BUFFER_INDICES16 && Type != BUFFER_INDICES32)
		DEBUGRETURN(deFALSE, "FillBuffer: Cannot fill buffer when using a DataBuffer");

	if (!m_CompositeBuffer)
		DEBUGRETURN(deFALSE, "FillBuffer: No Composite");

	//fill a buffer with data
	long	NumElements;
	long	RealStart;

	//make sure we know of the type
	if(Type >= BUFFER_COUNT || Type < BUFFER_POSITION)
		DEBUGRETURN(deFALSE, "SetRenderType: Bad Type");
	
	//if we aren't locked, fail
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "FillBuffer: Not Locked");

	if (Type == BUFFER_INDICES32 || Type == BUFFER_INDICES16)
	{
		NumElements = m_IndexCount;
		RealStart = StartEntry + m_CompositeIndexStart;
	}
	else 
	{
		NumElements = m_VertexCount;
		RealStart = StartEntry + m_CompositeVertexStart;
	}

	//make sure there is enough room in the buffer
	if(StartEntry + EntryCount > NumElements)
		DEBUGRETURN(deFALSE, "FillBuffer: Buffer not big enough");

	//make sure the buffers  are valid
	if(!Buffer || EntryCount == 0)
		DEBUGRETURN(deFALSE, "FillBuffer: Buffer not valid");
	if (!m_CompositeBuffer->BufferExists(Type))
		DEBUGRETURN(deFALSE, "FillBuffer: Composite doesn't have buffer");
	
	//copy it
	if (!m_CompositeBuffer->Lock())
		return deFALSE;
	if (!m_CompositeBuffer->FillBuffer(Type, Buffer, RealStart, EntryCount))
		return deFALSE;
	if (!m_CompositeBuffer->Unlock())
		return deFALSE;
	
	//update the driver buffer list
	DirtyBuffer(Type);

	return deTRUE;
}

void deSubVertexBuffer::DirtyBuffer(BufferType Type)
{
	void *					ptr;
	DriverBufferEntry *		Entry;
	long					NewFlag;

	//update the driver buffers to say a buffer changed

	//figure out the bit we are to set
	NewFlag = 1 << (long)Type;
	
	//go thru the list, updating each entry
	ptr = m_DriverBuffer.GetNext(NULL, Entry);
	while(ptr)
	{
		//update the bit
		Entry->UpdateList = Entry->UpdateList | NewFlag;

		//next
		ptr = m_DriverBuffer.GetNext(ptr, Entry);
	}

	if (m_CompositeBuffer)
		m_CompositeBuffer->DirtyBuffer(Type, this);
}

deBoolean deSubVertexBuffer::BufferExists(BufferType Type) const
{
	return m_CompositeBuffer->BufferExists(Type);
}

IdeVertexBuffer::BufferDataType deSubVertexBuffer::GetBufferDataType(BufferType Type, long *Size) const
{
	return IdeVertexBuffer_FillBufferDataType(Type, Size);
}

deBoolean deSubVertexBuffer::BindDataBuffer(IdeVertexBuffer* DataBuffer)
{
	if (!DataBuffer)
		DEBUGRETURN(deFALSE, "BindDataBuffer: Parameter 'DataBuffer' was NULL");

	if (m_DataBuffer)
		m_DataBuffer->Release();
	
	m_DataBuffer = DataBuffer;
	if (m_DataBuffer)
		m_DataBuffer->Claim();

	return deTRUE;
}

IdeVertexBuffer* deSubVertexBuffer::GetBoundDataBuffer() const
{
	return m_DataBuffer;
}

deBoolean deSubVertexBuffer::Lock()
{
	//lock the buffer so only 1 person can access it
	if(m_Locked)
		DEBUGRETURN(deFALSE, "Lock: Already Locked");

	m_Locked = ~m_Locked;
	return deTRUE;	
}

deBoolean deSubVertexBuffer::Unlock()
{
	//a way so only 1 person will access us at a time for modifcations
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "Unlock: Not Locked");

	m_Locked = ~m_Locked;
	return deTRUE;
}

deBoolean deSubVertexBuffer::SetDriverBuffer(long ID, void *Buffer, IdeDriver * Driver)
{
	DriverBufferEntry *		Entry;
	void *					ptr;

	//gotta make sure we're locked for modifying
	if(!m_Locked)
		DEBUGRETURN(deFALSE, "SetRenderType: Not Locked");

	//search thru the list for the value, if non, insert it, otherwise, update
	//note, if the buffer value is NULL, wipe it
	ptr = m_DriverBuffer.GetNext(NULL, Entry);
	while(ptr)
	{
		//if found, exit
		if(Entry->ID == ID)
			break;

		//next
		ptr = m_DriverBuffer.GetNext(ptr, Entry);
	}

	if(!ptr)
	{
		//no entry, add it
		DriverBufferEntry NewEntry;
		NewEntry.ID = ID;
		NewEntry.Buffer = Buffer;
		NewEntry.UpdateList = 0;
		NewEntry.Driver = Driver;
		if (!m_DriverBuffer.AddElement(NewEntry))
			return deFALSE;
		return deTRUE;
	}

	//if the buffer is null, delete it, otherwise, update it
	if(!Buffer)
		return m_DriverBuffer.RemoveElement(ptr);
	else
	{
		Entry->Buffer = Buffer;
		Entry->UpdateList = 0;
	}

	return deTRUE;
}

void* deSubVertexBuffer::GetDriverBuffer(long ID, long *BufferUpdateList) const
{
	DriverBufferEntry *		Entry;
	void *					ptr;

	//return the driver buffer set

	//search thru the list for the value
	ptr = m_DriverBuffer.GetNext(NULL, Entry);
	while(ptr)
	{
		//if found, exit
		if(Entry->ID == ID)
			break;

		//next
		ptr = m_DriverBuffer.GetNext(ptr, Entry);
	}

	//if no entry, return NULL
	if(!ptr)
		return NULL;

	//return the stored buffer if they want it
	if(BufferUpdateList)
		*BufferUpdateList = Entry->UpdateList;

	return Entry->Buffer;
}

IdeVertexBuffer* deSubVertexBuffer::GetParentBuffer() const
{
	return m_CompositeBuffer;
}

void deSubVertexBuffer::SetVertexStart(long Start)
{
	m_CompositeVertexStart = Start;
}

void deSubVertexBuffer::SetIndexStart(long Start)
{
	m_CompositeIndexStart = Start;
}

void* deSubVertexBuffer::GetVertexEntry()
{
	return m_VertexEntry;
}

void* deSubVertexBuffer::GetIndexEntry()
{
	return m_IndexEntry;
}

void* deSubVertexBuffer::GetListEntry()
{
	return m_ListEntry;
}

void deSubVertexBuffer::SetVertexEntry(void* Entry)
{
	m_VertexEntry = Entry;
}

void deSubVertexBuffer::SetIndexEntry(void* Entry)
{
	m_IndexEntry = Entry;
}

void deSubVertexBuffer::SetListEntry(void* Entry)
{
	m_ListEntry = Entry;
}

