#include "../Gaia/RuGaia_SQL.h"

#pragma unmanaged

// ************************************************************************************************************************************************************

ruRTTI_DEFINE_SUBCLASS(CRuGaia_SQL, CRuGaia_Base)
ruHEAP_DEFINE_SUBCLASS(CRuGaia_SQL, "CRuGaia_SQL", "CRuGaia_Base")
ruCLASSGUID_DEFINE(CRuGaia_SQL, 0x9613D5F3, 0xABC84ffb, 0x861FA045, 0x130AE994)

// ************************************************************************************************************************************************************

CRuGaia_SQL::Node::Node()
{
	m_colObjects = NULL;

	for(INT32 i = 0; i < 4; ++i)
	{
		m_children[i] = NULL;
	}
}

CRuGaia_SQL::Node::Node(const CRuAABB &bounds, float maxExtents)
{
	m_center = (bounds.Maxima() + bounds.Minima()) / 2.0f;
	m_center.y = 0.0f;
	m_bounds = bounds;

	m_colObjects = NULL;

	// Must we initialize child nodes?
	if(bounds.Maxima().x - bounds.Minima().x > maxExtents || bounds.Maxima().z - bounds.Minima().z > maxExtents)
	{
		CRuAABB childBounds;

		for(INT32 i = 0; i < 4; ++i)
		{
			childBounds.SetBoundsToQuadrantXZ(bounds, i);
			m_children[i] = ruNEW Node(childBounds, maxExtents);
		}
	}
	else
	{
		for(INT32 i = 0; i < 4; ++i)
		{
			m_children[i] = NULL;
		}
	}
}

CRuGaia_SQL::Node::~Node()
{
	if(m_colObjects)
	{
		for(INT32 i = 0; i < m_colObjects->Count(); ++i)
		{
			delete m_colObjects->Get(i);
		}

		// Delete collision object descriptor list
		delete m_colObjects;
	}

	// Delete child nodes
	for(INT32 i = 0; i < 4; ++i)
	{
		delete m_children[i];
	}
}

BOOL CRuGaia_SQL::Node::AddCollisionObject(IRuCollisionObject *colObject)
{
	// Is this node a leaf?
	if(IsLeaf())
	{
		if(m_colObjects == NULL)
		{
			m_colObjects = ruNEW CRuArrayList<ColObjectDesc *>();
		}

		// Create new object descriptor
		ColObjectDesc *objDesc = ruNEW ColObjectDesc();

		objDesc->m_dirty = TRUE;
		objDesc->m_key = colObject->GetCollisionObjectKey();
		objDesc->m_bounds = colObject->GetBounds();

		// Add object descriptor to list
		m_colObjects->Add(objDesc);

		// Update bounds
		m_bounds.Merge(colObject->GetBounds());

		return TRUE;
	}
	else
	{
		CRuVector3 colObjCenter = (colObject->GetBounds().Maxima() + colObject->GetBounds().Minima()) / 2.0f;
		colObjCenter.y = 0.0f;

		// Find nearest child
		INT32 nearestChild = 0;
		float nearestDistance = FLT_MAX;

		for(INT32 i = 0; i < 4; ++i)
		{
			float distance = (colObjCenter - m_children[i]->m_center).Magnitude();

			if(distance < nearestDistance)
			{
				nearestChild = i;
				nearestDistance = distance;
			}
		}

		// Add collision object to nearest child
		m_children[nearestChild]->AddCollisionObject(colObject);
	}

	return FALSE;
}

BOOL CRuGaia_SQL::Node::AddCollisionObject(ColObjectDesc *colObjectDesc)
{
	// Is this node a leaf?
	if(IsLeaf())
	{
		if(m_colObjects == NULL)
		{
			m_colObjects = ruNEW CRuArrayList<ColObjectDesc *>();
		}

		// Add object descriptor to list
		m_colObjects->Add(colObjectDesc);

		// Update bounds
		m_bounds.Merge(colObjectDesc->m_bounds);

		return TRUE;
	}
	else
	{
		CRuVector3 colObjCenter = (colObjectDesc->m_bounds.Maxima() + colObjectDesc->m_bounds.Minima()) / 2.0f;
		colObjCenter.y = 0.0f;

		// Find nearest child
		INT32 nearestChild = 0;
		float nearestDistance = FLT_MAX;

		for(INT32 i = 0; i < 4; ++i)
		{
			float distance = (colObjCenter - m_children[i]->m_center).Magnitude();

			if(distance < nearestDistance)
			{
				nearestChild = i;
				nearestDistance = distance;
			}
		}

		// Add collision object to nearest child
		m_children[nearestChild]->AddCollisionObject(colObjectDesc);
	}

	return FALSE;
}

BOOL CRuGaia_SQL::Node::RemoveCollisionObject(INT32 objKey)
{
	BOOL childRemoved = FALSE;

	if(m_colObjects)
	{
		// Search for a collision object with the matching key
		for(INT32 i = 0; i < m_colObjects->Count(); ++i)
		{
			if(m_colObjects->Get(i)->m_key == objKey)
			{
				// Remove object
				m_colObjects->RemoveAt(i);
				i--;

				// Set child removed to true
				childRemoved |= TRUE;
			}
		}
	}

	// Recurse to child nodes
	if(IsLeaf() == FALSE)
	{
		for(INT32 i = 0; i < 4; ++i)
		{
			childRemoved |= m_children[i]->RemoveCollisionObject(objKey);
		}
	}

	return childRemoved;
}

void CRuGaia_SQL::Node::ResetDirty()
{
	if(m_colObjects)
	{
		for(INT32 i = 0, numObjs = m_colObjects->Count(); i < numObjs; ++i)
		{
			m_colObjects->Get(i)->m_dirty = FALSE;
		}
	}

	// Recurse to children
	if(!IsLeaf())
	{
		for(INT32 i = 0; i < 4; ++i)
		{
			m_children[i]->ResetDirty();
		}
	}
}

