#ifndef _RUENTITYOPTIMALTERRAIN_H_
#define _RUENTITYOPTIMALTERRAIN_H_

// Rune entity base
#include "../../Core/DataStore/RuDataStore_Base.h"

#include "../../Gaia/RuGaia_DataStore.h"

#include "../../Scene/Base/RuEntityBase.h"

#include "../../Scene/Terrain/RuEntityTerrain_Common.h"
#include "../../Scene/Terrain/RuWorld_Base.h"
#include "../../Scene/Terrain/RuWorld_DoodadSystem.h"
#include "../../Scene/Terrain/RuWorld_EntitySystem.h"
#include "../../Scene/Terrain/RuWorld_TriggerSystem.h"
#include "../../Scene/Terrain/RuWorld_Water.h"

#pragma unmanaged

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

enum RuOptimalTerrainChunkLoadMode
{
	ruOPTIMALTERRAINCHUNKLOADMODE_ANY		= 1,								//!< Load chunk without any preference
	ruOPTIMALTERRAINCHUNKLOADMODE_FULL		= 2,								//!< Load chunk as a full chunk
	ruOPTIMALTERRAINCHUNKLOADMODE_PARTIAL	= 3,								//!< Load chunk as a partial chunk
	ruOPTIMALTERRAINCHUNKLOADMODE_COLLISION	= 4,								//!< Load a chunk as a collision-only chunk
	ruOPTIMALTERRAINCHUNKLOADMODE_MINIMAL	= 5,								//!< Load a chunk as a minimal chunk
	ruOPTIMALTERRAINCHUNKLOADMODE_OOB		= 6									//!< Force out-of-band chunk loading and load as a partial chunk (not yet implemented)
};

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

class CRuOptimalTerrain_Chunk;

class CRuWorld_Backdrop : public IRuEntity_Renderable
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	struct ChunkDescriptor
	{
		UINT32								m_chunkKey;
	};

	INT32									m_actualVertexCount;
	INT32									m_actualPrimitiveCount;
	CRuMesh*								m_mesh;
	CRuMaterial*							m_material;

	CRuAABB									m_boundingBox;
	CRuArrayList<ChunkDescriptor>			m_containedChunks;					//!< List of contained chunks

public:
											CRuWorld_Backdrop(INT32 vertexAllocation, INT32 primitiveAllocation);
	virtual									~CRuWorld_Backdrop();

	BOOL									InsertChunk(UINT32 chunkKey, CRuOptimalTerrain_Chunk *chunk);
	BOOL									ContainsChunk(UINT32 chunkKey);

	const CRuAABB&							GetBoundingBox() const;

	// IRuEntity_Renderable interface
	virtual INT32							GetNumMeshes() const;
	virtual BOOL							GetMesh(INT32 meshIndex, IRuMesh **meshOut, IRuMaterial **materialOut);
};

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

class CRuOptimalTerrain_Chunk : public IRuEntity_Renderable
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	INT32									m_chunkIteration;					//!< Chunk iteration number
	BOOL									m_dataValidated;					//!< Is all data loaded and validated?

	INT32									m_dimension;						//!< Dimensions of the chunk (N x N)
	float									m_gridSize;							//!< Size of grids defining the chunk vertices
	CRuAABB									m_bounds_AABB;						//!< Bounding box for the chunk
	CRuVector3								m_origin;							//!< Origin of the chunk

	INT32									m_lodLevels;						//!< Number of LOD levels
	INT32									m_numVertices;						//!< Number of vertices, including the skirt vertices
	INT32									m_numIndices;						//!< Number of indices, including all LOD and skirt indices

	BYTE*									m_vertexFlags;
	CRuMesh*								m_mesh;								//!< Chunk mesh data
	IRuCollisionStructure*					m_colStructure;						//!< Chunk mesh collision structure

	CRuArrayList<CRuGUID>					m_entityDescriptorGUIDs;			//!< List of entity descriptor GUIDs in this chunk
	CRuArrayList<CRuParticle_Grass>			m_grassParticles;					//!< List of grass particles in this chunk
	CRuArrayList<CRuWorld_DoodadCluster *>	m_doodadLists;						//!< List of doodad arrays
	CRuArrayList<CRuWorld_Water *>			m_waterLayers;						//!< List of water layers in this chunk

	CRuMaterial*							m_material;							//!< Material used by the chunk
	CRuProxyMesh*							m_collisionMesh;					//!< Collision mesh
	CRuProxyMesh**							m_lodMeshes;						//!< Array of LOD meshes
	INT32									m_currentLOD;						//!< Currently selected LOD mesh

	INT32									m_splatResolution;					//!< Splat ID map resolution
	CRuSplatIDMap*							m_splatIDMap;						//!< Splat ID map

	INT32									m_alphaTextureLOD;					//!< Current alpha texture LOD

	REAL									m_alphaUVModifier[4];

