/**
*
* \file Asura_StaticMessage_System.cpp
*
* Created by Kevin Lea - 17/04/2002 15:58:04
*
* Copyright (C) 2002 Rebellion, All Rights Reserved.
*
* \brief
*
**/

#include "Core/Asura.h"
#include "Entity/Asura_StaticMessage_System.h"

#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_Timers.h"
#include "Core/Memory/Asura_Mem.h"
#include "Core/Memory/Asura_ResourceAllocator.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"
#include "Debugging/Asura_Assert.h"

#ifdef ASURA_TOOLS
#include "Entity/Tools_Entity_Simulation.h"
#include "Tools_System.h"
#endif

#ifdef ASURA_USE_STREAMING
#include "Entity/Asura_Entity.h"
#include "Streaming/Asura_Streaming_System.h"
#endif // ASURA_USE_STREAMING

#include <stdlib.h> // for qsort

#ifdef ASURA_USE_STREAMING
Asura_StaticMessage_Handler*						Asura_StaticMessage_System::s_pxCurrentHandler = NULL;
Asura_TemplateHashTree<Asura_StaticMessage_Handler>	Asura_StaticMessage_System::s_xHandlerTree;
#else //#ifdef ASURA_USE_STREAMING
Asura_StaticMessage_Handler							Asura_StaticMessage_System::s_xHandler;
#endif //#else //#ifdef ASURA_USE_STREAMING

static const u_short g_ausFirstBlockID[ASURA_STATICMESSAGE_NUMSETS] =
{
	0,
	30000,
};

static const u_short g_ausLastBlockID[ASURA_STATICMESSAGE_NUMSETS] =
{
	29999,
	59999,
};

static bool(*g_pfnMessageFilterCallback)(const Asura_StaticMessage*) = NULL;

static u_int g_uStaticMessagesSendMessageRecursion = 0;
static const u_int uMAX_STATIC_MESSAGE_RECURSION = 64;

#ifdef ASURA_TOOLS
void (*g_pfnSendMessageCallback)(const Asura_StaticMessage*, Asura_Guid uFrom) = NULL;
#endif

////////////////////////////////
// Asura_StaticMessage_System //
////////////////////////////////

void Asura_StaticMessage_System::Initialise(void)
{
#ifndef ASURA_USE_STREAMING

	s_xHandler.Initialise();
#endif //#else // #ifndef ASURA_USE_STREAMING
}

/// Kevin Lea - 15/04/2002 17:39:08 - free-up storage space
void Asura_StaticMessage_System::Shutdown(void)
{
#ifdef ASURA_USE_STREAMING
	// Just free up the whole tree
	s_xHandlerTree.Reset();

#else // #ifdef ASURA_USE_STREAMING

	s_xHandler.Shutdown();

#endif //#else // #ifdef ASURA_USE_STREAMING
}

void Asura_StaticMessage_System::Reset()
{
#ifdef ASURA_USE_STREAMING
	s_xHandlerTree.Traverse(Reset_Callback);

#else // #ifdef ASURA_USE_STREAMING

	s_xHandler.Reset();
#endif //#else // #ifdef ASURA_USE_STREAMING
}

// Per-frame update for dealing with delayed messages
void Asura_StaticMessage_System::Update()
{
#if defined(ASURA_USE_STREAMING)
	// MGH 21/6/2006: Since we are not setting all the handlers, we mustn't send the
	// messages to server entities immediately, but should queue them instead.
	const bool bOldValue = Asura_Streaming_System::GetShouldQueueMessages();
	Asura_Streaming_System::SetShouldQueueMessages(true);
	s_xHandlerTree.Traverse(Update_Callback);
	Asura_Streaming_System::SetShouldQueueMessages(bOldValue);
#else
	GetCurrentHandler().Update();
#endif
}

void Asura_StaticMessage_System::ResetDelayedMessages()
{
#ifdef ASURA_USE_STREAMING
	s_xHandlerTree.Traverse(ResetDelayedMessages_Callback);

#else // #ifdef ASURA_USE_STREAMING

	s_xHandler.ResetDelayedMessages();
#endif //#else // #ifdef ASURA_USE_STREAMING
}

#ifdef ASURA_USE_STREAMING
void Asura_StaticMessage_System::SendMessages(int iBlockID, Asura_Guid uFrom,float fDelay /*= 0.0f*/)
{
	// MGH 18/5/2006: Switch handler to the correct one for the entity sending the messages.
	Asura_StaticMessage_Handler* pxHandler = 0;

	// MGH 7/6/2006: If this is an "on destruction" message the "from entity" may already
	// have been flagged as ready for destruction.
	Asura_Entity_Server* pxServer = Asura_Entity_Server::FindObject(uFrom, false);
	if (pxServer)
	{
		pxHandler = GetHandler(pxServer->GetZoneHashID());
	}

	if (!pxHandler)
	{
		// Try the current handler.
		Asura_Assert(0, "Static message system may be using the wrong handler!");
		pxHandler = &GetCurrentHandler();
	}

	if (pxHandler)
	{
		pxHandler->SendMessages(iBlockID, uFrom, fDelay);
	}
}

void Asura_StaticMessage_System::Reset_Callback(Asura_Hash_ID uHash, Asura_StaticMessage_Handler* pxData)
{
	// Don't need the hash, just reset the data in the pointer
	if (pxData)
	{
		pxData->Reset();
	}
}

void Asura_StaticMessage_System::ResetDelayedMessages_Callback(Asura_Hash_ID uHash, Asura_StaticMessage_Handler* pxData)
{
	// Don't need the hash, just reset the data in the pointer
	if (pxData)
	{
		pxData->ResetDelayedMessages();
	}
}

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_StaticMessage_System::CreateHandler
// Author       : Anthony Gregson
// Date         : 23-Mar-2006
//
// Description  : Attempts to create a static message system handler with the specified
//				  hash-ID.
//
// Parameters   : The hash-ID of the handler to create.
//
// Return value : A pointer to the handler created.
////////////////////////////////////////////////////////////////////////////////

