#ifndef _RUCOLLISIONHIERARCHY_QUADTREE_H_
#define _RUCOLLISIONHIERARCHY_QUADTREE_H_

// Base types
#include "../Collision/RuCollisionHierarchy_Base.h"

#pragma unmanaged

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

inline INT32 RuZoneFlags_GetID(UINT32 zoneFlags)
{
	return zoneFlags & 0x000FFFFF;
}

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

class CRuWorld_ZoneDescriptor
{
public:
	INT32									m_id;													//!< Zone ID
	INT32									m_timeGroup;											//!< Time group
	INT32									m_weatherGroup;											//!< Weather group
	char*									m_name;													//!< Zone name

	// Color setup
	DWORD									m_color_Primary;
	DWORD									m_color_Secondary;
	DWORD									m_color_Ambient;
	DWORD									m_color_Specular;
	DWORD									m_color_Fog;

	// Fog setup
	REAL									m_fog_Start;
	REAL									m_fog_End;

	// Lighting direction setup
	REAL									m_theta_Standard;
	REAL									m_phi_Standard;

	REAL									m_theta_Specular;
	REAL									m_phi_Specular;

	// Sky and cloud setup
	DWORD									m_cloud_HorizonColor;
	DWORD									m_cloud_OverheadColor;
	INT32									m_cloud_HorizonCount;
	INT32									m_cloud_OverheadCount;

	char*									m_skyGradientTextureName;								//!< Texture name to use for sky gradient
	char*									m_horizonCloudTextureName;
	char*									m_overheadCloudTextureName;
	DWORD									m_cloudMode;											//!< Cloud display mode

public:
											CRuWorld_ZoneDescriptor()
											{
												m_name = NULL;
												m_fog_Start = 200.0f;
												m_fog_End = 800.0f;
												m_theta_Standard = 15.0f;
												m_phi_Standard = 35.0f;
												m_theta_Specular = 15.0f;
												m_phi_Specular = 15.0f;
												m_cloud_HorizonColor = 0xFFFFFFFF;
												m_cloud_OverheadColor = 0xFFFFFFFF;
												m_cloud_HorizonCount = 0;
												m_cloud_OverheadCount = 0;
												m_skyGradientTextureName = NULL;
												m_horizonCloudTextureName = NULL;
												m_overheadCloudTextureName = NULL;
											}

											~CRuWorld_ZoneDescriptor()
											{
												delete[] m_name;
												delete[] m_skyGradientTextureName;
												delete[] m_horizonCloudTextureName;
												delete[] m_overheadCloudTextureName;
											}

	const char*								GetName() const
	{
		if(m_name)
		{
			return m_name;
		}

		return "";
	}

	void									SetName(const char *name)
	{
		delete[] m_name;
		
		m_name = ruNEW char [strlen(name) + 1];
		strcpy(m_name, name);
	}

	void									SetSkyGradientTextureName(const char *skyGradientTextureName)
	{
		delete[] m_skyGradientTextureName;

		if(skyGradientTextureName)
		{
			m_skyGradientTextureName = ruNEW char [strlen(skyGradientTextureName) + 1];
			strcpy(m_skyGradientTextureName, skyGradientTextureName);
		}
		else
		{
			m_skyGradientTextureName = NULL;
		}
	}

	void									SetHorizonCloudTextureName(const char *horizonCloudTextureName)
	{
		delete[] m_horizonCloudTextureName;

		if(horizonCloudTextureName)
		{
			m_horizonCloudTextureName = ruNEW char [strlen(horizonCloudTextureName) + 1];
			strcpy(m_horizonCloudTextureName, horizonCloudTextureName);
		}
		else
		{
			m_horizonCloudTextureName = NULL;
		}
	}

	void									SetOverheadCloudTextureName(const char *overheadCloudTextureName)
	{
		delete[] m_overheadCloudTextureName;

		if(overheadCloudTextureName)
		{
			m_overheadCloudTextureName = ruNEW char [strlen(overheadCloudTextureName) + 1];
			strcpy(m_overheadCloudTextureName, overheadCloudTextureName);
		}
		else
		{
			m_overheadCloudTextureName = NULL;
		}
	}

	void									CopySettings(const CRuWorld_ZoneDescriptor &zoneDesc);
	CRuWorld_ZoneDescriptor&				operator=(const CRuWorld_ZoneDescriptor &zoneDesc);
};

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