public:
											CRuOptimalTerrain_Chunk();
	virtual									~CRuOptimalTerrain_Chunk();

	// IRuObject serialization
	BOOL									MakeDataConformant(RuOptimalTerrainChunkLoadMode loadMode);
	BOOL									SerializeFrom(IRuStream *stream, RuOptimalTerrainChunkLoadMode loadMode);
	virtual BOOL							SerializeFrom(IRuStream *stream);
	virtual BOOL							SerializeTo(IRuStream *stream);

	// IRuEntity_Renderable interface
	virtual INT32							GetNumMeshes() const;
	virtual BOOL							GetMesh(INT32 meshIndex, IRuMesh **meshOut, IRuMaterial **materialOut);

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

	// LOD selection
	void									SetCamera(CRuCamera *viewCamera);

	// Splat ID map management
	void									ReleaseSplatIDMap();

	// Information query
	INT32									GetChunkIteration() const;
	const CRuAABB&							GetBoundingBox() const;
	CRuSplatIDMap*							GetSplatIDMap();

	// Entity GUID
	INT32									GetNumEntityDescriptorGUIDs();
	const CRuGUID&							GetEntityDescriptorGUID(INT32 index);

	// Grass particle
	const CRuArrayList<CRuParticle_Grass>&	GetGrassParticles() const;

	// Doodads
	INT32									GetNumDoodadClusters() const;
	CRuWorld_DoodadCluster*					GetDoodadCluster(INT32 index);

	// Water surfaces
	INT32									GetNumWaterLayers() const;
	CRuWorld_Water*							GetWaterLayerByIndex(INT32 index);

	INT32									GetMeshDataSize() const;
	INT32									GetSplatDataSize() const;

	void									SetAlphaTextureLOD(INT32 alphaTextureLOD);

protected:
	void									BuildChunkTopology(BOOL ignoreCollision, BOOL ignoreSkirt);
	void									UpdateSkirtVertices();
};

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

class IRuOptimalTerrain_DataSource : public IRuObject
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

public:
	virtual									~IRuOptimalTerrain_DataSource() { } 

	virtual BOOL							SetCollisionOnlyMode(BOOL collisionOnlyMode) = 0;
	virtual BOOL							SetChunkQueueSize(INT32 chunkQueueSize) = 0;

	virtual CRuOptimalTerrain_Chunk*		LoadChunk(INT32 x, INT32 z, RuOptimalTerrainChunkLoadMode loadMode = ruOPTIMALTERRAINCHUNKLOADMODE_ANY) = 0;
	virtual CRuOptimalTerrain_Chunk*		LoadMinimalChunk(INT32 x, INT32 z) = 0;
	virtual BOOL							WarmChunk(INT32 x, INT32 z) = 0;
	virtual BOOL							ChunkExistsInCache(INT32 x, INT32 z) = 0;
	virtual BOOL							ChunkExistsInMinimalCache(INT32 x, INT32 z) = 0;

	virtual CRuTextureList*					GetTextureList() = 0;
	virtual CRuGrassDatabase*				GetGrassDatabase() = 0;

	// Water body descriptor
	virtual INT32							GetNumWaterBodyDescriptors() = 0;
	virtual CRuWaterBodyDescriptor*			LoadWaterBodyDescriptorByIndex(INT32 index) = 0;

	virtual INT32							GetChunkDimension() const  = 0;
	virtual INT32							GetNumXChunks() const = 0;
	virtual INT32							GetNumZChunks() const = 0;
	virtual INT32							GetSplatResolution() const = 0;
	virtual CRuAABB							GetBounds(INT32 x, INT32 z) const = 0;
	virtual INT32							GetChunkIteration(INT32 x, INT32 z) = 0;

	virtual void							DumpDebugInfo() = 0;
};

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

