#ifndef _RUENTITYACT_H_
#define _RUENTITYACT_H_

#include "../../Scene/Base/RuEntityBase.h"
#include "../../Scene/Base/RuEntityContainer.h"
#include "../../Scene/Base/RuEntityModel.h"
#include "../../Scene/Base/RuEntityController.h"
#include "../../Scene/Paperdoll/RuEntityCompositeModel.h"
#include "../../Scene/Paperdoll/RuEntityPaperdoll.h"

#pragma unmanaged

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

class CRuACTTemplateNode;
class CRuACTTemplateModifier;
class CRuACTTemplate;
class CRuACTEntity;
class CRuACTLoaderModule;

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

// Internal defines
#define ruACTHIERARCHY_POP		0x00000001
#define ruACTHIERARCHY_PUSH		0x00000002

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

/*

	ACT Template Notes
	------------------
	All entries in the entity are added to a single linear array. The tree structure is constructed from
	two parameters in each template node. First, each node has m_entityParent, which specifies the top-level node
	to which the node is connected. If this value is less than 0, then the node is a top-level node. Next,
	each node specifies an entity. This entity is parsed to determine its structure, and the structure is stored depth-first
	as a linear array. m_connectivityIndex specifies an index into this linear array, which is the actual interior node
	to which the node is connected.

	Ex:
			Entity 0		Entity 1
			--------        --------
				A              A
			   / \            / \
		      B   G          B   C
		     / \                / \
		    C   D              D   E
			   / \
			  E   F

	Entity 0 is stored as a linear array [ABCDEFG]. AKA in-order traversal.
	                                PUSH:[0110100]
	                                 POP:[0010111]

	Entity 1 is stored as a linear array [ABCDE]. 

	Presuming an entity wants to connect to the interior node C of entity 0, then its m_entityParent would be 0,
	and m_connectivityIndex would be 2. If it wanted to connect to the interior node E of entity 1, then its
	m_entityParent would be 0, and m_connectivityIndex would be 4.

*/

// NOTE TO SELF: Watch closely at the clone and ref counts -- If we clone an entity tree, and then add a different entity
//               to the tree, then delete the tree, what happens?

enum RuACTTemplateNodeType
{
	ruACTTEMPLATENODETYPE_DEFAULT			= 0,											//!< Node specifies an attached entity loaded from a stream
	ruACTTEMPLATENODETYPE_FRAME				= 1,											//!< Node specifies a CRuFrame
	ruACTTEMPLATENODETYPE_EMBEDDEDENTITY	= 2,											//!< Node specifies an embedded entity
	ruACTTEMPLATENODETYPE_EMBEDDEDACT		= 3,											//!< Node specifies an embedded ACT template

	ruACTTEMPLATENODETYPE_FORCE_DWORD		= 0x7FFFFFFF
};

enum RuACTTemplateModifierType
{
	ruACTTEMPLATEMODIFIERTYPE_DEFAULT		= 0,											//!< Specifies a default modifier
	ruACTTEMPLATEMODIFIERTYPE_TEXTURE		= 1,											//!< Specifies a texture override modifier
	ruACTTEMPLATEMODIFIERTYPE_SHADER		= 2,											//!< Specifies a shader override modifier
	ruACTTEMPLATEMODIFIERTYPE_ENTITY		= 3,											//!< Specifies an entity override modifier
	ruACTTEMPLATEMODIFIERTYPE_MATPRIORITY	= 4,											//!< Specifies a material priority override modifier

	ruACTTEMPLATENODEMODTYPE_FORCE_DWORD	= 0x7FFFFFFF
};

enum RuACTMotionControllerType
{
	ruACTMOTIONCONTROLLER_DEFAULT			= 0,											//!< Default controller

	ruACTMOTIONCONTROLLER_HIERARCHY			= 1,											//!< Hierarchy animation controller

	ruACTMOTIONCONTROLLER_ALPHA				= 1000,											//!< Alpha controller
	ruACTMOTIONCONTROLLER_COLOR				= 2,											//!< Color controller

