//////////////////////////////////////////////////////////////////////////////
//
// Asura_PS2_SCE_RT.cpp
//
// Copyright (c) 2003 Rebellion. All rights reserved.
//
// Description:
//
//     Asura interface for SCE-RT API
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#include "Asura_PS2_SCE_RT.h"

// library headers
#include <sifdev.h>
#include <libgraph.h>	// hack for retry count.

// engine includes
#include "Core/Asura_Maths.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Logfile.h"
#include "Debugging/Asura_DebugText.h"
#include "Network/Asura_Network.h"		// needed for lobbying stuff
#include "Network/Asura_Message.h"
#include "Network/Asura_MessageHeader.h"
#include "Network/Asura_Network_Session.h"

// ps2 includes
#include "Core/Asura_PS2_OS.h"
#include "scert/include/dme.h"
#include "Network/Asura_PS2_SCE_RT_Server.h"
#include "Network/Asura_PS2_SCE_RT_Client.h"
#include "Network/eznetcnf/Asura_PS2_eznetcnf.h"

// D E F I N E S /////////////////////////////////////////////////////////////

// TODO: define this on a per-project basis
#define ASURA_PS2_APPLICATION_ID 0x6ABEE

const int ASURA_PS2_UDP_SEND_BUF_SIZE = 1024;
const int ASURA_PS2_UDP_RECV_BUF_SIZE = 1024;


// TODO: load config using ezntcnf, or at least get the icons from CD
#define MAIN_MOD_ROOT     "IRX\\NET\\"
#define SCERT_MOD_ROOT    "IRX\\NET\\"

#define IOP_MOD_INET      MAIN_MOD_ROOT "inet.irx"
#define IOP_MOD_NETCNF    MAIN_MOD_ROOT "netcnf.irx"
#define IOP_MOD_INETCTL   MAIN_MOD_ROOT "inetctl.irx"

#define IOP_MOD_MSIFRPC   MAIN_MOD_ROOT "msifrpc.irx"

#define IOP_MOD_DEV9 MAIN_MOD_ROOT "dev9.irx"
#define IOP_MOD_SMAP MAIN_MOD_ROOT "smap.irx"

#define NETCNF_ARG "icon=host0:/usr/local/sce/conf/neticon/english/icon.sys"  "\0" "iconsys=host0:/usr/local/sce/conf/neticon/english/SYS_NET.ICO" 
//#define INETCTL_ARG "-no_decode" "\0" "host0:/usr/local/sce/conf/net/net005.cnf"  
#define  INETCTL_ARG "-no-auto"


#define IOP_MOD_LIBNETB    SCERT_MOD_ROOT "libnetb.irx"

const u_int ASURA_PS2_SCE_RT_MAX_MESSAGE_TOTAL_SIZE = 1024;	// bytes.
const u_int ASURA_PS2_SCE_RT_MAX_MESSAGE_DATA_SIZE = ASURA_PS2_SCE_RT_MAX_MESSAGE_TOTAL_SIZE - sizeof(Asura_MessageHeader);

const u_int ASURA_PS2_SCE_RT_MAX_OBJECTS = 0;	// max number of objects receiving regular update messages

// T Y P E S /////////////////////////////////////////////////////////////////
struct Asura_PS2_SCE_RT_Message
{
	u_char m_aucBuffer[ASURA_PS2_SCE_RT_MAX_MESSAGE_TOTAL_SIZE];
};


// S T A T I C   C L A S S   M E M B E R S ///////////////////////////////////
bool Asura_PS2_SCE_RT::s_bIRXModulesLoaded = false;
Asura_PS2_SCE_RT_Server* Asura_PS2_SCE_RT::s_pxServer = NULL;
char Asura_PS2_SCE_RT::s_acServerIP[ASURA_PS2_MAX_IP_STRING_LENGTH];
int Asura_PS2_SCE_RT::s_iServerPortNumber;
int Asura_PS2_SCE_RT::s_iConnectionIndex;
int Asura_PS2_SCE_RT::s_iClientIndexOfServer;
u_int Asura_PS2_SCE_RT::s_uMessageTypeID;
bool Asura_PS2_SCE_RT::s_bWaitingForServerResponse = false;
Asura_PS2_SCE_RT::SceRtMode Asura_PS2_SCE_RT::s_eMode = Asura_PS2_SCE_RT::SceRtMode_Idle;
Asura_PS2_SCE_RT::JoinState Asura_PS2_SCE_RT::s_eJoinState = Asura_PS2_SCE_RT::JoinState_None;
u_int Asura_PS2_SCE_RT::s_uLatency = 0;
bool Asura_PS2_SCE_RT::s_bWaitingForPingResponse = false;
float Asura_PS2_SCE_RT::s_fTimeOfLastPingResponse = 0.0f;

#if ASURA_PS2_SCE_RT_BANDWIDTH_TEXT
char Asura_PS2_SCE_RT::s_acBandwidthText[ASURA_PS2_BANDWIDTH_TEXT_LENGTH];
#endif

