//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: 
//
// $Revision: $
// $NoKeywords: $
//
// The BSP tree leaf data system
//
//=============================================================================

#include "basetypes.h"
#include "bsptreedata.h"
#include "utllinkedlist.h"
#include "utlvector.h"
#include "tier0/dbg.h"
#include "tier0/memdbgon.h"


//-----------------------------------------------------------------------------
// The BSP tree leaf data system
//-----------------------------------------------------------------------------
class CBSPTreeData : public IBSPTreeData, public ISpatialLeafEnumerator
{
public:
	// constructor, destructor
	CBSPTreeData();
	virtual ~CBSPTreeData();

	// Methods of IBSPTreeData
	void Init( ISpatialQuery* pBSPTree );
	void Shutdown();

	BSPTreeDataHandle_t Insert( int userId, Vector const& mins, Vector const& maxs );
	void Remove( BSPTreeDataHandle_t handle );
	void ElementMoved( BSPTreeDataHandle_t handle, Vector const& mins, Vector const& maxs );

	// Enumerate elements in a particular leaf
	bool EnumerateElementsInLeaf( int leaf, IBSPTreeDataEnumerator* pEnum, int context );

	// For convenience, enumerates the leaves along a ray, box, etc.
	bool EnumerateLeavesAtPoint( Vector const& pt, ISpatialLeafEnumerator* pEnum, int context );
	bool EnumerateLeavesInBox( Vector const& mins, Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context );
	bool EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context );
	bool EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context );

	// methods of IBSPLeafEnumerator
	bool EnumerateLeaf( int leaf, int context );

	// Is the element in any leaves at all?
	bool IsElementInTree( BSPTreeDataHandle_t handle ) const;

private:
	// Creates a new handle
	BSPTreeDataHandle_t NewHandle( int userId );

	// Adds a handle to the list of handles
	void AddHandleToLeaf( int leaf, BSPTreeDataHandle_t handle );

	// insert, remove handles from leaves
	void InsertIntoTree( BSPTreeDataHandle_t handle, Vector const& mins, Vector const& maxs );
	void RemoveFromTree( BSPTreeDataHandle_t handle );

	// Returns the number of elements in a leaf
	int CountElementsInLeaf( int leaf );

private:
	// All the information associated with a particular handle
	struct HandleInfo_t
	{
		int					m_UserId;		// Client-defined id
		unsigned short		m_LeafList;		// What leafs is it in?
	};

	// The leaf contains an index into a list of elements
	struct Leaf_t
	{
		unsigned short	m_FirstElement;
	};

	// The handle knows about the leaves it lies in
	struct HandleInLeaf_t
	{
		int				m_Leaf;				// what leaf is the handle in?
		unsigned short	m_LeafElementIndex;	// what's the m_LeafElements index of the entry?
	};

	// Stores data associated with each leaf.
	CUtlVector< Leaf_t >	m_Leaf;

	// Stores all unique handles
	CUtlLinkedList< HandleInfo_t, unsigned short >		m_Handles;

	// Maintains the list of all handles in a particular leaf
	CUtlLinkedList< BSPTreeDataHandle_t, unsigned short >	m_LeafElements;

	// Maintains the list of all leaves a particular handle spans
	CUtlLinkedList< HandleInLeaf_t, unsigned short >	m_HandleLeafList;

	// Interface to BSP tree
	ISpatialQuery*	m_pBSPTree;
};


//-----------------------------------------------------------------------------
// Class factory
//-----------------------------------------------------------------------------

IBSPTreeData* CreateBSPTreeData()
{
	return new CBSPTreeData;
}

void DestroyBSPTreeData( IBSPTreeData* pTreeData )
{
	if (pTreeData)
		delete pTreeData;
}


//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------

CBSPTreeData::CBSPTreeData()
{
}

CBSPTreeData::~CBSPTreeData()
{
}


//-----------------------------------------------------------------------------
// Level init, shutdown
//-----------------------------------------------------------------------------