class CRuOptimalTerrain_DataSource_DS : public IRuOptimalTerrain_DataSource
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	struct ChunkDescriptor
	{
		DWORD								m_key;								// Chunk key = 0xXXXXZZZZ
		CRuOptimalTerrain_Chunk*			m_chunk;							// Chunk object
	};

	// Data store
	CRuDataStore_Base*						m_dataStore;

	// Property settings
	BOOL									m_collisionOnlyMode;				//!< Load only collision data? (For server use)

	// Table handles
	INT32									m_tableHandle_Header;
	INT32									m_tableHandle_Chunk;
	INT32									m_tableHandle_TextureList;
	INT32									m_tableHandle_GrassDB;
	INT32									m_tableHandle_WaterDescriptor;

	// Header information
	CRuVector3								m_origin;							//!< Origin of the terrain
	INT32									m_chunkDimension;					//!< Dimensions of each chunk
	INT32									m_numXChunks;						//!< Number of chunks along the X axis
	INT32									m_numZChunks;						//!< Number of chunks along the Z axis
	INT32									m_splatResolution;					//!< Resolution of the splat ID map, excluding padding
	REAL									m_gridSize;							//!< Size of grids defining the chunk vertices
	INT32									m_lodLevels;						//!< Number of LOD levels

	// Cache
	CRuStack<ChunkDescriptor *>				m_recycledChunkDescriptors;			//!< Stack of unused chunk descriptors
	CRuLRUQueue<DWORD, ChunkDescriptor *>*	m_chunkQueue;						// LRU queue of chunks
	CRuLRUQueue<DWORD, ChunkDescriptor *>*	m_minimalChunkQueue;				// LRU queue of chunks loaded as minimal chunks (lowest LOD, no texture)

	// Texture list
	CRuTextureList*							m_textureList;

	// Grass DB
	CRuGrassDatabase*						m_grassDatabase;

	// Utility objects
	CRuStreamCompressor*					m_streamCompressor;					//!< Stream compression/decompression utility object
	CRuMemoryStream*						m_compressedChunkStream;			//!< Temporary buffer space for compressed streams
	CRuMemoryStream*						m_decompressedChunkStream;			//!< Temporary buffer space for decompressed streams

	// Preloader
	DWORD									m_lastLoadTime;
	DWORD									m_slice;
	DWORD									m_loadedChunks;

	// Critical section
	CRuThread_CriticalSection*				m_objectCS;

public:
											CRuOptimalTerrain_DataSource_DS();
	virtual									~CRuOptimalTerrain_DataSource_DS();

	virtual BOOL							SetCollisionOnlyMode(BOOL collisionOnlyMode);
	virtual BOOL							SetChunkQueueSize(INT32 chunkQueueSize);

	virtual CRuOptimalTerrain_Chunk*		LoadChunk(INT32 x, INT32 z, RuOptimalTerrainChunkLoadMode loadMode = ruOPTIMALTERRAINCHUNKLOADMODE_ANY);
	virtual CRuOptimalTerrain_Chunk*		LoadMinimalChunk(INT32 x, INT32 z);
	virtual BOOL							WarmChunk(INT32 x, INT32 z);
	virtual BOOL							ChunkExistsInCache(INT32 x, INT32 z);
	virtual BOOL							ChunkExistsInMinimalCache(INT32 x, INT32 z);

	virtual CRuTextureList*					GetTextureList();
	virtual CRuGrassDatabase*				GetGrassDatabase();

	// Water descriptors
	virtual INT32							GetNumWaterBodyDescriptors();
	virtual CRuWaterBodyDescriptor*			LoadWaterBodyDescriptorByIndex(INT32 index);

	virtual INT32							GetChunkDimension() const;
	virtual INT32							GetNumXChunks() const;
	virtual INT32							GetNumZChunks() const;
	virtual INT32							GetSplatResolution() const;
	virtual CRuAABB							GetBounds(INT32 x, INT32 z) const;
	virtual INT32							GetChunkIteration(INT32 x, INT32 z);

	virtual void							DumpDebugInfo() { }

	CRuDataStore_Base*						GetDataStore();

	BOOL									Open(CRuDataStore_Base *dataStore);