void CRuGaia_SQL::Node::Enumerate(CRuCollisionQuery *colQuery, CRuArrayList<ColObjectDesc *> &objListOut)
{
	BOOL recurseToChildren = FALSE;

	switch(colQuery->m_queryType)
	{
		case ruCOLQUERY_RAY:
			{
				CRuCollisionQuery_Ray *rayQuery = static_cast<CRuCollisionQuery_Ray *>(colQuery);

				if(Intersects(rayQuery->m_colRay, m_bounds))
				{
					if(IsLeaf())
					{
						if(m_colObjects)
						{
							for(INT32 i = 0, numObjs = m_colObjects->Count(); i < numObjs; ++i)
							{
								if(Intersects(rayQuery->m_colRay, m_colObjects->Get(i)->m_bounds))
								{
									objListOut.Add(m_colObjects->Get(i));
								}
							}
						}
					}
					else
					{
						recurseToChildren = TRUE;
					}
				}
			}

			break;

		case ruCOLQUERY_SEGMENT:
			{
				CRuCollisionQuery_Segment *segQuery = static_cast<CRuCollisionQuery_Segment *>(colQuery);

				if(Intersects(segQuery->m_colSegment, m_bounds))
				{
					if(IsLeaf())
					{
						if(m_colObjects)
						{
							for(INT32 i = 0, numObjs = m_colObjects->Count(); i < numObjs; ++i)
							{
								if(Intersects(segQuery->m_colSegment, m_colObjects->Get(i)->m_bounds))
								{
									objListOut.Add(m_colObjects->Get(i));
								}
							}
						}
					}
					else
					{
						recurseToChildren = TRUE;
					}
				}
			}

			break;

		case ruCOLQUERY_SEGMENT_HITTEST:
			{
				CRuCollisionQuery_Segment_HitTest *segQuery = static_cast<CRuCollisionQuery_Segment_HitTest *>(colQuery);

				if(Intersects(segQuery->m_colSegment, m_bounds))
				{
					if(IsLeaf())
					{
						if(m_colObjects)
						{
							for(INT32 i = 0, numObjs = m_colObjects->Count(); i < numObjs; ++i)
							{
								if(Intersects(segQuery->m_colSegment, m_colObjects->Get(i)->m_bounds))
								{
									objListOut.Add(m_colObjects->Get(i));
								}
							}
						}
					}
					else
					{
						recurseToChildren = TRUE;
					}
				}
			}

			break;

		case ruCOLQUERY_SEGMENT_NEAREST:
			{
				CRuCollisionQuery_Segment_Nearest *segQuery = static_cast<CRuCollisionQuery_Segment_Nearest *>(colQuery);

				if(Intersects(segQuery->m_colSegment, m_bounds))
				{
					if(IsLeaf())
					{
						if(m_colObjects)
						{
							for(INT32 i = 0, numObjs = m_colObjects->Count(); i < numObjs; ++i)
							{
								if(Intersects(segQuery->m_colSegment, m_colObjects->Get(i)->m_bounds))
								{
									objListOut.Add(m_colObjects->Get(i));
								}
							}
						}
					}
					else
					{
						recurseToChildren = TRUE;
					}
				}
			}

			break;

		case ruCOLQUERY_RAY_TRIPICK:
			{
			}

			break;

		case ruCOLQUERY_AABB_SUBOBJECT:
			{
				CRuCollisionQuery_AABB_SubObject *subObjQuery = static_cast<CRuCollisionQuery_AABB_SubObject *>(colQuery);

				if(Intersects(subObjQuery->m_colAABB, m_bounds))
				{
					if(IsLeaf())
					{
						if(m_colObjects)
						{
							for(INT32 i = 0, numObjs = m_colObjects->Count(); i < numObjs; ++i)
							{
								if(Intersects(subObjQuery->m_colAABB, m_colObjects->Get(i)->m_bounds))
								{
									objListOut.Add(m_colObjects->Get(i));
								}
							}
						}
					}
					else
					{
						recurseToChildren = TRUE;
					}
				}
			}

			break;

		case ruCOLQUERY_ENUMERATE:
			{
			}

			break;

		case ruCOLQUERY_USER_FLAG_SET_RAY:
			{
				CRuCollisionQuery_User_Flag_Set_Ray *rayQuery = static_cast<CRuCollisionQuery_User_Flag_Set_Ray *>(colQuery);

				if(Intersects(rayQuery->m_markingRay, m_bounds))
				{
					if(IsLeaf())
					{
						if(m_colObjects)
						{
							for(INT32 i = 0, numObjs = m_colObjects->Count(); i < numObjs; ++i)
							{
								if(Intersects(rayQuery->m_markingRay, m_colObjects->Get(i)->m_bounds))
								{
									objListOut.Add(m_colObjects->Get(i));
								}
							}
						}
					}
					else
					{
						recurseToChildren = TRUE;
					}
				}
			}

			break;

		case ruCOLQUERY_USER_FLAG_SET_SPHERE:
			{
				CRuCollisionQuery_User_Flag_Set_Sphere *setQuery = static_cast<CRuCollisionQuery_User_Flag_Set_Sphere *>(colQuery);

				if(Intersects(m_bounds, setQuery->m_markingSphere))
				{
					if(IsLeaf())
					{
						if(m_colObjects)
						{
							for(INT32 i = 0, numObjs = m_colObjects->Count(); i < numObjs; ++i)
							{
								if(Intersects(m_colObjects->Get(i)->m_bounds, setQuery->m_markingSphere))
								{
									objListOut.Add(m_colObjects->Get(i));
								}
							}
						}
					}
					else
					{
						recurseToChildren = TRUE;
					}
				}
			}

			break;

		case ruCOLQUERY_USER_FLAG_GET_SEGMENT:
			{
				CRuCollisionQuery_User_Flag_Get_Segment *segQuery = static_cast<CRuCollisionQuery_User_Flag_Get_Segment *>(colQuery);

				if(Intersects(segQuery->m_colSegment, m_bounds))
				{
					if(IsLeaf())
					{
						if(m_colObjects)
						{
							for(INT32 i = 0, numObjs = m_colObjects->Count(); i < numObjs; ++i)
							{
								if(Intersects(segQuery->m_colSegment, m_colObjects->Get(i)->m_bounds))
								{
									objListOut.Add(m_colObjects->Get(i));
								}
							}
						}
					}
					else
					{
						recurseToChildren = TRUE;
					}
				}
			}

			break;
	}

	// Recurse to children
	if(recurseToChildren)
	{
		for(INT32 i = 0; i < 4; ++i)
		{
			m_children[i]->Enumerate(colQuery, objListOut);
		}
	}
}

void CRuGaia_SQL::Node::EnumerateDirty(CRuArrayList<ColObjectDesc *> &objListOut)
{
	if(m_colObjects)
	{
		for(INT32 i = 0, numObjs = m_colObjects->Count(); i < numObjs; ++i)
		{
			if(m_colObjects->Get(i)->m_dirty)
			{
				objListOut.Add(m_colObjects->Get(i));
			}
		}
	}

	// Recurse to children
	if(!IsLeaf())
	{
		for(INT32 i = 0; i < 4; ++i)
		{
			m_children[i]->EnumerateDirty(objListOut);
		}
	}
}

// ************************************************************************************************************************************************************

CRuGaia_SQL::CRuGaia_SQL()
:	m_sqlSession(NULL)
{
	m_sqlSession = ruNEW CRuSQLSession();

	// Initialize collision object queue
	m_colObjQueue = ruNEW CRuLRUQueue<INT32, IRuCollisionObject *>(256, 2048);
}

CRuGaia_SQL::~CRuGaia_SQL()
{
	m_sqlSession->Release();

	// Flush collision object queue
	FlushColObjQueue();

	ClearZoneDescriptors();
	ClearDeletedZoneDescriptors();

	// Destroy collision object queue
	delete m_colObjQueue;
}

