///////////////////////////////////////////////////////////////////////////////
//
// File: Asura_ServerEntity_Container.h
//
// Created by Martin G.Harris (6/5/2008)
// 
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
// Server entity that manages a container instance.
//
///////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_SERVERENTITY_CONTAINER_H__
#define __ASURA_SERVERENTITY_CONTAINER_H__

#ifdef ASURA_USE_CONTAINER_SYSTEM

// Includes
#include "Core/Asura_HashedList.h"
#include "Core/Asura_Maths.h"
#include "Entity/Asura_Entity.h"
#include "Entity/Container/Asura_Container_Messages.h"
#include "Entity/Container/Asura_ContainerState.h"

// Forward declarations
class Asura_ClientContainerInstance;
class Asura_ServerContainerInstance;
class Asura_AI_NavConnection_Index;

///////////////////////////////////////
// Asura_Container_ApplyDamageOutput //
///////////////////////////////////////
// Results of applying damage.
class Asura_Container_ApplyDamageOutput
{
public:
	Asura_Container_ApplyDamageOutput() {}

	void Reset()
	{
		m_fOldHealth = 0.0f;
		m_fNewHealth = 0.0f;

		m_fOldHealthPercent = 0.0f;
		m_fNewHealthPercent = 0.0f;

		m_bFound = false;
		m_bChangedHealth = false;
		m_bDamageOverrideChanged = false;
	}

	float m_fOldHealth;		// Old health value, before applying damage.
	float m_fNewHealth;		// New health value, after applying damage.
	
	float m_fOldHealthPercent;	// Old health value as a percentage.
	float m_fNewHealthPercent;	// New health value as a percentage.
	
	bool m_bFound;					// Found the relevant health container?
	bool m_bChangedHealth;			// Health value was changed?
	bool m_bDamageOverrideChanged;	// Has a damage override changed state.
};

//////////////////////////////////
// Asura_ServerEntity_Container //
//////////////////////////////////
class Asura_ServerEntity_Container : public Asura_Entity_Server
{
	typedef Asura_Entity_Server PARENT;

	static const u_int uDEFAULT_MAX_WAYPOINTS_TO_INCLUDE = UCHAR_MAX;
	static const u_int uMAX_LINKS_BLOCKED_PER_OBJECT     = UCHAR_MAX;

public:
	explicit Asura_ServerEntity_Container(Asura_Guid uGuid);
	virtual ~Asura_ServerEntity_Container();

	// Set a lifetime. Entity will be destroyed after the game time has advanced
	// by the specified number of seconds.
	void SetLifetime(float fSeconds);

	virtual void Update();

	virtual bool HandleMessage(const Asura_Message* pxMessage);

	virtual void PostLoadInitialise();

	// Chunk functions
	virtual	bool IsFilledFromChunkBuffer() const { return false; }
	virtual bool CanBeSavedToChunk() const { return true; }

    // Reads this entity from the specified chunk-stream.
	virtual	void ReadFromChunkStream(Asura_Chunk_Stream& xStream);

    // Writes this entity to the specified chunk-stream.
	virtual	void WriteToChunkStream(Asura_Chunk_Stream& xStream) const;

	// Creation messages
	virtual bool GetHasCreateMessage() const { return true; }
	virtual void MakeCreateMessage(Asura_Guid uRequestingClient, bool bSendGuaranteed = false);
	
	//
	// Accessors
	//
	Asura_Hash_ID GetContainerHash() const { return m_uContainerHash; }
	
	// Origin pos/orient for container instance as a whole. Never changes after creation.
	const Asura_Vector_3& GetPosition() const { return m_xPosition; }
	const Asura_Quat& GetOrientationQuat() const { return m_xOriQuat; }
	
	// Get number of stored physics parameters.
	u_int GetNumPhysicsParam() const { return m_uNumPhysicsParam; }

	// Get amount of extra float data (e.g. health values for collections).
	u_int GetNumFloatParam() const { return m_uNumFloatParam; }

	// Fill the physics parameter data.
	void FillPhysicsParam(Asura_Container_PhysicsParam*& pxPhysicsData, u_int uNumToWrite) const;

