//////////////////////////////////////////////////////////////////////////////
//
// Asura_NetMsg.h
//
// Copyright (c) 2003 Rebellion. All rights reserved.
//
// Description:
//
//     system to help reduce amount of data sent over external network for
//     regular entity updates.
//
//////////////////////////////////////////////////////////////////////////////

// B E G I N   F I L E   W R A P P E R ///////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef ___ASURA_NETMSG_H___
#define ___ASURA_NETMSG_H___ 1

#ifdef ASURA_SUPPORT_NETMSG_SYSTEM

#include "Core/Asura_HashedList.h"
#include "Debugging/Asura_Assert.h"
#include "Network/Asura_Guid.h"

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////

class Asura_Message;
class Asura_NetMsg;
class Asura_NetMsg_Field;
class Asura_NetMsg_MasterInstance;
class Asura_NetMsg_SlaveInstance;
class Asura_Vector_3;
class Asura_BitPacker;
class Asura_BitUnpacker;
class Asura_NetMsg_Recipients;

// E N U M E R A T I O N S ///////////////////////////////////////////////////
enum Asura_NetMsg_FieldType
{
	Asura_FieldType_char,
	Asura_FieldType_uchar,
	Asura_FieldType_short,
	Asura_FieldType_ushort,
	Asura_FieldType_int,
	Asura_FieldType_uint,
	Asura_FieldType_float,
	Asura_FieldType_vector3,
	Asura_FieldType_vector2,
	Asura_FieldType_euler3,
	Asura_FieldType_quat,
	Asura_FieldType_UprightQuat,		// for players (and other entities that don't rotate about x or z)
	Asura_FieldType_CompressedPosition,	// restricted range and accuracy for x,y,z and y has even less range
	Asura_FieldType_CompressedFloat,	// per-instance defined number of bits used to send float
	Asura_FieldType_uint_bits,			// per-instance defined number of bits used to send u_int
	Asura_FieldType_CompressedQuat,		// send as a compressed normalised quaternion
	Asura_FieldType_uint_Bitfield,		// send a specified number of bits from the given bit index without checking for overflow (as it's not a value)
	Asura_FieldType_UnicodeString,		// minimises data sent by only sending chars if possible, and only required number of characters for string.
	Asura_FieldType_Guid,				// uses the Asura_MessagePacking stuff to compress Asura_Guids
	NUM_ASURA_NETMSG_FIELD_TYPES
};


enum Asura_NetMsg_ThresholdType
{
	Asura_Threshold_ConstantValue,
	Asura_Threshold_AnyChange,
	Asura_Threshold_Magnitude,
	Asura_Threshold_NotEqual,
	NUM_ASURA_NETMSG_THRESHOLD_TYPES
};

// C O N S T A N T S /////////////////////////////////////////////////////////
static const u_int uASURA_NETMSG_BITS_PER_BYTE = 8;
static const u_int uASURA_NETMSG_MAX_FIELDS_IN_BITFIELD = 32;

// M A C R O S ///////////////////////////////////////////////////////////////

#define ASURA_NETMSG_SINGLEBITS NULL

// the various Asura_NetMsg_RegisterField* macros allow callers to register
// each member of a message data struct in an instance of Asura_NetMsg.
// Macros are used to that the size of each member and its byte-offset from 
// the start of the struct are calculated correctly.

// debug macros get the field name from the source var, release build macros don't.
#if _DEBUG
// Asura_NetMsg_RegisterField uses the default range for the field type
// e.g. float uses 32 bits, vector3 uses 3*32 bits, upright quat uses 16 bits.
#define Asura_NetMsg_RegisterField( \
	pxNetMsg, \
	xStruct, \
	eFieldType, \
	m_Field, \
	ucArraySize, \
	eThresholdType, \
	fThresholdValue) \
	pxNetMsg->RegisterField(#m_Field, (eFieldType), (int)&((xStruct).m_Field) - (int) &(xStruct), \
		uASURA_NETMSG_BITS_PER_BYTE * sizeof((xStruct).m_Field) / (ucArraySize), (ucArraySize), (eThresholdType), (fThresholdValue));