void CBSPTreeData::Init( ISpatialQuery* pBSPTree )
{
	Assert( pBSPTree );
	m_pBSPTree = pBSPTree;

	m_Handles.EnsureCapacity( 1024 );
	m_LeafElements.EnsureCapacity( 1024 );
	m_HandleLeafList.EnsureCapacity( 1024 );

	// Add all the leaves we'll need
	int leafCount = m_pBSPTree->LeafCount();
	m_Leaf.EnsureCapacity( leafCount );

	Leaf_t newLeaf;
	newLeaf.m_FirstElement = m_LeafElements.InvalidIndex();
	while ( --leafCount >= 0 )
	{
		m_Leaf.AddToTail( newLeaf );
	}
}

void CBSPTreeData::Shutdown()
{
	m_Handles.Purge();
	m_LeafElements.Purge();
	m_HandleLeafList.Purge();
	m_Leaf.Purge();
}


//-----------------------------------------------------------------------------
// Creates a new handle
//-----------------------------------------------------------------------------

BSPTreeDataHandle_t CBSPTreeData::NewHandle( int userId )
{
	BSPTreeDataHandle_t handle = m_Handles.AddToTail();
	m_Handles[handle].m_UserId = userId;
	m_Handles[handle].m_LeafList = m_HandleLeafList.InvalidIndex();

	return handle;
}

//-----------------------------------------------------------------------------
// Add/remove handle
//-----------------------------------------------------------------------------

BSPTreeDataHandle_t CBSPTreeData::Insert( int userId, Vector const& mins, Vector const& maxs )
{
	BSPTreeDataHandle_t handle = NewHandle( userId );
	InsertIntoTree( handle, mins, maxs );
	return handle;
}

void CBSPTreeData::Remove( BSPTreeDataHandle_t handle )
{
	if (!m_Handles.IsValidIndex(handle))
		return;

	RemoveFromTree( handle );
	m_Handles.Free( handle );
}


//-----------------------------------------------------------------------------
// Adds a handle to a leaf
//-----------------------------------------------------------------------------
void CBSPTreeData::AddHandleToLeaf( int leaf, BSPTreeDataHandle_t handle )
{	
	// Got to a leaf baby! Add the handle to the leaf's list of elements
	unsigned short leafElement = m_LeafElements.Alloc( true );
	if (m_Leaf[leaf].m_FirstElement != m_LeafElements.InvalidIndex() )
		m_LeafElements.LinkBefore( m_Leaf[leaf].m_FirstElement, leafElement );
	m_Leaf[leaf].m_FirstElement = leafElement;
	m_LeafElements[leafElement] = handle;

	// Insert the leaf into the handles's list of leaves
	unsigned short handleElement = m_HandleLeafList.Alloc( true );
	if (m_Handles[handle].m_LeafList != m_HandleLeafList.InvalidIndex() )
		m_HandleLeafList.LinkBefore( m_Handles[handle].m_LeafList, handleElement );
	m_Handles[handle].m_LeafList = handleElement;
	m_HandleLeafList[handleElement].m_Leaf = leaf;
	m_HandleLeafList[handleElement].m_LeafElementIndex = leafElement;
}


//-----------------------------------------------------------------------------
// Inserts an element into the tree
//-----------------------------------------------------------------------------
bool CBSPTreeData::EnumerateLeaf( int leaf, int context )
{
	BSPTreeDataHandle_t handle = (BSPTreeDataHandle_t)context;
	AddHandleToLeaf( leaf, handle );
	return true;
}

void CBSPTreeData::InsertIntoTree( BSPTreeDataHandle_t handle, Vector const& mins, Vector const& maxs )
{
	m_pBSPTree->EnumerateLeavesInBox( mins, maxs, this, handle );
}

//-----------------------------------------------------------------------------
// Removes an element from the tree
//-----------------------------------------------------------------------------