	// Fill the float parameter data.
	void FillFloatParam(Asura_Container_FloatParam*& pxFloatData) const;

	// Look up the stored physics velocities for the given sub-container.
	// Return true if found, in which case xLinVelocity and xAngVelocity are set with the output data.
	bool GetPhysicsVelocity(u_int uUniqueID, Asura_Vector_3& xLinVelocity, Asura_Vector_3& xAngVelocity);
	bool HasPendingVelocityData() const { return m_pxPendingVel != 0; }

	// State of container.
	u_int GetNumInputs() const { return m_xInputState.GetNumInputs(); }
	const Asura_Container_InputState& GetInputState() const { return m_xInputState; }
	void ChangeState(const Asura_Container_InputState& xNewInputState);

	// Apply damage.
	// xOutput	  - contains useful data about the result of the function call.
	// uUniqueID  - should refer to the part of the colllection that is being damaged and
	//				typically refers to a mesh that has been returned as a result of a call to LOSTest.
	// fDamage	  - damage to apply, can be negative to add health.
	// bSendMessageToClient - inform clients of the change.
	void ApplyDamage(Asura_Container_ApplyDamageOutput& xOutput,
					 u_int uUniqueID, float fDamage,
					 bool bSendMessageToClient = true);

	// Are any parts of the container damageable?
	bool IsDamageable() const;

#ifdef ASURA_TOOLS
	// Tools setter functions
	void SetContainerHash(Asura_Hash_ID uContainerHash);
	void SetPosition(const Asura_Vector_3& xPosition);
	void SetOrientationQuat(const Asura_Quat& xQuat);

	// Template functions
	virtual void ApplyChangesForTemplateInstance(class Tools_EntityTemplateInstance* pxInstance);
	virtual bool CanBeIncludedInTemplate() const;
#endif //ASURA_TOOLS

	// Waypoint link blocking related functions.
	static void InitialiseWaypointLinkBlockCalculations(const u_int uMaxWaypointsToInclude = uDEFAULT_MAX_WAYPOINTS_TO_INCLUDE);
	static void ShutdownWaypointLinkBlockCalculations();

	void UpdateWaypointLinkBlocks(const bool bForceRemoval = false);
	bool CalculateWaypointLinkBlocks(const bool bForceUpdate = true);
	bool DiscardWaypointLinkBlockInformation();

	bool ShouldBlockWaypointLinks() const { return m_bShouldBlockWaypointLinks; }

	// Number of project-specific bytes to append to create message.
	virtual u_int GetNumBytesForProjectCreateMessageData() const { return 0; }

	// Fill in the specified number of bytes of project-specific data.
	// Note: need to override ProcessProjectCreateMessageData for the client entity too.
	virtual void FillProjectCreateMessageData(void* pData) const {}

	// Find the blueprint in the root 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 class Asura_Container_Blueprint* FindBlueprint(Asura_Hash_ID uBlueprintTypeHash, bool bIncludeDisabled) const;

	// Function used to work out how far a point is away from this entity...
	float	CalculateSquaredDistanceTo(const Asura_Vector_3& xPosition) const	{ const Asura_Vector_3 xDistance(m_xPosition - xPosition); return(xDistance.MagnitudeSquared()); }


protected:
	void BlockWaypointLinks(bool bBlockLinks);
	bool HasBlockedLinks() const { return m_bBlockedLinks; }

	static bool WaypointCallback( u_short usWaypointIndex );
	static bool IsDuplicateLinkToBlock(u_short usNavVertexIndex, u_char ucConnectionIndex, u_int uEndIndex);
	static void AddWaypointLinkToBlock(u_short usNavVertexIndex, u_char ucConnectionIndex, u_int uEndIndex, Asura_Guid uGuid);
	static void TestForPhysicsObjectBlockedLinks(const Asura_Physics_Object* pxPhysicsObject, Asura_Guid uGuid);

	virtual void MakeClientUpdateMessage();

	virtual void OnEntityActivateMessage();
	virtual void OnEntityDeactivateMessage();

	// Called on state change caused by receiving a message, or due to damage.
	virtual void PreStateChange()  {}
	virtual void PostStateChange() {}