// G L O B A L S /////////////////////////////////////////////////////////////
static Asura_PS2_SCE_RT_Message g_xMessage;	// instance to use as message buffer


// D E B U G V A R S /////////////////////////////////////////////////////////
DEBUGVAR bool dbg_bEnableNetPingIP = true;
DEBUGVAR float dbg_fTimeBetweenPings = 5.0f;
DEBUGVAR u_int dbg_uMaxConnectRetries = 256;	// arbitrary number
DEBUGVAR u_int dbg_uMaxJoinRetries = 256;	// arbitrary number
DEBUGVAR u_int dbg_uUDPErrorCount = 50;

#if ASURA_PS2_SCE_RT_BANDWIDTH_TEXT
#if _DEBUG
DEBUGVAR u_int dbg_uProfileBandwidth = 1;	// 1 is bytes/s, 2 is packets/s
#else
static u_int dbg_uProfileBandwidth = 0;
#endif
#endif

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////
bool Asura_PS2_SCE_RT::Initialise()
{
	if (LoadIRXModules())
	{
		return InitialiseNetworkConnection();
	}
	else
	{
		return false;
	}
}


void Asura_PS2_SCE_RT::Shutdown()
{
	NetErrorCode eErrorCode = NetClose();
	Asura_PS2_SCE_RT_CheckError(eErrorCode);
	s_bWaitingForPingResponse = false;
	s_fTimeOfLastPingResponse = 0.0f;
}


bool Asura_PS2_SCE_RT::LoadIRXModule(const char* szRelativeFilename, int args, const char *argp)
{
	return Asura_PS2_OS::LoadIRXModule(szRelativeFilename, args, argp) >= 0;
}
   

bool Asura_PS2_SCE_RT::LoadIRXModules()
{
	if (!s_bIRXModulesLoaded)
	{
		s_bIRXModulesLoaded = true;

		LoadIRXModule(IOP_MOD_INET, 0, NULL);
//		LoadIRXModule(IOP_MOD_NETCNF, sizeof(NETCNF_ARG), NETCNF_ARG);
//		LoadIRXModule(IOP_MOD_INETCTL, sizeof(INETCTL_ARG), INETCTL_ARG);
//		// dev9 and smap for SCE Network Adaptor
//		LoadIRXModule(IOP_MOD_DEV9, 0, NULL);
//		LoadIRXModule(IOP_MOD_SMAP, 0, NULL);
		if (!Asura_PS2_EZNetCnf::Initialise())
		{
			return false;
		}

		
		LoadIRXModule(IOP_MOD_MSIFRPC, 0, NULL);
		LoadIRXModule(IOP_MOD_LIBNETB, 0, NULL);
	}
	return true;
}


bool Asura_PS2_SCE_RT::InitialiseNetworkConnection()
{
	NetErrorCode eNetError = NetErrorNone;
	
	// Initialize Distributed Memory Engine 
	NetInitializeInParams xInitInParams;
	NetInitializeOutParams xInitOutParams;

	eNetError = NetSetDefaultInitializeParams(&xInitInParams);
	if (eNetError != NetErrorNone)
    {
		Asura_OutputToDebugger_Retail(("NetSetDefaultInitializeParams error: %d\n", (int) eNetError));
        return false;
    }

    xInitInParams.ApplicationID = ASURA_PS2_APPLICATION_ID;
    eNetError = NetInitialize(&xInitInParams, &xInitOutParams);    
             
    if (eNetError != NetErrorNone)
    {
        Asura_OutputToDebugger_Retail(("NetInitialize error: %d\n", (int) eNetError));
        return false;
    }

#if DME_CLIENT_VERSION_BUILD < 0017
    eNetError= NetEnableUDP(ASURA_PS2_UDP_SEND_BUF_SIZE, ASURA_PS2_UDP_RECV_BUF_SIZE);
    if (eNetError != NetErrorNone)
    {
        Asura_OutputToDebugger_Retail(("NetEnableUDP error: %d\n", (int) eNetError));
        return false;
    }
#endif

	// print out the DME version numbers
	char szBuildStamp[256]="";
	char szClientVersion[256]="";

    eNetError = NetGetBuildTimeStamp(szBuildStamp);
    if (eNetError != NetErrorNone)
    {
        Asura_OutputToDebugger_Retail(("NetGetBuildTimeStamp error: %d\n", (int) eNetError));
        return false;
    }
    eNetError = NetGetClientVersion(szClientVersion);
    if (eNetError != NetErrorNone)
    {
        Asura_OutputToDebugger_Retail(("NetGetClientVersion error: %d\n", (int) eNetError));
        return false;
    }
	
    Asura_OutputToDebugger_Retail(("\n---------------------------------------------------------------------"));
    Asura_OutputToDebugger_Retail(("\nInitialized DME build version %s\n", szBuildStamp));
    Asura_OutputToDebugger_Retail(("\n                        DME client version %s\n", szClientVersion));
    Asura_OutputToDebugger_Retail(("\n---------------------------------------------------------------------\n"));

	// everything went OK
	return true;    
}


