/*!
	@file RuRenderer.h

	Copyright (c) 2004-2008 All rights reserved
*/

#ifndef _RUENTITY_RENDERER_H_
#define _RUENTITY_RENDERER_H_

#include "../../Engine/Texture/RuTextureAtlas_Aggregate.h"

#include "../../Scene/Base/RuEntityBase.h"
#include "../../Scene/Base/RuEntityACT.h"
#include "../../Scene/Base/RuHierarchy_GR2.h"
#include "../../Scene/Container/RuContainer_Portal.h"
#include "../../Scene/Container/RuEntityAABBTree.h"
#include "../../Scene/Occlusion/RuEntity_AntiPortal.h"

// Temp... remove when done
#include "../../Scene/Base/RuSceneBase.h"

#pragma managed(push, off)

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

class CRuRenderer;
class CRuRenderContext;

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

struct RuClipPlaneSet
{
	CRuPlane4								m_clipPlanes[6];
};

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

struct RuLightingInfo
{
	INT32									m_entityIteration;								//!< Iteration number of the target entity
	INT32									m_lightingIteration;							//!< Iteration number of the lighting information
	CRuColor4								m_ambientLight;									//!< Ambient light color
	CRuColor4								m_shCoefficients[9];							//!< Object-level SH lighting coefficients
	RUCOLOR									m_specularColor;
	CRuVector3								m_specularDirection;							//!< Object-space specular light source position
};

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

struct RuCachedSkin
{
	DWORD									m_lastUpdateTime;								//!< Time of last skinning update, in milliseconds
	CRuSkinnableMesh*						m_mesh;											//!< Cached skinnable mesh

	CRuArrayList<IRuMesh *>*				m_subdividedMeshes;								//!< List of sub-divided meshes
};

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

struct RuCommandQueueEntry
{
	RuRendererCommand						m_command;										//!< Renderer command

	// This might be non-portable...?
	union
	{
		struct a
		{
			IRuEngineResource*				m_srcTexture;									//!< Source texture object
			IRuEngineResource*				m_dstTexture;									//!< Destination texture object
		}									RTCOPY;

		struct b
		{
			INT32							m_flushIdx;										//!< Starting index of the flush
			INT32							m_flushCount;									//!< Number of elements to flush
		}									FLUSH;

		struct c
		{
			INT32							m_executeIdx;									//!< Index of the render queue entry to execute
		}									EXECUTESINGLE;

		struct d
		{
			INT32							m_left;											//!< Left edge of scissor rect
			INT32							m_top;
			INT32							m_right;
			INT32							m_bottom;
		}									SCISSOR;

		struct e
		{
			IRuViewport*					m_viewport;										//!< Viewport
		}									VIEWPORTSET;

		struct f
		{
			DWORD							m_clearFlags;
			RUCOLOR							m_clearColor;
			REAL							m_clearZ;
			DWORD							m_clearStencil;
		}									VIEWPORTCLEAR;

		struct g
		{
			IRuObject*						m_renderTarget;
		}									RENDERTARGETSET;
	};
};

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

class CRuSkinCache : public IRuObject
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	CRuHashMap<CRuGUID, RuCachedSkin>*		m_skinningCache;
	CRuArrayList<CRuGUID>					m_gcSkinningCache;

public:
											CRuSkinCache();
	virtual									~CRuSkinCache();

	RuCachedSkin							GetCachedSkin(IRuMesh *mesh);

protected:
	void									FlushSkinningCache();
	BOOL									FlushSkinningCacheCallback(const void *key, void *data);

};

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

class CRuClipPlaneSet : public IRuObject
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	CRuPlane4								m_clipPlanes[6];

public:
											CRuClipPlaneSet();
	virtual									~CRuClipPlaneSet();

	const CRuPlane4&						GetClipPlane(INT32 index);
	void									SetClipPlane(INT32 index, const CRuPlane4 &clipPlane);
};

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

struct RuRenderQueueEntry
{
	// Render target and camera settings
	IRuViewport*							m_viewport;										//!< Viewport to use