#define Asura_NetMsg_RegisterFieldSimple( \
	eFieldType, \
	m_Field) \
	pxNetMsg->RegisterField(#m_Field, (eFieldType), (int)&(this->m_Field) - (int) this, \
	uASURA_NETMSG_BITS_PER_BYTE * sizeof(this->m_Field), 1, Asura_Threshold_AnyChange, 0.0f);

#define Asura_NetMsg_RegisterIntFieldSimpleBits( \
	m_Field, \
	uBits) \
	pxNetMsg->RegisterIntFieldBits(#m_Field, Asura_FieldType_uint_bits, (int)&(this->m_Field) - (int) this, \
	uASURA_NETMSG_BITS_PER_BYTE * sizeof(this->m_Field), (uBits), 1, Asura_Threshold_AnyChange);

// acBFDesc should be an array of char with number of entries equal to uBits. Each entry corresponds to 
// the number of bits used each field in the bitfield. Specify ASURA_NETMSG_SINGLEBITS for the most common
// case of the bitfield being entirely composed of 1-bit flags. See RegisterBitfieldFieldBits() for an example.
#define Asura_NetMsg_RegisterBitfieldSimple( \
	m_Field, \
	uBits, \
	acBFDesc) \
	pxNetMsg->RegisterBitfieldFieldBits(#m_Field, Asura_FieldType_uint_Bitfield, (int)&(this->m_Field) - (int) this, \
	uASURA_NETMSG_BITS_PER_BYTE * sizeof(this->m_Field), uBits, 0, Asura_Threshold_AnyChange, acBFDesc );

// Asura_NetMsg_RegisterIntFieldBits uses the specified range when sending the field 
// value e.g. u_char using 5 bits is sent as 0-1023.
#define Asura_NetMsg_RegisterIntFieldBits( \
	pxNetMsg, \
	xStruct, \
	eFieldType, \
	uBits, \
	m_Field, \
	ucArraySize, \
	eThresholdType) \
	pxNetMsg->RegisterIntFieldBits(#m_Field, (eFieldType), (int)&((xStruct).m_Field) - (int) &(xStruct), \
		uASURA_NETMSG_BITS_PER_BYTE * sizeof((xStruct).m_Field) / (ucArraySize), uBits, (ucArraySize), (eThresholdType) );

// acBFDesc should be an array of char with number of entries equal to uBits. Each entry corresponds to 
// the number of bits used each field in the bitfield. Specify ASURA_NETMSG_SINGLEBITS for the most common
// case of the bitfield being entirely composed of 1-bit flags. See RegisterBitfieldFieldBits() for an example.
#define Asura_NetMsg_RegisterBitfieldFieldBits( \
	pxNetMsg, \
	xStruct, \
	eFieldType, \
	uBits, \
	uFirstBit, \
	m_Field, \
	eThresholdType, \
	acBFDesc) \
    pxNetMsg->RegisterBitfieldFieldBits(#m_Field, (eFieldType), (int)&((xStruct).m_Field) - (int)&(xStruct), \
		uASURA_NETMSG_BITS_PER_BYTE * sizeof((xStruct).m_Field), uBits, uFirstBit, (eThresholdType), acBFDesc );

#define Asura_NetMsg_RegisterUnicodeStringField( \
	pxNetMsg, \
	xStruct, \
	eFieldType, \
	m_Field, \
	ucArraySize, \
	eThresholdType) \
	pxNetMsg->RegisterUnicodeStringField(#m_Field, (eFieldType), (int)&((xStruct).m_Field) - (int) &(xStruct), \
		(ucArraySize), (eThresholdType) );

// Asura_NetMsg_RegisterFloatFieldBits uses the specified range when sending the field 
// value e.g. float from -50.0f to 100.0f is converted to range [0,1], then multiplied 
// by a float representation of ((1 << uBits) - 1) to get a value in [0,((1 << uBits) - 1)]. 
// This value is sent and the process reversed when the data is received.
#define Asura_NetMsg_RegisterFloatFieldBits( \
	pxNetMsg, \
	xStruct, \
	eFieldType, \
	uBits, \
	fMinValue, \
	fMaxValue, \
	m_Field, \
	ucArraySize, \
	eThresholdType, \
	fThresholdValue) \
	pxNetMsg->RegisterFloatFieldBits(#m_Field, (eFieldType), (int)&((xStruct).m_Field) - (int) &(xStruct), \
		uASURA_NETMSG_BITS_PER_BYTE * sizeof((xStruct).m_Field) / (ucArraySize), uBits, fMinValue, fMaxValue, (ucArraySize), (eThresholdType), (fThresholdValue));