BOOL CRuGaia_SQL::Create(char *collisionMapName, char *srcResourceName, const CRuAABB &bounds, float maxExtents, float gridSpacing)
{
	// Connect
	m_sqlSession->Connect("magnus", "runewaker_map_collision");
	m_sqlSession->EnableAutoCommit(FALSE);

	// Verify that the specified collision map name does not already exist
	SQLINTEGER identicalNameCount;
	SQLLEN identicalNameCountInd;

	m_sqlSession->ResetStatement();
	m_sqlSession->Prepare((SQLCHAR *) "SELECT COUNT(*) FROM ru_headers WHERE collision_map_name = ?");
	m_sqlSession->BindParameter_VarString(1, collisionMapName);
	m_sqlSession->BindColumn_Integer(1, &identicalNameCount, &identicalNameCountInd);
	m_sqlSession->Execute();

	if(!m_sqlSession->Fetch() || identicalNameCountInd == SQL_NULL_DATA || identicalNameCount > 0)
	{
		// TODO: Offer option here to delete the old map and create a new one
		MessageBoxA(NULL, "Name collision.", "Error", MB_OK);

		return FALSE;
	}

	// Save bounds and grid spacing
	m_bounds = bounds;
	m_maxExtents = maxExtents;
	m_gridSpacing = gridSpacing;

	// Save collision map name
	INT32 collisionMapNameLength = strlen(collisionMapName) + 1;
	m_collisionMapName = ruNEW char [collisionMapNameLength];
	memcpy(m_collisionMapName, collisionMapName, collisionMapNameLength);

	// Save source resource name
	INT32 srcResNameLength = strlen(srcResourceName) + 1;
	m_srcResourceName = ruNEW char [srcResNameLength];
	memcpy(m_srcResourceName, srcResourceName, srcResNameLength);

	// Save header entry into ru_headers
	char *insertString = "	INSERT INTO	ru_headers							\
									(										\
										collision_map_name,					\
										source_map_name,					\
										bounds_min_x,						\
										bounds_min_y,						\
										bounds_min_z,						\
										bounds_max_x,						\
										bounds_max_y,						\
										bounds_max_z,						\
										max_extents,						\
										grid_spacing						\
									)										\
							VALUES	(										\
										?, ?, ?, ?, ?, ?, ?, ?, ?, ?		\
									)										";

	m_sqlSession->ResetStatement();
	m_sqlSession->Prepare((SQLCHAR *) insertString);
	m_sqlSession->BindParameter_VarString(1, collisionMapName);
	m_sqlSession->BindParameter_VarString(2, m_srcResourceName);
	m_sqlSession->BindParameter_Float(3, &m_bounds.Minima().x);
	m_sqlSession->BindParameter_Float(4, &m_bounds.Minima().y);
	m_sqlSession->BindParameter_Float(5, &m_bounds.Minima().z);
	m_sqlSession->BindParameter_Float(6, &m_bounds.Maxima().x);
	m_sqlSession->BindParameter_Float(7, &m_bounds.Maxima().y);
	m_sqlSession->BindParameter_Float(8, &m_bounds.Maxima().z);
	m_sqlSession->BindParameter_Float(9, &m_maxExtents);
	m_sqlSession->BindParameter_Float(10, &m_gridSpacing);
	m_sqlSession->Execute();

	// Commit transaction
	m_sqlSession->Commit();

	// Initialize empty tree
	m_root = ruNEW Node(m_bounds, m_maxExtents);

	return TRUE;
}

BOOL CRuGaia_SQL::Open(char *collisionMapName)
{
	// Connect
	m_sqlSession->Connect("magnus", "runewaker_map_collision");

	char sourceMapName[512];
	SQLLEN sourceMapNameInd;

	SQLREAL gridSpacing;
	SQLLEN gridSpacingInd;


	char *queryString = "	SELECT											\
										source_map_name,					\
										bounds_min_x,						\
										bounds_min_y,						\
										bounds_min_z,						\
										bounds_max_x,						\
										bounds_max_y,						\
										bounds_max_z,						\
										max_extents,						\
										grid_spacing						\
							FROM		ru_headers							\
							WHERE		collision_map_name = ?				";

	m_sqlSession->ResetStatement();
	m_sqlSession->BindParameter_VarString(1, collisionMapName);
	m_sqlSession->BindColumn_VarString(1, sourceMapName, 512, &sourceMapNameInd);
	m_sqlSession->BindColumn_Float(2, &m_bounds.Minima().x, &gridSpacingInd);
	m_sqlSession->BindColumn_Float(3, &m_bounds.Minima().y, &gridSpacingInd);
	m_sqlSession->BindColumn_Float(4, &m_bounds.Minima().z, &gridSpacingInd);
	m_sqlSession->BindColumn_Float(5, &m_bounds.Maxima().x, &gridSpacingInd);
	m_sqlSession->BindColumn_Float(6, &m_bounds.Maxima().y, &gridSpacingInd);
	m_sqlSession->BindColumn_Float(7, &m_bounds.Maxima().z, &gridSpacingInd);
	m_sqlSession->BindColumn_Float(8, &m_maxExtents, &gridSpacingInd);
	m_sqlSession->BindColumn_Float(9, &gridSpacing, &gridSpacingInd);
	m_sqlSession->Execute((SQLCHAR *) queryString);

	if(m_sqlSession->Fetch())
	{
		m_gridSpacing = gridSpacing;

		if(sourceMapNameInd != SQL_NULL_DATA)
		{
			m_srcResourceName = ruNEW char [sourceMapNameInd + 1];
			memcpy(m_srcResourceName, sourceMapName, sizeof(char) * (sourceMapNameInd + 1));
		}
		else
		{
			m_srcResourceName = ruNEW char [1];
			m_srcResourceName[0] = NULL;
		}
	}

	// Commit transaction
	m_sqlSession->Commit();

	// Save collision map name
	INT32 collisionMapNameLength = strlen(collisionMapName) + 1;
	m_collisionMapName = ruNEW char [collisionMapNameLength];
	memcpy(m_collisionMapName, collisionMapName, collisionMapNameLength);

	// Initialize empty tree
	m_root = ruNEW Node(m_bounds, m_maxExtents);

	// Load collision descriptors
	LoadCollisionObjectDescriptors();

	// Load time groups
	LoadTimeGroups();

	return TRUE;
}

BOOL CRuGaia_SQL::Save()
{
	// Save collision object descriptors
	SaveCollisionObjectDescriptors();

	// Save collision objects
	SaveCollisionObjects();

	// Save time groups
	SaveTimeGroups();

	return FALSE;
}

const CRuAABB &CRuGaia_SQL::GetBounds() const
{
	return m_bounds;
}

float CRuGaia_SQL::GetGridSpacing() const
{
	return m_gridSpacing;
}

const char *CRuGaia_SQL::GetSourceResourceName() const
{
	return m_srcResourceName;
}

INT32 CRuGaia_SQL::GetNumZoneDescriptors() const
{
	return m_zoneDescriptors.Count();
}

const CRuGaia_ZoneDescriptor *CRuGaia_SQL::GetZoneDescriptor(INT32 id, INT32 timeGroup, INT32 weatherGroup) const
{
	// Search for a descriptor with matching time group, weather group, and zone ID
	for(INT32 i = 0; i < m_zoneDescriptors.Count(); ++i)
	{
		// TODO: Put in extra search criteria for when exact match cannot be found
		if(	m_zoneDescriptors[i].m_zoneDescriptor->m_id == id &&
			m_zoneDescriptors[i].m_zoneDescriptor->m_timeGroup == timeGroup &&
			m_zoneDescriptors[i].m_zoneDescriptor->m_weatherGroup == weatherGroup)
		{
			return m_zoneDescriptors[i].m_zoneDescriptor;
		}
	}

	return NULL;
}

const CRuGaia_ZoneDescriptor *CRuGaia_SQL::GetZoneDescriptorByIndex(INT32 index) const
{
	return m_zoneDescriptors[index].m_zoneDescriptor;
}

BOOL CRuGaia_SQL::SetZoneDescriptor(const CRuGaia_ZoneDescriptor *zoneDesc)
{
	// Search for a descriptor with matching time group, weather group, and zone ID
	for(INT32 i = 0; i < m_zoneDescriptors.Count(); ++i)
	{
		if(	m_zoneDescriptors[i].m_zoneDescriptor->m_id == zoneDesc->m_id &&
			m_zoneDescriptors[i].m_zoneDescriptor->m_timeGroup == zoneDesc->m_timeGroup &&
			m_zoneDescriptors[i].m_zoneDescriptor->m_weatherGroup == zoneDesc->m_weatherGroup)
		{
			// Copy members
			m_zoneDescriptors[i].m_dirty = TRUE;
			*(m_zoneDescriptors[i].m_zoneDescriptor) = *zoneDesc;

			return TRUE;
		}
	}

	// Make a copy of the descriptor and add it to the descriptor table
	INT32 index = m_zoneDescriptors.Count();
	m_zoneDescriptors.Add(ZoneDescriptorInfo());
	m_zoneDescriptors[index].m_zoneDescriptor = ruNEW CRuGaia_ZoneDescriptor();
	m_zoneDescriptors[index].m_dirty = TRUE;
	*(m_zoneDescriptors[index].m_zoneDescriptor) = *zoneDesc;

	return TRUE;
}