protected:
	BOOL									LoadHeader();
	BOOL									LoadTextureList(CRuTextureList *textureList);
	CRuGrassDatabase*						LoadGrassDatabase();

	BOOL									ClearChunkQueue();
	static BOOL								ClearChunkQueueCallback(const void *key, const void *data, void *userData);

	BOOL									ClearMinimalChunkQueue();
	static BOOL								ClearMinimalChunkQueueCallback(const void *key, const void *data, void *userData);

	BOOL									EnumerateChunkQueue(CRuArrayList<ChunkDescriptor *> *chunkList);
	static BOOL								EnumerateChunkQueueCallback(const void *key, const void *data, void *userData);
};

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

class CRuOptimalTerrain : public IRuEntity_Container
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	// Options
	BOOL									m_enableCollisionData;

	// Data source
	IRuOptimalTerrain_DataSource*			m_dataSource;

	// Spatial partitioning structure
	CRuAABB									m_terrainBounds;
	CRuWorld_ChunkTree*						m_chunkTree;

	// Current view volume
	REAL									m_viewVolumeMarginSize;				//!< Size of margins to insert to the camera sweep volume
	CRuAABB									m_viewVolume;
	CRuAABB									m_standardViewVolume;

	// Backdrop system
	CRuLRUQueue<DWORD, CRuWorld_Backdrop *>*m_backdropQueue;

	// Entity manager
	CRuWorld_ObjectContainer*				m_objectContainer;					//!<
	CRuWorld_ObjectManager*					m_objectManager;

	CRuWorld_DoodadContainer*				m_doodadContainer;					//!< Doodad container
	CRuWorld_DoodadManager*					m_doodadManager;					//!< Doodad manager

	CRuArrayList<CRuOptimalTerrain_Chunk *>	m_enumeratedChunks;					//!< List of enumerated chunks
	CRuArrayList<CRuOptimalTerrain_Chunk *>	m_enumeratedBackdropChunks;			//!< List of enumerated backdrop chunks
	CRuArrayList<CRuGUID>					m_enumeratedEntityGUIDs;			//!<

	CRuArrayList<INT32>						m_enumeratedDoodadKeys;
	CRuArrayList<CRuWorld_DoodadCluster *>	m_enumeratedDoodadClusters;

	CRuEntity*								m_chunkContainer;
	CRuEntity*								m_backdropChunkContainer;
	CRuEntity*								m_grassContainer;
	CRuEntity*								m_waterContainer;

	// Visible entities
	CRuArrayList<CRuEntity *>				m_visibleChunks;
	CRuArrayList<CRuEntity *>				m_visibleGrass;
	CRuArrayList<CRuEntity *>				m_visibleEntities;
	CRuArrayList<CRuEntity *>				m_visibleWaterEntities;

	// Water body
	CRuArrayList<CRuWaterBodyDescriptor *>	m_waterBodyDescriptors;				//!< List of water body descriptors
	CRuHashMap<CRuGUID, CRuWaterBodyDescriptor *>*			m_waterBodyDescriptorMap;	//!< Map of GUID -> Water Body Descriptor

	CRuMatrix4x4							m_identityTransform;

	// Texture list
	CRuTextureList*							m_textureList;

	// Grass
	CRuGrassDatabase*						m_grassDatabase;
	CRuLRUQueue<DWORD, CRuEntity_Particle_Grass_Optimized *>*		m_grassQueue;

	// GAIA
	CRuGaia_DataStore*						m_gaiaObject;

	// Workspace
	CRuKeyHash<CRuGUID>*					m_entityDescriptorGUIDHash;			//!< Temporary hash for storing entity descriptor GUIDs

	CRuVector3								m_movementDirection;

	// View options
	INT32									m_terrainDetailLevel;
	BOOL									m_renderTerrain;
	BOOL									m_renderTerrainTexture;
	BOOL									m_renderBackdropTerrain;
	BOOL									m_renderObjects;
	BOOL									m_renderGrass;
	BOOL									m_renderWater;

	// Chunk preloading
	REAL									m_maxPreloadsPerSecond;
	REAL									m_preloadsPerSecond;
	CRuStack<INT32>							m_preloadList;

	// Backdrop chunk preloading
	REAL									m_maxBackdropPreloadsPerSecond;
	REAL									m_backdropPreloadsPerSecond;
	CRuStack<INT32>							m_backdropPreloadList;

	// Metrics
	char									m_metric_String[1024];
	INT32									m_metric_NumCacheMisses;
	INT32									m_metric_NumPreloadedChunks;
	INT32									m_metric_NumPreloadedBackdropChunks;
	INT32									m_metric_NumEnumeratedEntities;

