///////////////////////////////////////////////////////////////////////////////
//
// File: Asura_ClientContainerInstance.h
//
// Created by Martin G.Harris (6/5/2008)
// 
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
// Instance of a container owned by a client entity.
// Contains the data that differs between instances, such as sound event handles,
// PFX handles, photon handles etc.
//
///////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_CLIENTCONTAINERINSTANCE_H__
#define __ASURA_CLIENTCONTAINERINSTANCE_H__

#ifdef ASURA_USE_CONTAINER_SYSTEM

// Includes
#include "Core/Asura_Maths.h"
#include "Entity/Container/Asura_Container.h"

// Forward declarations
class Asura_Animation;
class Asura_AnimationAttachment_ContainerInstance_Internal;
class Asura_ClientContainerInstance;
class Asura_ClientContainerInstance_Attachment;
class Asura_ClientContainerInstance_Collection;
class Asura_ClientEntity_Container;
class Asura_LOS_InputData;
class Asura_LOS_OutputData;
class Asura_PFX_Entity_Effect;
class Asura_Physics_Object;
class Asura_Shadow;

/////////////////////////////
// Asura_Container_Functor //
/////////////////////////////
class Asura_Container_Functor
{
public:
	Asura_Container_Functor() {}
	virtual ~Asura_Container_Functor() {}

	virtual void Process(const Asura_ClientContainerInstance* pxEntry) = 0;
};

////////////////////////////////////////
// Asura_ContainerInstance_Properties //
////////////////////////////////////////
// On initialising a container instance it reports back what properties it has.
// E.g. the number of dynamic physics objects, whether it has shadows, collisions, render meshes,
// needs LOS, needs updating etc. This allows for some optimisations in the client entity.
class Asura_ContainerInstance_Properties
{
public:
	Asura_ContainerInstance_Properties(Asura_ClientEntity_Container* pxOwner = 0,
									   Asura_Guid uEntityGuid = ASURA_GUID_UNREGISTERED)
		: m_iNumUpdate(0)
		, m_iNumRender(0)
		, m_iNumRenderShadow(0)
		, m_iNumPhysics(0)
		, m_iNumDynamicPhysics(0)
		, m_iNumLOS(0)
		, m_iNumMovingLOS(0)
		, m_pxOwner(pxOwner)
		, m_uEntityGuid(uEntityGuid)
	{
	}

	// Add to the dynamic physics count and store the physics pointer in the entity.
	// Return true, if we initialised the pos/ori data in the physics object.
	bool AddDynamicPhysics(Asura_Physics_Object* pxPhysics, u_int uUniqueID);

	// Get owner entity guid.
	Asura_Guid GetOwnerGuid() const { return m_uEntityGuid; }

public:
	int m_iNumUpdate;				// Number of containers that need call to Update.
	int m_iNumRender;				// Number of containers that need call to Render.
	int m_iNumRenderShadow;			// Number of containers that need call to RenderShadow.
	int m_iNumPhysics;				// Number of physics objects.
	int m_iNumDynamicPhysics;		// Number of dynamic physics objects.
	int m_iNumLOS;					// Number of containers that have LOS enabled.
	int m_iNumMovingLOS;			// Number of containers that are not static with LOS enabled.

private:
	Asura_ClientEntity_Container* const m_pxOwner;
	const Asura_Guid m_uEntityGuid;
};


///////////
// Flags //
///////////
// Flags for container instances (stored in m_uInstanceFlags)
enum ASURA_CONTAINER_INSTANCE_FLAGS
{
	// Mask for flags shared by all instance types.
	ASURA_CONTAINER_INSTANCE_FLAG_MASK_BASE					= 0x0000ffff,

	// Mask for flags whose meaning depends on the specific instance type.
	ASURA_CONTAINER_INSTANCE_FLAG_MASK_DERIVED				= 0xffff0000,

	// Base flags (all instance types have these flags)
	ASURA_CONTAINER_INSTANCE_FLAG_ATTACHED_TO_MARKER		= 0x00000001,	// Container attached to marker or physics?
	ASURA_CONTAINER_INSTANCE_FLAG_DYNAMIC_PHYSICS			= 0x00000002,	// Has dynamic physics object?
	ASURA_CONTAINER_INSTANCE_FLAG_STROBE_ON					= 0x00000004,	// Enabled after strobing taken into account?
	ASURA_CONTAINER_INSTANCE_FLAG_USING_GRAPH				= 0x00000008,	// Uses a graph colour?
	ASURA_CONTAINER_INSTANCE_FLAG_SHARED_PHYSICS			= 0x00000010,	// Uses shared dynamic physics object?
	ASURA_CONTAINER_INSTANCE_FLAG_DISABLED					= 0x00000020,	// Disabled (due to transition only flag)?