BOOL CRuGaia_SQL::RemoveZoneDescriptor(const CRuGaia_ZoneDescriptor *zoneDesc)
{
	// Search for a descriptor with matching time group, weather group, and zone ID
	for(INT32 i = 0; i < m_zoneDescriptors.Count(); ++i)
	{
		if(	m_zoneDescriptors[i].m_zoneDescriptor->m_id == zoneDesc->m_id &&
			m_zoneDescriptors[i].m_zoneDescriptor->m_timeGroup == zoneDesc->m_timeGroup &&
			m_zoneDescriptors[i].m_zoneDescriptor->m_weatherGroup == zoneDesc->m_weatherGroup)
		{
			m_deletedZoneDescriptors.Add(m_zoneDescriptors[i]);
			m_zoneDescriptors.RemoveAt(i);

			return TRUE;
		}
	}

	return FALSE;
}

BOOL CRuGaia_SQL::RemoveZoneDescriptorAtIndex(INT32 index)
{
	if(index >= 0 && index < m_zoneDescriptors.Count())
	{
		m_deletedZoneDescriptors.Add(m_zoneDescriptors[index]);
		m_zoneDescriptors.RemoveAt(index);

		return TRUE;
	}

	return FALSE;
}

BOOL CRuGaia_SQL::SetZoneName(INT32 id, const char *zoneName)
{
	return TRUE;
}

BOOL CRuGaia_SQL::SetZoneID(INT32 oldID, INT32 newID)
{
	return TRUE;
}

BOOL CRuGaia_SQL::AddCollisionObject(IRuCollisionObject *colObject)
{
	// NOTE: Prollycan assume we will never insert an object with the same key, since we always call removecollisionobject first
	// Search the uncommitted collision object list and release the old collision object if it is found
	for(INT32 i = 0; i < m_uncommittedCollisionObjects.Count(); ++i)
	{
		if(m_uncommittedCollisionObjects[i]->GetCollisionObjectKey() == colObject->GetCollisionObjectKey())
		{
			m_uncommittedCollisionObjects[i]->Release();
			m_uncommittedCollisionObjects.RemoveAt(i);
			break;
		}
	}

	// Add new collision object to the uncommitted collision object list
	colObject->AddRef();
	m_uncommittedCollisionObjects.Add(colObject);

	// Add collision object to the object tree
	return m_root->AddCollisionObject(colObject);
}

BOOL CRuGaia_SQL::RemoveCollisionObject(INT32 objKey)
{
	// Search the uncommitted collision object list and release the deleted collision object if it is found
	for(INT32 i = 0; i < m_uncommittedCollisionObjects.Count(); ++i)
	{
		if(m_uncommittedCollisionObjects[i]->GetCollisionObjectKey() == objKey)
		{
			m_uncommittedCollisionObjects[i]->Release();
			m_uncommittedCollisionObjects.RemoveAt(i);
			break;
		}
	}

	// Add the object key to the list of deleted keys
	m_deletedCollisionObjectDescriptors.Add(objKey);

	// Remove collision object from the object tree
	return m_root->RemoveCollisionObject(objKey);
}

BOOL CRuGaia_SQL::QueryCollision(CRuCollisionQuery *colQuery)
{
	if(m_root)
	{
		m_objList.Clear();

		// Enumerate all collision objects involved in this query
		m_root->Enumerate(colQuery, m_objList);

		// Perform collision query on all of the enumerated collision objects
		for(INT32 i = 0; i < m_objList.Count(); ++i)
		{
			// Instance the collision object
			IRuCollisionObject *colObject = InstanceCollisionObject(m_objList[i]);

			if(colObject)
			{
				if(colQuery->m_queryType == ruCOLQUERY_AABB_SUBOBJECT)
				{
					static_cast<CRuCollisionQuery_AABB_SubObject *>(colQuery)->m_colObjects->Add(colObject);
				}
				else
				{
					// Perform collision query
					if(colObject)
					{
						colObject->QueryCollision(colQuery);
					}
				}
			}
		}

		return TRUE;
	}

	return FALSE;
}

void CRuGaia_SQL::ClearZoneDescriptors()
{
	for(INT32 i = 0; i < m_zoneDescriptors.Count(); ++i)
	{
		delete m_zoneDescriptors[i].m_zoneDescriptor;
	}

	m_zoneDescriptors.Clear();
}

void CRuGaia_SQL::ClearDeletedZoneDescriptors()
{
	for(INT32 i = 0; i < m_deletedZoneDescriptors.Count(); ++i)
	{
		delete m_deletedZoneDescriptors[i].m_zoneDescriptor;
	}

	m_deletedZoneDescriptors.Clear();
}

void CRuGaia_SQL::SaveCollisionObjectDescriptors()
{
	// Enumerate dirty descriptors
	m_objList.Clear();
	m_root->EnumerateDirty(m_objList);

	// Determine total number of dirty entries and deleted entries
	INT32 numDirtyEntries = m_objList.Count();
	INT32 numDeletedEntries = numDirtyEntries + m_deletedCollisionObjectDescriptors.Count();

	if(numDeletedEntries > 0)
	{
		char*								collisionMapName = ruNEW char [256 * numDeletedEntries];
		SQLINTEGER*							key = ruNEW SQLINTEGER [numDeletedEntries];

		SQLUSMALLINT*						paramStatusArray = ruNEW SQLUSMALLINT [numDeletedEntries];
		SQLUSMALLINT						paramsProcessed;

		INT32 j = 0;
		for(INT32 i = 0; i < m_objList.Count(); ++i, ++j)
		{
			if(m_collisionMapName)
				strcpy(&collisionMapName[j * 256], m_collisionMapName);

			key[j] = m_objList[i]->m_key;
		}

		for(INT32 i = 0; i < m_deletedCollisionObjectDescriptors.Count(); ++i, ++j)
		{
			if(m_collisionMapName)
				strcpy(&collisionMapName[j * 256], m_collisionMapName);

			key[j] = m_deletedCollisionObjectDescriptors[i];
		}

		char *deleteString = "	DELETE FROM		ru_colobj_descriptors			\
								WHERE			collision_map_name = ?			\
								AND				chunk_key = ?					";

		m_sqlSession->ResetStatement();
		m_sqlSession->BindParameterArray(numDeletedEntries, paramStatusArray, &paramsProcessed);
		m_sqlSession->BindParameter_VarString(1, collisionMapName, 256);
		m_sqlSession->BindParameter_Integer(2, key);
		m_sqlSession->Execute((SQLCHAR *) deleteString);

		delete[] collisionMapName;
		delete[] key;

		delete[] paramStatusArray;
	}

	if(numDirtyEntries > 0)
	{
		char*								collisionMapName = ruNEW char [256 * numDirtyEntries];
		SQLINTEGER*							key = ruNEW SQLINTEGER [numDirtyEntries];
		SQLREAL*							min_x = ruNEW SQLREAL [numDirtyEntries];
		SQLREAL*							min_y = ruNEW SQLREAL [numDirtyEntries];
		SQLREAL*							min_z = ruNEW SQLREAL [numDirtyEntries];
		SQLREAL*							max_x = ruNEW SQLREAL [numDirtyEntries];
		SQLREAL*							max_y = ruNEW SQLREAL [numDirtyEntries];
		SQLREAL*							max_z = ruNEW SQLREAL [numDirtyEntries];

		SQLUSMALLINT*						paramStatusArray = ruNEW SQLUSMALLINT [numDirtyEntries];
		SQLUSMALLINT						paramsProcessed;

		memset(collisionMapName, 0, sizeof(char) * 256 * numDirtyEntries);

		for(INT32 i = 0, j = 0; i < m_objList.Count(); ++i, ++j)
		{
			// Copy data into parameter arrays
			if(m_collisionMapName)
				strcpy(&collisionMapName[j * 256], m_collisionMapName);

			key[j] = m_objList[i]->m_key;
			min_x[j] = m_objList[i]->m_bounds.Minima().x;
			min_y[j] = m_objList[i]->m_bounds.Minima().y;
			min_z[j] = m_objList[i]->m_bounds.Minima().z;
			max_x[j] = m_objList[i]->m_bounds.Maxima().x;
			max_y[j] = m_objList[i]->m_bounds.Maxima().y;
			max_z[j] = m_objList[i]->m_bounds.Maxima().z;
		}

		char *queryString = "	INSERT INTO	ru_colobj_descriptors				\
										(										\
											collision_map_name,					\
											chunk_key,							\
											bounds_min_x,						\
											bounds_min_y,						\
											bounds_min_z,						\
											bounds_max_x,						\
											bounds_max_y,						\
											bounds_max_z						\
										)										\
								VALUES	(										\
											?, ?, ?, ?, ?, ?, ?, ?				\
										)										";

		m_sqlSession->ResetStatement();
		m_sqlSession->BindParameterArray(numDirtyEntries, paramStatusArray, &paramsProcessed);
		m_sqlSession->BindParameter_VarString(1, collisionMapName, 256);
		m_sqlSession->BindParameter_Integer(2, key);
		m_sqlSession->BindParameter_Float(3, min_x);
		m_sqlSession->BindParameter_Float(4, min_y);
		m_sqlSession->BindParameter_Float(5, min_z);
		m_sqlSession->BindParameter_Float(6, max_x);
		m_sqlSession->BindParameter_Float(7, max_y);
		m_sqlSession->BindParameter_Float(8, max_z);
		m_sqlSession->Execute((SQLCHAR *) queryString);

		delete[] collisionMapName;
		delete[] key;
		delete[] min_x;
		delete[] min_y;
		delete[] min_z;
		delete[] max_x;
		delete[] max_y;
		delete[] max_z;

		delete[] paramStatusArray;
	}

	// Commit transaction
	m_sqlSession->Commit();

	// Clear
	m_root->ResetDirty();
	m_deletedCollisionObjectDescriptors.Clear();
}