	void SendOnDestructionMessages();
	void SendOnHealthPercentMessages(float fOldHealthPercent, float fNewHealthPercent);

	void CreateInstance(bool bCallChangeState = true);
	void DestroyInstance();

	// Adjust the number of input states to match the current collection definition.
	void AdjustNumberOfInputStates();

	// Update the server-side dynamic physics data.
	void UpdateDynamicPhysicsData();

	// Peek the client container instance, get it from the client or Tools.
	const Asura_ClientContainerInstance* GetClientInstance() const;

protected:
	// Container
	Asura_Hash_ID m_uContainerHash;

	// Origin pos/orient.
	Asura_Vector_3 m_xPosition;
	Asura_Quat m_xOriQuat;
	
	// Velocity data that we are about to apply to the container.
	Asura_Container_PosOrientVel* m_pxPendingVel;

	// Flags
	union
	{
		u_int		m_uContainerFlags;	// Single access point for flags.

		struct
		{
			u_int			m_bSentCreateMessages		: 1;	// Have we sent a create message yet?
			u_int			m_bUpdateAlways				: 1;	// Send regular update messages to client?
			u_int			m_bDynamicPhysicsAwake		: 1;	// Has a dynamic physics object, which is currently awake?
			u_int			m_bDeletePendingVelocities	: 1;	// Should delete the pending velocity information?
			u_int			m_bShouldBlockWaypointLinks	: 1;	// Set if any of the meshes block waypoint links.
			u_int			m_bBlockedLinks				: 1;	// Waypoint link blocks have been set?
			mutable u_int	m_bIsDamageable				: 1;	// Is damageable?
			mutable u_int	m_bIsDamageableInitialised	: 1;	// Has damageable status been initialised?	
		};
	};

	// These contain the waypoint links that we need to block.
	unsigned char                 m_ucNumLinksToBlock;
	Asura_AI_NavConnection_Index* m_pxLinksToBlock;


	// State
	Asura_Container_InputState m_xInputState; // Hashes for input states.

	u_int m_uNumDynamicPhysics;	// Number of dynamic physics objects active.
	u_int m_uNumPhysicsParam;	// Number of stored physics pos/ori data blocks.
	u_int m_uUpdatedDynamicPhysicsFrameStamp;

	// Server instance, contains more state.
	Asura_ServerContainerInstance* m_pxServerInstance;
	u_int m_uNumFloatParam;

	// Game created containers may have a finite lifetime.
	float m_fGameTimeToDie;

	// Static members
public:
	// Create from chunkstream.
	static void Create( Asura_Guid uGuid, Asura_Chunk_Stream& xStream );

	// Create during the game. Container starts off with no initial velocity.
	static Asura_ServerEntity_Container* Create(Asura_Hash_ID uContainerHash,
												Asura_Hash_ID uStateHash,
												const Asura_Vector_3& xPosition,
												const Asura_Quat& xOrientationQuat);

	// Create during the game, with an initial velocity.
	// The position and orientation are used for the origin.
	// The velocities are applied to all the dynamic physics objects in the container for the given state.
	static Asura_ServerEntity_Container* Create(Asura_Hash_ID uContainerHash,
												Asura_Hash_ID uStateHash,
												const Asura_Container_PosOrientVel& xInitial);


	static Asura_ServerEntity_Container* FindEntity( Asura_Guid uGuid ) { return s_xList.FindEntity( uGuid ); }
    static const Asura_Hashed_List<Asura_ServerEntity_Container>* GetList() { return &s_xList; }

protected:
	static Asura_Hashed_List<Asura_ServerEntity_Container> s_xList;

	// Waypoint link blocking storage.
	static Asura_AI_NavConnection_Index* s_pxLinksToBlock;
	static unsigned char                 s_ucNumLinksToBlock;
	static unsigned short                s_usNumWaypoints;
	static unsigned int                  s_uNumWaypointsToInclude;
	static unsigned short*               s_pusWaypoints;

	// Friends
	friend class Tools_Entity_Container;
	friend class Asura_Container_Functor_BlockWaypointLinks;
};

#endif //ifdef ASURA_USE_CONTAINER_SYSTEM

#endif //__ASURA_SERVERENTITY_CONTAINER_H__