	// Other...
	// Mesh
	ASURA_CONTAINER_INSTANCE_MESH_FLAG_INITIALISEDPOSORI	= 0x00010000,	// Already initialised pos/ori (so teleport not needed)?
};

// Constants
const u_int uASURA_CONTAINER_UNIQUE_ID_INVALID = 0xffffffff;
const u_char ucASURA_CONTAINER_MAX_CACHED_INDEX = 0xff;

const u_int uASURA_CONTAINER_MAX_STORED_ANIM_TIMERS = 2;

///////////////////////////////////
// Asura_ClientContainerInstance //
///////////////////////////////////
class Asura_ClientContainerInstance
{
public:
	Asura_ClientContainerInstance(const Asura_Container* pxContainer);
	virtual ~Asura_ClientContainerInstance() {}

	Asura_Hash_ID GetContainerHash() const;	// Returns hash of container.
	u_int GetClassification() const;		// Returns classification of container.
	Asura_Guid GetOwnerGuid() const;		// Returns owner entity guid.

	// Returns classification of container, skipping over attachments.
	u_int GetClassificationSkipAttachment() const;

	u_int GetUniqueID() const { return m_uUniqueID; } // Returns unique ID, used as a LOS attachment ID.
	void SetUniqueID(u_int uUniqueID) { m_uUniqueID = uUniqueID; }

	// Change to a new state.
	// Should never be called with state "off"; the parent should delete the child in that case.
	virtual void ChangeState(const Asura_Container_InputState& xInputState) {}
	// Strobe switches some other container instances on/off.
	virtual void SetStrobe(bool bStrobeOn);
	// Set any float parameter.
	virtual void SetParam(float fValue) {}

	virtual void TeleportTo(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation) {}
	virtual void Render(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation) {}
	virtual void RenderShadow(Asura_Shadow* pxShadow) {}
	virtual void Update() {}

	// Get bounding sphere for skin/animation. Return true if set. Return false if there is no relevant sphere.
	virtual bool GetTightBoundingSphere(float& fRadius, Asura_Vector_3& xCentre) const { return false; }
	virtual bool GetCoarseBoundingSphere(float& fRadius, Asura_Vector_3& xCentre) const { return false; }
	virtual bool GetCoarseBoundingSphereIncludingEffects(float& fRadius, Asura_Vector_3& xCentre) const { return false; }
	
	// Returns the conservative axially-aligned WORLD SPACE box covering the container.
	// Return true if the AABB was set.
	virtual bool GetLineOfSightAABB( Asura_Bounding_Box& xAABB ) { return false; }
	
	// Do LOS test. Return true if the output data is changed.
	virtual bool LOSTest(const Asura_LOS_InputData* pxInput, Asura_LOS_OutputData* pxOutput) const { return false; }

	// Get the animation, if it has one (typically used for mesh containers).
	virtual const Asura_Animation* GetAnimation() const { return 0; }
	virtual Asura_Animation* GetAnimation() { return 0; }

	// Initialise guid and attachments etc.
	virtual void Initialise(Asura_ClientContainerInstance_Collection* pxParentCollection,
							Asura_ContainerInstance_Properties& xData)
	{
		m_pxParentCollection = pxParentCollection;
	}

	// Search for the relevant marker point. If found then set up the attachment and return true.
	virtual bool FindMarkerPoint(Asura_ClientContainerInstance_Attachment* pxAttachment) const { return false; }

	virtual void SetAttachedToMarkerPoint() { SetFlag(ASURA_CONTAINER_INSTANCE_FLAG_ATTACHED_TO_MARKER); }

	// Flags
	inline bool GetFlag(u_int uFlag) const	{ return (m_uInstanceFlags & uFlag) != 0; }
	inline void SetFlag(u_int uFlag)		{ m_uInstanceFlags |= uFlag; }
	inline void ResetFlag(u_int uFlag)		{ m_uInstanceFlags &= ~uFlag; }
	inline void ToggleFlag(u_int uFlag)		{ m_uInstanceFlags ^= uFlag; }

	inline bool IsAttachedToMarker() const { return GetFlag(ASURA_CONTAINER_INSTANCE_FLAG_ATTACHED_TO_MARKER); }

