///////////////////////////////////////////////////////////////////////////////
//
// File: Asura_Container.h
//
// Created by Martin G.Harris (6/5/2008)
// 
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
// Classes for different types of container. Each is either a collection of other
// containers or represents something that can be glued together using the container
// system (e.g. mesh/animation, PFX, sound events, dynamic lights etc).
//
// Attachments allow containers to be attached to another mesh's marker point,
// another container's physics object or to be positioned using an offset.
//
///////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_CONTAINER_H__
#define __ASURA_CONTAINER_H__

#ifdef ASURA_USE_CONTAINER_SYSTEM

// Includes
#include "Animation/Asura_Animation.h"
#include "Animation/Asura_Hierarchy.h"
#include "Core/Asura_Graph.h"
#include "Debugging/Asura_Assert.h"
#include "Entity/Container/Asura_Container_Classification.h"
#include "Entity/Container/Asura_ContainerState.h"
#include "Entity/ParticleGenerator/Asura_Entity_PFX_Effect_Data.h"
#include "Rendering/Asura_Colour.h"
#include "Shadows/Asura_Shadow_System.h"


// Forward declarations
class Asura_ClientContainerInstance;
class Asura_ClientContainerInstance_Attachment;
class Asura_Chunk_Stream;

/////////////////////
// Asura_Container //
/////////////////////
class Asura_Container
{
public:
	Asura_Container()
		: m_uClassification(AsuraContainerClass_NotSet)
		, m_uContainerHash(ASURA_HASH_ID_UNSET)
		, m_uNumInstances(0)
	{}
	
	virtual ~Asura_Container() {}

	Asura_Hash_ID GetHash() const { return m_uContainerHash; }
	u_int GetClassification() const { return m_uClassification; }

	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	virtual u_int GetNumInputs() const { return 1; }

private:
	u_int m_uClassification;		// From ASURA_CONTAINER_CLASSIFICATION or equivalent project enum.

	Asura_Hash_ID m_uContainerHash; // Hash of name of this container.
	u_int m_uNumInstances;			// Number of created instances of this container.

	// Friends
	friend class Asura_Container_System;
	friend class Asura_ContainerRegistry;
	friend class Tools_Container_System;
};

//////////////////////////
// Asura_Container_Mesh //
//////////////////////////

// Flags for mesh container (stored in m_uContainerMeshFlags)
enum ASURA_CONTAINER_MESH_FLAGS
{
	// Collision proxy
	ASURA_CONTAINER_MESH_COLLISION_PROXY_MASK		= 0x0000000f,

	ASURA_CONTAINER_MESH_COLLISION_PROXY_OBB		= 0x00000000,
	ASURA_CONTAINER_MESH_COLLISION_PROXY_SPHERE		= 0x00000001,
	ASURA_CONTAINER_MESH_COLLISION_PROXY_SPHERESTACK= 0x00000002,
	ASURA_CONTAINER_MESH_COLLISION_PROXY_POLYGONAL	= 0x00000003,

	// Flags
	ASURA_CONTAINER_MESH_FLAG_NO_COLLISIONS					= 0x00000010,	// No collisions at all
	ASURA_CONTAINER_MESH_FLAG_IGNORE_FOR_LOS				= 0x00000020,	// Ignored for line-of-sight tests
	ASURA_CONTAINER_MESH_FLAG_PHYSICS_OBJECT				= 0x00000040,	// Dynamic physics object
	ASURA_CONTAINER_MESH_FLAG_IGNORE_FOR_DAMAGE				= 0x00000080,	// Invulnerable to damage
	ASURA_CONTAINER_MESH_FLAG_HAS_OWN_PHYSICS				= 0x00000100,	// Has own dynamic physics object rather than sharing within collection
	ASURA_CONTAINER_MESH_FLAG_USE_SELF_ILLUMINATION_COLOUR	= 0x00000200,	// Use graph colour for self-illumination
	ASURA_CONTAINER_MESH_FLAG_USE_SELF_ILLUMINATION_FLICKER	= 0x00000400,	// Use graph flicker for self-illumination
	ASURA_CONTAINER_MESH_FLAG_USE_SELF_ILLUMINATION_STROBE	= 0x00000800,	// Use strobe for self-illumination
	ASURA_CONTAINER_MESH_FLAG_ANIMTIMER_RANDOMISE			= 0x00001000,	// Randomise animation timer on creation (to desync instances)
	ASURA_CONTAINER_MESH_FLAG_ANIMTIMER_PRESERVE			= 0x00002000,	// Try to preserve animation timer across state changes
	ASURA_CONTAINER_MESH_FLAG_DONT_RENDER					= 0x00004000,	// Don't render
	ASURA_CONTAINER_MESH_FLAG_BLOCK_NAVDATA_GENERATION		= 0x00008000,	// Block generation of waypoints and links
	ASURA_CONTAINER_MESH_FLAG_BLOCK_WAYPOINT_LINKS			= 0x00010000,	// Mark waypoint links going through it as blocked
};