bool Asura_PS2_SCE_RT::StartServerAndHostSession()
{
	NetErrorCode eNetError = NetErrorNone;

	Asura_OutputToDebugger_Retail(("Starting SCE-RT p2p server\n"));

	s_pxServer = Asura_PS2_SCE_RT_Server::Create();
	if (!s_pxServer)
	{
		return false;
	}

	if (!s_pxServer->Initialise())
    {
		Asura_OutputToDebugger_Retail(("Failed to Initialize Server\n"));
		return false;
	}
    
         
    // not needed if P2P host
//	Asura_OutputToDebugger_Retail(("Assigning ServerIP/Port for LAN Beacon:%s:%d\n", s_acServerIP, s_iServerPortNumber));
//	eNetError = NetAssignLocalServer(s_acServerIP, s_iServerPortNumber);
//
//    if(eNetError != NetErrorNone)
//    {
//		Asura_OutputToDebugger_Retail(("Error:NetAssignLocalServer failed, result:%d\n",eNetError));
//        Asura_OutputToDebugger_Retail(("Error:The LAN Beacon will not work\n"));
//	}

// (MH - 25.03.2003) need to add this data, part of lobbying system
//	const Asura_Network_Session* pxSession = Asura_Network::GetSession(0);
//	pxSession->Set();

	RegisterMessageTypes();
	
	// only call connect if using client/server
	//Connect();
	Join();

	s_eMode = SceRtMode_Hosting;
	return true;
}


void Asura_PS2_SCE_RT::StopServer()
{
	Asura_Assert(s_pxServer, "no server");
	if (s_pxServer)
	{
		s_pxServer->Shutdown();
		Asura_PS2_SCE_RT_Server::Destroy(s_pxServer);
		s_pxServer = NULL;
	}
	CloseConnection();
}


bool Asura_PS2_SCE_RT::StartClientAndFindSessions()
{
	NetErrorCode eNetError = NetErrorNone;

	Asura_OutputToDebugger_Retail(("Attempting to find Servers using LAN Beacon\n"));
     //LAN BEACON Requires UDP

    eNetError= NetRequestServersOnSubnet(ServerResponseCallback);
    if(eNetError != NetErrorNone)
    {
		Asura_OutputToDebugger_Retail(("Failed to register NetTypeServerResponseCallback:%d\n",eNetError));
		return false;
    }

	s_eMode = SceRtMode_WaitingForServer;
	s_bWaitingForServerResponse = true;
	
	return true;
}


void Asura_PS2_SCE_RT::RefreshSessions()
{
	NetErrorCode eNetError = NetErrorNone;

	Asura_OutputToDebugger_Retail(("Attempting to find Servers using LAN Beacon\n"));
     //LAN BEACON Requires UDP

    eNetError= NetRequestServersOnSubnet(ServerResponseCallback);
    if(eNetError != NetErrorNone)
    {
		Asura_OutputToDebugger_Retail(("Failed to register NetTypeServerResponseCallback:%d\n",eNetError));
    }
	else
	{
		s_eMode = SceRtMode_WaitingForServer;
	}
}


void Asura_PS2_SCE_RT::StopClient()
{
	CloseConnection();
}


void Asura_PS2_SCE_RT::CloseConnection()
{
	NetErrorCode eErrorCode;
	if (s_iConnectionIndex != -1)
	{
		if (s_eJoinState == JoinState_OK)
		{
			eErrorCode = NetLeave(s_iConnectionIndex);
			Asura_PS2_SCE_RT_CheckError(eErrorCode);
		}
		eErrorCode = NetDisconnect(s_iConnectionIndex);
		Asura_PS2_SCE_RT_CheckError(eErrorCode);
	}
	
	s_bWaitingForServerResponse = false;
	s_eMode = SceRtMode_Idle;
	s_eJoinState = JoinState_None;
}

void Asura_PS2_SCE_RT::ServerResponseCallback(char acServerIpString[ASURA_PS2_MAX_IP_STRING_LENGTH], int iServerPort)
{
	strcpy(s_acServerIP, acServerIpString);
	Asura_PS2_SCE_RT::s_iServerPortNumber = iServerPort;
	s_bWaitingForServerResponse = false;
}


#if ASURA_PS2_SCE_RT_BANDWIDTH_TEXT
const char* Asura_PS2_SCE_RT::GetBandwidthString()
{ 
	if (dbg_uProfileBandwidth)
	{
		return s_acBandwidthText; 
	}
	else
	{
		return "bandwidth not calculated";
	}
}
#endif