	// Get the current graph colour. Caller should initialise the output data before calling.
	// Returns true if results have been returned (i.e there are enabled GraphColour containers).
	virtual bool GetGraphColour(Asura_Container_GraphColour_Output& xOutput) { return false; }

	// Returns this, unless it is a collection (returns null),
	// or attachment (returns GetRelevantInstance() applied to child or null if no child).
	virtual Asura_ClientContainerInstance* GetRelevantInstance() { return this; }

	// Find instance from unique ID.
	virtual Asura_ClientContainerInstance* FindFromUniqueID(u_int uUniqueID)
	{
		return (uUniqueID == 0) ? this : 0;
	}

	// Search for the container that has the given rigid body handle.
	virtual Asura_ClientContainerInstance* FindFromRigidBodyHandle(Asura_Handle iHandle) { return 0; }

	// Traverse the tree with callback functor.
	virtual void Traverse(Asura_Container_Functor& xFunctor) const { xFunctor.Process(this); }

protected:
	// Helper function
	void ToolsRender(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);

	// Return the child (if it exists), ignoring collections (which return null) and skipping past attachments.
	static Asura_ClientContainerInstance* ConvertChild(Asura_ClientContainerInstance* pxChild)
	{
		return (pxChild ? pxChild->GetRelevantInstance() : 0);
	}

	// Helper. Returns true if we need to update from the output.
	bool GetGraphColourFromCollection(Asura_Container_GraphColour_Output& xOutput);

protected:
	const Asura_Container* const m_pxContainer;

	// Parent collection instance (might be a grandparent).
	Asura_ClientContainerInstance_Collection* m_pxParentCollection;

	u_int m_uInstanceFlags;

	u_int m_uUniqueID; // Identifier for use by LOS code.
};


////////////////////////////////////////
// Asura_ClientContainerInstance_Mesh //
////////////////////////////////////////
class Asura_ClientContainerInstance_Mesh : public Asura_ClientContainerInstance
{
	typedef Asura_ClientContainerInstance PARENT;

public:
	Asura_ClientContainerInstance_Mesh(const Asura_Container_Mesh* pxContainer);
	virtual ~Asura_ClientContainerInstance_Mesh();

	// Get the animation.
	virtual const Asura_Animation* GetAnimation() const { return m_pxAnimation; }
	virtual Asura_Animation* GetAnimation() { return m_pxAnimation; }

	const Asura_Physics_Object* GetPhysicsObject() const { return m_pxPhysicsObject; }
		  Asura_Physics_Object* GetPhysicsObject()		 { return m_pxPhysicsObject; }