Asura_StaticMessage_Handler* Asura_StaticMessage_System::CreateHandler(const Asura_Hash_ID uHash)
{
	Asura_StaticMessage_Handler* pxHandler = s_xHandlerTree.GetDataByHash(uHash);

	// Do we already have this handler?
	if(pxHandler == NULL)
	{
		// No - create one...
		pxHandler = s_xHandlerTree.AddNode(uHash);
	}
	else
	{
		// Yes - this shouldn't happen...
		Asura_Assert(false, "Already have a static message handler with this hash ID.");
	}

	// Return the handler...
	return(pxHandler);
}

/// End of function 'Asura_StaticMessage_System::CreateHandler'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_StaticMessage_System::DestroyHandler
// Author       : Anthony Gregson
// Date         : 23-Mar-2006
//
// Description  : Attempts to destroy the static message system handler with the specified
//				  unique hash-ID.
//
// Parameters   : The hash-ID of the handler to destroy.
//
// Return value : "true" if destroyed, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_StaticMessage_System::DestroyHandler(const Asura_Hash_ID uHash)
{
	// Is this the current handler?
	if(s_pxCurrentHandler == s_xHandlerTree.GetDataByHash(uHash))
	{
		s_pxCurrentHandler	= NULL;
	}

	// Call the delete function...
	return(s_xHandlerTree.DeleteNodeByHash(uHash));
}

/// End of function 'Asura_StaticMessage_System::DestroyHandler'. ///

#endif //#ifdef ASURA_USE_STREAMING

bool Asura_StaticMessage_System::ShouldSendMessage( const Asura_StaticMessage* pxMessage )
{
#ifdef ASURA_TOOLS
    if( pxMessage->IsDisabled() ) { return false; }
#endif

    if( pxMessage->IsRandom() )
    {
        if( Asura_Random::GetFloat() > pxMessage->m_fProbability ) { return false; }
    }

    if( g_pfnMessageFilterCallback )
    {
        if( !g_pfnMessageFilterCallback(pxMessage) ) { return false; }
    }

    return true;
}


void Asura_StaticMessage_System::SetMessageFilterCallback( bool(*pfnCallback)(const Asura_StaticMessage*) )
{
    g_pfnMessageFilterCallback = pfnCallback;
}

#ifdef ASURA_TOOLS
void Asura_StaticMessage_System::SetSendMessageCallback(void(*pfnCallback)(const Asura_StaticMessage*, Asura_Guid uFrom))
{
	g_pfnSendMessageCallback = pfnCallback;
}
#endif

/////////////////////////////////
// Asura_StaticMessage_Handler //
/////////////////////////////////

Asura_StaticMessage_Handler::Asura_StaticMessage_Handler()
{
	// AJG - 27/03/06 - Non-streaming one doesn't need to initialise at this point
#ifdef ASURA_USE_STREAMING
	Initialise();
#endif //#ifdef ASURA_USE_STREAMING

	m_iNumDelayedMessages = 0;
	m_iNumDelayedMessagesBeingSent = 0;
}

Asura_StaticMessage_Handler::~Asura_StaticMessage_Handler()
{
	// AJG - 27/03/06 - Non-streaming one doesn't need to shutdown at this point
#ifdef ASURA_USE_STREAMING
	Shutdown();
#endif //#ifdef ASURA_USE_STREAMING
}

void Asura_StaticMessage_Handler::Initialise()
{
	for(int i=0;i<ASURA_STATICMESSAGE_NUMSETS;i++)
	{
		m_xMessageSet[i].Initialise();
	}

	m_iNumDelayedMessages = 0;
	m_iNumDelayedMessagesBeingSent = 0;
}

void Asura_StaticMessage_Handler::Shutdown()
{
	ResetDelayedMessages();

	for(int i=0;i<ASURA_STATICMESSAGE_NUMSETS;i++)
	{
		m_xMessageSet[i].Shutdown();
	}
}

void Asura_StaticMessage_Handler::Reset()
{
	// Basic version for now, as with the old system reset.
	Shutdown();
	Initialise();
}

void Asura_StaticMessage_Handler::ResetDelayedMessages()
{
	for(int i=0; i<m_iNumDelayedMessages; i++)
	{
		m_xDelayedMessages[i].DeallocateMessage();
	}

	m_iNumDelayedMessages = 0;
	m_iNumDelayedMessagesBeingSent = 0;
}

/// Kevin Lea - 15/04/2002 17:38:08 - this method is called to set up storage space for the messages,
/// and will typically be called when loading level data
void Asura_StaticMessage_Handler::SetupStorageSpace(enum Asura_StaticMessageSetIndex eSet, u_short usNumberOfMessageBlocks)
{
	m_xMessageSet[eSet].SetupStorageSpace(usNumberOfMessageBlocks);
}

