///////////////////////////////////////////////////////////////////////////////
//
// File: Asura_Container_Messages.h
//
// Created by Martin G.Harris (22/5/2008)
// 
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////
//
// MGH 26/9/2008: 
// Note: Since the netmsg system doesn't support variable sized update messages,
// I've implemented three different update messages, which differ only in the
// number of dynamic physics objects in the container.
// They correspond to 0, 1 and up to uMAX_DYNAMIC_PHYSICS_PER_CONTAINER_ENTITY
// dynamic physics objects. This should cover the most common cases, without
// wasting large amounts of bandwidth sending unnecessary/unused physics data.
//
///////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_CONTAINER_MESSAGES_H__
#define __ASURA_CONTAINER_MESSAGES_H__

#ifdef ASURA_USE_CONTAINER_SYSTEM

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

// Forward declarations
class Asura_ServerEntity_Container;
class Asura_ClientEntity_Container;

///////////////
// Constants //
///////////////

// Maximum number of dynamic physics objects that a container entity can have.
// Note: don't make this too big as it'll kill the bandwidth in multiplayer.
const u_int uMAX_DYNAMIC_PHYSICS_PER_CONTAINER_ENTITY = 5;

// Number of bits required to store the above value (for NetMsg compression).
const u_int uBITS_FOR_MAX_DYNAMIC_PHYSICS = 3;


///////////////////////////////
// Asura_Container_PosOrient //
///////////////////////////////
// Pos/orient information that is saved, loaded and sent to remote machines.
// Needed for those containers with dynamic physics objects.
class Asura_Container_PosOrient
{
public:
	Asura_Container_PosOrient()
		: m_xPosition(Asura_Maths::ZeroVector3)
		, m_xOrient(Asura_Maths::IdentityQuat)
	{}

	Asura_Vector_3 m_xPosition;
	Asura_Quat m_xOrient;
};

//////////////////////////////////
// Asura_Container_PosOrientVel //
//////////////////////////////////
// Position/orientation/velocity information that is saved and loaded.
// Needed for those containers with dynamic physics objects.
class Asura_Container_PosOrientVel : public Asura_Container_PosOrient
{
public:
	Asura_Container_PosOrientVel()
		: m_xLinVelocity(Asura_Maths::ZeroVector3)
		, m_xAngVelocity(Asura_Maths::ZeroVector3)
	{}

	void SetDataFromPhysics(const class Asura_Physics_Object* pxPhysics);

	Asura_Vector_3 m_xLinVelocity;
	Asura_Vector_3 m_xAngVelocity;
};

//////////////////////////////////
// Asura_Container_PhysicsParam //
//////////////////////////////////
// Position, orientation and unique ID.
class Asura_Container_PhysicsParam : public Asura_Container_PosOrient
{
public:
	Asura_Container_PhysicsParam()
		: m_uUniqueID(0xffffffff)
	{}

	void Reset()
	{
		m_xPosition.Zero();
		m_xOrient.Identity();
		m_uUniqueID = 0xffffffff;
	}

	u_int m_uUniqueID;
};

////////////////////////////////
// Asura_Container_FloatParam //
////////////////////////////////
// Contains a sub-container ID and a float parameter (e.g. health value).
class Asura_Container_FloatParam
{
public:
	Asura_Container_FloatParam()
		: m_uUniqueID(0xffffffff)
		, m_fFloatValue(0.0f)
	{}

	u_int m_uUniqueID;
	float m_fFloatValue;
};

////////////////////////////////////
// Asura_Message_Container_Create //
////////////////////////////////////
// Container creation message sent to client.
// It contains the container hash, the state hashes and the origin position/orientation.
// Also the position/orientation of any physics blocks, and any float parameters
// for sub-containers (typically health values).
// There is also the option of adding project-specific data.

class Asura_Message_Container_Create
{
public:
	void FillIn(const Asura_ServerEntity_Container* pxServer);
	void FillOut(Asura_ClientEntity_Container* pxClient, float fServerTime) const;

	// Return size needed for the message.
	static u_int GetSize(u_int uNumInputs, u_int uNumPhysicsParam, u_int uNumFloatParam, u_int uNumProjectBytes)
	{
		return sizeof(Asura_Message_Container_Create) +
				uNumInputs		 * sizeof(Asura_Hash_ID) +
				uNumPhysicsParam * sizeof(Asura_Container_PhysicsParam) +
				uNumFloatParam   * sizeof(Asura_Container_FloatParam) +
				uNumProjectBytes;
	}

protected:
	Asura_Hash_ID m_uContainerHash;

	u_char m_ucNumInputStates;		// Number of state input hashes.
	u_char m_ucNumPhysicsParam;		// Number of physics parameters.
	u_char m_ucNumFloatParam;		// Number of float server parameters (these all contain health at the moment.)
	u_char m_ucNumProjectBytes;		// Number of bytes of project-specific data.

	Asura_Container_PosOrient m_xOriginPosOri; // The origin's pos/ori.
	