#else
// Asura_NetMsg_RegisterField uses the default range for the field type
// e.g. float uses 32 bits, vector3 uses 3*32 bits, upright quat uses 16 bits.
#define Asura_NetMsg_RegisterField( \
	pxNetMsg, \
	xStruct, \
	eFieldType, \
	m_Field, \
	ucArraySize, \
	eThresholdType, \
	fThresholdValue) \
	pxNetMsg->RegisterField(NULL, (eFieldType), (int)&((xStruct).m_Field) - (int) &(xStruct), \
		uASURA_NETMSG_BITS_PER_BYTE * sizeof((xStruct).m_Field) / (ucArraySize), (ucArraySize), (eThresholdType), (fThresholdValue));

#define Asura_NetMsg_RegisterFieldSimple( \
	eFieldType, \
	m_Field) \
	pxNetMsg->RegisterField(NULL, (eFieldType), (int)&(this->m_Field) - (int) this, \
	uASURA_NETMSG_BITS_PER_BYTE * sizeof(this->m_Field), 1, Asura_Threshold_AnyChange, 0.0f);

#define Asura_NetMsg_RegisterIntFieldSimpleBits( \
	m_Field, \
	uBits) \
	pxNetMsg->RegisterIntFieldBits(NULL, Asura_FieldType_uint_bits, (int)&(this->m_Field) - (int) this, \
	uASURA_NETMSG_BITS_PER_BYTE * sizeof(this->m_Field), (uBits), 1, Asura_Threshold_AnyChange);

// acBFDesc should be an array of char with number of entries equal to uBits. Each entry corresponds to 
// the number of bits used each field in the bitfield. Specify ASURA_NETMSG_SINGLEBITS for the most common
// case of the bitfield being entirely composed of 1-bit flags. See RegisterBitfieldFieldBits() for an example.
#define Asura_NetMsg_RegisterBitfieldSimple( \
	m_Field, \
	uBits, \
	acBFDesc) \
	pxNetMsg->RegisterBitfieldFieldBits(NULL, Asura_FieldType_uint_Bitfield, (int)&(this->m_Field) - (int) this, \
	uASURA_NETMSG_BITS_PER_BYTE * sizeof(this->m_Field), uBits, 0, Asura_Threshold_AnyChange, acBFDesc );

// Asura_NetMsg_RegisterIntFieldBits uses the specified range when sending the field 
// value e.g. u_char using 5 bits is sent as 0-1023.
#define Asura_NetMsg_RegisterIntFieldBits( \
	pxNetMsg, \
	xStruct, \
	eFieldType, \
	uBits, \
	m_Field, \
	ucArraySize, \
	eThresholdType) \
	pxNetMsg->RegisterIntFieldBits(NULL, (eFieldType), (int)&((xStruct).m_Field) - (int) &(xStruct), \
		uASURA_NETMSG_BITS_PER_BYTE * sizeof((xStruct).m_Field) / (ucArraySize), uBits, (ucArraySize), (eThresholdType) );

// acBFDesc should be an array of char with number of entries equal to uBits. Each entry corresponds to 
// the number of bits used each field in the bitfield. Specify ASURA_NETMSG_SINGLEBITS for the most common
// case of the bitfield being entirely composed of 1-bit flags. See RegisterBitfieldFieldBits() for an example.
#define Asura_NetMsg_RegisterBitfieldFieldBits( \
	pxNetMsg, \
	xStruct, \
	eFieldType, \
	uBits, \
	uFirstBit, \
	m_Field, \
	eThresholdType,\
	acBFDesc) \
	pxNetMsg->RegisterBitfieldFieldBits(NULL, (eFieldType), (int)&((xStruct).m_Field) - (int)&(xStruct), \
	uASURA_NETMSG_BITS_PER_BYTE * sizeof((xStruct).m_Field), uBits, uFirstBit, (eThresholdType), acBFDesc );