class Asura_Container_Mesh : public Asura_Container
{
public:
	Asura_Container_Mesh()
		: m_uSkinHash(ASURA_HASH_ID_UNSET)
		, m_uAnimationHash(ASURA_HASH_ID_UNSET)
		, m_iAnimFlags(0)
		, m_uRenderFlags(ASURA_HIERARCHY_RENDER_NO_SHADOW)
		, m_uContainerMeshFlags(ASURA_CONTAINER_MESH_FLAG_BLOCK_NAVDATA_GENERATION)
		, m_fSelfIlluminationContribution(1.0f)
		, m_fSelfIlluminationColourise(1.0f)
	{}

	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	Asura_Hash_ID GetSkinHash() const { return m_uSkinHash; }
	Asura_Hash_ID GetAnimHash() const { return m_uAnimationHash; }
	
	// Flags
	int   GetAnimFlags()   const { return m_iAnimFlags; }
	u_int GetRenderFlags() const { return m_uRenderFlags; }
	
	bool UseMovementOffset() const	{ return (m_iAnimFlags & AsuraAnimationFlag_UseMovementOffset) != 0; }
	bool IsLooping()  const			{ return (m_iAnimFlags & AsuraAnimationFlag_Loop) != 0; }
	bool IsReversed() const			{ return (m_iAnimFlags & AsuraAnimationFlag_Reversed) != 0; }

	bool HasShadow() const { return (m_uRenderFlags & ASURA_HIERARCHY_RENDER_NO_SHADOW) == 0; }

	bool HasNoCollisions()		const { return (m_uContainerMeshFlags & ASURA_CONTAINER_MESH_FLAG_NO_COLLISIONS) != 0; }
	bool IsIgnoreForLOS()		const { return (m_uContainerMeshFlags & ASURA_CONTAINER_MESH_FLAG_IGNORE_FOR_LOS) != 0; }
	bool IsPhysicsObject()		const { return (m_uContainerMeshFlags & ASURA_CONTAINER_MESH_FLAG_PHYSICS_OBJECT) != 0; }
	bool IsIgnoreForDamage()	const { return (m_uContainerMeshFlags & ASURA_CONTAINER_MESH_FLAG_IGNORE_FOR_DAMAGE) != 0; }
	bool HasOwnPhysicsObject()	const { return (m_uContainerMeshFlags & ASURA_CONTAINER_MESH_FLAG_HAS_OWN_PHYSICS) != 0; }

	// Self-illumination based on GraphColour.
	bool IsUsingGraphColourForSelfIllumination()  const { return (m_uContainerMeshFlags & ASURA_CONTAINER_MESH_FLAG_USE_SELF_ILLUMINATION_COLOUR) != 0;  }
	bool IsUsingGraphFlickerForSelfIllumination() const { return (m_uContainerMeshFlags & ASURA_CONTAINER_MESH_FLAG_USE_SELF_ILLUMINATION_FLICKER) != 0;  }
	bool IsUsingStrobeForSelfIllumination()		  const { return (m_uContainerMeshFlags & ASURA_CONTAINER_MESH_FLAG_USE_SELF_ILLUMINATION_STROBE) != 0;   }

	bool HasSelfIllumination() const
	{
		return 0 != (m_uContainerMeshFlags & ( ASURA_CONTAINER_MESH_FLAG_USE_SELF_ILLUMINATION_COLOUR  |
											   ASURA_CONTAINER_MESH_FLAG_USE_SELF_ILLUMINATION_FLICKER |
											   ASURA_CONTAINER_MESH_FLAG_USE_SELF_ILLUMINATION_STROBE ) );
	}

	float GetSelfIlluminationContribution() const { return m_fSelfIlluminationContribution; }
	float GetSelfIlluminationColourise()	const { return m_fSelfIlluminationColourise; }

	// Randomise the initial anim timer.
	bool ShouldRandomiseAnimTimer() const { return (m_uContainerMeshFlags & ASURA_CONTAINER_MESH_FLAG_ANIMTIMER_RANDOMISE) != 0; }

	// Preserve the anim timer across state changes.
	bool ShouldPreserveAnimTimer() const { return (m_uContainerMeshFlags & ASURA_CONTAINER_MESH_FLAG_ANIMTIMER_PRESERVE) != 0; }

	bool IsDontRender() const { return (m_uContainerMeshFlags & ASURA_CONTAINER_MESH_FLAG_DONT_RENDER) != 0; }

	// Block waypoint generation and waypoint links respectively.
	bool ShouldBlockNavDataGeneration() const { return (m_uContainerMeshFlags & ASURA_CONTAINER_MESH_FLAG_BLOCK_NAVDATA_GENERATION) != 0; }
	bool ShouldBlockWaypointLinks()     const { return (m_uContainerMeshFlags & ASURA_CONTAINER_MESH_FLAG_BLOCK_WAYPOINT_LINKS) != 0; }


	// TODO not implemented yet.
	bool IsFullBright() const { return false; }
	float GetAlpha() const { return 1.0f; }