void Asura_StaticMessage_Handler::Update()
{
	float fTime = Asura_Timers::GetGameTimeSlice();
	
	Asura_Assertf( m_iNumDelayedMessages <= ASURA_STATICMESSAGE_MAXDELAYEDMESSAGES, ("Number of Delayed Messages is too high. Max %d, count %d", ASURA_STATICMESSAGE_MAXDELAYEDMESSAGES, m_iNumDelayedMessages));

	m_iNumDelayedMessagesBeingSent = 0;

	//update the timers for any delayed messages , and find those whose time has come
	Asura_DelayedStaticMessage* pxDelayedMessage = m_xDelayedMessages;
	for(int i=0;i<m_iNumDelayedMessages;)
	{
		pxDelayedMessage->m_fDelay -= fTime;
		if(pxDelayedMessage->m_fDelay <= 0.0f)
		{
			//This message is ready to go. Copy this to the end of the array , and copy the
			//last message down to here

			Asura_DelayedStaticMessage xTemp = *pxDelayedMessage;
		
			*pxDelayedMessage = m_xDelayedMessages[--m_iNumDelayedMessages];
			m_xDelayedMessages[m_iNumDelayedMessages].m_pxStaticMessage = 0;
			Asura_Assertf( m_iNumDelayedMessages <= ASURA_STATICMESSAGE_MAXDELAYEDMESSAGES, ("Number of Delayed Messages is too high. Max %d, count %d", ASURA_STATICMESSAGE_MAXDELAYEDMESSAGES, m_iNumDelayedMessages));

			m_iNumDelayedMessagesBeingSent++;
			m_xDelayedMessages[ASURA_STATICMESSAGE_MAXDELAYEDMESSAGES-m_iNumDelayedMessagesBeingSent] = xTemp;

		}
		else
		{
			i++;
			pxDelayedMessage++;
		}
	}

	//If any messages are to be sent this frame , then sort them so that earlier ones should still
	//come earlier , and send them off.
	if(m_iNumDelayedMessagesBeingSent)
	{
		pxDelayedMessage = &m_xDelayedMessages[ASURA_STATICMESSAGE_MAXDELAYEDMESSAGES-m_iNumDelayedMessagesBeingSent];

		qsort(pxDelayedMessage, m_iNumDelayedMessagesBeingSent, sizeof(Asura_DelayedStaticMessage), DelayedMessageSort);

		while(m_iNumDelayedMessagesBeingSent)
		{
			m_iNumDelayedMessagesBeingSent--;
			Asura_StaticMessage* pxStaticMessage = pxDelayedMessage->m_pxStaticMessage;
			if(pxStaticMessage)
			{
				#ifdef ASURA_TOOLS
				SendMessage(pxStaticMessage, pxDelayedMessage->m_uFromGuid, pxDelayedMessage->m_fInitialDelay);
				#else
				SendMessage(pxStaticMessage, pxDelayedMessage->m_uFromGuid);
				#endif
			}
			pxDelayedMessage->DeallocateMessage();
			pxDelayedMessage++;
		}
	}
}

int Asura_StaticMessage_Handler::DelayedMessageSort(const void* p1, const void* p2)
{
	const Asura_DelayedStaticMessage* pxDelayedMessage1 = (const Asura_DelayedStaticMessage*)p1;
	const Asura_DelayedStaticMessage* pxDelayedMessage2 = (const Asura_DelayedStaticMessage*)p2;

	//Sort primarily by delay , but also sort by index , so that messages with the same delay
	//in a message block will be sent in order.
	if(pxDelayedMessage1->m_fDelay < pxDelayedMessage2->m_fDelay) return -1;
	if(pxDelayedMessage1->m_fDelay > pxDelayedMessage2->m_fDelay) return 1;
	if(pxDelayedMessage1->m_uMessageIndex < pxDelayedMessage2->m_uMessageIndex) return -1;
	if(pxDelayedMessage1->m_uMessageIndex > pxDelayedMessage2->m_uMessageIndex) return 1;

	return 0;
}


void Asura_StaticMessage_Handler::SendMessages(int iBlockID, Asura_Guid uFrom,float fDelay)
{
	if (++g_uStaticMessagesSendMessageRecursion > uMAX_STATIC_MESSAGE_RECURSION)
	{
		Asura_Assertf(false, ("probable infinite loop in messages sent from GUID %u", uFrom));
		return;
	}

	if(iBlockID != ASURA_STATICMESSAGE_EMPTYBLOCK)
	{
		//find out which message set this block id belongs to
		for(int i=0;i<ASURA_STATICMESSAGE_NUMSETS;i++)
		{
			if(iBlockID>=g_ausFirstBlockID[i] && iBlockID<=g_ausLastBlockID[i])
			{
				int iBlockIndex = iBlockID-g_ausFirstBlockID[i];
				Asura_StaticMessageBlock* pxMessageBlock  = m_xMessageSet[i].GetMessageBlock(iBlockIndex);

				if(pxMessageBlock)
				{
					pxMessageBlock->SendMessages(uFrom, fDelay, this);
				}
				else
				{
					Asura_Assert(0, "Attempting to send messages from an invalid message block");
				}
				
				break;
			}
		}
		
	}
	else
	{
		Asura_Assertf(ASURA_STATICMESSAGE_EMPTYBLOCK!=iBlockID, ("Static message sent with empty block from %d", uFrom));
	}

	--g_uStaticMessagesSendMessageRecursion;
}