	ruACTMOTIONCONTROLLER_LINEAR			= 3,											//!< Linear controller

	ruACTMOTIONCONTROLLER_MATERIAL			= 4,											//!< Material controller
	ruACTMOTIONCONTROLLER_EMBEDDEDHIERARCHY	= 5,											//!< Embedded hierarchy animation controller
	ruACTMOTIONCONTROLLER_MORPH				= 6,											//!< Morphing controller
	ruACTMOTIONCONTROLLER_EVENT				= 7,											//!< Event trigger controller
	ruACTMOTIONCONTROLLER_PARTICLESYSTEM	= 8,											//!< Particle system controller
	ruACTMOTIONCONTROLLER_ENTITY			= 9,											//!< Entity controller

	ruACTMOTIONCONTROLLER_FORCE_DWORD		= 0x7FFFFFFF
};

enum RuACTMotionChannel
{
	ruACTMOTIONCHANNEL_BODY_FULL			= 1,											//!< Full body
	ruACTMOTIONCHANNEL_BODY_LOWER			= 2,											//!< Lower body
	ruACTMOTIONCHANNEL_BODY_UPPER			= 4,											//!< Upper body
	ruACTMOTIONCHANNEL_BODY_HEAD			= 8,											//!< Head
	ruACTMOTIONCHANNEL_BODY_AUXILIARY		= 16,											//!< Auxiliary full body

	ruACTMOTIONCHANNEL_DEFAULT				= 0xFFFFFFFF									//!< Default unspecified channel
};

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

class CRuACTTemplateNode_Runtime
{
public:
	CRuACTTemplateNode*						m_sourceTemplateNode;							//!< Template node that constructed this runtime node

	// Non-persistent run-time data
	INT32									m_numEntities;									//!< Size of entity hierarchy array
	CRuEntity**								m_entities;										//!< Entity hierarchy array

public:
											CRuACTTemplateNode_Runtime();
											~CRuACTTemplateNode_Runtime();

	// Template queries
	RuACTTemplateNodeType					GetNodeType() const;							// Gets the type of the template node
	const char*								GetName() const;								// Gets the name of the entity
	INT32									GetParentIndex() const;							// Gets the parent index
	INT32									GetConnectivityIndex() const;					// Gets the entity connectivity index
	CRuACTTemplateNode*						GetTemplateNode();

	// Run-time data queries
	INT32									IndexOf(CRuEntity *entity) const;				// Gets the index of the entity in the entity array
	INT32									GetNumEntities();							// Gets the total number of entities at this node
	CRuEntity*								GetRootEntity();								// Gets the root entity of this template node
	CRuEntity*								GetChildEntity(INT32 entityIndex);				// Gets the child entity at the specified index

	// Run-time updating
	BOOL									UpdateTemplateNode(BOOL replaceTemplateNodes);	// Updates the source template node with the runtime node's persistence information
};

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

class CRuACTTemplateNode : public IRuObject
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	RuACTTemplateNodeType					m_nodeType;										//!< Type of the template node
	INT32									m_parentIndex;									//!< Top-level parent index of the entity
	INT32									m_connectivityIndex;							//!< Interior connectivity index (See notes)

	// Common data
	CRuVector3								m_frame_Scale;									//!< Scale factor
	CRuQuaternion							m_frame_Rotation;								//!< Rotation
	CRuVector3								m_frame_Translation;							//!< Translation

	// Type-specific data (m_nodeType = ruACTTEMPLATENODETYPE_DEFAULT)
	char*									m_default_EntityName;							//!< Name of the entity

	// Type-specific data (m_nodeType = ruACTTEMPLATENODETYPE_EMBEDDEDENTITY)
	CRuEntity*								m_embedded_Entity;								//!< Embedded entity

	// Type-specific data (m_nodeType = ruACTTEMPLATENODETYPE_EMBEDDEDACT)
	CRuACTTemplate*							m_embedded_ACT;									//!< Embedded ACT template