void Asura_PS2_SCE_RT::UpdateNetwork()
{
	if (s_pxServer)
	{
		s_pxServer->Update();
	}

	NetErrorCode eNetError = NetUpdate();
	if (NetErrorNone != eNetError)
	{
		if(eNetError== NetErrorBadPacketReceived)
		{
			/*If a bad packet is received during development, you
			*should figure out what is causing it. In general, this
			*is bad, but not necessarily fatal.
			*/  
			/*do nothing*/
			Asura_OutputToDebugger_Retail(("NetUpdate error <Bad Packet received>\n"));
		}
		else 
		{
#ifdef ASURA_ASSERT
			// (MH - 23.05.2003) hack - we're getting a lot of error 37 so count it and display every 50
			if (eNetError == NetErrorUDPError)
			{
				static u_int ls_uUDPErrorCount = 0;
				ls_uUDPErrorCount++;
				if (ls_uUDPErrorCount < 5 || (ls_uUDPErrorCount % dbg_uUDPErrorCount == 0))
				{
					Asura_OutputToDebugger_Retail(("NetUpdate error: %s, count = %u\n", GetErrorString(eNetError), ls_uUDPErrorCount));
				}
			}
			else
			{
				Asura_OutputToDebugger_Retail(("NetUpdate error: %s\n", GetErrorString(eNetError)));
			}
#else
			Asura_OutputToDebugger_Retail(("NetUpdate error: %d\n", (int)eNetError));
#endif
		}
	}

#if ASURA_PS2_SCE_RT_BANDWIDTH_TEXT
	// bandwidth usage
	if (dbg_uProfileBandwidth)
	{
		NetBandwidthInfo xBandwidthInfo;
		eNetError = NetGetBandwidthInfo(&xBandwidthInfo);
		Asura_PS2_SCE_RT_CheckError(eNetError);

		if (dbg_uProfileBandwidth == 1)
		{
			//extern float g_fGlobalTimeSyncOffset;
			Asura_PrintfToBfr(s_acBandwidthText, 
				"latency %u ms\n"
				"rcvd bytes/s %8.0f (max %8.0f)\n"
				"sent bytes/s %8.0f (max %8.0f)\n"
				"global time %0.1f",//, current clock drift %.02f", 
				ASURA_PS2_BANDWIDTH_TEXT_LENGTH, true, 
				GetLatency(),
				Asura_Maths::FtoD(xBandwidthInfo.RecvBytes.AverageRate), 
				Asura_Maths::FtoD(xBandwidthInfo.RecvBytes.MaxAverageRate),
				Asura_Maths::FtoD(xBandwidthInfo.SendBytes.AverageRate), 
				Asura_Maths::FtoD(xBandwidthInfo.SendBytes.MaxAverageRate),
				Asura_Maths::FtoD(Asura_Timers::GetGlobalGameTime())
				//Asura_Maths::FtoD(g_fGlobalTimeSyncOffset)
				);
		}
		else
		{
			//extern float g_fGlobalTimeSyncOffset;
			Asura_PrintfToBfr(s_acBandwidthText, 
				"latency %u ms\n"
				"rcvd packets/s %8.0f (max %8.0f)\n"
				"sent packets/s %8.0f (max %8.0f)\n" 
				"global time %0.1f",//, current clock drift %.02f", 
				ASURA_PS2_BANDWIDTH_TEXT_LENGTH, true, 
				GetLatency(),
				Asura_Maths::FtoD(xBandwidthInfo.Recvs.AverageRate), 
				Asura_Maths::FtoD(xBandwidthInfo.Recvs.MaxAverageRate),
				Asura_Maths::FtoD(xBandwidthInfo.Sends.AverageRate), 
				Asura_Maths::FtoD(xBandwidthInfo.Sends.MaxAverageRate),
				Asura_Maths::FtoD(Asura_Timers::GetGlobalGameTime())
				//Asura_Maths::FtoD(g_fGlobalTimeSyncOffset)
				);
		}
	}
#endif
}


void Asura_PS2_SCE_RT::Update()
{
	if (s_eMode != SceRtMode_Idle)
	{
		UpdateNetwork();

	}
	
	switch (s_eMode)
	{
		case SceRtMode_WaitingForServer:
		{
			if (!s_bWaitingForServerResponse)
			{
				s_eMode = SceRtMode_Client;

				// HACK - this info should be obtained from the server
				Asura_Network_Session* pxSession = Asura_Network::GetSession(0);
				pxSession->m_uCurrentNumberOfPlayers = 0;
				pxSession->m_uMaxNumberOfPlayers = 4;
				pxSession->m_wszSessionName = L"PS2 game";
				pxSession->m_uRoundTripLatency = 0;
				pxSession->m_bValid = true;

				Asura_OutputToDebugger(("server response received.\n"));
			}
			break;
		}

		case SceRtMode_Client:
		{
			if (dbg_bEnableNetPingIP)
			{
				if (!s_bWaitingForPingResponse)
				{
					if ((Asura_Timers::GetLocalScaledTime() - s_fTimeOfLastPingResponse) >= dbg_fTimeBetweenPings)
					{
						// send another ping
						NetErrorCode eError = NetPingIP(s_acServerIP, PingCallback);
						Asura_PS2_SCE_RT_CheckError(eError);
						s_bWaitingForPingResponse = true;
					}
				}
			}
			break;
		}
		
		default:
		{
			break;
		}
	}
}