void Asura_StaticMessage_Handler::QueueMessage(const Asura_StaticMessage* pxStaticMessage, Asura_Guid uFrom, u_int uMessageIndex,float fExtraDelay)
{
	bool bHasDisplacedMessage = false;
	Asura_DelayedStaticMessage xDisplacedMessage = { 0, 0, 0.0f, 0 };

	//Find place to put message
	Asura_DelayedStaticMessage* pxNewDelayedMessage = 0;

	Asura_Assertf( m_iNumDelayedMessages <= ASURA_STATICMESSAGE_MAXDELAYEDMESSAGES, ("Number of Delayed Messages is too high. Max %d, count %d", ASURA_STATICMESSAGE_MAXDELAYEDMESSAGES, m_iNumDelayedMessages));

	if((m_iNumDelayedMessages+m_iNumDelayedMessagesBeingSent) < ASURA_STATICMESSAGE_MAXDELAYEDMESSAGES)
	{
		pxNewDelayedMessage = &m_xDelayedMessages[m_iNumDelayedMessages++];
		Asura_Assertf( m_iNumDelayedMessages <= ASURA_STATICMESSAGE_MAXDELAYEDMESSAGES, ("Number of Delayed Messages is too high. Max %d, count %d", ASURA_STATICMESSAGE_MAXDELAYEDMESSAGES, m_iNumDelayedMessages));
	}
	else
	{
		Asura_Assert(0,"Too many delayed messages already stored up.\nASURA_STATICMESSAGE_MAXDELAYEDMESSAGES may need to be changed for this project.");

		//Out of space.
		//Find the message with shortest remaining delay , and send that one now.

		Asura_DelayedStaticMessage* pxDelayedMessage = &m_xDelayedMessages[0];
		Asura_DelayedStaticMessage* pxMessageToReplace = 0;
		float fLowestDelay = pxStaticMessage->m_fDelay+fExtraDelay;

		for(int i=0;i<m_iNumDelayedMessages;i++,pxDelayedMessage++)
		{
			if(pxDelayedMessage->m_fDelay < fLowestDelay)
			{
				fLowestDelay = pxDelayedMessage->m_fDelay;
				pxMessageToReplace = pxDelayedMessage;
			}
		}

		if(pxMessageToReplace)
		{
			//Take a copy of the message being replaced . It will be sent at the end
			xDisplacedMessage = *pxMessageToReplace;
			pxMessageToReplace->m_pxStaticMessage = 0;
			bHasDisplacedMessage = true;
			
			pxNewDelayedMessage = pxMessageToReplace;
		}
	}

	if(!pxNewDelayedMessage)
	{
		//Failed to find space for the new message ,so we'll just have to send it now instead
		SendMessage(pxStaticMessage, uFrom);
		return;
	}

	//Fill in the message
	pxNewDelayedMessage->SetMessage(pxStaticMessage);
	pxNewDelayedMessage->m_uMessageIndex = uMessageIndex;
	pxNewDelayedMessage->m_uFromGuid = uFrom; 
	pxNewDelayedMessage->m_fDelay = pxStaticMessage->m_fDelay + fExtraDelay;
	#ifdef ASURA_TOOLS
	pxNewDelayedMessage->m_fInitialDelay = pxNewDelayedMessage->m_fDelay;
	#endif

	#ifdef ASURA_TOOLS
	if(!g_pfnSendMessageCallback)
	{
		Tools_Entity_Simulation::IncreaseMessageLogIndent();
		Tools_Entity_Simulation::LogInGameMessage( pxStaticMessage, uFrom, true, pxNewDelayedMessage->m_fInitialDelay);
		Tools_Entity_Simulation::DecreaseMessageLogIndent();
	}
	#endif

	
	//If we ran out of space and had to displace a message ,then send it now.
	//Doing it at the end to avoid problems should this message cause other delayed
	//messages to be sent
	if(bHasDisplacedMessage)
	{
		const Asura_StaticMessage* pxDisplacedStaticMessage = xDisplacedMessage.m_pxStaticMessage;
		if(pxDisplacedStaticMessage)
		{
			SendMessage(pxDisplacedStaticMessage, xDisplacedMessage.m_uFromGuid);
		}
		xDisplacedMessage.DeallocateMessage();
	}
}

#ifdef ASURA_TOOLS
void Asura_StaticMessage_Handler::SendMessage(const Asura_StaticMessage* pxStaticMessage, Asura_Guid uFrom, float fDelayUsed /*=0.0f*/)
#else
void Asura_StaticMessage_Handler::SendMessage(const Asura_StaticMessage* pxStaticMessage, Asura_Guid uFrom)
#endif
{
	#ifdef ASURA_TOOLS
	Tools_Entity_Simulation::IncreaseMessageLogIndent();
	#endif

	#ifdef ASURA_TOOLS
	if(g_pfnSendMessageCallback)
	{
		g_pfnSendMessageCallback(pxStaticMessage,uFrom);
	}
	else
	#endif 
	{
		Asura_Message* pxMessage = Asura_Network::CreateMessage(pxStaticMessage->m_usMsgID, pxStaticMessage->m_usDataSize);
		if(pxMessage)
		{
			pxMessage->To = pxStaticMessage->m_uSendTo;
			pxMessage->From = uFrom;
			if(pxStaticMessage->m_usDataSize)
			{
				Asura_Mem::Copy(pxMessage->GetData(), pxStaticMessage->m_pData, pxStaticMessage->m_usDataSize);
			}
	
			#ifdef ASURA_TOOLS
			Tools_Entity_Simulation::LogInGameMessage(pxStaticMessage, uFrom, false, fDelayUsed);
			#endif

			Asura_Network::Send(pxMessage);
		}
	}

	#ifdef ASURA_TOOLS
	Tools_Entity_Simulation::DecreaseMessageLogIndent();
	#endif
}


void Asura_StaticMessage_Handler::AddMessage(const Asura_StaticMessage& xMessage, u_short& usBlockID)
{
	//temporary until I can checkout the code that still calls this function.
	Asura_Assert(0, "RAR - Please don't use this function");
	AddMessage(ASURA_STATICMESSAGE_ENTITYSET, xMessage, usBlockID);
}

/// Kevin Lea - 15/04/2002 17:36:51 - the AddMessage method only get used infrequently in the Tools,
/// not in-game, and as such it can be cavalier when it comes to re-allocating more memory.
// TODO - AJG - 23/03/06 -	Shouldn't this be within a tools define then?
//							Looks like it can't be for now (called by project code outside of tools), but that may be something else that should be tools only.
void Asura_StaticMessage_Handler::AddMessage(enum Asura_StaticMessageSetIndex eSet, const Asura_StaticMessage& xMessage, u_short& usBlockID)
{
	//convert the block ID to an index
	u_short usBlockIndex = usBlockID;
	if(usBlockIndex != ASURA_STATICMESSAGE_EMPTYBLOCK)
	{
		Asura_Assert(usBlockID>=g_ausFirstBlockID[eSet] && usBlockID<=g_ausLastBlockID[eSet],"");

		usBlockIndex = (u_short)(usBlockIndex - g_ausFirstBlockID[eSet]);
	}

	//add the message
	m_xMessageSet[eSet].AddMessage(xMessage, usBlockIndex);

	//convert block index back to ID
	if(usBlockIndex == ASURA_STATICMESSAGE_EMPTYBLOCK)
		usBlockID = ASURA_STATICMESSAGE_EMPTYBLOCK;
	else
		usBlockID = (u_short)(usBlockIndex + g_ausFirstBlockID[eSet]);

}