public:
											CRuACTTemplateNode();
	virtual									~CRuACTTemplateNode();

	// IRuObject serialization
	virtual IRuObject*						Clone(IRuObject *clonedObject = NULL);
	virtual BOOL							SerializeFrom(IRuStream *stream);
	virtual BOOL							SerializeTo(IRuStream *stream);

	// Template setup
	void									Set(INT32 parentIndex, INT32 connectivityIndex);
	void									Set(RuACTTemplateNodeType nodeType, INT32 parentIndex, INT32 connectivityIndex);
	void									Set(char *entityName, INT32 parentIndex, INT32 connectivityIndex);
	void									Set(CRuEntity *entity, INT32 parentIndex, INT32 connectivityIndex);
	void									SetOrientation(const CRuVector3 &scale, const CRuQuaternion &rotation, const CRuVector3 &translation);
	void									SetOrientation(REAL scale, const CRuQuaternion &rotation, const CRuVector3 &translation);

	void									SetParentIndex(INT32 parentIndex);				// Sets the parent index

	// Template queries
	RuACTTemplateNodeType					GetNodeType() const;							// Gets the type of the template node
	const char*								GetName() const;								// Gets the name of the entity
	INT32									GetParentIndex() const;							// Gets the parent index
	INT32									GetConnectivityIndex() const;					// Gets the entity connectivity index

	// Run-time data construction
	CRuACTTemplateNode_Runtime*				Construct();									// Constructs the template node's run-time data

	// Run-time updating
	BOOL									UpdateEmbeddedEntity(CRuEntity *embeddedEntity);

protected:
	void									Clear();
};

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

class CRuACTTemplateModifier : public IRuObject
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	RuACTTemplateModifierType				m_modifier;										//!< Type of the modifier
	INT32									m_templateIndex;								//!< Top-level template index of the target entity
	INT32									m_connectivityIndex;							//!< Interior index of the target entity

	// Common stuff
	INT32									m_meshOverride_Index;							//!< Index of the mesh to override

	// Type-specific data (m_nodeType = ruACTTEMPLATENODEMODTYPE_TEXTURE)
	RuTextureChannel						m_texOverride_Channel;							//!< Texture channel to override
	char*									m_texOverride_TextureName;						//!< Name of the texture to use

	// Type-specific data (m_nodeType = ruACTTEMPLATENODEMODTYPE_SHADER)
	char*									m_shaderOverride_ShaderName;					//!< Name of the shader to use

	// Type-specific data (m_nodeType = ruACTTEMPLATEMODIFIERTYPE_MATPRIORITY)
	INT32									m_matPriorityOverride_Priority;					//!< New material priority

public:
											CRuACTTemplateModifier();
	virtual									~CRuACTTemplateModifier();

	// IRuObject serialization
	virtual IRuObject*						Clone(IRuObject *clonedObject = NULL);
	virtual BOOL							SerializeFrom(IRuStream *stream);
	virtual BOOL							SerializeTo(IRuStream *stream);

	// Template queries
	RuACTTemplateModifierType				GetModifierType() const;
	INT32									GetTemplateIndex() const;
	INT32									GetConnectivityIndex() const;

	// Template setup
	void									Set(INT32 templateIndex, INT32 connectivityIndex);
	void									Set(RuACTTemplateModifierType modifierType, INT32 templateIndex, INT32 connectivityIndex);

	void									SetMeshIndex(INT32 meshIndex);

	const char*								Texture_GetName() const;
	void									Texture_SetMeshIndex(INT32 meshIndex);
	void									Texture_SetChannel(RuTextureChannel channel);
	void									Texture_SetName(const char *textureName);

	const char*								Shader_GetName() const;
	void									Shader_SetMeshIndex(INT32 meshIndex);
	void									Shader_SetName(const char *shaderName);

	INT32									MatPriority_GetPriority() const;
	void									MatPriority_SetPriority(INT32 priority);

	// Run-time construction
	BOOL									Construct(CRuACTTemplateNode_Runtime *target);
};

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