	int GetCollisionProxy() const;

#ifdef ASURA_TOOLS
	void SetCollisionProxy(int iCollisionProxy);
#endif //ASURA_TOOLS

protected:
	Asura_Hash_ID m_uSkinHash;
	Asura_Hash_ID m_uAnimationHash;

	int   m_iAnimFlags;
	u_int m_uRenderFlags;
	u_int m_uContainerMeshFlags;

	float m_fSelfIlluminationContribution;
	float m_fSelfIlluminationColourise;

	// Friends
	friend class Tools_Container_Mesh;
};


////////////////////////////////
// Asura_Container_SoundEvent //
////////////////////////////////

// Flags for sound event container.
enum ASURA_CONTAINER_SE_FLAGS
{
	// Flags
	ASURA_CONTAINER_SE_FLAG_TRANSITION_ONLY = 0x00000001, // Only enabled for state transitions.
};

class Asura_Container_SoundEvent : public Asura_Container
{
public:
	Asura_Container_SoundEvent()
		: m_uSoundEventHash(ASURA_HASH_ID_UNSET)
		, m_uSEFlags(0)
	{}

	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	Asura_Hash_ID GetSoundEventHash() const { return m_uSoundEventHash; }

	// Only enabled for state transitions? (i.e. for state change messages and damage overrides).
	bool IsTransitionOnly() const { return (m_uSEFlags & ASURA_CONTAINER_SE_FLAG_TRANSITION_ONLY) != 0; }

protected:
	Asura_Hash_ID m_uSoundEventHash;
	u_int m_uSEFlags;

	// Friends
	friend class Tools_Container_SoundEvent;
};


////////////////////////////////
// Asura_Container_Collection //
////////////////////////////////
class Asura_Container_Collection : public Asura_Container
{
public:
	Asura_Container_Collection();
	virtual ~Asura_Container_Collection();

	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	u_int GetNumChildren() const { return m_uNumChildren; }
	
	Asura_Hash_ID GetChildContainerHash(u_int uIndex) const
	{
		if (m_puChildren && uIndex < m_uNumChildren)
		{
			return m_puChildren[uIndex];
		}
		Asura_Assert(0, "Invalid child index");
		return ASURA_HASH_ID_UNSET;
	}

	virtual u_int GetNumInputs() const { return m_xStateMachine.GetNumInputs(); }

	// Return true if the damage override state will change as a result of the change in health.
	bool HasDamageOverrideChanged(const Asura_Container_InputState& xInputState,
								  float fOldHealthPercent,
								  float fNewHealthPercent) const
	{
		return m_xStateMachine.HasDamageOverrideChanged(xInputState, fOldHealthPercent, fNewHealthPercent);
	}

	// Convert input state to an output state taking into account any damage overrides.
	void ConvertState(const Asura_Container_InputState& xInputState,
					  float fHealthPercent,
					  Asura_Container_OutputState& xOutputState) const
	{
		m_xStateMachine.ConvertState(xInputState, fHealthPercent, xOutputState);
	}

	// Find index of health container. There should be at most one in the collection.
	// Return -1 if no health container was found.
	int FindHealthIndex() const;

	// Look for a blueprint contained in this collection.
	// Return the blueprint container found (or NULL if none found).
	// If uBlueprintTypeHash != ASURA_HASH_ID_UNSET, then only return blueprints of the specified type.
	const class Asura_Container_Blueprint* FindBlueprintContainer(Asura_Hash_ID uBlueprintTypeHash) const;
	
	// Get the indices of the first and last blueprints.
	// Return true, if there are some blueprints, in which case set the index parameters.
	// Return false, if there are no blueprints, leave index parameters alone.
	bool FindBlueprintIndices(u_int& uFirst, u_int& uLast) const;

#ifdef ASURA_TOOLS
	void AllocateSize(u_int uSize);
	void SetChild(Asura_Hash_ID uChildHash, u_int uIndex);
	Asura_ContainerStateMachine& GetStateMachine() { return m_xStateMachine; }
	int FindChildIndex(Asura_Hash_ID uChildHash, int iMultiplicity) const;
#endif //ASURA_TOOLS

protected:
	void Reset();
	void UpdateCachedData() const;

protected:
	u_int m_uNumChildren;
	Asura_Hash_ID* m_puChildren;

	// Stores the possible states of the collection.
	Asura_ContainerStateMachine m_xStateMachine;

	// Cached data
	// Store the positions of some children.
	struct CachedChildData
	{
		CachedChildData() { Reset(); }
		
		void Reset()
		{
			m_sHealthIndex    = -2; // Invalid value (-2 = not initialised, -1 = not found).
			m_sFirstBlueprint = -2;
			m_sLastBlueprint  = -2;
		}

		short m_sHealthIndex;		// Health container index.
		short m_sFirstBlueprint;	// First blueprint index.
		short m_sLastBlueprint;		// Last blueprint index.
	};

	mutable CachedChildData m_xCachedData;
};

////////////////////////////////
// Asura_Container_Attachment //
////////////////////////////////
class Asura_Container_Attachment : public Asura_Container
{
public:
	Asura_Container_Attachment();