void Asura_StaticMessage_Handler::DeleteMessage(u_short usBlockID, int iMessageIndex)
{
	enum Asura_StaticMessageSetIndex eSet;
	int iBlockIndex;
	if(ConvertIDToIndex(usBlockID, &iBlockIndex, &eSet))
	{
		u_short usBlockIndex = (u_short) iBlockIndex;

		//delete the message
		m_xMessageSet[eSet].DeleteMessage(usBlockIndex, iMessageIndex);
	}
}

void Asura_StaticMessage_Handler::DeleteMessage(u_short* pusBlockID, int iMessageIndex)
{
	Asura_Assert(pusBlockID, "");

	DeleteMessage(*pusBlockID, iMessageIndex);
	
	//Delete the message block if now empty
	if(GetNumberOfMessagesInBlock(*pusBlockID) == 0)
	{
		DeleteMessageBlock(*pusBlockID);
		*pusBlockID = ASURA_STATICMESSAGE_EMPTYBLOCK;
	}
}


void Asura_StaticMessage_Handler::DeleteMessageBlock(u_short usBlockID)
{
	//just return without doing anything if the empty block id was passed
	if(usBlockID == ASURA_STATICMESSAGE_EMPTYBLOCK) return;

	//find out which message set this block id belongs to
	for(int i=0;i<ASURA_STATICMESSAGE_NUMSETS;i++)
	{
		if(usBlockID>=g_ausFirstBlockID[i] && usBlockID<=g_ausLastBlockID[i])
		{
			m_xMessageSet[i].DeleteMessageBlock((u_short)(usBlockID-g_ausFirstBlockID[i]));
			break;
		}
	}
}

int Asura_StaticMessage_Handler::GetNumberOfMessageBlocks(enum Asura_StaticMessageSetIndex eSet)
{
	return m_xMessageSet[eSet].GetNumberOfMessageBlocks();
}

int Asura_StaticMessage_Handler::GetNumberOfMessagesInBlock(enum Asura_StaticMessageSetIndex eSet,int iBlockIndex)
{
	return m_xMessageSet[eSet].GetNumberOfMessagesInBlock(iBlockIndex);
}

int Asura_StaticMessage_Handler::GetNumberOfMessagesInBlock(int iBlockID)
{
	enum Asura_StaticMessageSetIndex eSet;
	int iBlockIndex;
	if(ConvertIDToIndex(iBlockID, &iBlockIndex, &eSet))
	{

		if(iBlockIndex == ASURA_STATICMESSAGE_EMPTYBLOCK) return 0;

		return m_xMessageSet[eSet].GetNumberOfMessagesInBlock(iBlockIndex);
	}
	return 0;
}

Asura_StaticMessage* Asura_StaticMessage_Handler::GetMessagePointer(enum Asura_StaticMessageSetIndex eSet, int iBlockIndex, int iMessageIndex)
{
	return m_xMessageSet[eSet].GetMessagePointer(iBlockIndex,iMessageIndex);
}

Asura_StaticMessage* Asura_StaticMessage_Handler::GetMessagePointer(int iBlockID, int iMessageIndex)
{
	enum Asura_StaticMessageSetIndex eSet;
	int iBlockIndex;
	if(ConvertIDToIndex(iBlockID, &iBlockIndex, &eSet))
	{
		return m_xMessageSet[eSet].GetMessagePointer(iBlockIndex, iMessageIndex);
	}
	return 0;
}



bool Asura_StaticMessage_Handler::IsMessageBlockValid(int iBlockID)
{
	enum Asura_StaticMessageSetIndex eSet;
	int iBlockIndex;
	if(ConvertIDToIndex(iBlockID, &iBlockIndex, &eSet))
	{
		if(iBlockIndex == ASURA_STATICMESSAGE_EMPTYBLOCK) return false;
		return m_xMessageSet[eSet].IsMessageBlockValid(iBlockIndex);
	}
	return false;
}


u_short Asura_StaticMessage_Handler::CopyMessageBlock(u_short usBlockID, Asura_Guid uFromGuid /*= ASURA_GUID_UNREGISTERED*/, Asura_Guid uToGuid /*= ASURA_GUID_UNREGISTERED*/)
{
	if(usBlockID == ASURA_STATICMESSAGE_EMPTYBLOCK)
	{
		return ASURA_STATICMESSAGE_EMPTYBLOCK;
	}

	enum Asura_StaticMessageSetIndex eSet;
	int iBlockIndex;
	if(ConvertIDToIndex(usBlockID, &iBlockIndex, &eSet))
	{
		u_short usNewBlockIndex = m_xMessageSet[eSet].CopyMessageBlock(&m_xMessageSet[eSet],(u_short)iBlockIndex, uFromGuid, uToGuid);

		return (u_short)(usNewBlockIndex + g_ausFirstBlockID[eSet]);
	}

	return ASURA_STATICMESSAGE_EMPTYBLOCK;
}

u_short Asura_StaticMessage_Handler::CopyMessageBlock( const Asura_StaticMessageBlock* pxBlock, const Asura_StaticMessageSetIndex eSet, Asura_Guid uFromGuid /*= ASURA_GUID_UNREGISTERED*/, Asura_Guid uToGuid /*= ASURA_GUID_UNREGISTERED*/)
{
	if( !pxBlock || ( pxBlock->GetNumberOfMessagesInBlock() == 0 ) )
	{
		return ASURA_STATICMESSAGE_EMPTYBLOCK;
	}

	u_short usNewBlockIndex = m_xMessageSet[eSet].CopyMessageBlock( pxBlock, uFromGuid, uToGuid );
	
	return (u_short)(usNewBlockIndex + g_ausFirstBlockID[eSet]);
}