class CRuACTMotionNode : public IRuObject
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	INT32									m_templateIndex;								//!< Top-level parent index of the entity that the motion controls
	INT32									m_nodeIndex;									//!< Interior connectivity index (See notes)
	IRuEntity_Controller*					m_controller;									//!< Template controller

public:
											CRuACTMotionNode();
	virtual 								~CRuACTMotionNode();

	// IRuObject serialization
	virtual IRuObject*						Clone(IRuObject *clonedObject = NULL);
	virtual BOOL							SerializeFrom(IRuStream *stream);
	virtual BOOL							SerializeTo(IRuStream *stream);

	void									Set(INT32 templateIndex, INT32 nodeIndex);
	void									Set(RuACTMotionControllerType controllerType, INT32 templateIndex, INT32 nodeIndex);
	void									Set(IRuEntity_Controller *controller, INT32 templateIndex, INT32 nodeIndex);
	void									Set(INT32 templateIndex, INT32 nodeIndex, const char *animationName);

	INT32									GetTemplateIndex() const;
	INT32									GetNodeIndex() const;

	// Common functions
	IRuEntity_Controller*					Template_GetController();
	IRuEntity_Controller*					GetController();
};

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

class CRuACTMotion : public IRuObject
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	INT32									m_motionID;									//!< Motion ID
	CRuString								m_motionName;								//!< Motion name
	REAL									m_motionScale;								//!< Motion scale

	CRuArrayList<CRuACTMotionNode *>		m_motionNodes;

public:
											CRuACTMotion();
	virtual 								~CRuACTMotion();

	// IRuObject serialization
	virtual IRuObject*						Clone(IRuObject *clonedObject = NULL);
	virtual BOOL							SerializeFrom(IRuStream *stream);
	virtual BOOL							SerializeTo(IRuStream *stream);

	void									Set(INT32 motionID, const char *motionName);

	BOOL									AdjustTemplateIndex(INT32 offset);
	BOOL									DecrementTemplateIndexAfter(INT32 index);

	BOOL									AddMotionNode(RuACTMotionControllerType controllerType, INT32 templateIndex, INT32 nodeIndex);
	BOOL									AddMotionNode(IRuEntity_Controller *controller, INT32 templateIndex, INT32 nodeIndex);
	BOOL									AddMotionNode(INT32 templateIndex, INT32 nodeIndex, const char *animationName);

	BOOL									RemoveMotionNodeAt(INT32 motionNodeIndex);
	BOOL									RemoveMotionNodesAffecting(INT32 templateIndex);

	BOOL									Merge(CRuACTMotion *srcMotion, INT32 templateIndexOffset);

	INT32									GetMotionID() const;
	const CRuString&						GetMotionName() const;
	REAL									GetMotionScale() const;

	void									SetMotionID(INT32 motionID);
	void									SetMotionName(const char *motionName);
	void									SetMotionScale(REAL motionScale);

	INT32									GetNumMotionNodes() const;
	CRuACTMotionNode*						GetMotionNode(INT32 index);
};

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

class CRuACTTemplate : public IRuObject
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	CRuArrayList<CRuACTTemplateNode *>		m_templateNodes;								// Template nodes defining the structure of the ACT entity
	CRuArrayList<CRuACTTemplateModifier *>	m_templateModifiers;							// Template modifiers defining post-build operations on template nodes
	CRuArrayList<CRuACTMotion *>			m_motions;										// Motions available to this ACT entity

	friend									CRuACTLoaderModule;