public:
											CRuOptimalTerrain();
	virtual									~CRuOptimalTerrain();

	// IRuObject serialization
	virtual BOOL							SerializeFrom(IRuStream *stream);
	virtual BOOL							SerializeTo(IRuStream *stream);

	// Entity interface
	virtual BOOL							ResetStates();
	virtual BOOL							Update(float elapsedTime);

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

	// Initialization
	BOOL									Initialize();
	BOOL									AttachDataSource(IRuOptimalTerrain_DataSource *dataSource, BOOL enableCollisionData = FALSE);

	// Queue management
	BOOL									SetChunkQueueSize(INT32 chunkQueueSize);

	// LOD selection and frustum culling
	void									SetMovementDirection(const CRuVector3 &direction);
	void									SetCamera(CRuCamera *viewCamera);

	const CRuAABB&							GetTerrainBounds() const;

	INT32									GetNumXChunks() const;
	INT32									GetNumZChunks() const;
	INT32									GetChunkIteration(INT32 x, INT32 z);

	CRuGaia_Base*							GetGaiaObject() { return m_gaiaObject; }

	CRuArrayList<CRuGUID>&					GetEntityGUIDEnumeration() { return m_enumeratedEntityGUIDs; }
	void									GetDoodadEnumeration(CRuArrayList<INT32> **enumeratedKeys, CRuArrayList<CRuWorld_DoodadCluster *> **enumeratedClusters)
											{
												*enumeratedKeys = &m_enumeratedDoodadKeys;
												*enumeratedClusters = &m_enumeratedDoodadClusters;
											}


	BOOL									ClearEnumerationCache();

	// Other shit
	INT32									GetZoneID(const CRuSegment3 &segment);

	// Viewing options
	void									SetTerrainDetailLevel(INT32 detailLevel);
	void									ToggleOption_RenderTerrain();
	void									ToggleOption_RenderTerrainTexture();
	void									ToggleOption_RenderBackdropTerrain();
	void									ToggleOption_RenderObjects();
	void									ToggleOption_RenderGrass();
	void									ToggleOption_RenderWater();

	// Metrics
	INT32									GetNumMetricStrings();
	const char*								GetMetricString(INT32 stringIndex);

	void									DumpDebugInfo();

protected:
	BOOL									ClearBackdropQueue();
	static BOOL								ClearBackdropQueueCallback(const void *key, const void *data, void *userData);

	BOOL									ClearGrassQueue();
	static BOOL								ClearGrassQueueCallback(const void *key, const void *data, void *userData);
};

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

ruOBJECTMODULE_DEFINE(CRuObjectModule_OptimalTerrain_Chunk, CRuOptimalTerrain_Chunk)

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

#pragma managed

#endif