#define Asura_NetMsg_RegisterUnicodeStringField( \
	pxNetMsg, \
	xStruct, \
	eFieldType, \
	m_Field, \
	ucArraySize, \
	eThresholdType) \
	pxNetMsg->RegisterUnicodeStringField(NULL, (eFieldType), (int)&((xStruct).m_Field) - (int) &(xStruct), \
		(ucArraySize), (eThresholdType) );

// Asura_NetMsg_RegisterFloatFieldBits uses the specified range when sending the field 
// value e.g. float from -50.0f to 100.0f is converted to range [0,1], then multiplied 
// by a float representation of ((1 << uBits) - 1) to get a value in [0,((1 << uBits) - 1)]. 
// This value is sent and the process reversed when the data is received.
#define Asura_NetMsg_RegisterFloatFieldBits( \
	pxNetMsg, \
	xStruct, \
	eFieldType, \
	uBits, \
	fMinValue, \
	fMaxValue, \
	m_Field, \
	ucArraySize, \
	eThresholdType, \
	fThresholdValue) \
	pxNetMsg->RegisterFloatFieldBits(NULL, (eFieldType), (int)&((xStruct).m_Field) - (int) &(xStruct), \
		uASURA_NETMSG_BITS_PER_BYTE * sizeof((xStruct).m_Field) / (ucArraySize), uBits, fMinValue, fMaxValue, (ucArraySize), (eThresholdType), (fThresholdValue));
#endif

// This macro allows you to have an update message which comprises 2 or more instances of 
// other structs (e.g. Project update data and Asura update data). You register the first
// subclass as normal, then use this macro to set the base offset for the second class, 
// then call the appropriate function to register that class.
#define Asura_NetMsg_BeginRegisteringSubclass( \
	pxNetMsg, \
	xContainingClass, \
	xSubclass) \
	pxNetMsg->SetBaseOffset( (int)&((xContainingClass).xSubclass) - (int)&(xContainingClass) );

// Asura_NetMsg_FinishedRegisteringSubclass resets the offset to zero. Obviously
// this won't work if you have many levels of subclass... so it's a good idea to
// assert about the entity classification when using this macro.
#define Asura_NetMsg_FinishedRegisteringSubclass( pxNetMsg ) \
	pxNetMsg->SetBaseOffset( 0 );


// S T R U C T S /////////////////////////////////////////////////////////////
struct Asura_NetMsg_CompressedPositionSettings
{
	u_int m_uBitsX;
	u_int m_uBitsY;
	u_int m_uBitsZ;
	u_int m_uBitsF;
};

static const Asura_NetMsg_CompressedPositionSettings Asura_NetMsg_CompressedPositionSettings_Normal =
{
	9, //m_uBitsX; +/- 512
	5, //m_uBitsY; +/- 32
	9, //m_uBitsZ; +/- 512
	4, //m_uBitsF; ~6cm Resolution
};

static const Asura_NetMsg_CompressedPositionSettings Asura_NetMsg_CompressedPositionSettings_Large =
{
	12, //m_uBitsX; +/- 4096
	10, //m_uBitsY; +/- 1024
	12, //m_uBitsZ; +/- 4096
	4, //m_uBitsF;	~6cm Resolution
};

// C L A S S E S /////////////////////////////////////////////////////////////
class Asura_NetMsg_System
{
	friend class Asura_NetMsg_UpdateData;
	friend class Asura_NetMsg_MasterInstance;

public:

	// the ent class for players is different on each project. Since it is the 
	// kind of entity that is likely to consume the most bandwidth in multiplayer
	// games (players are always moving) there is an optimisation for player updates.
	static void SetPlayerEntityClassForProject( u_short usEntityClass ) { s_usEntityClassificationPlayer = usEntityClass; }

	static void Initialise();
	static void BeginUpdate();	// called each time updates will be generated, to insert time offset from start
	static bool AddAsuraMessage(const Asura_Message* pxMsg);
	static void SendUpdate();	// creates an Asura_Message and sends it.
	static void Shutdown();
	static void Reset();