	// Clip plane setting
	CRuClipPlaneSet*						m_clipPlaneSet;									//!< Clip plane set to use, NULL to disable

	// Transformation and parameters
	const CRuMatrix4x4*						m_worldTransform;								//!< World transform
	CRuParameterCache*						m_paramCache;									//!< Entity-specific parameter cache

	// Entity render information
	IRuMesh*								m_mesh;											//!< Mesh
	IRuMaterial*							m_material;										//!< Material
	IRuShader*								m_shader;										//!< Shader to use
	REAL									m_priority;										//!< Entity priority, calculated based on distance to camera and alpha blending settings

	CRuEntity*								m_entity;	// debugging only
};

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

class CRuRendererCommand : public IRuObject
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

public:
	virtual									~CRuRendererCommand();

	virtual void							Begin() = 0;
	virtual void							End() = 0;
	virtual void							Execute() = 0;
};

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

class CRuRendererCommand_CopyTexture : public CRuRendererCommand
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	IRuEngineResource*						m_srcTexture;									//!< Source texture object
	IRuEngineResource*						m_tgtTexture;									//!< Target texture object

public:
											CRuRendererCommand_CopyTexture();
	virtual									~CRuRendererCommand_CopyTexture();

	IRuEngineResource*						GetSourceTexture();
	void									SetSourceTexture(IRuEngineResource *srcTexture);

	IRuEngineResource*						GetTargetTexture();
	void									SetTargetTexture(IRuEngineResource *tgtTexture);

	virtual void							Begin();
	virtual void							End();
	virtual void							Execute();
};

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

class CRuRenderContext : public CRuRendererCommand
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	typedef boost::function<IRuMaterial * (IRuMaterial *)> MaterialOverrideCallback;		//!< Defined: IRuMaterial* MaterialOverrideCallback(IRuMaterial *sourceMaterial);

	struct EventListenerDesc
	{
		IRuEntityEventListener*				m_listener;										//!< Event listener
		void*								m_userData;										//!< User-specified listener data
		EventListenerDesc*					m_next;											//!< Pointer to the next event listener descriptor
	};

	// Base render context information
	CRuRenderer*							m_owner;										//!< Owner of this context
	IRuObject*								m_renderTarget;									//!< Render target on which this render context acts upon
	IRuSurface*								m_depthStencilSurface;							//!< Depth stencil surface, NULL = use default
	IRuViewport*							m_defaultViewport;								//!< Default viewport object;

	CRuMatrix4x4							m_identityTransform;

	INT32									m_iteration;									//!< Current iteration number

	// Event system data (deprecate)
	EventListenerDesc*						m_eventListeners;								//!< Linked list of event listeners
	CRuArrayList<RuEntityEvent>				m_eventSuppressionList;							//!< List of suppressed events

	// Parameter cache
	CRuParameterCache						m_paramCache0;									// Outer loop parameter cache
	CRuParameterCache						m_paramCache1;									// Inner loop parameter cache

	// Currents
	IRuViewport*							m_curViewport;
	CRuClipPlaneSet*						m_curClipPlaneSet;

	// Submit stacks
	CRuStack<IRuViewport *>					m_viewportStack;								//!< Viewport stack
	CRuStack<CRuClipPlaneSet *>				m_clipPlaneStack;								//!< Clip plane stack
	CRuStack<CRuCamera *>					m_cameraStack;									//!< Camera stack

	CRuArrayList<CRuParameterBlock *>		m_extraParameterBlocks;

	// Material override
	CRuStack<CRuRenderContext::MaterialOverrideCallback>	m_materialOverrideCallbackStack;
	CRuRenderContext::MaterialOverrideCallback				m_materialOverrideCallback;

	// Render queue
	CRuArrayList<RuRenderQueueEntry>		m_renderQueue;									//!< Rendering queue

	// Support objects
	BOOL									m_sortRenderQueue;								//!< Sort the render queue when rendering?
	CRuArrayList<UINT32>					m_sortCriterias[7];								//!< Sorting criteria
	CRuRadixSort							m_radixSort;									//!< Rendering queue sorting mechanism

	// Traversal stacks & queues
	CRuStack<CRuEntity *>					m_entityStack;									//!< Stack used for traversing an entity tree
	CRuQueue<const CRuParameterBlock*>		m_parameterBlocks;

	// Matrix stacks
	CRuStack<CRuMatrix4x4 *>				m_usedMatrixStack;								//!< Used temporary matrix stack
	CRuStack<CRuMatrix4x4 *>				m_unusedMatrixStack;							//!< Unused temporary matrix stack

	// Parameter cache stacks
	CRuStack<CRuParameterCache *>			m_usedParameterCacheStack;
	CRuStack<CRuParameterCache *>			m_unusedParameterCacheStack;

	// Statistics
	UINT32									m_numPrimitivesRendered;						//!< Total number of rendered primitives this frame
	UINT32									m_numBatchesIssued;								//!< Total number of batches issued this frame

	// Probes
	INT32									m_probe_3D_Submit;
	INT32									m_probe_3D_BatchCount;
	INT32									m_probe_3D_Flush;
	INT32									m_probe_3D_Skinning;
	INT32									m_probe_3D_Morphing;
	INT32									m_probe_3D_Sorting;
	INT32									m_probe_3D_Parameters;
	INT32									m_probe_3D_Lighting;
	INT32									m_probe_3D_TransformSetup;
	INT32									m_probe_3D_ParameterSetup;
	INT32									m_probe_3D_TextureSetup;
	INT32									m_probe_3D_RenderAllPasses;
	INT32									m_probe_3D_Render;
	INT32									m_probe_3D_RenderD3D;
	INT32									m_probe_Engine_InstancingTime;
	INT32									m_probe_Misc_AntiPortalDiscarded;