void CRuGaia_SQL::LoadCollisionObjectDescriptors()
{
	char *queryString = "	SELECT		chunk_key,								\
										bounds_min_x,							\
										bounds_min_y,							\
										bounds_min_z,							\
										bounds_max_x,							\
										bounds_max_y,							\
										bounds_max_z							\
							FROM		ru_colobj_descriptors					\
							WHERE		collision_map_name = ?					";

	SQLINTEGER						key;
	SQLREAL							min_x;
	SQLREAL							min_y;
	SQLREAL							min_z;
	SQLREAL							max_x;
	SQLREAL							max_y;
	SQLREAL							max_z;

	SQLLEN							dummyInd;

	m_sqlSession->ResetStatement();
	m_sqlSession->BindParameter_VarString(1, m_collisionMapName);
	m_sqlSession->BindColumn_Integer(1, &key, &dummyInd);
	m_sqlSession->BindColumn_Float(2, &min_x, &dummyInd);
	m_sqlSession->BindColumn_Float(3, &min_y, &dummyInd);
	m_sqlSession->BindColumn_Float(4, &min_z, &dummyInd);
	m_sqlSession->BindColumn_Float(5, &max_x, &dummyInd);
	m_sqlSession->BindColumn_Float(6, &max_y, &dummyInd);
	m_sqlSession->BindColumn_Float(7, &max_z, &dummyInd);
	m_sqlSession->Execute((SQLCHAR *) queryString);

	while(m_sqlSession->Fetch())
	{
		// Create new object descriptor
		ColObjectDesc *objDesc = ruNEW ColObjectDesc();

		objDesc->m_dirty = FALSE;
		objDesc->m_key = key;
		objDesc->m_bounds.Set(min_x, min_y, min_z, max_x, max_y, max_z);

		// Add object descriptor to the descriptor tree
		m_root->AddCollisionObject(objDesc);
	}

	// Commit transaction
	m_sqlSession->Commit();
}

void CRuGaia_SQL::SaveCollisionObjects()
{
	INT32 numDirtyEntries = m_uncommittedCollisionObjects.Count();

	if(numDirtyEntries > 0)
	{
		char *queryString = "	INSERT INTO	ru_colobj_chunks					\
										(										\
											collision_map_name,					\
											chunk_key,							\
											chunk_data							\
										)										\
								VALUES	(										\
											?, ?, ?								\
										)										";

		char								collisionMapName[256];
		SQLINTEGER							key;
		CRuMemoryStream*					stream = ruNEW CRuMemoryStream();

		for(INT32 i = 0; i < m_uncommittedCollisionObjects.Count(); ++i)
		{
			// Serialize collision object into memory stream
			stream = ruNEW CRuMemoryStream();
			m_uncommittedCollisionObjects[i]->SerializeTo(stream);

			SQLLEN streamInd = stream->GetStreamSize();

			if(m_collisionMapName)
				strcpy(collisionMapName, m_collisionMapName);

			key = m_uncommittedCollisionObjects[i]->GetCollisionObjectKey();

			m_sqlSession->ResetStatement();
			m_sqlSession->BindParameter_VarString(1, collisionMapName, 256);
			m_sqlSession->BindParameter_Integer(2, &key);
			m_sqlSession->BindParameter_VarImage(3, stream->GetBuffer(), &streamInd);
			m_sqlSession->Execute((SQLCHAR *) queryString);
		}

/*
		char*								collisionMapName = ruNEW char [256 * numDirtyEntries];
		SQLINTEGER*							key = ruNEW SQLINTEGER [numDirtyEntries];
		CRuMemoryStream**					streams = ruNEW CRuMemoryStream * [numDirtyEntries];

		SQLINTEGER*							indArray = ruNEW SQLINTEGER [numDirtyEntries];
		SQLUSMALLINT*						paramStatusArray = ruNEW SQLUSMALLINT [numDirtyEntries];
		SQLUSMALLINT						paramsProcessed;

		// Copy data into parameter arrays
		memset(collisionMapName, 0, sizeof(char) * 256 * numDirtyEntries);
		for(INT32 i = 0, j = 0; i < m_uncommittedCollisionObjects.Count(); ++i, ++j)
		{
			// Serialize collision object into memory stream
			streams[j] = ruNEW CRuMemoryStream();
			m_uncommittedCollisionObjects[i]->SerializeTo(streams[j]);

			if(m_collisionMapName)
				strcpy(&collisionMapName[j * 256], m_collisionMapName);

			key[j] = m_uncommittedCollisionObjects[i]->GetCollisionObjectKey();
			indArray[j] = SQL_LEN_DATA_AT_EXEC((INT32) streams[j]->GetStreamSize());
		}

		char *queryString = "	INSERT INTO	ru_colobj_chunks					\
										(										\
											collision_map_name,					\
											chunk_key,							\
											chunk_data							\
										)										\
								VALUES	(										\
											?, ?, ?								\
										)										";

		m_sqlSession->ResetStatement();
		m_sqlSession->BindParameterArray(numDirtyEntries, paramStatusArray, &paramsProcessed);
		m_sqlSession->BindParameter_VarString(1, collisionMapName, 256);
		m_sqlSession->BindParameter_Integer(2, key);
		m_sqlSession->BindParameter_VarImage(3, indArray);
		m_sqlSession->Execute((SQLCHAR *) queryString);

		delete[] collisionMapName;
		delete[] key;

		delete[] paramStatusArray;
*/
	}

	// Commit transaction
	m_sqlSession->Commit();
}