u_short Asura_StaticMessage_Handler::TransferMessageBlock(u_short usBlockID, enum Asura_StaticMessageSetIndex eDestinationSet)
{
	if(usBlockID == ASURA_STATICMESSAGE_EMPTYBLOCK)
	{
		//empty mesage block doesn't live any set as such , so just return
		return usBlockID;
	}

	enum Asura_StaticMessageSetIndex eSet;
	int iBlockIndex;
	if(!ConvertIDToIndex(usBlockID, &iBlockIndex, &eSet)) return usBlockID;

	if(eSet == eDestinationSet) return usBlockID;//no need to move it

	//make a copy of the message block in the destination set
	u_short usNewBlockIndex = m_xMessageSet[eDestinationSet].CopyMessageBlock(&m_xMessageSet[eSet], (u_short)iBlockIndex);

	//delete the old message block
	m_xMessageSet[eSet].DeleteMessageBlock((u_short)iBlockIndex);

	//convert the index back to an ID
	usBlockID = (u_short)(usNewBlockIndex + g_ausFirstBlockID[eDestinationSet]);

	return usBlockID;
}

bool Asura_StaticMessage_Handler::ConvertIDToIndex(int iBlockID, int* piOutputBlockIndex, enum Asura_StaticMessageSetIndex *peOutputSet)
{
	if(iBlockID == ASURA_STATICMESSAGE_EMPTYBLOCK)
	{
		//invalid block index and block id use the same value
		*piOutputBlockIndex = ASURA_STATICMESSAGE_EMPTYBLOCK;
		*peOutputSet = (Asura_StaticMessageSetIndex)0;//set doesn't matter
		return true;
	}

	//find out which message set this block id belongs to
	for(int i=0;i<ASURA_STATICMESSAGE_NUMSETS;i++)
	{
		if(iBlockID>=g_ausFirstBlockID[i] && iBlockID<=g_ausLastBlockID[i])
		{
			*piOutputBlockIndex =  iBlockID - g_ausFirstBlockID[i];
			*peOutputSet = (Asura_StaticMessageSetIndex)i;
			return true;
		}
	}

	return false;

}

u_short Asura_StaticMessage_Handler::ConvertIndexToID(enum Asura_StaticMessageSetIndex eSet, int iBlockIndex)
{
	if(iBlockIndex == ASURA_STATICMESSAGE_EMPTYBLOCK) return ASURA_STATICMESSAGE_EMPTYBLOCK;
	return (u_short)(g_ausFirstBlockID[eSet] + iBlockIndex);
}



////////////////////////////////////////////////////////////////////////////////////////////////
/*------------------------**
** Asura_StaticMessageSet **
**------------------------*/

void Asura_StaticMessageSet::Initialise()
{
	m_usNumberOfMessageBlocks = 0;
	m_ppxMessageBlocks = 0;
}

void Asura_StaticMessageSet::Shutdown()
{
	Asura_ResourcePushType(ASURA_RESOURCE_STATIC_MESSAGE);
	{
		if (m_ppxMessageBlocks)
		{
			for(int i=0;i<m_usNumberOfMessageBlocks;i++)
			{
				delete m_ppxMessageBlocks[i];
			}
			Asura_ResourceDeallocateArray(m_ppxMessageBlocks);
			m_ppxMessageBlocks = 0;
		}
		m_usNumberOfMessageBlocks = 0;
	}
	Asura_ResourcePopType();
}

void Asura_StaticMessageSet::SetupStorageSpace(u_short usNumberOfMessageBlocks)
{
	m_usNumberOfMessageBlocks = usNumberOfMessageBlocks;

	if(m_usNumberOfMessageBlocks)
	{
		Asura_ResourcePushType(ASURA_RESOURCE_STATIC_MESSAGE);
		{
			Asura_ResourceAllocateArray(m_ppxMessageBlocks, m_usNumberOfMessageBlocks);
			Asura_Mem::Set(m_ppxMessageBlocks, 0, sizeof(Asura_StaticMessageBlock*) * m_usNumberOfMessageBlocks);
		}
		Asura_ResourcePopType();
	}
}




u_short Asura_StaticMessageSet::AllocateMessageBlock()
{
	//First see if any of the blocks in the middle are unused
	for(u_short i=0;i<m_usNumberOfMessageBlocks;i++)
	{
		if(m_ppxMessageBlocks[i]==0)
		{
			//this one is free
			m_ppxMessageBlocks[i] = new Asura_StaticMessageBlock;
			return i;
		}
	}

	Asura_ResourcePushType(ASURA_RESOURCE_STATIC_MESSAGE);
	{
		//No free entries , so enlarge the array of message blocks
		Asura_StaticMessageBlock** ppxNewMessageBlocks;

		Asura_ResourceAllocateArray(ppxNewMessageBlocks, m_usNumberOfMessageBlocks+1);

		if (m_usNumberOfMessageBlocks)
		{
			for (int i=0; i<m_usNumberOfMessageBlocks; i++)
			{
				ppxNewMessageBlocks[i] = m_ppxMessageBlocks[i];
			}
			Asura_ResourceDeallocateArray(m_ppxMessageBlocks);
		}
		m_ppxMessageBlocks = ppxNewMessageBlocks;

		m_ppxMessageBlocks[m_usNumberOfMessageBlocks] = new Asura_StaticMessageBlock;
	}
	Asura_ResourcePopType();

	return m_usNumberOfMessageBlocks++;

}


void Asura_StaticMessageSet::AddMessage(const Asura_StaticMessage& xMessage, u_short& usBlockIndex)
{
	if (usBlockIndex==ASURA_STATICMESSAGE_EMPTYBLOCK)
	{
		//Need to allocate a new message block.
		usBlockIndex = AllocateMessageBlock();
	}
	else if(usBlockIndex >= m_usNumberOfMessageBlocks || !m_ppxMessageBlocks[usBlockIndex])
	{
		Asura_Assert(0,"Adding to an invalid message block");
		usBlockIndex = AllocateMessageBlock();
	}

	m_ppxMessageBlocks[usBlockIndex]->AddMessage(xMessage);
}

void Asura_StaticMessageSet::DeleteMessage(u_short usBlockIndex,int iMessageIndex)
{
    Asura_Assert(usBlockIndex<m_usNumberOfMessageBlocks,"");
	Asura_Assert(m_ppxMessageBlocks[usBlockIndex], "Invalid message block index");
		
	m_ppxMessageBlocks[usBlockIndex]->DeleteMessage(iMessageIndex);
}