void Asura_PS2_SCE_RT::EndOfFrame(void)
{
	if (s_eMode != SceRtMode_Idle)
	{
		NetErrorCode eNetError = NetTick();
		Asura_PS2_SCE_RT_CheckError(eNetError);
	}
}


bool Asura_PS2_SCE_RT::Connect()
{
	NetErrorCode eNetError = NetErrorNone;
	NetConnectInParams xConnectInParams;    
	NetConnectOutParams xConnectOutParams;    
	NetConnectStatus eConnectStatus = ConnectStatusPending;

    printf("Connecting to %s:%d\n", s_acServerIP, s_iServerPortNumber);
   
	eNetError = NetSetDefaultConnectParams(&xConnectInParams);
    if(eNetError != NetErrorNone)
    {
		printf("NetSetDefaultConnectParams failed result:%d\n",eNetError);
		return false;
    }

    memcpy(xConnectInParams.ConnectionInfo.AddressList.aAddressList[0].szIPAddress,
           s_acServerIP, ASURA_PS2_MAX_IP_STRING_LENGTH);
    xConnectInParams.ConnectionInfo.AddressList.aAddressList[0].Port = s_iServerPortNumber;
	xConnectInParams.ConnectionInfo.AddressList.aAddressList[0].AddressType = NetAddressTypeInternal;
    //not for p2p?
	//xConnectInParams.ConnectionInfo.WorldID  = ASURA_PS2_SCE_RT_WORLD_ID;

    xConnectInParams.ConnectionInfo.Type = NetConnectionTypePeerToPeerUDP;
    xConnectInParams.pfLocalConnectCallback = NetConnectCallback;
    xConnectInParams.pfLocalDisconnectCallback = NULL;
    xConnectInParams.pfRemoteClientConnectCallback = NULL;
    xConnectInParams.pfRemoteClientDisconnectCallback = NULL;
	xConnectInParams.pfPeerToPeerHostChangeCallback = NULL;
    // not for p2p?
	//xConnectInParams.MaxClients = ASURA_PS2_SCE_RT_MAX_NUM_CLIENTS;
	
	eNetError= NetConnect(&xConnectInParams, &xConnectOutParams);
    if(eNetError != NetErrorNone)
    {
		 printf("NetConnect failed result:%d\n",eNetError);
		 return false;
    }


	u_int uRetries = dbg_uMaxConnectRetries;
	do
	{
		UpdateNetwork();
		// hack - this should be done via a polling loop. limit retries with vsync.
		sceGsSyncV(0);

		NetErrorCode eNetError = NetGetConnectStatus(&eConnectStatus, xConnectOutParams.ConnectionIndex);
		if (NetErrorNone != eNetError)
		{
			Asura_OutputToDebugger_Retail(("NetGetConnectStatus error: %d\n", (int)eNetError));
			return false;
		}
	} 
	while (uRetries-- && ConnectStatusPending == eConnectStatus);

	if (ConnectStatusOpen == eConnectStatus)
	{
		Asura_OutputToDebugger_Retail(("Connection Open!\n"));

		char acLocalIP[ASURA_PS2_MAX_IP_STRING_LENGTH];
		int iMyClientIndex;

		if (NetErrorNone != NetGetMyClientIndex(&iMyClientIndex, xConnectOutParams.ConnectionIndex))
		{
			Asura_OutputToDebugger_Retail(("ERROR: NetGetMyClientIndex\n"));
			return false;
		}

		if (NetErrorNone != NetGetMyIpAddress(acLocalIP))
		{
			Asura_OutputToDebugger_Retail(("ERROR: NetGetMyIpAddress\n"));
			return false;
		}
		
		// get client index of server
		eNetError = NetGetPeerToPeerHostClientIndex(s_iConnectionIndex, &s_iClientIndexOfServer);
		Asura_PS2_SCE_RT_CheckError(eNetError);
		if (NetErrorNone != eNetError)
		{
			return false;
		}
	
		// remember my connection index
		s_iConnectionIndex = xConnectOutParams.ConnectionIndex;
		return true;
	}
	else
	{
		Asura_OutputToDebugger_Retail(("Failed to connect.\n"));
		return false;
	}
}