	// Append m_ucNumStateInputs copies of Asura_Hash_ID.
	// Append m_ucNumPhysicsParam copies of Asura_Container_PhysicsParam.
	// Append m_ucNumFloatParam copies of Asura_Container_FloatParam.
	// Append m_ucNumProjectBytes bytes of project-specific data.
};

//////////////////////////////////////////
// Asura_Message_Container_ClientUpdate //
//////////////////////////////////////////
// Basic update for containers with no dynamic physics objects. This is much smaller than the extended versions.
class Asura_Message_Container_ClientUpdate
{
public:
	void FillIn(const Asura_ServerEntity_Container* pxServer);
	void FillOut(Asura_ClientEntity_Container* pxClient, float fServerTime) const;

#ifdef ASURA_SUPPORT_NETMSG_SYSTEM
	static void RegisterFields(u_short usMsgID, u_short usEntityClassification);
	static void RegisterFields(class Asura_NetMsg* pxNetMsg);
	static u_char GetNumFields() { return uASURA_MAX_NUM_CONTAINER_INPUTS; }
#endif//#ifdef ASURA_SUPPORT_NETMSG_SYSTEM

protected:
	Asura_Hash_ID m_auStateHash[uASURA_MAX_NUM_CONTAINER_INPUTS];
};

////////////////////////////////////////////////////
// Asura_Message_Container_ClientUpdate_Extended1 //
////////////////////////////////////////////////////
// Extended update for containers with exactly one dynamic physics object.
class Asura_Message_Container_ClientUpdate_Extended1
{
public:
	void FillIn(const Asura_ServerEntity_Container* pxServer);
	void FillOut(Asura_ClientEntity_Container* pxClient, float fServerTime) const;

#ifdef ASURA_SUPPORT_NETMSG_SYSTEM
	static void RegisterFields(u_short usMsgID, u_short usEntityClassification);
	static void RegisterFields(class Asura_NetMsg* pxNetMsg);
	static u_char GetNumFields() { return uASURA_MAX_NUM_CONTAINER_INPUTS + 3; }
#endif//#ifdef ASURA_SUPPORT_NETMSG_SYSTEM

protected:
	Asura_Hash_ID m_auStateHash[uASURA_MAX_NUM_CONTAINER_INPUTS];
	Asura_Container_PhysicsParam m_xPhysicsParam;
};

///////////////////////////////////////////////////
// Asura_Message_Container_ClientUpdate_Extended //
///////////////////////////////////////////////////
// Extended update for containers with more than one dynamic physics object.
// Maximum of uMAX_DYNAMIC_PHYSICS_PER_CONTAINER_ENTITY such objects.
class Asura_Message_Container_ClientUpdate_Extended
{
public:
	void FillIn(const Asura_ServerEntity_Container* pxServer);
	void FillOut(Asura_ClientEntity_Container* pxClient, float fServerTime) const;

#ifdef ASURA_SUPPORT_NETMSG_SYSTEM
	static void RegisterFields(u_short usMsgID, u_short usEntityClassification);
	static void RegisterFields(class Asura_NetMsg* pxNetMsg);
	static u_char GetNumFields() { return (uASURA_MAX_NUM_CONTAINER_INPUTS + 1 + 3*uMAX_DYNAMIC_PHYSICS_PER_CONTAINER_ENTITY); }
#endif//#ifdef ASURA_SUPPORT_NETMSG_SYSTEM

protected:
	Asura_Hash_ID m_auStateHash[uASURA_MAX_NUM_CONTAINER_INPUTS];
	u_int m_uNumPhysicsParam; // Number of valid pos/ori/id data included below.

	// Pos/ori/id for any dynamic physics blocks. Only non-server machines need this info.
	Asura_Container_PhysicsParam m_axPhysicsParam[uMAX_DYNAMIC_PHYSICS_PER_CONTAINER_ENTITY];
};

/////////////////////////////////////////
// Asura_Message_Container_ChangeState //
/////////////////////////////////////////
// Static message to change the container state (for input slot 0).
// Do *not* change this as it is saved out with the message blocks.
class Asura_Message_Container_ChangeState
{
public:
	Asura_Hash_ID m_uStateHash; // New state.
};

/////////////////////////////////////////////////
// Asura_Message_Container_ChangeStateExtended //
/////////////////////////////////////////////////
// Static message to change the container state (for arbitrary input slot).
// Do *not* change this as it is saved out with the message blocks.
class Asura_Message_Container_ChangeStateExtended
{
public:
	u_int m_uInputIndex;		// Input slot index.
	Asura_Hash_ID m_uStateHash; // New state hash.
};


//////////////////////////////////////////
// Asura_Message_Container_ChangeHealth //
//////////////////////////////////////////
// Change the health of a container.
class Asura_Message_Container_ChangeHealth
{
public:
	u_int m_uUniqueID; // Sub-container ID
	float m_fHealth;
};


#endif // ASURA_USE_CONTAINER_SYSTEM

#endif //__ASURA_CONTAINER_MESSAGES_H__