	virtual u_int GetNumInputs() const;

	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;
	
	Asura_Hash_ID GetChildContainerHash() const { return m_uChildContainerHash; }
	Asura_Hash_ID GetMarkerPointHash()    const { return m_uMarkerPointHash; }

	// Apply offset to specified initial position/orientation,
	// writing out new position/orientation to different variables.
	// Return false if failed to calculate pos/ori (due to missing marker point),
	// in which case the returned pos/ori are set to the input values.
	bool ApplyOffset(const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOri,
					 Asura_Vector_3& xNewPosition, Asura_Matrix_3x3& xNewOri,
					 const Asura_ClientContainerInstance_Attachment* pxInstance) const;

	// Should attach to the parent collection's dynamic physics object?
	bool ShouldAttachToPhysicsObject() const { return m_bAttachToPhysicsObject; }

	bool HasOffset() const { return m_bHasOffset; }
	void UpdateHasOffsetFlag();

	// Return the classification of the child.
	u_int GetChildClassification() const;

protected:
	Asura_Hash_ID m_uChildContainerHash;
	Asura_Hash_ID m_uMarkerPointHash;

	// Offsets
	Asura_Vector_3 m_xAttachPosition;
	Asura_Quat m_xAttachQuat;

	bool m_bHasOffset; // Has an offset?
	bool m_bAttachToPhysicsObject;	// Attach to the parent collection's physics object (if there is one).

	// Cache the classification of the child.
	mutable u_int m_uCachedChildClass;

	// Friends
	friend class Tools_Container_Attachment;
};


/////////////////////////
// Asura_Container_PFX //
/////////////////////////

// Flags for PFX container.
enum ASURA_CONTAINER_PFX_FLAGS
{
	// Flags
	ASURA_CONTAINER_PFX_FLAG_TRANSITION_ONLY			= 0x00000001, // Only enabled for state transitions.
	ASURA_CONTAINER_PFX_FLAG_ALLOW_RETRIGGER_IF_STOPPED	= 0x00000002, // For retriggering effects that may have stopped.
};

class Asura_Container_PFX : public Asura_Container
{
public:
	Asura_Container_PFX()
		: m_xPFXData()
		, m_uPFXFlags(0)
	{}

	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	Asura_Hash_ID GetEffectTemplateHash() const { return m_xPFXData.m_uEffectTemplateHash; }

	const Asura_Entity_PFX_Effect_Data& GetPFXData() const { return m_xPFXData; }

	// Only enabled for state transitions? (i.e. for state change messages and damage overrides).
	bool IsTransitionOnly() const { return (m_uPFXFlags & ASURA_CONTAINER_PFX_FLAG_TRANSITION_ONLY) != 0; }

	// Allow an effect to retrigger if it has stopped.
	bool IsRetriggerAllowed() const { return (m_uPFXFlags & ASURA_CONTAINER_PFX_FLAG_ALLOW_RETRIGGER_IF_STOPPED) != 0; }

protected:
	Asura_Entity_PFX_Effect_Data m_xPFXData;
	u_int m_uPFXFlags;

	// Friends
	friend class Tools_Container_PFX;
};

////////////////////////////
// Asura_Container_Corona //
////////////////////////////

// Flags for corona container (stored in m_uCoronaFlags)
enum ASURA_CONTAINER_CORONA_FLAGS
{
	// Flags
	ASURA_CONTAINER_CORONA_FLAG_NO_DISTANCE_FADEOUT		= 0x00000001,
	ASURA_CONTAINER_CORONA_FLAG_USE_VISIBILTY_TEST		= 0x00000002,
	ASURA_CONTAINER_CORONA_FLAG_USE_STROBE				= 0x00000004,
	ASURA_CONTAINER_CORONA_FLAG_USE_GRAPH_COLOUR		= 0x00000008,
	ASURA_CONTAINER_CORONA_FLAG_USE_GRAPH_FLICKER		= 0x00000010,
	ASURA_CONTAINER_CORONA_FLAG_USE_DIRECTION_FADEOUT	= 0x00000020,
};

// Asura_Container_Corona_Param
class Asura_Container_Corona_Param
{
public:
	Asura_Container_Corona_Param() { ResetToDefaults(); }
	void ResetToDefaults();

	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	bool IsUsingStrobe()	   const { return (m_uCoronaFlags & ASURA_CONTAINER_CORONA_FLAG_USE_STROBE) != 0; }
	bool IsUsingGraphColour()  const { return (m_uCoronaFlags & ASURA_CONTAINER_CORONA_FLAG_USE_GRAPH_COLOUR) != 0; }
	bool IsUsingGraphFlicker() const { return (m_uCoronaFlags & ASURA_CONTAINER_CORONA_FLAG_USE_GRAPH_FLICKER) != 0; }
	bool IsUsingDirectionFadeout() const { return (m_uCoronaFlags & ASURA_CONTAINER_CORONA_FLAG_USE_DIRECTION_FADEOUT) != 0; }