bool Asura_PS2_SCE_RT::Join()
{
	NetErrorCode eNetError;
	NetJoinInParams xJoinInParams;    
	NetJoinOutParams xJoinOutParams;    

	s_eJoinState = JoinState_Pending;
	
	eNetError = NetSetDefaultJoinParams(&xJoinInParams);

	if(eNetError != NetErrorNone)
	{
		Asura_OutputToDebugger_Retail(("NetSetDefaultJoinParams failed, result:%d\n",eNetError));
		return false;
	}
	
	xJoinInParams.ConnectionIndex = s_iConnectionIndex;
	strncpy(xJoinInParams.szClientName,"foobar",NET_MAX_CLIENT_NAME_LENGTH);
	xJoinInParams.NetObjectCount = ASURA_PS2_SCE_RT_MAX_OBJECTS;
	xJoinInParams.DataStreamCount = 0; 
	xJoinInParams.SessionMasterStatus = OwnershipShared;
	xJoinInParams.pfLocalJoinCallback = NetJoinCallback; 
	xJoinInParams.pfRemoteClientEventCallback = RemoteClientEventCallback;

	eNetError = NetJoin(&xJoinInParams, &xJoinOutParams); 

	if (eNetError != NetErrorNone)
	{
		Asura_OutputToDebugger_Retail(("Error:NetJoin failed (%d).\n",eNetError));
		return false;
	}

	u_int uRetries = dbg_uMaxJoinRetries;
	do
	{
		UpdateNetwork();
		// hack - this should be done via a polling loop. limit retries with vsync.
		sceGsSyncV(0);
	} 
	while (uRetries-- && JoinState_Pending == s_eJoinState);

	if (s_eJoinState != JoinState_OK)
	{
		Asura_OutputToDebugger_Retail(("NetJoin Status:%d\n", s_eJoinState));
		return false;
	}

	Asura_OutputToDebugger_Retail(("Joined sucessfully!\n"));

	return true;
	
}


#if DME_CLIENT_VERSION_BUILD < 0017
void Asura_PS2_SCE_RT::NetConnectCallback(int iConnectionIndex, NetConnectStatus eConnectStatus, 
											NetConnectFailureReason eFailureReason, NetErrorCode eErrorCode)
{
	Asura_OutputToDebugger_Retail(("NetConnectCallback...\n"));
}
#else
void Asura_PS2_SCE_RT::NetConnectCallback(NetTypeConnectCallbackData* pxCallbackData)
{
	Asura_OutputToDebugger_Retail(("NetConnectCallback...\n"));
}
#endif


void Asura_PS2_SCE_RT::NetJoinCallback(NetCompletionData *pxCompletionData)
{
   if(pxCompletionData == NULL)
   {
		Asura_Assert(false, "no completion data");
		s_eJoinState = JoinState_Failed;
		return;
   }

   if(pxCompletionData->Result != NetErrorNone)
   {
       Asura_OutputToDebugger_Retail(("Error: NetJoin failed, result:%d\n", pxCompletionData->Result));
       s_eJoinState = JoinState_Failed;
       return;
   }

   s_eJoinState = JoinState_OK;

   Asura_OutputToDebugger_Retail(("Joined!\n"));
}


void Asura_PS2_SCE_RT::RemoteClientEventCallback(NetRemoteClientEventData *pxRemoteClientEventData)
{
	if(pxRemoteClientEventData== NULL)
	{
		Asura_OutputToDebugger_Retail(("Warn:AppNetRemoteEventCallback data is null\n"));
		return;
	}	

	if(pxRemoteClientEventData->EventType == NetClientEventJoin)
	{
		Asura_OutputToDebugger_Retail(("\n(Callback) Remote Client has Joined ClientIndex'%d'\n", pxRemoteClientEventData->ClientIndex));
	}
  
	if(pxRemoteClientEventData->EventType == NetClientEventLeave)
	{
		Asura_OutputToDebugger_Retail(("\n(Callback) Remote Client has Left ClientIndex'%d'\n", pxRemoteClientEventData->ClientIndex));
	}
}


void Asura_PS2_SCE_RT::PingCallback(NetErrorCode eCompletionResult, char acDestIP[16], unsigned int uLatency)
{
	if (eCompletionResult == NetErrorNone)
	{
		s_uLatency = uLatency;
	}
	else if (eCompletionResult == NetErrorTimedOut)
	{
		Asura_OutputToDebugger(("ping request timed out.\n"));
	}
	else
	{
		Asura_PS2_SCE_RT_CheckError(eCompletionResult);
	}

	s_bWaitingForPingResponse = false;
	s_fTimeOfLastPingResponse = Asura_Timers::GetLocalScaledTime();
}


void Asura_PS2_SCE_RT::RegisterMessageTypes()
{
	NetRegisterMessageStart();
	NetRegisterMessageField(g_xMessage, m_aucBuffer[0], ASURA_PS2_SCE_RT_MAX_MESSAGE_TOTAL_SIZE);
	NetRegisterMessage(&s_uMessageTypeID, MessageClassApplication, MessageHandlerCallback);
}