/*!
	A streamed AABB tree. 
*/
class CRuCollisionHierarchy_Quadtree : public IRuCollisionHierarchy
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	struct ZoneDescriptorInfo
	{
		INT32									m_timeGroup;											//!< Time group
		INT32									m_weatherGroup;											//!< Weather group
		CRuWorld_ZoneDescriptor*				m_zoneDescriptor;										//!< Zone descriptor
	};

	struct ColObjectDesc
	{
		INT32									m_key;													//!< Collision object key
		INT32									m_iteration;											//!< TODO: Iteration number of the collision object
		INT32									m_offset;												//!< Collision object offset
		CRuAABB									m_bounds;												//!< Collision object bounds
	};

	struct Node
	{
		CRuVector3								m_center;												//!< True center of the node
		CRuAABB									m_bounds;												//!< Bounding box of the node
		CRuArrayList<ColObjectDesc *>*			m_colObjects;											//!< List of collison object descriptors
		Node*									m_children[4];											//!< Pointers to children

												Node();
												Node(const CRuAABB &bounds, float maxExtents);
												~Node();
	
		BOOL									IsLeaf()
		{
			return m_children[0] == NULL;
		}

		BOOL									AddCollisionObject(IRuCollisionObject *colObject, INT32 objOffset);
		BOOL									RemoveCollisionObject(INT32 objKey);

		BOOL									SerializeFrom(IRuStream *stream);
		BOOL									SerializeTo(IRuStream *stream);

		void									Enumerate(CRuCollisionQuery *colQuery, CRuArrayList<ColObjectDesc *> &objListOut);
	};

	// Source stream
	IRuStream*									m_stream;												//!< Data stream

	// Header stuff
	INT32										m_tableAddressOffset;									//!< Offset, from beginning of data stream, to the 32-bit quantity that stores the collision tree table offset in the stream
	INT32										m_zoneTableAddressOffset;								//!< Offset, from beginning of data stream, to the 32-bit quantity that stores the zone table offset in the stream

	// House-keeping
	CRuAABB										m_bounds;
	float										m_gridSpacing;
	char*										m_srcResourceName;

	// Tree
	Node*										m_root;													//!< Root node

	// Zone descriptor table
	CRuArrayList<CRuWorld_ZoneDescriptor *>		m_zoneDescriptors;										//!< Zone descriptor array

	// Collision object instancing data
	CRuLRUQueue<INT32, IRuCollisionObject *>*	m_colObjQueue;											//!< LRU queue of instanced collision objects

	// Workspace
	CRuArrayList<ColObjectDesc *>				m_objList;												//!< Scratch pad

public:
												CRuCollisionHierarchy_Quadtree();
	virtual										~CRuCollisionHierarchy_Quadtree();

	BOOL										Create(IRuStream *stream, const CRuAABB &bounds, float maxExtents, float gridSpacing, const char *srcResourceName);
	BOOL										Open(IRuStream *stream);
	BOOL										Save();

	const CRuAABB&								GetBounds() const;
	float										GetGridSpacing() const;
	const char*									GetSourceResourceName() const;
	
	INT32										GetNumZoneDescriptors() const;

	const CRuWorld_ZoneDescriptor*				GetZoneDescriptor(INT32 id, INT32 timeGroup, INT32 weatherGroup) const;
	const CRuWorld_ZoneDescriptor*				GetZoneDescriptorByIndex(INT32 index) const;


	void										SetZoneDescriptor(const CRuWorld_ZoneDescriptor *zoneDesc);
	void										RemoveZoneDescriptor(const CRuWorld_ZoneDescriptor *zoneDesc);
	void										RemoveZoneDescriptorAtIndex(INT32 index);

	// Hierarchy management
	virtual BOOL								AddCollisionObject(IRuCollisionObject *colObject);
	virtual BOOL								RemoveCollisionObject(INT32 objKey);

	// Collision query interface
	virtual BOOL								QueryCollision(CRuCollisionQuery *colQuery);

protected:
	void										SaveTimeGroups(IRuStream *stream);
	void										LoadTimeGroups(IRuStream *stream);
	IRuCollisionObject*							InstanceCollisionObject(ColObjectDesc *objDesc);
	BOOL										FlushColObjQueue();
	static BOOL									FlushColObjQueueCallback(const void *key, const void *data, void *userData);
};

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

#pragma managed

#endif