	bool IsUsingGraph() const
	{
		return 0 != ( m_uCoronaFlags & (ASURA_CONTAINER_CORONA_FLAG_USE_GRAPH_COLOUR |
										ASURA_CONTAINER_CORONA_FLAG_USE_GRAPH_FLICKER) );
	}

	Asura_Hash_ID			m_uCoronaTextureHash;
	float					m_fCoronaSize;
	float					m_fCoronaDepth;
	Asura_RGBA				m_xCoronaColour;

	u_int m_uCoronaFlags;
};

// Asura_Container_Corona
class Asura_Container_Corona : public Asura_Container
{
public:
	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	bool HasNoDistanceFadeOut() const { return (m_xParam.m_uCoronaFlags & ASURA_CONTAINER_CORONA_FLAG_NO_DISTANCE_FADEOUT) != 0; }
	bool UsesVisibilityTest()   const { return (m_xParam.m_uCoronaFlags & ASURA_CONTAINER_CORONA_FLAG_USE_VISIBILTY_TEST) != 0; }
	
	bool IsUsingStrobe() const { return m_xParam.IsUsingStrobe(); }
	bool IsUsingDirectionFadeout() const { return m_xParam.IsUsingDirectionFadeout(); }

	Asura_Hash_ID GetTextureHash() const { return m_xParam.m_uCoronaTextureHash; }
	float GetRadius() const				 { return m_xParam.m_fCoronaSize; }
	float GetDepth()  const				 { return m_xParam.m_fCoronaDepth; }
	const Asura_RGBA& GetColour() const  { return m_xParam.m_xCoronaColour; }

	const Asura_Container_Corona_Param& GetParam() const { return m_xParam; }

protected:
	Asura_Container_Corona_Param m_xParam;

	// Friends
	friend class Tools_Container_Corona;
};


////////////////////////////////
// Asura_Container_LightShaft //
////////////////////////////////
#ifdef ASURA_TARGET_SM3

// Asura_Container_LightShaft_Param
class Asura_Container_LightShaft_Param
{
public:
	Asura_Container_LightShaft_Param() { ResetToDefaults(); }
	void ResetToDefaults();

	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	// Top, bottom of cone.
	float		m_afRadius[2];
	float		m_afDistance[2];
	Asura_RGBA	m_axColour[2];

	// 2 layers of texturing.
	Asura_Hash_ID	m_auTextureHash[2];
	float			m_afTextureUScale[2];
	float			m_afTextureScrollRate[2];

	// Distance fade out.
	float m_fDistFadeStart;
	float m_fDistFadeRange;
	// Distance fade out.
	float m_fCloseFadeStart;
	float m_fCloseFadeRange;

	// Corona
	float m_fCoronaAngleRange;
	Asura_Container_Corona_Param m_xCoronaParam;
};


// Asura_Container_LightShaft
class Asura_Container_LightShaft : public Asura_Container
{
public:
	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	// Lightshaft doesn't have its own flags, but the corona does.
	bool IsUsingStrobe() const { return m_xParam.m_xCoronaParam.IsUsingStrobe(); }

	const Asura_Container_LightShaft_Param& GetParam() const { return m_xParam; }
	const Asura_Container_Corona_Param& GetCoronaParam() const { return m_xParam.m_xCoronaParam; }

protected:
	Asura_Container_LightShaft_Param m_xParam;

	// Friends
	friend class Tools_Container_LightShaft;
};
#endif //ASURA_TARGET_SM3


///////////////////////////////
// Asura_Container_Projector //
///////////////////////////////
#ifdef ASURA_TARGET_SM3

// Flags for projector container (stored in Asura_Container_Projector_Param)
enum ASURA_CONTAINER_PROJECTOR_FLAGS
{
	// Types (these are mutually exclusive)
	ASURA_CONTAINER_PROJECTOR_FLAG_TYPE_MASK				= 0x000000ff,

	ASURA_CONTAINER_PROJECTOR_FLAG_FAKE_LIGHT				= 0x00000000,
	ASURA_CONTAINER_PROJECTOR_FLAG_DYNAMIC_LIGHT			= 0x00000001,
	ASURA_CONTAINER_PROJECTOR_FLAG_DYNAMIC_SPOTLIGHT		= 0x00000002,
	ASURA_CONTAINER_PROJECTOR_FLAG_BLEND_ADD				= 0x00000003,
	ASURA_CONTAINER_PROJECTOR_FLAG_BLEND_MULTIPLY			= 0x00000004,
	ASURA_CONTAINER_PROJECTOR_FLAG_BLEND_MULTIPLYx2			= 0x00000005,
	ASURA_CONTAINER_PROJECTOR_FLAG_BLEND_STANDARD			= 0x00000006,