void Asura_PS2_SCE_RT::ConnectToSession(u_int uSessionID)
{
	RegisterMessageTypes();
	if (Connect())
	{
		if (Join())
		{
			Asura_Network::SetWaitingForConnection(false);
			Asura_Network::SetConnectionOkay(true);
			Asura_OutputToDebugger_Retail(("Connected To Session %u.\n", uSessionID));
			return;
		}
	}

	Asura_Network::SetWaitingForConnection(false);
	Asura_Network::SetConnectionOkay(false);
	Asura_OutputToDebugger_Retail(("ConnectToSession %u failed.\n", uSessionID));
}


int Asura_PS2_SCE_RT::GetLatency()
{
	if (dbg_bEnableNetPingIP)
	{
		return s_uLatency;
	}
	else
	{
		u_int uDelayToServer;
		NetErrorCode eNetError = NetGetAverageDelayToClient(&uDelayToServer, s_iClientIndexOfServer);
		Asura_PS2_SCE_RT_CheckError(eNetError);
		if (eNetError == NetErrorNone)
		{
			// this delay is 1-way
			return uDelayToServer * 2;
		}
		return 0;
	}
}


void Asura_PS2_SCE_RT::SendMessageToServer(const Asura_Message* pxMessage)
{
	//SendMessage(NET_SEND_TO_SERVER, pxMessage);
	SendMessage(s_iClientIndexOfServer, pxMessage);
}


void Asura_PS2_SCE_RT::SendMessageToAllClients(const Asura_Message* pxMessage)
{
	Asura_Assert(s_pxServer, "no server!");
	SendMessage(NET_SEND_TO_ALL_CLIENTS, pxMessage);
}


void Asura_PS2_SCE_RT::SendMessageToClientMachine(int iMachineID, const Asura_Message* pxMessage)
{
	Asura_Assert(s_pxServer, "no server!");
	int iClientIndex = s_pxServer->GetClientIndex(iMachineID);
	SendMessage(iClientIndex, pxMessage);
}


int Asura_PS2_SCE_RT::MessageHandlerCallback(int iConnectionID, int iWorldID, int iSourceClientIndex, void* pMessageData)
{
	Asura_MessageHeader xHeader;
	memcpy(reinterpret_cast<void*>(&xHeader), pMessageData, sizeof(Asura_MessageHeader));;
	Asura_Assert(xHeader.m_uDataSize < ASURA_PS2_SCE_RT_MAX_MESSAGE_DATA_SIZE, "invalid size for received message");

	if (xHeader.m_usMsgID == ASURA_MSG_VALIDCLIENTGUID || Asura_Network::IsReadyForMessages())
	{
		void* pData = reinterpret_cast<void*>(reinterpret_cast<char*>(pMessageData) + sizeof(Asura_MessageHeader));
		Asura_Network::AddMessageToQueue(&xHeader, pData);
	}
	return sizeof(Asura_MessageHeader) + xHeader.m_uDataSize;
}


//void Asura_PS2_SCE_RT::ProcessServerMessage(void* pMessageData)
//{
//	Asura_MessageHeader xHeader;
//	memcpy(reinterpret_cast<void*>(&xHeader), pMessageData, sizeof(Asura_MessageHeader));;
//	
//	Asura_Assert(xHeader.m_uDataSize < ASURA_PS2_SCE_RT_MAX_MESSAGE_DATA_SIZE, "invalid size for received message");
//
//	void* pData = reinterpret_cast<void*>(reinterpret_cast<char*>(pMessageData) + sizeof(Asura_MessageHeader));
//	Asura_Network::AddMessageToQueue(&xHeader, pData);
//}


DEBUGVAR bool dbg_bPrintMessagesToServer = false;
DEBUGVAR bool dbg_bDisableMessageSending = false;
DEBUGVAR char dbg_cDefaultNetTransportFlags = NET_LATENCY_CRITICAL;//0;// was NET_LATENCY_CRITICAL == 128;
bool Asura_PS2_SCE_RT::SendMessage(int iDestID, const Asura_Message* pxMessage)
{
	if (dbg_bDisableMessageSending)
	{
		return false;
	}

	if (pxMessage->GetDataSize() > (int)ASURA_PS2_SCE_RT_MAX_MESSAGE_DATA_SIZE)
	{
		Asura_Assert(false, "message data too large");
		return false;
	}

	Asura_MessageHeader* pxHeader = reinterpret_cast<Asura_MessageHeader*>(&g_xMessage);
	*pxHeader = *pxMessage;

	void* pData = reinterpret_cast<void*>(reinterpret_cast<char*>(&g_xMessage) + sizeof(Asura_MessageHeader));
	memcpy(pData, pxMessage->GetData(), pxMessage->GetDataSize());
	
	int iMessageLength = sizeof(Asura_MessageHeader) + pxMessage->GetDataSize();
	u_char* pcMessageData = reinterpret_cast<u_char*>(&g_xMessage);

	// Asura already aggregates messages, so always use the "latency critical" flag
	char cTransportFlags = dbg_cDefaultNetTransportFlags;
	if (pxMessage->IsUsingGuaranteedDelivery())
	{
		cTransportFlags |= NET_DELIVERY_CRITICAL;
	}
	NetErrorCode eErrorCode = NetSendApplicationMessage(cTransportFlags, 
		s_iConnectionIndex, iDestID, s_uMessageTypeID, iMessageLength, pcMessageData);

	if (eErrorCode != NetErrorNone)
	{
		Asura_Assertf(false, ("NetSendApplicationMessage failed with error %d", (int)eErrorCode));
		return false;
	}

	if (dbg_bPrintMessagesToServer)
	{
		// print messages which aren't broadcast to all clients
		if (iDestID != -1)
		{
			Asura_OutputToDebugger(("Send message with length %d to client ID %d.\n", iMessageLength, iDestID));
			u_char* pcMessageEnd = pcMessageData + iMessageLength;
			for (; pcMessageData < pcMessageEnd; ++pcMessageData)
			{
				Asura_OutputToDebugger(("%02x ", *pcMessageData));
			}
			Asura_OutputToDebugger(("\n"));
		}
	}
	return true;
}