	static void ProcessSlaveUpdatePackage(const u_char* const pucData, u_short usNumBytes, float fSentTime);
	
	// functions to pack and unpack a complete message
	static bool PackMessageData( const Asura_Message& xMsg, u_int& uDataSize, const void*& pData );
	static bool UnpackMessageData( Asura_Message& xMsg, u_int uDataSize, const void* pData );

	// MGH 27/1/2006: Initialise a netmsg instance for a ASURA_MSG_CLIENTUPDATE message
	// from the corresponding create message. This is just a helper function for the
	// following function. Pass in pointers to the message header and the update data
	// that is contained within the create message. NOTE: the data must have the
	// correct data layout in memory otherwise bad things will happen.
	static void InitialiseFromClientUpdateData(const Asura_Message* pxMsg,
											   const void* pxData);

	// MGH 27/1/2006: Initialise a netmsg instance from a (possibly) different message
	// type that contains the same data. For example, create messages often contain the
	// same data that is contained in an update message. By initialising the master or
	// slave netmsg instance from the create message rather than waiting for a full update
	// to be sent, we can not only ensure that the netmsg system is initialised sooner,
	// but also it saves us from having to send the same data over the network twice.
	// NOTE: the data must have the correct data layout in memory otherwise bad things will happen.
	static void InitialiseFromData( const void* pxData,
									const u_short usMsgID,
									const u_short usClassification,
									const Asura_Guid uFromGuid,
									const bool bMaster);

	// (MH - 26.02.2004) function to analyse data size for each message ID - used for logging / profiling
	// function returns number of messages in packet, and writes up to uArraySize entries to aucHeaderSize, 
	// ausMsgID, ausEntityClassOut, auGuid, aucDataSize and abIsFullUpdate
	static u_int GetMessageSummaries(const u_char* const pucData, u_short usNumBytes, u_int uArraySize, 
		u_char* aucHeaderSize, u_short* ausMsgIDOut, u_short* ausEntityClassOut, Asura_Guid* auGuidOut, 
		u_short* ausDataSizeOut, bool* abIsFullUpdate, bool* abIsTimeOffset);
		
	static bool BeginMessage(u_short usMsgID, u_short usEntityClassification, Asura_Guid uGuid, u_int uMaxDataSize, u_char ucMaxFields);
	static void AddMessageDataFieldID(u_int uFieldIndex);
	static bool EndMessage(bool bAddEndOfDataField, bool bSkippedLastUpdate);	// returns whether anything changed
	
	static void EntityDestroyed(Asura_Guid uGuid);

	// before initialising, set whether LOD updates should be used with AllowLODUpdates()
	// after initialising, they can be turned on and off for debugging using SetUseLODUpdates()
	static void AllowLODUpdates( bool bEnable );
	static bool AreLODUpdatesAllowed( void );
	static void SetUseLODUpdates( bool bEnable );
	static bool IsUsingLODUpdates( void );

	static bool SetPositionCompression( const Asura_NetMsg_CompressedPositionSettings& xSettings );
	static const Asura_NetMsg_CompressedPositionSettings& GetPositionCompression();

	// compressed vectors allocate different numbers of bits to each direction, and this changes depending
	// on whether large compressed vectors are being used or not. These functions can be used to access
	// the maximum possible value in each direction. This is useful to stop entities leaving the arena,
	// otherwise clients will get a chopped version of the server position which causes wrapping effects.
	static float GetCompressedPosMaxX();
	static float GetCompressedPosMaxY();
	static float GetCompressedPosMaxZ();

	// MGH 22/1/2009: Compress/uncompress the position vector.
	static void CompressPosition(const Asura_Vector_3& xPosition, Asura_BitPacker& xPacker);
	static void UncompressPosition(Asura_Vector_3& xPosition, Asura_BitUnpacker& xUnpacker);

	// MGH 10/1/2006: Call this function on the server to send a full update in the next message
	// rather than waiting for the heartbeat. This can be used to reduce the delay between a player
	// being created and the first full update being sent (during which time the player can't move).
	static void SendFullUpdate(u_short usMsgID, u_short usEntityClassification, Asura_Guid uGuid);
	