	virtual void TeleportTo(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Render(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void RenderShadow(Asura_Shadow* pxShadow);
	virtual void Update();

	virtual bool GetTightBoundingSphere(float& fRadius, Asura_Vector_3& xCentre) const;
	virtual bool GetCoarseBoundingSphere(float& fRadius, Asura_Vector_3& xCentre) const;

	virtual bool GetLineOfSightAABB( Asura_Bounding_Box& xAABB );
	virtual bool LOSTest(const Asura_LOS_InputData* pxInput, Asura_LOS_OutputData* pxOutput) const;

	virtual void Initialise(Asura_ClientContainerInstance_Collection* pxParentCollection,
							Asura_ContainerInstance_Properties& xData);

	// Search for the relevant marker point. If found then set up the attachment and return true.
	virtual bool FindMarkerPoint(Asura_ClientContainerInstance_Attachment* pxAttachment) const;

	// Set this mesh as being attached. Its physics block is converted to be external control.
	virtual void SetAttachedToMarkerPoint();

	// Apply a linear impulse to a point on the object (position in world-space)
	void ApplyImpulse( const Asura_Vector_3& xImpulse, const Asura_Vector_3& xPosition, bool bIgnoreMass = true );

	// Search for the container that has the given rigid body handle.
	virtual Asura_ClientContainerInstance* FindFromRigidBodyHandle(Asura_Handle iHandle);

	bool IsIgnoreForDamage() const;

	// Get the base physics position/orientation.
	// Returns true, if the position and orientation were set.
	bool GetBasePosOriForAttachment(Asura_Vector_3& xPosition, Asura_Matrix_3x3& xOrientation) const;

	const Asura_Container_Mesh* GetContainer() const { return static_cast<const Asura_Container_Mesh*>(m_pxContainer); }

protected:
	void CreateAnimation(const Asura_Container_Mesh* pxContainer);
	void SetParamFromContainer(const Asura_Container_Mesh* pxContainer);
	void CreatePhysicsObject(const Asura_Container_Mesh* pxContainer);
	void DestroyPhysicsObject();
	void MovePhysics(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	void CopyPhysicsProperties();
	void UpdateFromGraphColour();

protected:
	Asura_Animation* m_pxAnimation;
	Asura_Physics_Object* m_pxPhysicsObject;

#if defined(ASURA_USE_NEW_VISIBILITY_SYSTEM)
	Asura_Visibility_Query	m_xShadowVisibilityQuery;
#endif	//defined(ASURA_USE_NEW_VISIBILITY_SYSTEM)
};


//////////////////////////////////////////////
// Asura_ClientContainerInstance_SoundEvent //
//////////////////////////////////////////////
class Asura_ClientContainerInstance_SoundEvent : public Asura_ClientContainerInstance
{
	typedef Asura_ClientContainerInstance PARENT;

public:
	Asura_ClientContainerInstance_SoundEvent(const Asura_Container_SoundEvent* pxContainer);
	virtual ~Asura_ClientContainerInstance_SoundEvent();

	virtual void TeleportTo(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Render(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Update();

	virtual void Initialise(Asura_ClientContainerInstance_Collection* pxParentCollection,
							Asura_ContainerInstance_Properties& xData);
	
protected:
	Asura_Handle m_iSoundEventHandle;
};


//////////////////////////////////////////////
// Asura_ClientContainerInstance_Collection //
//////////////////////////////////////////////
class Asura_ClientContainerInstance_Collection : public Asura_ClientContainerInstance
{
	typedef Asura_ClientContainerInstance PARENT;

public:
	Asura_ClientContainerInstance_Collection(const Asura_Container_Collection* pxContainer);
	virtual ~Asura_ClientContainerInstance_Collection();

	// Change to a new state.
	virtual void ChangeState(const Asura_Container_InputState& xInputState);
	virtual void SetStrobe(bool bStrobeOn);
	bool IsOff() const { return m_xInputState.IsOff(); }

	virtual void TeleportTo(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Render(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void RenderShadow(Asura_Shadow* pxShadow);
	virtual void Update();

	virtual bool GetTightBoundingSphere(float& fRadius, Asura_Vector_3& xCentre) const;
	virtual bool GetCoarseBoundingSphere(float& fRadius, Asura_Vector_3& xCentre) const;
	virtual bool GetCoarseBoundingSphereIncludingEffects(float& fRadius, Asura_Vector_3& xCentre) const;
	
	// Returns the conservative axially-aligned WORLD SPACE box covering the container.
	// Return true if the AABB was set.
	virtual bool GetLineOfSightAABB( Asura_Bounding_Box& xAABB );
	
	// Do LOS test. Return true if the output data is changed.
	virtual bool LOSTest(const Asura_LOS_InputData* pxInput, Asura_LOS_OutputData* pxOutput) const;

	virtual void Initialise(Asura_ClientContainerInstance_Collection* pxParentCollection,
							Asura_ContainerInstance_Properties& xData);

	// Search for the relevant marker point. If found then set up the attachment and return true.
	virtual bool FindMarkerPoint(Asura_ClientContainerInstance_Attachment* pxAttachment) const;

	virtual void SetAttachedToMarkerPoint();

	// Get the current graph colour. Caller should initialise the output data before calling.
	// Returns true if results have been returned (i.e there are enabled GraphColour containers).
	virtual bool GetGraphColour(Asura_Container_GraphColour_Output& xOutput);

	virtual Asura_ClientContainerInstance* GetRelevantInstance() { return 0; }

	// Find instance from unique ID.
	virtual Asura_ClientContainerInstance* FindFromUniqueID(u_int uUniqueID);

	// Search for the container that has the given rigid body handle.
	virtual Asura_ClientContainerInstance* FindFromRigidBodyHandle(Asura_Handle iHandle);

	// Find the relevant health container.
	const class Asura_ClientContainerInstance_Health* FindHealth() const;

	// Find the relevant physics properties.
	const class Asura_ClientContainerInstance_Physics* FindPhysics() const;

	// Find the blueprint in the collection.
	// Set bIncludeDisabled to include both enabled and disabled blueprint containers.
	// If uBlueprintTypeHash != ASURA_HASH_ID_UNSET, then only return blueprints of the specified type.
	// Returns NULL if no relevant blueprint found.
	const Asura_Container_Blueprint* FindBlueprint(Asura_Hash_ID uBlueprintTypeHash, bool bIncludeDisabled) const;

	// Return true if the damage override state will change as a
	// result of the change in health.
	bool HasDamageOverrideChanged(float fOldHealthPercent, float fNewHealthPercent) const;

	// The owner entity.
	Asura_Guid GetOwnerEntityGuid() const { return m_uOwnerGuid; }

	// Get the base physics position/orientation.
	// Returns true, if the position and orientation were set.
	bool GetBasePosOriForAttachment(Asura_Vector_3& xPosition, Asura_Matrix_3x3& xOrientation) const;

	// Traverse the tree with callback functor.
	virtual void Traverse(Asura_Container_Functor& xFunctor) const;

	// Store/restore the animation timers across state changes.
	void StoreAnimTimer(Asura_Hash_ID uAnimHash, float fTimer);
	float RestoreAnimTimer(Asura_Hash_ID uAnimHash, bool bRestore, bool bRandomise);

protected:
	void DeleteAllChildren();
	void RefreshCachedChildData();

	// Store the positions of up to 4 GraphColours and 1 physics property (that are enabled).
	struct CachedChildData
	{
		CachedChildData() { Reset(); }

		void Reset()
		{
			ResetColourGraphs();
			m_ucPhysics = ucASURA_CONTAINER_MAX_CACHED_INDEX;
			m_ucSharedPhysics = ucASURA_CONTAINER_MAX_CACHED_INDEX;
		}

		void ResetColourGraphs()
		{
			m_aucGraph[0] = m_aucGraph[1] = m_aucGraph[2] = m_aucGraph[3] = ucASURA_CONTAINER_MAX_CACHED_INDEX;
			m_ucNumGraphColours = ucASURA_CONTAINER_MAX_CACHED_INDEX;
		}

		u_char m_aucGraph[4];		// Up to 4 enabled colour graphs.
		u_char m_ucNumGraphColours;	// Number of enabled graph colours.
		u_char m_ucPhysics;			// Up to 1 enabled physics property.
		u_char m_ucSharedPhysics;	// Mesh with shared dynamic physics.
	};

	// Store up to uASURA_CONTAINER_MAX_STORED_ANIM_TIMERS animation/timer pairs which are preserved
	// across state changes.
	struct CachedAnimTimers
	{
		CachedAnimTimers() { Reset(); }
		
		void Reset();
		
		// Search for animation/timer. Return true if found.
		bool GetAnimTimer(Asura_Hash_ID uAnimHash, float& fTimer);

		// Store animation/timer. Return true if stored.
		bool StoreAnimTimer(Asura_Hash_ID uAnimHash, float fTimer);
	
	private:
		Asura_Hash_ID m_auAnimHash[uASURA_CONTAINER_MAX_STORED_ANIM_TIMERS];
		float m_afParametricTimer[uASURA_CONTAINER_MAX_STORED_ANIM_TIMERS];
	};

protected:
	u_int m_uNumChildren;
	Asura_ClientContainerInstance** m_ppxChildren;
	Asura_Guid m_uOwnerGuid; // Entity guid.

	// Current state of collection (this is the input state before applying damage overrides).
	Asura_Container_InputState m_xInputState;

	// Cached child data
	CachedChildData m_xCachedChildData;
	// Cached animation timers
	CachedAnimTimers m_xCachedAnimTimers;
};

//////////////////////////////////////////////
// Asura_ClientContainerInstance_Attachment //
//////////////////////////////////////////////
class Asura_ClientContainerInstance_Attachment : public Asura_ClientContainerInstance
{
	typedef Asura_ClientContainerInstance PARENT;

public:
	Asura_ClientContainerInstance_Attachment(const Asura_Container_Attachment* pxContainer);
	virtual ~Asura_ClientContainerInstance_Attachment();

	// Change to a new state.
	virtual void ChangeState(const Asura_Container_InputState& xInputState);

	Asura_Hash_ID GetMarkerPointHash() const;

	virtual void TeleportTo(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Render(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void RenderShadow(Asura_Shadow* pxShadow);
	virtual void Update();

	virtual bool GetTightBoundingSphere(float& fRadius, Asura_Vector_3& xCentre) const;
	virtual bool GetCoarseBoundingSphere(float& fRadius, Asura_Vector_3& xCentre) const;
	virtual bool GetCoarseBoundingSphereIncludingEffects(float& fRadius, Asura_Vector_3& xCentre) const;

	// Returns the conservative axially-aligned WORLD SPACE box covering the container.
	// Return true if the AABB was set.
	virtual bool GetLineOfSightAABB( Asura_Bounding_Box& xAABB );
	
	// Do LOS test. Return true if the output data is changed.
	virtual bool LOSTest(const Asura_LOS_InputData* pxInput, Asura_LOS_OutputData* pxOutput) const;

	virtual void Initialise(Asura_ClientContainerInstance_Collection* pxParentCollection,
							Asura_ContainerInstance_Properties& xData);

	// Search for the relevant marker point. If found then set up the attachment and return true.
	virtual bool FindMarkerPoint(Asura_ClientContainerInstance_Attachment* pxAttachment) const;

	virtual void SetAttachedToMarkerPoint();

	// Set up cached marker data.
	void SetMarkerData( const Asura_ClientContainerInstance_Mesh* pxMeshContainer,
						int iMarkerIndex,
						Asura_AnimationAttachment_ContainerInstance_Internal* pxAnimAttachment)
	{
		m_pxParentMeshForMarker = pxMeshContainer;
		m_iMarkerIndex = iMarkerIndex;
		m_pxAnimAttachment = pxAnimAttachment;
	}

	const Asura_ClientContainerInstance_Mesh* GetParentMeshForMarker() const { return m_pxParentMeshForMarker; }
	int GetMarkerIndex() const { return m_iMarkerIndex; }

	Asura_ClientContainerInstance* GetChild() const { return m_pxChild; }

	const Asura_Container_Attachment* GetContainer() const;

	void ApplyOffset(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOri,
					 Asura_Vector_3& xNewPosition, Asura_Matrix_3x3& xNewOri) const;

	virtual Asura_ClientContainerInstance* GetRelevantInstance()
	{
		return (m_pxChild ? m_pxChild->GetRelevantInstance() : 0);
	}

	// Find instance from unique ID.
	virtual Asura_ClientContainerInstance* FindFromUniqueID(u_int uUniqueID)
	{
		return (m_pxChild ? m_pxChild->FindFromUniqueID(uUniqueID) : 0);
	}

	// Search for the container that has the given rigid body handle.
	virtual Asura_ClientContainerInstance* FindFromRigidBodyHandle(Asura_Handle iHandle)
	{
		return (m_pxChild ? m_pxChild->FindFromRigidBodyHandle(iHandle) : 0);
	}

	bool ShouldAttachToPhysicsObject() const;

	// Get the base marker or physics position/orientation.
	// Returns true, if the position and orientation were set.
	bool GetBasePosOriForAttachment(Asura_Vector_3& xPosition, Asura_Matrix_3x3& xOrientation) const;

	// Traverse the tree with callback functor.
	virtual void Traverse(Asura_Container_Functor& xFunctor) const
	{
		xFunctor.Process(this);
		if (m_pxChild)
		{
			m_pxChild->Traverse(xFunctor);
		}
	}

protected:
	void CreateChild();
	void DeleteChild();
	void UnlinkFromParent();

protected:
	Asura_ClientContainerInstance* m_pxChild;

	// Data for optional marker point attachment.
	const Asura_ClientContainerInstance_Mesh* m_pxParentMeshForMarker;
	u_int m_iMarkerIndex;
	Asura_AnimationAttachment_ContainerInstance_Internal* m_pxAnimAttachment;
};



///////////////////////////////////////
// Asura_ClientContainerInstance_PFX //
///////////////////////////////////////
class Asura_ClientContainerInstance_PFX : public Asura_ClientContainerInstance
{
	typedef Asura_ClientContainerInstance PARENT;

public:
	Asura_ClientContainerInstance_PFX(const Asura_Container_PFX* pxContainer);
	virtual ~Asura_ClientContainerInstance_PFX();

	virtual void TeleportTo(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Render(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);

	virtual void Initialise(Asura_ClientContainerInstance_Collection* pxParentCollection,
							Asura_ContainerInstance_Properties& xData);
	
protected:
	void StartEffect(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	void StopEffect(bool bImmediate);

	virtual bool GetCoarseBoundingSphereIncludingEffects(float& fRadius, Asura_Vector_3& xCentre) const;

	Asura_PFX_Entity_Effect* GetEffect();
	const Asura_PFX_Entity_Effect* GetEffect() const;

protected:
	u_int m_uEffectID;
	float m_fTriggerTimer;
};



//////////////////////////////////////////
// Asura_ClientContainerInstance_Corona //
//////////////////////////////////////////
class Asura_ClientContainerInstance_Corona : public Asura_ClientContainerInstance
{
	typedef Asura_ClientContainerInstance PARENT;

public:
	Asura_ClientContainerInstance_Corona(const Asura_Container_Corona* pxContainer);
	virtual ~Asura_ClientContainerInstance_Corona();

	virtual void SetStrobe(bool bStrobeOn);
	virtual void TeleportTo(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Render(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Update();

	virtual void Initialise(Asura_ClientContainerInstance_Collection* pxParentCollection,
							Asura_ContainerInstance_Properties& xData);

	const Asura_Container_Corona_Param& GetParam() const;
	
protected:
	void UpdateCorona(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	void UpdatePosition(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	void DestroyCorona();
	void UpdateFromGraphColour();

	virtual bool GetCoarseBoundingSphereIncludingEffects(float& fRadius, Asura_Vector_3& xCentre) const;

protected:
	Asura_Handle m_iCoronaHandle;
};


//////////////////////////////////////////////
// Asura_ClientContainerInstance_LightShaft //
//////////////////////////////////////////////
#ifdef ASURA_TARGET_SM3

class Asura_ClientContainerInstance_LightShaft : public Asura_ClientContainerInstance
{
	typedef Asura_ClientContainerInstance PARENT;

public:
	Asura_ClientContainerInstance_LightShaft(const Asura_Container_LightShaft* pxContainer);
	virtual ~Asura_ClientContainerInstance_LightShaft();

	virtual void SetStrobe(bool bStrobeOn);
	virtual void TeleportTo(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Render(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Update();

	virtual void Initialise(Asura_ClientContainerInstance_Collection* pxParentCollection,
							Asura_ContainerInstance_Properties& xData);

	const Asura_Container_LightShaft_Param& GetParam() const;
	const Asura_Container_Corona_Param& GetCoronaParam() const;
	
protected:
	void UpdateLightShaft(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	void UpdatePosition(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	void DestroyLightShaft();
	void UpdateFromGraphColour();

	virtual bool GetCoarseBoundingSphereIncludingEffects(float& fRadius, Asura_Vector_3& xCentre) const;

protected:
	class Asura_LightShaft* m_pxLightShaft;
};


/////////////////////////////////////////////
// Asura_ClientContainerInstance_Projector //
/////////////////////////////////////////////

class Asura_ClientContainerInstance_Projector : public Asura_ClientContainerInstance
{
	typedef Asura_ClientContainerInstance PARENT;

public:
	Asura_ClientContainerInstance_Projector(const Asura_Container_Projector* pxContainer);
	virtual ~Asura_ClientContainerInstance_Projector();

	virtual void SetStrobe(bool bStrobeOn);
	virtual void TeleportTo(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Render(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Update();

	virtual void Initialise(Asura_ClientContainerInstance_Collection* pxParentCollection,
							Asura_ContainerInstance_Properties& xData);

	const Asura_Container_Projector_Param& GetParam() const;
	const Asura_ShadowParameters& GetShadowParam() const;

	const class Asura_Projector* GetProjector() const { return m_pxProjector; }
	
protected:
	void UpdateProjector(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	void UpdatePosition(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	void DestroyProjector();
	void UpdateFromGraphColour();

	virtual bool GetCoarseBoundingSphereIncludingEffects(float& fRadius, Asura_Vector_3& xCentre) const;

protected:
	class Asura_Projector* m_pxProjector;
};



//////////////////////////////////////////
// Asura_ClientContainerInstance_Photon //
//////////////////////////////////////////

class Asura_ClientContainerInstance_Photon : public Asura_ClientContainerInstance
{
	typedef Asura_ClientContainerInstance PARENT;

public:
	Asura_ClientContainerInstance_Photon(const Asura_Container_Photon* pxContainer);
	virtual ~Asura_ClientContainerInstance_Photon();

	virtual void SetStrobe(bool bStrobeOn);
	virtual void TeleportTo(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Render(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	virtual void Update();

	virtual void Initialise(Asura_ClientContainerInstance_Collection* pxParentCollection,
							Asura_ContainerInstance_Properties& xData);

	const Asura_Container_Photon_Param& GetParam() const;
	const Asura_ShadowParameters& GetShadowParam() const;

	const class Asura_Photon* GetPhoton() const { return m_pxPhoton; }
	
protected:
	void UpdatePhoton(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	void UpdatePosition(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation);
	void DestroyPhoton();
	void UpdateFromGraphColour();

	virtual bool GetCoarseBoundingSphereIncludingEffects(float& fRadius, Asura_Vector_3& xCentre) const;

protected:
	class Asura_Photon* m_pxPhoton;
};
#endif //#ifdef ASURA_TARGET_SM3


//////////////////////////////////////////
// Asura_ClientContainerInstance_Strobe //
//////////////////////////////////////////
class Asura_ClientContainerInstance_Strobe : public Asura_ClientContainerInstance
{
	typedef Asura_ClientContainerInstance PARENT;

public:
	Asura_ClientContainerInstance_Strobe(const Asura_Container_Strobe* pxContainer);
	virtual ~Asura_ClientContainerInstance_Strobe();

	virtual void SetStrobe(bool bStrobeOn) {}
	virtual void Update();

	virtual void Initialise(Asura_ClientContainerInstance_Collection* pxParentCollection,
							Asura_ContainerInstance_Properties& xData);

protected:
	float m_fStrobeTimer; // Time for next strobe state change.
};

///////////////////////////////////////////////
// Asura_ClientContainerInstance_GraphColour //
///////////////////////////////////////////////
class Asura_ClientContainerInstance_GraphColour : public Asura_ClientContainerInstance
{
	typedef Asura_ClientContainerInstance PARENT;

public:
	Asura_ClientContainerInstance_GraphColour(const Asura_Container_GraphColour* pxContainer);
	virtual ~Asura_ClientContainerInstance_GraphColour();

	virtual void Initialise(Asura_ClientContainerInstance_Collection* pxParentCollection,
							Asura_ContainerInstance_Properties& xData);

	const Asura_Container_GraphColour_Param& GetParam() const;

	// Get the current graph colour. Caller should initialise the output data before calling.
	// Returns true if results have been returned (i.e there are enabled GraphColour containers).
	virtual bool GetGraphColour(Asura_Container_GraphColour_Output& xOutput);

protected:
	float m_fOffset;			// Random offset  [0,1] (used to desync instances)
	float m_fGraphStartTime;	// Graph start time for non-looped graphs.

	// Cached output
	u_int m_uCacheFrameNumber;
	Asura_Container_GraphColour_Output m_xCacheOutput;
};


//////////////////////////////////////////
// Asura_ClientContainerInstance_Health //
//////////////////////////////////////////
class Asura_ClientContainerInstance_Health : public Asura_ClientContainerInstance
{
	typedef Asura_ClientContainerInstance PARENT;

public:
	Asura_ClientContainerInstance_Health(const Asura_Container_Health* pxContainer);

	// Get the health as a percentage of the maximum value.
	float GetHealthPercent() const;

	// Get/set the health values.
	float GetHealth() const { return m_fHealth; }
	void SetHealth(float fHealth);

	// Set any float parameter.
	virtual void SetParam(float fValue) { SetHealth(fValue); }

	// Return true if the parent collection's damage override state will change as a
	// result of the change in health.
	bool HasDamageOverrideChanged(float fOldHealthPercent, float fNewHealthPercent) const;

protected:
	float m_fHealth;
};

///////////////////////////////////////////
// Asura_ClientContainerInstance_Physics //
///////////////////////////////////////////
class Asura_ClientContainerInstance_Physics : public Asura_ClientContainerInstance
{
public:
	Asura_ClientContainerInstance_Physics(const Asura_Container_Physics* pxContainer);

	const Asura_Container_Physics_Param& GetParam() const;

protected:
};

/////////////////////////////////////////////
// Asura_ClientContainerInstance_Blueprint //
/////////////////////////////////////////////
class Asura_ClientContainerInstance_Blueprint : public Asura_ClientContainerInstance
{
public:
	Asura_ClientContainerInstance_Blueprint(const Asura_Container_Blueprint* pxContainer);

	Asura_Hash_ID GetBlueprintTypeHash() const;
	Asura_Hash_ID GetBlueprintHash() const;

	const Asura_Container_Blueprint* GetBlueprintContainer() const { return static_cast<const Asura_Container_Blueprint*>(m_pxContainer); }
};

#endif //#ifdef ASURA_USE_CONTAINER_SYSTEM

#endif //__ASURA_CLIENTCONTAINERINSTANCE_H__