public:
											CRuRenderContext(CRuRenderer *owner);
	virtual									~CRuRenderContext();

	BOOL									SetRenderTarget(IRuObject *renderTarget);
	BOOL									SetDefaultClearParameters(DWORD clearFlags, RUCOLOR color, REAL z, DWORD stencil);

	void									EnableRenderQueueSorting(BOOL enable);

	BOOL									RegisterListener(IRuEntityEventListener *listener, void *userData);
	BOOL									UnregisterListener(IRuEntityEventListener *listener);
	void									RaiseEvent(CRuEntity *entity, IRuObject *eventProxy, INT32 eventID, PTRVALUE eventParameter0, PTRVALUE eventParameter1, const char *eventString, REAL eventDuration, BOOL propagateEvent = TRUE);

	BOOL									Viewport_Push(IRuViewport *viewport);
	BOOL									Viewport_Pop();

	BOOL									ClipPlaneSet_Push(CRuClipPlaneSet *clipPlaneSet);
	BOOL									ClipPlaneSet_Pop();

	BOOL									ParameterBlock_Push(CRuParameterBlock *paramBlock);
	BOOL									ParameterBlock_Pop();

	BOOL									Camera_Push(CRuCamera *camera);
	BOOL									Camera_Pop();

	BOOL									EventSuppression_Push(RuEntityEvent suppressedEvent);
	BOOL									EventSuppression_Pop();

	BOOL									MaterialOverrideCallback_Push(CRuRenderContext::MaterialOverrideCallback overrideCallback);
	BOOL									MaterialOverrideCallback_Pop();

	BOOL									Submit(CRuEntity *sceneRoot, BOOL noStatusMod = FALSE);
	BOOL									Submit(CRuEntity *sceneRoot, IRuMesh *mesh, IRuMaterial *material);

	virtual void							Begin();
	virtual void							End();
	virtual void							Execute();

	void									Flush();

protected:
	BOOL									IsEventSuppressed(RuEntityEvent entityEvent);

	void									Submit_Internal(CRuEntity *sceneRoot, CRuMatrix4x4 const *worldTransform, IRuMesh *mesh, IRuMaterial *material, BOOL firstEntityInSeries);

	BOOL									SelectRenderTarget();
	void									UpdateDefaultViewportParameters();
	void									SortRenderQueue();

	CRuMatrix4x4*							CreateMatrix();
	void									FlushMatrixStack();

	CRuParameterCache*						CreateParameterCache();
	void									FlushParameterCacheStack();

	RuRenderQueueEntry&						AllocateRenderQueueEntry();
	void									UndoAllocateRenderQueueEntry();

	friend CRuRenderer;
};

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