	// Flags
	ASURA_CONTAINER_PROJECTOR_FLAG_PARALLEL					= 0x00000100, // Parallel or perspective?
	ASURA_CONTAINER_PROJECTOR_FLAG_USE_LIMITING_PLANE		= 0x00000200, // Clip to limiting plane
	ASURA_CONTAINER_PROJECTOR_FLAG_TOOLS_RENDER_FRUSTRUM	= 0x00000400, // Render frustum in Tools
	ASURA_CONTAINER_PROJECTOR_FLAG_USE_STROBE				= 0x00000800, // Enable use of strobe
	ASURA_CONTAINER_PROJECTOR_FLAG_USE_GRAPH_COLOUR			= 0x00001000, // Use graph colour.
	ASURA_CONTAINER_PROJECTOR_FLAG_USE_GRAPH_RANGE			= 0x00002000, // Use graph range.
	ASURA_CONTAINER_PROJECTOR_FLAG_USE_GRAPH_FLICKER		= 0x00004000, // Use graph flicker.
};


// Asura_Container_Projector_Param
class Asura_Container_Projector_Param
{
public:
	Asura_Container_Projector_Param() { ResetToDefaults(); }
	void ResetToDefaults();

	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	bool IsSpotLight() const
	{
		return (m_uFlags & ASURA_CONTAINER_PROJECTOR_FLAG_TYPE_MASK) == ASURA_CONTAINER_PROJECTOR_FLAG_DYNAMIC_SPOTLIGHT;
	}

	bool IsParallel()		   const { return (m_uFlags & ASURA_CONTAINER_PROJECTOR_FLAG_PARALLEL) != 0; }
	bool IsUsingStrobe()	   const { return (m_uFlags & ASURA_CONTAINER_PROJECTOR_FLAG_USE_STROBE) != 0; }
	bool IsUsingGraphColour()  const { return (m_uFlags & ASURA_CONTAINER_PROJECTOR_FLAG_USE_GRAPH_COLOUR) != 0; }
	bool IsUsingGraphRange()   const { return (m_uFlags & ASURA_CONTAINER_PROJECTOR_FLAG_USE_GRAPH_RANGE) != 0; }
	bool IsUsingGraphFlicker() const { return (m_uFlags & ASURA_CONTAINER_PROJECTOR_FLAG_USE_GRAPH_FLICKER) != 0; }

	bool IsUsingGraph() const
	{
		return 0 != ( m_uFlags & (ASURA_CONTAINER_PROJECTOR_FLAG_USE_GRAPH_COLOUR |
								  ASURA_CONTAINER_PROJECTOR_FLAG_USE_GRAPH_RANGE |
								  ASURA_CONTAINER_PROJECTOR_FLAG_USE_GRAPH_FLICKER) );
	}

	// Textures
	Asura_Hash_ID m_uTextureHash;
	Asura_Hash_ID m_uDetailTextureHash;

	// Projection
	float m_fFoV;
	float m_fMinRange;
	float m_fMaxRange;
	float m_fParallelScale;

	// Colour
	float m_afRGB[3];

	// Fall-off
	float m_fInnerFoV;		// Inner angle for spotlight

	float m_fFalloffInnerRatio;
	float m_fFalloffPower;

	// Detail texture scrolling
	float m_fUScrollSpeed;
	float m_fVScrollSpeed;
	float m_fUScrollRange;
	float m_fVScrollRange;

	// Texture param
	float m_fDetailTextureScale;
	
	// Aspect
	float m_fUVAspect;

	// Limiting plane
	Asura_Vector_3 m_xLimitingPlanePos;		// Relative to instance
	Asura_Vector_3 m_xLimitingPlaneNormal;	// Relative to instance

	// Flags from ASURA_CONTAINER_PROJECTOR_FLAGS
	u_int m_uFlags;

	// Distance fade out
	float m_fDistFadeStart;
	float m_fDistFadeRange;
};

// Asura_Container_Projector
class Asura_Container_Projector : public Asura_Container
{
public:
	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	const Asura_Container_Projector_Param& GetParam() const { return m_xParam; }
	const Asura_ShadowParameters& GetShadowParam() const { return m_xShadowParam; }

protected:
	Asura_Container_Projector_Param m_xParam;
	Asura_ShadowParameters m_xShadowParam;

	// Friends
	friend class Tools_Container_Projector;
};
#endif //ASURA_TARGET_SM3


////////////////////////////
// Asura_Container_Photon //
////////////////////////////
#ifdef ASURA_TARGET_SM3

// Flags for photon container (stored in Asura_Container_Photon_Param)
enum ASURA_CONTAINER_PHOTON_FLAGS
{
	ASURA_CONTAINER_PHOTON_FLAG_USE_LIGHT_VOLUME			= 0x00000001,	// Clip light to volume
	ASURA_CONTAINER_PHOTON_FLAG_TOOLS_RENDER_LIGHT_VOLUME	= 0x00000002,	// Display light volume in Tools
	ASURA_CONTAINER_PHOTON_FLAG_USE_LIMITING_ANGLES			= 0x00000004,	// Limit angle
	ASURA_CONTAINER_PHOTON_FLAG_USE_STROBE					= 0x00000008,	// Enable use of strobe
	ASURA_CONTAINER_PHOTON_FLAG_USE_GRAPH_COLOUR			= 0x00000010,	// Use graph colour.
	ASURA_CONTAINER_PHOTON_FLAG_USE_GRAPH_RANGE				= 0x00000020,	// Use graph range.
	ASURA_CONTAINER_PHOTON_FLAG_USE_GRAPH_FLICKER			= 0x00000040,	// Use graph flicker.
};