public:
											CRuACTTemplate();
	virtual									~CRuACTTemplate();

	// IRuObject serialization
	virtual IRuObject*						Clone(IRuObject *clonedObject = NULL);
	virtual BOOL							SerializeFrom(IRuStream *stream);
	virtual BOOL							SerializeTo(IRuStream *stream);

	// Template modification
	BOOL									AddTemplateNode(INT32 parentIndex, INT32 connectivityIndex);
	BOOL									AddTemplateNode(char *entityName, INT32 parentIndex, INT32 connectivityIndex);
	BOOL									AddTemplateNode(CRuEntity *entity, INT32 parentIndex, INT32 connectivityIndex);
	BOOL									DeleteTemplateNode(INT32 templateNodeIndex);
	BOOL									MoveTemplateNode(INT32 templateNodeIndex, INT32 parentIndex, INT32 connectivityIndex);

	INT32									AddTemplateModifier(CRuACTTemplateModifier *modifier);
	BOOL									DeleteTemplateModifier(INT32 modifierIndex);

	BOOL									Merge(CRuACTTemplate *srcTemplate);
	BOOL									Sort();

	// Template query
	INT32									GetNumTemplateNodes() const;
	CRuACTTemplateNode*						GetTemplateNode(INT32 index);
	INT32									GetTemplateNodeIndex(CRuACTTemplateNode *templateNode);

	INT32									GetNumTemplateModifiers() const;
	CRuACTTemplateModifier*					GetTemplateModifier(INT32 index);

	// Motion modification
	BOOL									AddMotion(INT32 motionID, const char *motionName);
	BOOL									DeleteMotion(INT32 index);

	// Motion query
	INT32									GetNumMotions() const;
	CRuACTMotion*							GetMotion(INT32 index);
	CRuACTMotion*							GetMotionByID(INT32 id);
	CRuACTMotion*							GetMotionByName(const char *motionName);
	INT32									GetNextMotionID() const;

	INT32									GetMotionIDByName(const char *motionName) const;
	const char*								GetMotionNameByID(INT32 id) const;

	void									SetMotion(INT32 index, CRuACTMotion *motion);

	// ACT entity construction
	CRuACTEntity*							Construct();									// Constructs the ACT entity from the definition template
};

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

class CRuACTEntity : public CRuEntity_Container_Impl
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	struct InterpolatorDesc
	{
		CRuFrameHierarchy*					m_boundHierarchy;								//!< Entity to which this interpolator is bound
		CRuAnimationInterpolator*			m_interpolator;									//!< The interpolator
	};

	class ActiveMotionDesc
	{
	public:
		INT32								m_motionID;										//!< Currently playing motion ID. -1 = no motion.
		REAL								m_motionTime;									//!< Time marker of the current playing motion.
		UINT32								m_channel;										//!< Channel occupied by this motion descriptor.
		REAL								m_channelWeight;								//!< Blend weight of this channel
		BOOL								m_paused;										//!< Is the motion playback paused?
		CRuArrayList<InterpolatorDesc>		m_interpolators;								//!< Interpolators used by this motion.
		CRuArrayList<IRuEntity_Controller *>m_controllers;									//!< List of all active controllers for this motion.

		REAL								m_leadIn;										//!< Lead-in time
		REAL								m_leadOut;										//!< Lead-out time

		REAL								m_timeMultiplier;

		REAL								m_motionLength;									//!< Length of the motion
		REAL								m_fadeTime;										//!< Current motion fading time.
		REAL								m_fadeDuration;									//!< Motion fading duration.

	public:
											ActiveMotionDesc();
											~ActiveMotionDesc();

		CRuAnimationInterpolator*			GetInterpolator(CRuFrameHierarchy *hierarchy);

		void								Synchronize(ActiveMotionDesc *keyedMotion);
		void								Update(REAL elapsedTime, BOOL surpressControllers);
		void								Mix(REAL elapsedTime);
		void								UpdateMotionLength();

		BOOL								IsMotionComplete() const;

		void								ClearMotion(REAL transitionDuration, REAL fadeDuration);
		void								Clear();
	};

	struct AnimCompensatorDesc
	{
		UINT32								m_sourceChannel;
		UINT32								m_targetChannel;
		INT32								m_nodeID;
		REAL								m_strength;
	};

	// Constructed entity
	CRuACTTemplate*							m_template;										//!< Template from which this entity was generated

	INT32									m_numRuntimeNodes;								//!< Number of runtime nodes
	CRuACTTemplateNode_Runtime**			m_runtimeNodes;									//!< Runtime template nodes

	REAL									m_motionTime;									//!< Global time marker
	ActiveMotionDesc*						m_persistentMotion;
	CRuArrayList<ActiveMotionDesc *>		m_activeMotions;

	CRuArrayList<AnimCompensatorDesc>		m_pendingAnimCompensators;						//!< List of animation compensators that need to be applied