	// Project callback to allow messages to be filtered on a per-player basis. Currently only works
	// when LOD filtering is active. The message passed as a parameter is a standard message sent by 
	// a server entity before interception by the netmsg system. PJC 29/04/08
	static void SetProjectShouldSendMessageToClientFunction( bool (*pfnFn)( Asura_Guid uPlayerGuid, const Asura_Message* pxOriginalMessage ) ) 
	{ 
		s_pfnProjectShouldSendClientMessage = pfnFn; 
	}

	static void DoSomeTests();

	static void SendTestData();
	static void ProcessTestData( const void* pData, u_int uDataSize );

private:

	// private helper functions
	static bool IsInitialised( void );
	static void SkipMessageData( const Asura_NetMsg& xNetMsg, class Asura_BitUnpacker& xBitUnpacker );
	static bool NeedToAddMessageHeader( void ) { return s_pxAsuraMessageToAdd != NULL; }
	static void AddMessageHeader( void );
	static void AddBytes(const u_char* pucData, u_int uDataSize);
	static void AddBits(u_int uValue, u_int uNumBits);

	// static class data - system-wide
	static bool s_bIsInitialised;
	static bool s_bLODUpdatesAllowed;
	static bool s_bIsUsingLODUpdates;
	static u_short s_usEntityClassificationPlayer;
	static u_int s_uMaxDistinctClientUpdates;
	static u_int s_uNumClientsForThisUpdate;
	static const Asura_Message* s_pxAsuraMessageToAdd;
	static bool s_bCanAddMessages;

	// before a message is processed, this array is filled out with the clients who are interested in it.
	static class Asura_NetMsg_UpdateData** s_apxClientsForCurMessage;
	static u_int s_uNumClientsForCurMessage;
	
	// Project message filtering callback
	static bool (*s_pfnProjectShouldSendClientMessage)( Asura_Guid uPlayerGuid, const Asura_Message* pxOriginalMessage );

	// data for each updated client
	static class Asura_NetMsg_UpdateData* s_axUpdateData;
	static class Asura_NetMsg_Recipients* s_pxRecipients;
	// shared data for compression of whole messages
	static class Asura_NetMsg_UpdateData* s_pxTemporaryUpdateData;
};


class Asura_NetMsg_Field
{
public:
#if _DEBUG
	const char* m_szDbgName;
#endif
	u_char 	m_ucFieldType;			// from enum Asura_NetMsg_FieldType
	u_char 	m_ucBitOffset;			// for bitfield only
	u_short	m_usByteOffset;
	u_char 	m_ucFieldDataSourceSizeInBits;
	u_char 	m_ucFieldDataSendSizeInBits;
	u_char 	m_ucThresholdType;		// from enum Asura_NetMsg_ThresholdType
	u_char	m_ucFieldSizeInBytes;		// for string type
	float  	m_fThresholdValue;
	float  	m_fMinValue;				// min/max value for compressing floats
	float  	m_fMaxValue;
	char	m_acNumBits[uASURA_NETMSG_MAX_FIELDS_IN_BITFIELD]; // for bitfield only. TODO: compress it and/or union with other data?

	// Check for change exceeding threshold 
	// if returns true and bReplaceLastValue is true, it writes *pucCurValue to *pucLastSentValue
	bool HasChanged(const u_char* pucCurValue, u_char* pucLastSentValue, bool bReplaceLastValue = true) const;

	// add this field data to the message
	void AddDataToMessage(const u_char* pucCurValue, Asura_NetMsg_Recipients& xRecipients) const;

	void ExtractFieldData( u_char* pucInstanceCurData, class Asura_BitUnpacker& xBitUnpacker ) const;

	void GetSendDataSize(u_int& uBytes, u_int& uBits) const;

#if _DEBUG
	const char* GetName() const { return m_szDbgName; }
#else
	const char* GetName() const { return "";}
#endif

private:

	// relevant function is called based on field type
	bool HasIntegerChanged(const u_char* pucCurValue, u_char* pucLastSentValue, bool bReplaceLastValue) const;
	bool HasFloatChanged(const u_char* pucCurValue, u_char* pucLastSentValue, bool bReplaceLastValue) const;
	bool HasVector3Changed(const u_char* pucCurValue, u_char* pucLastSentValue, bool bReplaceLastValue) const;
	bool HasEuler3Changed(const u_char* pucCurValue, u_char* pucLastSentValue, bool bReplaceLastValue) const;
	bool HasQuatChanged(const u_char* pucCurValue, u_char* pucLastSentValue, bool bReplaceLastValue) const;
	bool HasBitfieldChanged(const u_char* pucCurValue, u_char* pucLastSentValue, bool bReplaceLastValue) const;
	bool HasStringChanged(const u_char* pucCurValue, u_char* pucLastSentValue, bool bReplaceLastValue) const;
	bool HasVector2Changed(const u_char* pucCurrentValue, u_char* pucLastSentValue, bool bReplaceLastValue) const;

};



class Asura_NetMsg
{

	friend class Asura_NetMsg_System;

public:
	
	Asura_NetMsg(u_short usMsgID, u_short usEntityClassification, u_char ucNumFields, u_short usDataSize);
	~Asura_NetMsg();

	void RegisterField(
		const char* szDbgName, 
		Asura_NetMsg_FieldType eFieldType, u_int uByteOffset, u_int uFieldSize, 
		u_char ucArraySize, Asura_NetMsg_ThresholdType eThresholdType, float fThresholdValue);
	void RegisterIntFieldBits(
		const char* szDbgName, 
		Asura_NetMsg_FieldType eFieldType, u_int uByteOffset, u_int uFieldSize, 
		u_int uBits, u_char ucArraySize, Asura_NetMsg_ThresholdType eThresholdType);
	// RegisterBitfieldFieldBits()
	// pcBFDesc should be an array of char with number of entries equal to uBits. Each entry corresponds to 
	// the number of bits used each field in the bitfield. Specify ASURA_NETMSG_SINGLEBITS for the most common
	// case of the bitfield being entirely composed of 1-bit flags. This just passes in NULL instead of an array.
	// e.g. for 
	// union
	// {
	//   struct {
	//     m_uField1 : 1,
	//     m_uField2 : 3;
	//     m_uField3 : 5;
	//   };
	//   m_uBitfield;
	// };
	// declare an array like this to pass in for the last argument:
	// char acBitfieldDesc[] = { 1, 3, 5 };
	void RegisterBitfieldFieldBits(
		const char* szDbgName, 
		Asura_NetMsg_FieldType eFieldType, u_int uByteOffset, u_int uFieldSize, 
		u_int uBits, u_char ucFirstBit, Asura_NetMsg_ThresholdType eThresholdType,
		const char* pcBFDesc );
	void RegisterUnicodeStringField(
		const char* szDbgName, 
		Asura_NetMsg_FieldType eFieldType, u_int uByteOffset, 
		u_char ucArraySize, Asura_NetMsg_ThresholdType eThresholdType);
	void RegisterFloatFieldBits(
		const char* szDbgName, 
		Asura_NetMsg_FieldType eFieldType, u_int uByteOffset, u_int uFieldSize, 
		u_int uBits, float fMinValue, float fMaxValue, u_char ucArraySize, 
		Asura_NetMsg_ThresholdType eThresholdType, float fThresholdValue);
	void EndRegistration();
	// to allow registering of classes which comprise of instances of 2 or more 
	// other classes which already have methods for registering their data, I've
	// added SetBaseOffset which just gets added to m_ucByteOffset when registering.
	// It is called by the Asura_NetMsg_BeginRegisteringSubclass macro
	void SetBaseOffset(u_int uOffset);

	u_short GetMsgID() const { return m_usMsgID; }
	u_short GetEntityClassification() const { return m_usEntityClassification; }
	u_short GetDataSize() const { return m_usDataSize; }
	u_int GetMaxSendDataSize() const { return m_usMaxSendDataSize; }
	const Asura_NetMsg_Field& GetField(u_int uFieldID) const 
	{ 
		Asura_Assert(uFieldID < m_ucNumFields, "invalid field index");
		return m_axFields[uFieldID]; 
	}
	u_char GetNumFields() const { return m_ucNumFields; }