class CRuRenderer : public IRuObject
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	// Renderer commands & stacks
	CRuArrayList<CRuRendererCommand *>		m_allocatedRendererCommands;					//!< List of allocated renderer commands
	CRuStack<CRuRenderContext *>			m_unusedRenderContexts;							//!< Stack of unused render contexts

	// Default parameter block
	CRuParameterBlock						m_defaultParamBlock;							//!< Default parameter block

	// Cache
	CRuSkinCache*							m_skinCache;									//!< Skinning cache

	// Current scene state
	INT32									m_iterationNumber;								//!< Iteration number of the current scene

	// Support textures
	IRuTexture*								m_nullTexture;									//!< NULL texture map
	IRuTexture*								m_clampTexture;									//!< Clamping texture map

	// Lighting prop
	CRuAABBTree*							m_lightTree;									//!< Light tree
	CRuArrayList<CRuEntity_Light *>			m_recycledLightEntities;						//!< Unused light structures
	DWORD									m_specularColor;
	CRuVector3								m_specularDirection;

	CRuArrayList<RuLightingInfo *>			m_activeLightingInfo;

	CRuArrayList<CRuEntity *>				m_enumeratedLights;
	CRuArrayList<REAL>						m_enumeratedLightDistances;

	// Traversal structures
	CRuStack<CRuEntity *>					m_entityStack;									//!< Stack used for traversing an entity tree

	// 3rd-order spherical harmonic constants
	REAL									m_sh9_Const1;
	REAL									m_sh9_Const2;
	REAL									m_sh9_Const3;
	REAL									m_sh9_Const4;
	REAL									m_sh9_Const5;

	// Sorting mechanism
	CRuRadixSort							m_radixSort2;									//!< Sorting mechanism

public:
											CRuRenderer();
	virtual									~CRuRenderer();

	virtual CRuRenderContext*				CreateRenderContext();
	virtual BOOL							DestroyRenderContext(CRuRenderContext *renderContext);

//	virtual BOOL							LinkRenderContexts(CRuRenderContext *primaryContext, CRuRenderContext *dependentContext);

	virtual void							Begin();
	virtual void							End();

	// Light prop setup
	virtual void							SetLightingProp(CRuEntity *lightingProp);
	virtual void							SetSpecularColor(DWORD specularColor);
	virtual void							SetSpecularDirection(const CRuVector3 &specularDir);

	void									AddtoSH9(CRuColor4 *shCoefficients, CRuColor4 &lightColor, CRuVector3 &dirN);
	RuLightingInfo*							BuildLightingInfo(IRuEntity_Renderable *renderableEntity);

//	virtual UINT8							GetNextStencilID();

	virtual CRuParameterBlock&				GetDefaultParameterBlock();
	virtual CRuSkinCache*					GetSkinCache();

//	virtual void							ExecuteCommandQueue();

protected:
	RuCommandQueueEntry&					AllocateCommandQueueEntry();
	void									UndoAllocateCommandQueueEntry();
};

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

__forceinline RuRenderQueueEntry& CRuRenderContext::AllocateRenderQueueEntry()
{
	INT32 allocatedIndex = m_renderQueue.Count();

	if(m_renderQueue.SetNumEntries(allocatedIndex + 1) == FALSE)
	{
		m_renderQueue.Add(RuRenderQueueEntry());
	}

	return m_renderQueue[allocatedIndex];
}

__forceinline void CRuRenderContext::UndoAllocateRenderQueueEntry()
{
	if(m_renderQueue.Count() > 0)
	{
		m_renderQueue.SetNumEntries(m_renderQueue.Count() - 1);
	}
}

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

#pragma managed(pop)

#endif