void Asura_StaticMessageSet::DeleteMessageBlock(u_short usBlockIndex)
{
    if(usBlockIndex<m_usNumberOfMessageBlocks)
	{
		delete m_ppxMessageBlocks[usBlockIndex];
		m_ppxMessageBlocks[usBlockIndex] = 0;
	}
	else
	{
		Asura_Assert(usBlockIndex<m_usNumberOfMessageBlocks,"Invalid message block number. (Entity file may be out of sync with other files)");
	}
}


int Asura_StaticMessageSet::GetNumberOfMessagesInBlock(int iBlockIndex)
{
	
	if(iBlockIndex>=0 && iBlockIndex<m_usNumberOfMessageBlocks && m_ppxMessageBlocks[iBlockIndex])
	{
		return m_ppxMessageBlocks[iBlockIndex]->GetNumberOfMessagesInBlock();
	}
	else
	{
		Asura_Assert(iBlockIndex>=0 && iBlockIndex<m_usNumberOfMessageBlocks,"Invalid message block number. (Entity file may be out of sync with other files)");
		return 0;
	}
}

Asura_StaticMessage* Asura_StaticMessageSet::GetMessagePointer(int iBlockIndex,int iMessageIndex)
{
	Asura_Assert(iBlockIndex>=0 && iBlockIndex<m_usNumberOfMessageBlocks,"");
	Asura_Assert(m_ppxMessageBlocks[iBlockIndex], "Invalid message block index");

	return m_ppxMessageBlocks[iBlockIndex]->GetMessagePointer(iMessageIndex);
}

Asura_StaticMessageBlock* Asura_StaticMessageSet::GetMessageBlock(int iBlockIndex)
{
	Asura_Assert(iBlockIndex>=0 && iBlockIndex<m_usNumberOfMessageBlocks,"");
	Asura_Assert(m_ppxMessageBlocks[iBlockIndex], "Invalid message block index");

	return m_ppxMessageBlocks[iBlockIndex];
}

bool Asura_StaticMessageSet::IsMessageBlockValid(int iBlockIndex)
{
	if(iBlockIndex<0 || iBlockIndex>=m_usNumberOfMessageBlocks) return false;
	return m_ppxMessageBlocks[iBlockIndex] != 0;
}


u_short Asura_StaticMessageSet::CopyMessageBlock(Asura_StaticMessageSet* pxSourceSet,u_short usBlockIndex,Asura_Guid uFromGuid /*= ASURA_GUID_UNREGISTERED*/,Asura_Guid uToGuid /*= ASURA_GUID_UNREGISTERED*/)
{
	if(usBlockIndex == ASURA_STATICMESSAGE_EMPTYBLOCK)
	{
		return ASURA_STATICMESSAGE_EMPTYBLOCK;
	}

	if(!(usBlockIndex<pxSourceSet->m_usNumberOfMessageBlocks))
	{
		Asura_Assert(usBlockIndex<pxSourceSet->m_usNumberOfMessageBlocks,"Invalid message block number. (Entity file may be out of sync with other files)");
		return ASURA_STATICMESSAGE_EMPTYBLOCK;
	}

	u_short usNewBlock = ASURA_STATICMESSAGE_EMPTYBLOCK;
	int iNumMessagesInBlock = pxSourceSet->GetNumberOfMessagesInBlock(usBlockIndex);
	//Not exactly an efficient way of doing the copying , but it shouldn't matter much , and it allows
	//the existing functions to be used. RAR 23/08/02
	for(int i=0;i<iNumMessagesInBlock;i++)
	{
		Asura_StaticMessage xMessage = *pxSourceSet->GetMessagePointer(usBlockIndex,i);
		if(xMessage.m_usDataSize)
		{
			Asura_ResourcePushType(ASURA_RESOURCE_STATIC_MESSAGE);
			{
				void* pData;

				//need to duplicate the extra message data
				Asura_ResourceAllocateMemoryBlock(pData, xMessage.m_usDataSize, ASURA_ALIGN_4_BYTE);
				Asura_Mem::Copy(pData,xMessage.m_pData,xMessage.m_usDataSize);
				xMessage.m_pData = pData;
			}
			Asura_ResourcePopType();
		}

		//deal with redirecting self-targeted message blocks
		if(uFromGuid != ASURA_GUID_UNREGISTERED && uToGuid != ASURA_GUID_UNREGISTERED)
		{
			if(xMessage.m_uSendTo == uFromGuid)
			{
				xMessage.m_uSendTo = uToGuid;
			}
		}
	
		AddMessage(xMessage,usNewBlock);
	}
	return usNewBlock;
}

u_short Asura_StaticMessageSet::CopyMessageBlock( const Asura_StaticMessageBlock* pxSourceBlock, Asura_Guid uFromGuid /*= ASURA_GUID_UNREGISTERED*/, Asura_Guid uToGuid /*= ASURA_GUID_UNREGISTERED*/ )
{
	if( !pxSourceBlock || ( pxSourceBlock->GetNumberOfMessagesInBlock() == 0 ) )
	{
		return ASURA_STATICMESSAGE_EMPTYBLOCK;
	}

	u_short usNewBlock = ASURA_STATICMESSAGE_EMPTYBLOCK;
	int iNumMessagesInBlock = pxSourceBlock->GetNumberOfMessagesInBlock();
	
	//Not exactly an efficient way of doing the copying , but it shouldn't matter much , and it allows
	//the existing functions to be used. RAR 23/08/02
	for(int i=0;i<iNumMessagesInBlock;i++)
	{
		Asura_StaticMessage xMessage = *pxSourceBlock->GetMessagePointer( i );
		if(xMessage.m_usDataSize)
		{
			Asura_ResourcePushType(ASURA_RESOURCE_STATIC_MESSAGE);
			{
				void* pData;

				//need to duplicate the extra message data
				Asura_ResourceAllocateMemoryBlock(pData, xMessage.m_usDataSize, ASURA_ALIGN_4_BYTE);
				Asura_Mem::Copy(pData,xMessage.m_pData,xMessage.m_usDataSize);
				xMessage.m_pData = pData;
			}
			Asura_ResourcePopType();
		}

		//deal with redirecting self-targeted message blocks
		if(uFromGuid != ASURA_GUID_UNREGISTERED && uToGuid != ASURA_GUID_UNREGISTERED)
		{
			if(xMessage.m_uSendTo == uFromGuid)
			{
				xMessage.m_uSendTo = uToGuid;
			}
		}

		AddMessage(xMessage,usNewBlock);
	}
	return usNewBlock;
}