void CRuGaia_SQL::SaveTimeGroups()
{
	// Determine total number of dirty entries and deleted entries
	INT32 numDeletedEntries = 0;
	INT32 numDirtyEntries = 0;
	for(INT32 i = 0; i < m_zoneDescriptors.Count(); ++i)
	{
		if(m_zoneDescriptors[i].m_dirty == TRUE)
		{
			++numDeletedEntries;
			++numDirtyEntries;
		}
	}

	numDeletedEntries += m_deletedZoneDescriptors.Count();

	if(numDeletedEntries > 0)
	{
		char*								collisionMapName = ruNEW char [256 * numDeletedEntries];
		SQLINTEGER*							id = ruNEW SQLINTEGER [numDeletedEntries];
		SQLINTEGER*							timeGroup = ruNEW SQLINTEGER [numDeletedEntries];
		SQLINTEGER*							weatherGroup = ruNEW SQLINTEGER [numDeletedEntries];

		SQLUSMALLINT*						paramStatusArray = ruNEW SQLUSMALLINT [numDeletedEntries];
		SQLUSMALLINT						paramsProcessed;

		INT32 j = 0;
		for(INT32 i = 0; i < m_zoneDescriptors.Count(); ++i)
		{
			if(m_zoneDescriptors[i].m_dirty == FALSE)
				continue;

			if(m_collisionMapName)
				strcpy(&collisionMapName[j * 256], m_collisionMapName);

			id[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_id;
			timeGroup[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_timeGroup;
			weatherGroup[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_weatherGroup;

			++j;
		}

		for(INT32 i = 0; i < m_deletedZoneDescriptors.Count(); ++i, ++j)
		{
			if(m_collisionMapName)
				strcpy(&collisionMapName[j * 256], m_collisionMapName);

			id[j] = m_deletedZoneDescriptors[i].m_zoneDescriptor->m_id;
			timeGroup[j] = m_deletedZoneDescriptors[i].m_zoneDescriptor->m_timeGroup;
			weatherGroup[j] = m_deletedZoneDescriptors[i].m_zoneDescriptor->m_weatherGroup;
		}

		// Delete dirty entries and entries in the deletion queue
		char *deleteString = "	DELETE FROM		ru_zone_descriptors				\
								WHERE			collision_map_name = ?			\
								AND				id = ?							\
								AND				time_group = ?					\
								AND				weather_group = ?				";

		m_sqlSession->ResetStatement();
		m_sqlSession->BindParameterArray(numDeletedEntries, paramStatusArray, &paramsProcessed);
		m_sqlSession->BindParameter_VarString(1, collisionMapName, 256);
		m_sqlSession->BindParameter_Integer(2, id);
		m_sqlSession->BindParameter_Integer(3, timeGroup);
		m_sqlSession->BindParameter_Integer(4, weatherGroup);
		m_sqlSession->Execute((SQLCHAR *) deleteString);

		delete[] collisionMapName;
		delete[] id;
		delete[] timeGroup;
		delete[] weatherGroup;

		delete[] paramStatusArray;
	}

	if(numDirtyEntries > 0)
	{
		char*								collisionMapName = ruNEW char [256 * numDirtyEntries];
		SQLINTEGER*							id = ruNEW SQLINTEGER [numDirtyEntries];
		SQLINTEGER*							timeGroup = ruNEW SQLINTEGER [numDirtyEntries];
		SQLINTEGER*							weatherGroup = ruNEW SQLINTEGER [numDirtyEntries];
		char*								name = ruNEW char [256 * numDirtyEntries];
		SQLINTEGER*							color_Primary = ruNEW SQLINTEGER [numDirtyEntries];
		SQLINTEGER*							color_Secondary = ruNEW SQLINTEGER [numDirtyEntries];
		SQLINTEGER*							color_Ambient = ruNEW SQLINTEGER [numDirtyEntries];
		SQLINTEGER*							color_Specular = ruNEW SQLINTEGER [numDirtyEntries];
		SQLINTEGER*							color_Fog = ruNEW SQLINTEGER [numDirtyEntries];
		SQLREAL*							fog_Start = ruNEW SQLREAL [numDirtyEntries];
		SQLREAL*							fog_End = ruNEW SQLREAL [numDirtyEntries];
		SQLREAL*							theta_Standard = ruNEW SQLREAL [numDirtyEntries];
		SQLREAL*							phi_Standard = ruNEW SQLREAL [numDirtyEntries];
		SQLREAL*							theta_Specular = ruNEW SQLREAL [numDirtyEntries];
		SQLREAL*							phi_Specular = ruNEW SQLREAL [numDirtyEntries];
		SQLINTEGER*							cloud_HorizonColor = ruNEW SQLINTEGER [numDirtyEntries];
		SQLINTEGER*							cloud_OverheadColor = ruNEW SQLINTEGER [numDirtyEntries];
		SQLINTEGER*							cloud_HorizonCount = ruNEW SQLINTEGER [numDirtyEntries];
		SQLINTEGER*							cloud_OverheadCount = ruNEW SQLINTEGER [numDirtyEntries];
		char*								skyGradientTextureName = ruNEW char [256 * numDirtyEntries];
		char*								horizonCloudTextureName = ruNEW char [256 * numDirtyEntries];
		char*								overheadCloudTextureName = ruNEW char [256 * numDirtyEntries];

		SQLUSMALLINT*						paramStatusArray = ruNEW SQLUSMALLINT [numDirtyEntries];
		SQLUSMALLINT						paramsProcessed;

		memset(collisionMapName, 0, sizeof(char) * 256 * numDirtyEntries);
		memset(name, 0, sizeof(char) * 256 * numDirtyEntries);
		memset(skyGradientTextureName, 0, sizeof(char) * 256 * numDirtyEntries);
		memset(horizonCloudTextureName, 0, sizeof(char) * 256 * numDirtyEntries);
		memset(overheadCloudTextureName, 0, sizeof(char) * 256 * numDirtyEntries);

		for(INT32 i = 0, j = 0; i < m_zoneDescriptors.Count(); ++i)
		{
			if(m_zoneDescriptors[i].m_dirty == FALSE)
				continue;

			// Reset dirty flag
			m_zoneDescriptors[i].m_dirty = FALSE;

			// Copy data into parameter arrays
			if(m_collisionMapName)
				strcpy(&collisionMapName[j * 256], m_collisionMapName);

			id[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_id;
			timeGroup[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_timeGroup;
			weatherGroup[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_weatherGroup;

			if(m_zoneDescriptors[i].m_zoneDescriptor->m_name)
				strcpy(&name[j * 256], m_zoneDescriptors[i].m_zoneDescriptor->m_name);

			color_Primary[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_color_Primary;
			color_Secondary[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_color_Secondary;
			color_Ambient[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_color_Ambient;
			color_Specular[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_color_Specular;
			color_Fog[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_color_Fog;

			fog_Start[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_fog_Start;
			fog_End[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_fog_End;

			theta_Standard[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_theta_Standard;
			phi_Standard[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_phi_Standard;
			theta_Specular[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_theta_Specular;
			phi_Specular[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_phi_Specular;

			cloud_HorizonColor[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_cloud_HorizonColor;
			cloud_OverheadColor[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_cloud_OverheadColor;
			cloud_HorizonCount[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_cloud_HorizonCount;
			cloud_OverheadCount[j] = m_zoneDescriptors[i].m_zoneDescriptor->m_cloud_OverheadCount;

			if(m_zoneDescriptors[i].m_zoneDescriptor->m_skyGradientTextureName)
				strcpy(&skyGradientTextureName[j * 256], m_zoneDescriptors[i].m_zoneDescriptor->m_skyGradientTextureName);

			if(m_zoneDescriptors[i].m_zoneDescriptor->m_horizonCloudTextureName)
				strcpy(&horizonCloudTextureName[j * 256], m_zoneDescriptors[i].m_zoneDescriptor->m_horizonCloudTextureName);

			if(m_zoneDescriptors[i].m_zoneDescriptor->m_overheadCloudTextureName)
				strcpy(&overheadCloudTextureName[j * 256], m_zoneDescriptors[i].m_zoneDescriptor->m_overheadCloudTextureName);

			++j;
		}

		// Perform INSERT statement for all dirty descriptors
		char *queryString = "	INSERT INTO	ru_zone_descriptors					\
										(										\
											collision_map_name,					\
											id,									\
											time_group,							\
											weather_group,						\
											zone_name,							\
											color_primary,						\
											color_secondary,					\
											color_ambient,						\
											color_specular,						\
											color_fog,							\
											fog_start,							\
											fog_end,							\
											theta_standard,						\
											phi_standard,						\
											theta_specular,						\
											phi_specular,						\
											cloud_horizon_color,				\
											cloud_overhead_color,				\
											cloud_horizon_count,				\
											cloud_overhead_count,				\
											sky_gradient_texture_name,			\
											horizon_cloud_texture_name,			\
											overhead_cloud_texture_name			\
										)										\
								VALUES	(										\
											?, ?, ?, ?, ?, ?, ?, ?, ?, ?,		\
											?, ?, ?, ?, ?, ?, ?, ?, ?, ?,		\
											?, ?, ?								\
										)										";

		m_sqlSession->ResetStatement();
		m_sqlSession->BindParameterArray(numDirtyEntries, paramStatusArray, &paramsProcessed);
		m_sqlSession->BindParameter_VarString(1, collisionMapName, 256);
		m_sqlSession->BindParameter_Integer(2, id);
		m_sqlSession->BindParameter_Integer(3, timeGroup);
		m_sqlSession->BindParameter_Integer(4, weatherGroup);
		m_sqlSession->BindParameter_VarString(5, name, 256);
		m_sqlSession->BindParameter_Integer(6, color_Primary);
		m_sqlSession->BindParameter_Integer(7, color_Secondary);
		m_sqlSession->BindParameter_Integer(8, color_Ambient);
		m_sqlSession->BindParameter_Integer(9, color_Specular);
		m_sqlSession->BindParameter_Integer(10, color_Fog);
		m_sqlSession->BindParameter_Float(11, fog_Start);
		m_sqlSession->BindParameter_Float(12, fog_End);
		m_sqlSession->BindParameter_Float(13, theta_Standard);
		m_sqlSession->BindParameter_Float(14, phi_Standard);
		m_sqlSession->BindParameter_Float(15, theta_Specular);
		m_sqlSession->BindParameter_Float(16, phi_Specular);
		m_sqlSession->BindParameter_Integer(17, cloud_HorizonColor);
		m_sqlSession->BindParameter_Integer(18, cloud_OverheadColor);
		m_sqlSession->BindParameter_Integer(19, cloud_HorizonCount);
		m_sqlSession->BindParameter_Integer(20, cloud_OverheadCount);
		m_sqlSession->BindParameter_VarString(21, skyGradientTextureName, 256);
		m_sqlSession->BindParameter_VarString(22, horizonCloudTextureName, 256);
		m_sqlSession->BindParameter_VarString(23, overheadCloudTextureName, 256);
		m_sqlSession->Execute((SQLCHAR *) queryString);

		delete[] collisionMapName;
		delete[] id;
		delete[] timeGroup;
		delete[] weatherGroup;
		delete[] name;
		delete[] color_Primary;
		delete[] color_Secondary;
		delete[] color_Ambient;
		delete[] color_Specular;
		delete[] color_Fog;
		delete[] fog_Start;
		delete[] fog_End;
		delete[] theta_Standard;
		delete[] phi_Standard;
		delete[] theta_Specular;
		delete[] phi_Specular;
		delete[] cloud_HorizonColor;
		delete[] cloud_OverheadColor;
		delete[] cloud_HorizonCount;
		delete[] cloud_OverheadCount;
		delete[] skyGradientTextureName;
		delete[] horizonCloudTextureName;
		delete[] overheadCloudTextureName;

		delete[] paramStatusArray;
	}

	// Commit transaction
	m_sqlSession->Commit();

	// Flush deleted descriptors list
	ClearDeletedZoneDescriptors();
}

void CRuGaia_SQL::LoadTimeGroups()
{
	char *queryString = "	SELECT		id,									\
										time_group,							\
										weather_group,						\
										zone_name,							\
										color_primary,						\
										color_secondary,					\
										color_ambient,						\
										color_specular,						\
										color_fog,							\
										fog_start,							\
										fog_end,							\
										theta_standard,						\
										phi_standard,						\
										theta_specular,						\
										phi_specular,						\
										cloud_horizon_color,				\
										cloud_overhead_color,				\
										cloud_horizon_count,				\
										cloud_overhead_count,				\
										sky_gradient_texture_name,			\
										horizon_cloud_texture_name,			\
										overhead_cloud_texture_name			\
							FROM		ru_zone_descriptors					\
							WHERE		collision_map_name = ?";

	SQLINTEGER							id;
	SQLINTEGER							timeGroup;
	SQLINTEGER							weatherGroup;
	char								name[256];
	SQLINTEGER							color_Primary;
	SQLINTEGER							color_Secondary;
	SQLINTEGER							color_Ambient;
	SQLINTEGER							color_Specular;
	SQLINTEGER							color_Fog;
	REAL								fog_Start;
	REAL								fog_End;
	REAL								theta_Standard;
	REAL								phi_Standard;
	REAL								theta_Specular;
	REAL								phi_Specular;
	SQLINTEGER							cloud_HorizonColor;
	SQLINTEGER							cloud_OverheadColor;
	SQLINTEGER							cloud_HorizonCount;
	SQLINTEGER							cloud_OverheadCount;
	char								skyGradientTextureName[256];
	char								horizonCloudTextureName[256];
	char								overheadCloudTextureName[256];

	SQLLEN								dummyInd;
	SQLLEN								nameInd;
	SQLLEN								skyGradientTextureNameInd;
	SQLLEN								horizonCloudTextureNameInd;
	SQLLEN								overheadCloudTextureNameInd;

	m_sqlSession->ResetStatement();
	m_sqlSession->BindParameter_VarString(1, m_collisionMapName);

	m_sqlSession->BindColumn_Integer(1, &id, &dummyInd);
	m_sqlSession->BindColumn_Integer(2, &timeGroup, &dummyInd);
	m_sqlSession->BindColumn_Integer(3, &weatherGroup, &dummyInd);
	m_sqlSession->BindColumn_VarString(4, name, 256, &nameInd);
	m_sqlSession->BindColumn_Integer(5, &color_Primary, &dummyInd);
	m_sqlSession->BindColumn_Integer(6, &color_Secondary, &dummyInd);
	m_sqlSession->BindColumn_Integer(7, &color_Ambient, &dummyInd);
	m_sqlSession->BindColumn_Integer(8, &color_Specular, &dummyInd);
	m_sqlSession->BindColumn_Integer(9, &color_Fog, &dummyInd);
	m_sqlSession->BindColumn_Float(10, &fog_Start, &dummyInd);
	m_sqlSession->BindColumn_Float(11, &fog_End, &dummyInd);
	m_sqlSession->BindColumn_Float(12, &theta_Standard, &dummyInd);
	m_sqlSession->BindColumn_Float(13, &phi_Standard, &dummyInd);
	m_sqlSession->BindColumn_Float(14, &theta_Specular, &dummyInd);
	m_sqlSession->BindColumn_Float(15, &phi_Specular, &dummyInd);
	m_sqlSession->BindColumn_Integer(16, &cloud_HorizonColor, &dummyInd);
	m_sqlSession->BindColumn_Integer(17, &cloud_OverheadColor, &dummyInd);
	m_sqlSession->BindColumn_Integer(18, &cloud_HorizonCount, &dummyInd);
	m_sqlSession->BindColumn_Integer(19, &cloud_OverheadCount, &dummyInd);
	m_sqlSession->BindColumn_VarString(20, skyGradientTextureName, 256, &skyGradientTextureNameInd);
	m_sqlSession->BindColumn_VarString(21, horizonCloudTextureName, 256, &horizonCloudTextureNameInd);
	m_sqlSession->BindColumn_VarString(22, overheadCloudTextureName, 256, &overheadCloudTextureNameInd);

	m_sqlSession->Execute((SQLCHAR *) queryString);

	while(m_sqlSession->Fetch())
	{
		// Create new zone descriptor
		INT32 i = m_zoneDescriptors.Count();
		m_zoneDescriptors.Add(ZoneDescriptorInfo());
		m_zoneDescriptors[i].m_zoneDescriptor = ruNEW CRuGaia_ZoneDescriptor();
		m_zoneDescriptors[i].m_dirty = FALSE;

		m_zoneDescriptors[i].m_zoneDescriptor->m_id = id;
		m_zoneDescriptors[i].m_zoneDescriptor->m_timeGroup = timeGroup;
		m_zoneDescriptors[i].m_zoneDescriptor->m_weatherGroup = weatherGroup;

		if(nameInd > 0)
		{
			m_zoneDescriptors[i].m_zoneDescriptor->m_name = ruNEW char [nameInd + 1];
			memcpy(m_zoneDescriptors[i].m_zoneDescriptor->m_name, name, nameInd + 1);
		}

		m_zoneDescriptors[i].m_zoneDescriptor->m_color_Primary = color_Primary;
		m_zoneDescriptors[i].m_zoneDescriptor->m_color_Secondary = color_Secondary;
		m_zoneDescriptors[i].m_zoneDescriptor->m_color_Ambient = color_Ambient;
		m_zoneDescriptors[i].m_zoneDescriptor->m_color_Specular = color_Specular;
		m_zoneDescriptors[i].m_zoneDescriptor->m_color_Fog = color_Fog;
		m_zoneDescriptors[i].m_zoneDescriptor->m_fog_Start = fog_Start;
		m_zoneDescriptors[i].m_zoneDescriptor->m_fog_End = fog_End;
		m_zoneDescriptors[i].m_zoneDescriptor->m_theta_Standard = theta_Standard;
		m_zoneDescriptors[i].m_zoneDescriptor->m_phi_Standard = phi_Standard;
		m_zoneDescriptors[i].m_zoneDescriptor->m_theta_Specular = theta_Specular;
		m_zoneDescriptors[i].m_zoneDescriptor->m_phi_Specular = phi_Specular;
		m_zoneDescriptors[i].m_zoneDescriptor->m_cloud_HorizonColor = cloud_HorizonColor;
		m_zoneDescriptors[i].m_zoneDescriptor->m_cloud_OverheadColor = cloud_OverheadColor;
		m_zoneDescriptors[i].m_zoneDescriptor->m_cloud_HorizonCount = cloud_HorizonCount;
		m_zoneDescriptors[i].m_zoneDescriptor->m_cloud_OverheadCount = cloud_OverheadCount;

		if(skyGradientTextureNameInd > 0)
		{
			m_zoneDescriptors[i].m_zoneDescriptor->m_skyGradientTextureName = ruNEW char [skyGradientTextureNameInd + 1];
			memcpy(m_zoneDescriptors[i].m_zoneDescriptor->m_skyGradientTextureName, skyGradientTextureName, skyGradientTextureNameInd + 1);
		}

		if(horizonCloudTextureNameInd > 0)
		{
			m_zoneDescriptors[i].m_zoneDescriptor->m_horizonCloudTextureName = ruNEW char [horizonCloudTextureNameInd + 1];
			memcpy(m_zoneDescriptors[i].m_zoneDescriptor->m_horizonCloudTextureName, horizonCloudTextureName, horizonCloudTextureNameInd + 1);
		}

		if(overheadCloudTextureNameInd > 0)
		{
			m_zoneDescriptors[i].m_zoneDescriptor->m_overheadCloudTextureName = ruNEW char [overheadCloudTextureNameInd + 1];
			memcpy(m_zoneDescriptors[i].m_zoneDescriptor->m_overheadCloudTextureName, overheadCloudTextureName, overheadCloudTextureNameInd + 1);
		}
	}

	// Commit transaction
	m_sqlSession->Commit();
}

IRuCollisionObject *CRuGaia_SQL::InstanceCollisionObject(ColObjectDesc *objDesc)
{
	DWORD keyHash = RuGlobalHashGen().GetHashCode(static_cast<UINT32>(objDesc->m_key));
	IRuCollisionObject *colObject = NULL;

	// Locate the collision key
	if(m_colObjQueue->Get(objDesc->m_key, keyHash, colObject) == FALSE)
	{
		// Reclaim space
		IRuCollisionObject *reclaimedColObject;
		while(m_colObjQueue->Reclaim(objDesc->m_key, keyHash, 1, reclaimedColObject))
		{
			// Release
			if(reclaimedColObject)
			{
				reclaimedColObject->Release();
			}
		}

		// Search the uncommitted collision object list
		for(INT32 i = 0; i < m_uncommittedCollisionObjects.Count(); ++i)
		{
			if(m_uncommittedCollisionObjects[i]->GetCollisionObjectKey() == objDesc->m_key)
			{
				colObject = m_uncommittedCollisionObjects[i];
				colObject->AddRef();
				break;
			}
		}

		// Attempt to serialize from the database
		if(colObject == NULL)
		{
		}
/*
		// Instance the collision object
		m_stream->Seek(objDesc->m_offset, ruSSM_Begin);
		IRuObject *obj = g_ruObjectFactory.CreateObject(m_stream);
		if(obj && obj->GetType().IsSubClassOf(IRuCollisionObject::Type))
		{
			colObject = static_cast<IRuCollisionObject *>(obj);
		}
*/
		// Add the collision object to the queue
		m_colObjQueue->Add(objDesc->m_key, keyHash, colObject, 1);
	}

	return colObject;
}

BOOL CRuGaia_SQL::FlushColObjQueue()
{
	if(m_colObjQueue)
	{
		// Iterate through all instanced entities
		m_colObjQueue->ForAllElements(FlushColObjQueueCallback, this);

		// Clear instanced entity queue
		m_colObjQueue->Clear();
	}

	return TRUE;
}

BOOL CRuGaia_SQL::FlushColObjQueueCallback(const void *key, const void *data, void *userData)
{
	IRuCollisionObject *colObj = *((IRuCollisionObject **) data);

	if(colObj)
	{
		colObj->Release();
	}

	return TRUE;
}

// ************************************************************************************************************************************************************

#pragma managed