	// function to unpack to an instance of the update message. Fills out bSkippedLastUpdate with the flag at the 
	// end of the data, and returns number of fields unpacked.
	u_int UnpackToInstance( u_char* pucInstanceData, bool& bSkippedLastUpdate, Asura_BitUnpacker& xBitUnpacker ) const;

	// functions to pack and unpack a complete message
	void PackWholeMessage( const void* pMessage, Asura_NetMsg_Recipients& xRecipients ) const;
	void UnpackWholeMessage( void* pMessage, Asura_BitUnpacker& xBitUnpacker ) const;

	// lists of client and server instances for this message.
	Asura_NetMsg_MasterInstance* FindMasterInstance(Asura_Guid uGuid) const;
	Asura_NetMsg_SlaveInstance* FindSlaveInstance(Asura_Guid uGuid) const;
	void AddToMasterInstanceList(Asura_NetMsg_MasterInstance*) const;
	void AddToSlaveInstanceList(Asura_NetMsg_SlaveInstance*) const;
	void RemoveFromMasterInstanceList(Asura_NetMsg_MasterInstance*) const;
	void RemoveFromSlaveInstanceList(Asura_NetMsg_SlaveInstance*) const;

	// required to use Asura_HashedList
	Asura_Guid GetGuid() const 
	{ 
		return MakeGuid(m_usMsgID, m_usEntityClassification); 
	}
	bool IsDestroyed() const { return false; }
		
	// static functions
	static Asura_Guid MakeGuid(u_short usMsgID, u_short usEntityClassification)
	{
		u_int uTemp = usMsgID; 
		uTemp = (uTemp << 16) | usEntityClassification; 
		return uTemp;
	}

	static const Asura_NetMsg* FindNetMsgObject(u_short usMsgID, u_short usEntityClassification);

	static void UnregisterAll();

private:
	
	const u_short m_usMsgID;
	const u_short m_usEntityClassification;
	class Asura_NetMsg_Field* m_axFields;
	const u_short m_usDataSize;
	u_short m_usMaxSendDataSize;
	u_char m_ucNumFields;	// num registered
	Asura_Hashed_List<Asura_NetMsg_MasterInstance>* m_pxMasterInstanceList;
	Asura_Hashed_List<Asura_NetMsg_SlaveInstance>* m_pxSlaveInstanceList;

	static u_char s_ucMaxFields;
	static u_int s_uRegistrationBaseOffset;
	static Asura_Hashed_List<Asura_NetMsg> s_xNetMsgList;
	
};

// Utility class for compressing normals with varying degrees of accuracy.
// Input normals are assumed to be normalised.
class Asura_NetMsg_Data_CompressedNormal
{

public:

    // M E T H O D S /////////////////////////////////////////////////////////

    // 1:0:4 fixed point. Note that the high bit is unused.
    static void To16Bits( const Asura_Vector_3& xIn, u_short* pusOut );
    static void From16Bits( u_short usIn, Asura_Vector_3* pxOut );

    // 1:0:7 fixed point.
    static void To24Bits( const Asura_Vector_3& xIn, u_char* pucOut );
    static void From24Bits( const u_char* pucIn, Asura_Vector_3* pxOut );

};

// Utility template class to help with NetMsg registration.
// class used must implement the following functions:
// static u_char GetNumFields();
// void RegisterFields( Asura_NetMsg* pxNetMsg, u_short usMsgID ) const;
// RegisterFields() should check the message ID is the one expected.
template<typename T> 
class Asura_NetMsg_RegistrationHelper
{
public:
	static const Asura_NetMsg* RegisterFields(u_short usMsgID, u_short usEntityClassification)
	{
		T xDummy;
		Asura_NetMsg* pxNetMsg = new Asura_NetMsg(usMsgID, usEntityClassification, T::GetNumFields(), sizeof(T) );
		xDummy.RegisterFields(pxNetMsg);
		pxNetMsg->EndRegistration();
		return pxNetMsg;
	}
};

#endif //#ifdef ASURA_SUPPORT_NETMSG_SYSTEM

// E N D   F I L E   W R A P P E R ///////////////////////////////////////////

#endif // #ifndef ___ASURA_NETMSG_H___