#ifdef ASURA_ASSERT
const char* Asura_PS2_SCE_RT::GetErrorString(NetErrorCode eErrorCode)
{
	switch(eErrorCode) 
	{
    case NetErrorNone:					return "0: No Error";
    case NetErrorNotConnected:			return "1: Not Connected to Server";
    case NetErrorConnectionLost:		return "2: Connection lost to Server";
    case NetErrorConnectionFailed:		return "3: Unable to connect to Server";
    case NetErrorClientRejected:		return "4: Client was rejected by Server";
    case NetErrorDisconnectFailed:		return "5: Failed to properly disconnect from Server";
    case NetErrorNewClient:				return "6: NetErrorNewClient";
    case NetErrorBadSessionMaster:		return "7: NetErrorBadSessionMaster";
    case NetErrorBadIndex:				return "8: index out of range ";
    case NetErrorBadDataStreamChannel:	return "9: Invalid DataStream Channel.";
    case NetErrorBadPointer:			return "10: tried to use a NULL pointer ";
    case NetErrorObjectNotShared:		return "11: Ownership requested on private object";
    case NetErrorBadPacketReceived:		return "12: Invalid message received ";
    case NetErrorSendFailed:			return "13: Unable to send message to server ";
    case NetErrorTimedOut:				return "14: Timeout period has elapsed";
    case NetErrorBadConnectionIndex:	return "15: Invalid Connection Index.";
    case NetErrorBadMode:				return "16: NetErrorBadMode";
    case NetErrorDmeNotInitialized:		return "17: NetInitialize has not been called.";
    case NetErrorInitFailed:			return "18: NetInitialize has failed";
    case NetErrorNoFreeObject:			return "19: No more free objects. See NetJoin.";
    case NetErrorOpenDataStreamFailed:	return "20: Unable to create a stream.See NetJoin";
    case NetErrorClientNotValid:		return "21: NetErrorClientNotValid";
    case NetErrorMemory:				return "22: memory err(allocate, free, callback) ";
    case NetErrorInvalidArg:			return "23: invalid arg was passed to a function ";
    case NetErrorMsgError:				return "24: message layer error ";
    case NetErrorTooManyPendingEvents:	return "25: call exceeds pending event queue ";
    case NetErrorUDPNotEnabled:			return "26: UDP has not been enabled ";
    case NetErrorUpdate:				return "27: fatal error in NetUpdate ";
    case NetErrorGameIsFull:			return "28: Game is full, MaxClientsPerConnection set by NetInitialize exceeded";
	case NetErrorHostGameFailed:		return "29: failed to host game ";
	case NetErrorUnknown:				return "30: unknown error ";
    case NetErrorMsgTooLarge:			return "31: the message is too big to send ";
	case NetErrorSecurity:				return "32: security error ";
	case NetErrorNotImplemented:		return "33: feature not implemented yet ";
	case NetErrorBadServerVersion:		return "34: server version not compatible with this client ";
	case NetErrorCommError:				return "35: communications layer error FATAL ERROR ";
	case NetErrorBufferError:			return "36: buffering error ";
	case NetErrorUDPError:				return "37: udp messaging layer error ";
	case NetErrorSetDefaultsNotCalled:	return "38: parameters not filled with default (i.e., NetSetDefault... not called)";
	case NetErrorSizeofParamMismatch:	return "39: Sizeof... parameter does not match library version.  Potential header/library mismatch or it wasn't set";
	case NetErrorDeprecated:			return "40: Function call is deprecated ";
	case NetErrorStreamMedia:			return "41: Stream medius error ";
	case NetErrorStreamMediaComm:		return "42: Stream medius communication error ";
	case NetErrorMutex:					return "43: Mutex error ";
	
	default:
		{
			static char acBuffer[32];
			Asura_PrintfToBfr(acBuffer, "unknown code %d", 32, true, eErrorCode);
			return acBuffer;
		}
	}
}
#endif
