/*************************************************************************
	Crytek Source File.
	Copyright (C), Crytek Studios, 2001-2005.
 -------------------------------------------------------------------------
	$Id$
	$DateTime$
	Description:	This file implements dispatching RMI calls in C++ to
	              relevant game object code

 -------------------------------------------------------------------------
	History:
		- 24 Oct 2005 : Created by Craig Tiller

*************************************************************************/

#include "StdAfx.h"
#include "GameObject.h"
#include "GameObjectDispatch.h"
#include "Network/GameContext.h"

CGameObjectDispatch * CGameObjectDispatch::m_pGOD = NULL;

CGameObjectDispatch::CGameObjectDispatch() : m_bSafety(false)
{
	assert(m_pGOD == NULL);
	m_pGOD = this;
}

CGameObjectDispatch::~CGameObjectDispatch()
{
	m_pGOD = NULL;
}

void CGameObjectDispatch::RegisterInterface( SGameObjectExtensionRMI* pMessages, size_t nCount )
{
	if (m_bSafety)
	{
		CryError("CGameObjectDispatch::RegisterInterface: occurs too late");
		return;
	}

	// watch out for duplicate registrations...
	for (size_t i=0; i<m_messages.size(); i++)
		if (m_messages[i]->pBase == pMessages)
			return; // messages already registered

	// actually build protocol definitions for the messages
	for (size_t i=0; i<nCount; i++)
	{
		std::vector<SNetMessageDef> * pMsgDef = pMessages[i].isServerCall? &m_serverCalls : &m_clientCalls;

		SNetMessageDef md;
		md.description = pMessages[i].description;
		md.handler = Trampoline;
		md.nUser = m_messages.size();
		if (pMessages[i].attach != eRAT_NoAttach)
			md.reliability = eNRT_UnreliableOrdered;
		else
			md.reliability = pMessages[i].reliability;
		md.parallelFlags = eMPF_BlocksStateChange | eMPF_DiscardIfNoEntity;
		if (pMessages[i].lowDelay)
			md.parallelFlags |= eMPF_NoSendDelay;

		pMsgDef->push_back(md);
		m_messages.push_back(pMessages + i);
	}
}

TNetMessageCallbackResult CGameObjectDispatch::Trampoline( 
	uint32 userId, 
	INetMessageSink* handler, 
	TSerialize serialize, 
	uint32 curSeq, 
	uint32 oldSeq,
	EntityId * pEntityId, 
	INetChannel *pNetChannel )
{
	const SGameObjectExtensionRMI * pRMI = m_pGOD->m_messages[userId];
	IRMIAtSyncItem * pItem = (IRMIAtSyncItem*) pRMI->decoder( serialize, pEntityId, pNetChannel );
	if (pItem)
		CCryAction::GetCryAction()->GetGameContext()->GetNetContext()->LogCppRMI(*pEntityId, pItem);
	return TNetMessageCallbackResult( pItem!=0, pItem );
}

void CGameObjectDispatch::LockSafety()
{
	if (m_bSafety)
		return;

	for (size_t i=0; i<m_serverCalls.size(); i++)
		m_messages[m_serverCalls[i].nUser]->pMsgDef = &m_serverCalls[i];
	for (size_t i=0; i<m_clientCalls.size(); i++)
		m_messages[m_clientCalls[i].nUser]->pMsgDef = &m_clientCalls[i];

	m_bSafety = true;
}

bool CGameObjectDispatch::CProtocolDef::IsServer()
{
	return this == &m_pGOD->m_serverDef;
}

void CGameObjectDispatch::CProtocolDef::DefineProtocol( IProtocolBuilder * pBuilder )
{
	m_pGOD->LockSafety();

	std::vector<SNetMessageDef> * pSending   = IsServer()? &m_pGOD->m_clientCalls : &m_pGOD->m_serverCalls;
	std::vector<SNetMessageDef> * pReceiving = IsServer()? &m_pGOD->m_serverCalls : &m_pGOD->m_clientCalls;

	SNetProtocolDef protoSending;
	protoSending.nMessages = pSending->size();
	protoSending.vMessages = protoSending.nMessages? &*pSending->begin() : 0;
	SNetProtocolDef protoReceiving;
	protoReceiving.nMessages = pReceiving->size();
	protoReceiving.vMessages = protoReceiving.nMessages? &*pReceiving->begin() : 0;

	pBuilder->AddMessageSink( this, protoSending, protoReceiving );
}

bool CGameObjectDispatch::CProtocolDef::HasDef( const SNetMessageDef * pDef )
{
	return 
		pDef >= &*m_pGOD->m_serverCalls.begin() && pDef < &*m_pGOD->m_serverCalls.end() || 
		pDef >= &*m_pGOD->m_clientCalls.begin() && pDef < &*m_pGOD->m_clientCalls.end();
}

void CGameObjectDispatch::GetMemoryStatistics(ICrySizer * s)
{
	s->AddContainer(m_messages);
	s->AddContainer(m_clientCalls);
	s->AddContainer(m_serverCalls);
}