////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------**
** Asura_StaticMessage **
**---------------------*/

void Asura_StaticMessage::AllocateMessageData(u_int uSize,bool bZero)
{
	Asura_Assert(!m_pData,"Message data already allocated");
	
	Asura_ResourcePushType(ASURA_RESOURCE_STATIC_MESSAGE);
	m_usDataSize = static_cast<unsigned short>(uSize);
	if(m_usDataSize)
	{
		Asura_ResourceAllocateMemoryBlock(m_pData, m_usDataSize, ASURA_ALIGN_4_BYTE);
		if(bZero)
		{
				Asura_Mem::Set( m_pData, 0, m_usDataSize );
		}
	}
	else
	{
		m_pData = 0;
	}
	Asura_ResourcePopType();
}

void Asura_StaticMessage::DeallocateMessageData()
{
	if(m_pData)
	{
		Asura_ResourcePushType(ASURA_RESOURCE_STATIC_MESSAGE);
		Asura_ResourceDeallocateMemoryBlock(m_pData);
		Asura_ResourcePopType();

		m_pData = 0;
		m_usDataSize = 0;
	}
}

void Asura_StaticMessage::Copy(const Asura_StaticMessage* pxMessageToCopy)
{
	if(this==pxMessageToCopy) return;

	Asura_ResourcePushType(ASURA_RESOURCE_STATIC_MESSAGE);
	{
		Asura_ResourceDeallocateMemoryBlock(m_pData);

		m_usMsgID = pxMessageToCopy->m_usMsgID;
		m_usDataSize = pxMessageToCopy->m_usDataSize;
		m_uSendTo = pxMessageToCopy->m_uSendTo;
		m_fDelay = pxMessageToCopy->m_fDelay;
		m_fProbability = pxMessageToCopy->m_fProbability;
        m_uFlags = pxMessageToCopy->m_uFlags;
		#ifdef ASURA_TOOLS
		m_uToolsFlags = pxMessageToCopy->m_uToolsFlags;
		#endif

		if(m_usDataSize)
		{
			Asura_ResourceAllocateMemoryBlock(m_pData, m_usDataSize, ASURA_ALIGN_4_BYTE);
			Asura_Mem::Copy(m_pData,pxMessageToCopy->m_pData,m_usDataSize);
		}
		else
		{
			m_pData = 0;
		}
	}
	Asura_ResourcePopType();
}

#ifdef ASURA_TOOLS
bool Asura_StaticMessage::operator == ( const Asura_StaticMessage& xMessage) const
{
	if(	m_usMsgID == xMessage.m_usMsgID &&
		m_usDataSize ==	xMessage.m_usDataSize &&
		m_uSendTo == xMessage.m_uSendTo &&
		m_fDelay == xMessage.m_fDelay &&
		m_fProbability == xMessage.m_fProbability &&
		m_uFlags == xMessage.m_uFlags &&
		m_uToolsFlags == xMessage.m_uToolsFlags)
	{
		return (Asura_Mem::Compare(m_pData,xMessage.m_pData,m_usDataSize) == 0);
	}
	return false;

}
#endif //ASURA_TOOLS


////////////////////////////////////////////////////////////////////////////////////////////////
/*----------------------------**
** Asura_DelayedStaticMessage **
**----------------------------*/

void Asura_DelayedStaticMessage::SetMessage(const Asura_StaticMessage* pxStaticMessage)
{
	Asura_Assert(!m_pxStaticMessage,"Already have a message set for this delayed message");

	m_pxStaticMessage = new Asura_StaticMessage();
	m_pxStaticMessage->Copy(pxStaticMessage);
}

void Asura_DelayedStaticMessage::DeallocateMessage()
{
	if(m_pxStaticMessage)
	{
		m_pxStaticMessage->DeallocateMessageData();
		delete m_pxStaticMessage;
		m_pxStaticMessage = 0;
	}
}

void Asura_DelayedStaticMessage::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	m_pxStaticMessage = new Asura_StaticMessage();	

	u_short usDataSize;
	xStream >> m_pxStaticMessage->m_usMsgID;
	xStream >> usDataSize;
	xStream >> m_pxStaticMessage->m_uSendTo;
	xStream >> m_pxStaticMessage->m_fDelay;
	xStream >> m_pxStaticMessage->m_fProbability;
    xStream >> m_pxStaticMessage->m_uFlags;

	if(usDataSize)
	{
		m_pxStaticMessage->AllocateMessageData(usDataSize, false);
		xStream.Read(usDataSize, m_pxStaticMessage->m_pData);
	}

	xStream >> m_fDelay;
	xStream >> m_uFromGuid;
	#ifdef ASURA_TOOLS
	xStream >> m_fInitialDelay;
	#endif
}

void Asura_DelayedStaticMessage::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	xStream << m_pxStaticMessage->m_usMsgID;
	xStream << m_pxStaticMessage->m_usDataSize;
	xStream << m_pxStaticMessage->m_uSendTo;
	xStream << m_pxStaticMessage->m_fDelay;
	xStream << m_pxStaticMessage->m_fProbability;
    xStream << m_pxStaticMessage->m_uFlags;

	if(m_pxStaticMessage->m_usDataSize)
	{
		xStream.Write(m_pxStaticMessage->m_usDataSize, m_pxStaticMessage->m_pData);
	}

	xStream << m_fDelay;
	xStream << m_uFromGuid;
	#ifdef ASURA_TOOLS
	xStream << m_fInitialDelay;
	#endif
}