public:
											CRuACTEntity();
	virtual									~CRuACTEntity();

	// IRuObject interface
	virtual IRuObject*						Clone(IRuObject *clonedObject = NULL);
	virtual BOOL							SerializeFrom(IRuStream *stream);
	virtual BOOL							SerializeTo(IRuStream *stream);

	// CRuEntity overrides
	virtual BOOL							Update(REAL elapsedTime);

	// Template query
	CRuACTTemplate*							GetTemplate();
	INT32									GetNumRuntimeNodes() const;
	CRuACTTemplateNode_Runtime*				GetRuntimeNode(INT32 index);
	CRuEntity*								FindEntity(INT32 parentIndex, INT32 connectivityIndex);
	BOOL									UpdateTemplate(BOOL replaceTemplateNodes);

	// Motion channel operations
	BOOL									PlayPersistentMotion(BOOL forceOverride = TRUE);
	BOOL									PlayMotion(UINT32 channel, INT32 motionID, REAL blendTime, BOOL forceOverride = TRUE, REAL channelWeight = 1.0f, REAL leadIn = 0.0f, REAL leadOut = 0.0f, REAL expectedMotionScale = 0.0f);
	BOOL									SwapMotion(UINT32 srcchannel, UINT32 destchannel);
	BOOL									ClearMotion(UINT32 channel, REAL fadeDuration);
	BOOL									SynchronizeMotion(INT32 syncIndex);

	BOOL									PauseChannel(UINT32 channel);
	BOOL									ResumeChannel(UINT32 channel);

	BOOL									CreateAnimationCompensator(UINT32 sourceChannel, UINT32 targetChannel, INT32 nodeID, REAL strength);

	// Motion channel queries
	INT32									GetActiveMotionID(UINT32 channel) const;
	const char*								GetActiveMotionName(UINT32 channel) const;
	REAL									GetActiveMotionTime(UINT32 channel) const;
	REAL									GetMotionTime() const;

	BOOL									IsActiveMotion(UINT32 channel, const char *motionName);
	BOOL									IsMotionComplete(UINT32 channel) const;

	// Other queries
	INT32									GetMotionIDByName(const char *motionName);

	BOOL									CheckForController(UINT32 channel, const CRuRTTI &controllerType);
	BOOL									CheckForEvent(UINT32 channel, RuEntityEvent eventType, REAL *startTimeOut = NULL);

protected:
	INT32									GetMotionDescriptorIndex(UINT32 channel);
	void									CreatePendingAnimationCompensators();

	friend									CRuACTTemplate;
};

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

ruOBJECTMODULE_DEFINE(CRuObjectModule_ACTTemplateNode, CRuACTTemplateNode)
ruOBJECTMODULE_DEFINE(CRuObjectModule_ACTTemplateModifier, CRuACTTemplateModifier)
ruOBJECTMODULE_DEFINE(CRuObjectModule_ACTMotionNode, CRuACTMotionNode)
ruOBJECTMODULE_DEFINE(CRuObjectModule_ACTMotion, CRuACTMotion)
ruOBJECTMODULE_DEFINE(CRuObjectModule_ACTTemplate, CRuACTTemplate)
ruOBJECTMODULE_DEFINE(CRuObjectModule_ACTEntity, CRuACTEntity)

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

#pragma managed

#endif