// Asura_Container_Photon_Param
class Asura_Container_Photon_Param
{
public:
	Asura_Container_Photon_Param() { ResetToDefaults(); }
	void ResetToDefaults();

	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	bool IsUsingLightVolume()	 const { return (m_uFlags & ASURA_CONTAINER_PHOTON_FLAG_USE_LIGHT_VOLUME) != 0; }
	bool IsUsingLimitingAngles() const { return (m_uFlags & ASURA_CONTAINER_PHOTON_FLAG_USE_LIMITING_ANGLES) != 0; }
	bool IsUsingStrobe()		 const { return (m_uFlags & ASURA_CONTAINER_PHOTON_FLAG_USE_STROBE)	!= 0; }
	bool IsUsingGraphColour()	 const { return (m_uFlags & ASURA_CONTAINER_PHOTON_FLAG_USE_GRAPH_COLOUR) != 0; }
	bool IsUsingGraphRange()	 const { return (m_uFlags & ASURA_CONTAINER_PHOTON_FLAG_USE_GRAPH_RANGE) != 0; }
	bool IsUsingGraphFlicker()	 const { return (m_uFlags & ASURA_CONTAINER_PHOTON_FLAG_USE_GRAPH_FLICKER) != 0; }

	bool IsUsingGraph() const
	{
		return 0 != ( m_uFlags & (ASURA_CONTAINER_PHOTON_FLAG_USE_GRAPH_COLOUR |
								  ASURA_CONTAINER_PHOTON_FLAG_USE_GRAPH_RANGE |
								  ASURA_CONTAINER_PHOTON_FLAG_USE_GRAPH_FLICKER) );
	}

	// Colour
	float m_afRGB[3];

	// Range
	float m_fPhotonRange;	// Outer range of the light
	
	// Light volume
	Asura_Bounding_Box		m_xLightVolume;				// Relative to instance
	Asura_Quat				m_xLightVolumeOrient;		// Relative to instance

	// Limiting angle
	float m_fLimitingAngleInner;
	float m_fLimitingAngleOuter;

	// Textures
	Asura_Hash_ID m_uProjCubeTextureHash;
	Asura_Hash_ID m_uProjCubeBlurredTextureHash;

	float m_fProjCubeLerpDist;

	// Fall off
	float m_fFalloffInnerRatio;
	float m_fFalloffPower;

	// Flags from ASURA_CONTAINER_PHOTON_FLAGS
	u_int m_uFlags;

	// Distance fade out
	float m_fDistFadeStart;
	float m_fDistFadeRange;
};

// Asura_Container_Photon
class Asura_Container_Photon : public Asura_Container
{
public:
	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	const Asura_Container_Photon_Param& GetParam() const { return m_xParam; }
	const Asura_ShadowParameters& GetShadowParam() const { return m_xShadowParam; }

protected:
	Asura_Container_Photon_Param m_xParam;
	Asura_ShadowParameters m_xShadowParam;

	// Friends
	friend class Tools_Container_Photon;
};
#endif //ASURA_TARGET_SM3

////////////////////////////
// Asura_Container_Strobe //
////////////////////////////
class Asura_Container_Strobe : public Asura_Container
{
public:
	Asura_Container_Strobe();

	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	float GetMinTimeOff() const { return m_fMinTimeOff; }
	float GetMaxTimeOff() const { return m_fMaxTimeOff; }
	float GetMinTimeOn()  const { return m_fMinTimeOn; }
	float GetMaxTimeOn()  const { return m_fMaxTimeOn; }

protected:
	float m_fMinTimeOff;
	float m_fMaxTimeOff;
	float m_fMinTimeOn;
	float m_fMaxTimeOn;

	// Friends
	friend class Tools_Container_Strobe;
};


/////////////////////////////////
// Asura_Container_GraphColour //
/////////////////////////////////

// Flags for graph colour container (stored in Asura_Container_GraphColour_Param)
enum ASURA_CONTAINER_GRAPHCOLOUR_FLAGS
{
	ASURA_CONTAINER_GRAPHCOLOUR_FLAG_ENABLE_GRAPH_FLICKER	= 0x00000001, // Enable the graph flicker.
	ASURA_CONTAINER_GRAPHCOLOUR_FLAG_ENABLE_GRAPH_COLOUR	= 0x00000002, // Enable the graph colour.
	ASURA_CONTAINER_GRAPHCOLOUR_FLAG_ENABLE_GRAPH_RANGE		= 0x00000004, // Enable the graph range.
	ASURA_CONTAINER_GRAPHCOLOUR_FLAG_GRAPH_IS_LOOPED		= 0x00000008, // Loop the graph.
};

// Asura_Container_GraphColour_Param
// Parameters for the colour/range/flicker graphs.
class Asura_Container_GraphColour_Param
{
public:
	Asura_Container_GraphColour_Param();
	void ResetToDefaults(bool bInitialiseGraphs);

	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	void WriteToChunkStream(Asura_Chunk_Stream& xStream, bool bExporting) const;