void CBSPTreeData::RemoveFromTree( BSPTreeDataHandle_t handle )
{
	// Iterate over the list of all leaves the handle is in
	unsigned short i = m_Handles[handle].m_LeafList;
	while (i != m_HandleLeafList.InvalidIndex())
	{
		int leaf = m_HandleLeafList[i].m_Leaf;
		unsigned short leafElement = m_HandleLeafList[i].m_LeafElementIndex; 

		// Unhook the handle from the leaf handle list
		if (leafElement == m_Leaf[leaf].m_FirstElement)
			m_Leaf[leaf].m_FirstElement = m_LeafElements.Next(leafElement);
		m_LeafElements.Free(leafElement);

		unsigned short prevNode = i;
		i = m_HandleLeafList.Next(i);
		m_HandleLeafList.Free(prevNode);
	}

	m_Handles[handle].m_LeafList = m_HandleLeafList.InvalidIndex();
}


//-----------------------------------------------------------------------------
// Call this when the element moves
//-----------------------------------------------------------------------------
void CBSPTreeData::ElementMoved( BSPTreeDataHandle_t handle, Vector const& mins, Vector const& maxs )
{
	if (handle != TREEDATA_INVALID_HANDLE)
	{
		RemoveFromTree( handle );
		InsertIntoTree( handle, mins, maxs );
	}
}


//-----------------------------------------------------------------------------
// Is the element in any leaves at all?
//-----------------------------------------------------------------------------
bool CBSPTreeData::IsElementInTree( BSPTreeDataHandle_t handle ) const
{
	return m_Handles[handle].m_LeafList != m_HandleLeafList.InvalidIndex();
}


//-----------------------------------------------------------------------------
// Enumerate elements in a particular leaf
//-----------------------------------------------------------------------------
int CBSPTreeData::CountElementsInLeaf( int leaf )
{
	int i;
	int nCount = 0;
	for( i = m_Leaf[leaf].m_FirstElement; i != m_LeafElements.InvalidIndex(); i = m_LeafElements.Next(i) )
	{
		++nCount;
	}

	return nCount;
}

//-----------------------------------------------------------------------------
// Enumerate elements in a particular leaf
//-----------------------------------------------------------------------------
bool CBSPTreeData::EnumerateElementsInLeaf( int leaf, IBSPTreeDataEnumerator* pEnum, int context )
{
#ifdef _DEBUG
	// The enumeration method better damn well not change this list...
	int nCount = CountElementsInLeaf(leaf);
#endif

	unsigned short idx = m_Leaf[leaf].m_FirstElement;
	while (idx != m_LeafElements.InvalidIndex())
	{
		BSPTreeDataHandle_t handle = m_LeafElements[idx];
		if (!pEnum->EnumerateElement( m_Handles[handle].m_UserId, context ))
		{
			Assert( CountElementsInLeaf(leaf) == nCount );
			return false;
		}
		idx = m_LeafElements.Next(idx);
	}

	Assert( CountElementsInLeaf(leaf) == nCount );

	return true;
}


//-----------------------------------------------------------------------------
// For convenience, enumerates the leaves along a ray, box, etc.
//-----------------------------------------------------------------------------
bool CBSPTreeData::EnumerateLeavesAtPoint( Vector const& pt, ISpatialLeafEnumerator* pEnum, int context )
{
	return m_pBSPTree->EnumerateLeavesAtPoint( pt, pEnum, context );
}

bool CBSPTreeData::EnumerateLeavesInBox( Vector const& mins, Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context )
{
	return m_pBSPTree->EnumerateLeavesInBox( mins, maxs, pEnum, context );
}

bool CBSPTreeData::EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context )
{
	return m_pBSPTree->EnumerateLeavesInSphere( center, radius, pEnum, context );
}

bool CBSPTreeData::EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context )
{
	return m_pBSPTree->EnumerateLeavesAlongRay( ray, pEnum, context );
}