	bool IsFlickerEnabled() const { return (m_uFlags & ASURA_CONTAINER_GRAPHCOLOUR_FLAG_ENABLE_GRAPH_FLICKER) != 0; }
	bool IsColourEnabled()  const { return (m_uFlags & ASURA_CONTAINER_GRAPHCOLOUR_FLAG_ENABLE_GRAPH_COLOUR)  != 0; }
	bool IsRangeEnabled()   const { return (m_uFlags & ASURA_CONTAINER_GRAPHCOLOUR_FLAG_ENABLE_GRAPH_RANGE)   != 0; }
	bool IsLooped()			const { return (m_uFlags & ASURA_CONTAINER_GRAPHCOLOUR_FLAG_GRAPH_IS_LOOPED)	  != 0; }

	// Flags from ASURA_CONTAINER_GRAPHCOLOUR_FLAGS
	u_int m_uFlags;

	// Duration of the graph
	float m_fGraphDuration;

	Asura_Graph<Asura_RGBA> m_xColourRangeGraph; // Colour (RGB) and range (A)
	Asura_Graph<float>		m_xFlickerGraph;	 // Flickering
};

// Asura_Container_GraphColour_Output
class Asura_Container_GraphColour_Output
{
public:
	Asura_Container_GraphColour_Output() { Reset(); }
	
	void Reset()
	{
		m_fIntensity = 1.0f;
		m_xColour.Set(1.0f, 1.0f, 1.0f, 1.0f);
	}

	Asura_Container_GraphColour_Output& operator *= (const Asura_Container_GraphColour_Output& xRight)
	{
		m_fIntensity   *= xRight.m_fIntensity;
		m_xColour.m_fR *= xRight.m_xColour.m_fR;
		m_xColour.m_fG *= xRight.m_xColour.m_fG;
		m_xColour.m_fB *= xRight.m_xColour.m_fB;
		m_xColour.m_fA *= xRight.m_xColour.m_fA;
		return *this;
	}

	float m_fIntensity;		// Intensity scale from flickering.
	Asura_RGBA m_xColour;	// Colour scale (RGB) and range scale (A).
};

// Asura_Container_GraphColour
class Asura_Container_GraphColour : public Asura_Container
{
public:
	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	const Asura_Container_GraphColour_Param& GetParam() const { return m_xParam; }

protected:
	Asura_Container_GraphColour_Param m_xParam;

	// Friends
	friend class Tools_Container_GraphColour;
};

////////////////////////////
// Asura_Container_Health //
////////////////////////////
class Asura_Container_Health : public Asura_Container
{
public:
	Asura_Container_Health();

	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	float GetInitialHealth() const { return m_fInitialHealth; }
	float GetMaxHealth() const { return m_fMaxHealth; }

protected:
	float m_fInitialHealth;
	float m_fMaxHealth;

	// Friends
	friend class Tools_Container_Health;
};


/////////////////////////////
// Asura_Container_Physics //
/////////////////////////////

// Asura_Container_Physics_Param
class Asura_Container_Physics_Param
{
public:
	Asura_Container_Physics_Param() { ResetToDefaults(); }

	void ResetToDefaults();

	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	float GetGravityScale() const { return m_fGravityScale; }
	float GetElasticity()	const { return m_fElasticity; }
	float GetFriction()		const { return m_fFriction; }
	float GetDrag()			const { return m_fDrag; }
	float GetAngularDrag()	const { return m_fAngularDrag; }
	float GetMass()			const { return m_fMass; }
	
	float GetInvMass()		const { return (m_fMass>0.0f) ? (1.0f/m_fMass) : 10.0f; }

	float m_fGravityScale;
	float m_fElasticity;
	float m_fFriction;
	float m_fDrag;
	float m_fAngularDrag;
	float m_fMass;
};

// Asura_Container_Physics
class Asura_Container_Physics : public Asura_Container
{
public:
	Asura_Container_Physics() {}

	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	const Asura_Container_Physics_Param& GetParam() const { return m_xParam; }

protected:
	Asura_Container_Physics_Param m_xParam;

	// Friends
	friend class Tools_Container_Physics;
};

// Asura_Container_Blueprint
class Asura_Container_Blueprint : public Asura_Container
{
public:
	Asura_Container_Blueprint()
		: m_uBlueprintTypeHash(ASURA_HASH_ID_UNSET)
		, m_uBlueprintHash(ASURA_HASH_ID_UNSET)
	{}

	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	Asura_Hash_ID GetBlueprintTypeHash() const { return m_uBlueprintTypeHash; }
	Asura_Hash_ID GetBlueprintHash() const { return m_uBlueprintHash; }

protected:
	Asura_Hash_ID m_uBlueprintTypeHash;
	Asura_Hash_ID m_uBlueprintHash;

	// Friends
	friend class Tools_Container_Blueprint;
};


#endif //#ifdef ASURA_USE_CONTAINER_SYSTEM

#endif //__ASURA_CONTAINER_H__

