//////////////////////////////////////////////////////////////////////////////
//
// Asura_PS2_SCE_RT_Server.cpp
//
// Copyright (c) 2003 Rebellion. All rights reserved.
//
// Description:
//
//     server-specific code. Mostly based on appdmeserver.c in Integrated
//		Pooballs sample.
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

#include "Asura_PS2_SCE_RT_Server.h"

// libraries
#include <stdio.h>
#include <string.h>

// independent
#include "Debugging/Asura_Assert.h"
#include "Debugging/Asura_Logfile.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"

// ps2
#include "Network/Asura_PS2_SCE_RT.h"


// S T A T I C   C L A S S   D A T A /////////////////////////////////////////
Asura_PS2_SCE_RT_Server* Asura_PS2_SCE_RT_Server::s_pxTheServer = NULL;

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

Asura_PS2_SCE_RT_Server::Asura_PS2_SCE_RT_Server()
{
	m_pxPlayerInfo = new PlayerInfo[ASURA_PS2_SCE_RT_MAX_NUM_CLIENTS];
}

Asura_PS2_SCE_RT_Server::~Asura_PS2_SCE_RT_Server()
{
	delete[] m_pxPlayerInfo;
}

Asura_PS2_SCE_RT_Server* Asura_PS2_SCE_RT_Server::Create()
{
	if (s_pxTheServer) 
	{
		Asura_Assert(false, "server object already exists");
		return NULL;
	}
	s_pxTheServer = new Asura_PS2_SCE_RT_Server();
	return s_pxTheServer;
}


void Asura_PS2_SCE_RT_Server::Destroy(Asura_PS2_SCE_RT_Server* pxServer)
{
	Asura_Assert(s_pxTheServer == pxServer, "server object doesn't match");
	delete s_pxTheServer;
	s_pxTheServer = NULL;
}



#if DME_CLIENT_VERSION_BUILD < 0017
void Asura_PS2_SCE_RT_Server::ConnectionCallback(int iConnectionIndex, 
												 int iClientIndex, 
												 const char* szClientIpString)
{
	Asura_Assert(s_pxTheServer, "no server object");
	s_pxTheServer->ProcessClientConnected(iClientIndex);
}

void Asura_PS2_SCE_RT_Server::DisconnectCallback(int iConnectionIndex, 
												 int iClientIndex, 
												 const char* szClientIpString)
{
	Asura_Assert(s_pxTheServer, "no server object");
	s_pxTheServer->ProcessClientDisconnected(iClientIndex);
}

#else

void Asura_PS2_SCE_RT_Server::ConnectionCallback(NetTypeClientConnectCallbackData* pxData)
{
	Asura_Assert(s_pxTheServer, "no server object");
	Asura_Assert(pxData, "no connection data");
	s_pxTheServer->ProcessClientConnected(pxData->ClientIndex);
}


void Asura_PS2_SCE_RT_Server::DisconnectCallback(NetTypeClientConnectCallbackData* pxData)
{
	Asura_Assert(s_pxTheServer, "no server object");
	Asura_Assert(pxData, "no disconnection data");
	s_pxTheServer->ProcessClientDisconnected(pxData->ClientIndex);
}
#endif

void Asura_PS2_SCE_RT_Server::ProcessClientConnected(u_short uClientIndex)
{
	// could add as many clients as there are split screens, but 1 will do.
	Asura_Handle iMachineHandle = Asura_Network::AddMachineToGame(1);

	// (MH - 27.03.2003) TODO: max machines is set in independent code to a hardcoded 16.
	// We are using a define to set max client machines to 4 in the PS2 at the moment.
	Asura_Assert(iMachineHandle < ASURA_PS2_SCE_RT_MAX_NUM_CLIENTS, "too many client machines");
	Asura_Assert(iMachineHandle != ASURA_INVALID_HANDLE, "Too many machines! Should have been stopped before now!");

	m_pxPlayerInfo[iMachineHandle].m_sClientID = uClientIndex;
	
	Asura_Guid uClientGuid = Asura_Network::GetNewClientGuid(iMachineHandle);
	
	Asura_Message* pxMessage = Asura_Network::CreateMessage(ASURA_MSG_VALIDCLIENTGUID,
		sizeof(Asura_MsgData_ValidClientGuid));

	Asura_MsgData_ValidClientGuid* pxData = (Asura_MsgData_ValidClientGuid*) pxMessage->GetData();

	pxData->Guid = uClientGuid;

	pxMessage->To = ASURA_GUID_CLIENT;
	pxMessage->UseGuaranteedDelivery();

	// send it now
	int iClientIndex = GetClientIndex(iMachineHandle);
	SendMessage(iClientIndex, pxMessage);
	
	Asura_Network::DestroyMessage(pxMessage);
}

void Asura_PS2_SCE_RT_Server::ProcessClientDisconnected(u_short uClientIndex)
{
	int iMachineIndex = FindMachineIndex(uClientIndex);
	Asura_Assert(iMachineIndex != -1, "invalid machine index");
	if (iMachineIndex != -1)
	{
		// clear this client from the machine index
		m_pxPlayerInfo[iMachineIndex].m_sClientID = -1;
		// check he wasn't added twice
		Asura_Assert(FindMachineIndex(uClientIndex) == -1, "failed to completely remove client");
		Asura_Network::RemoveMachineFromGame(iMachineIndex);
	}
}

// return -1 if not found
int Asura_PS2_SCE_RT_Server::FindMachineIndex(u_short usClientIndex)
{
	for (int i = 0; i < ASURA_PS2_SCE_RT_MAX_NUM_CLIENTS; ++i)
	{
		if (m_pxPlayerInfo[i].m_sClientID == usClientIndex)
		{
			return i;
		}
	}
	return -1;
}


bool Asura_PS2_SCE_RT_Server::Initialise()
{
	NetErrorCode eNetError = NetErrorNone;
	NetHostPeerToPeerInParams xInParams;
	NetHostPeerToPeerOutParams xOutParams;

    // the sample code zeros these out.
	memset(&xInParams,    0, sizeof(NetHostPeerToPeerInParams));
    memset(&xOutParams,   0, sizeof(NetHostPeerToPeerOutParams));

	NetSetDefaultHostPeerToPeerParams(&xInParams);

    xInParams.pfLocalConnectCallback = 0;//blocking
    xInParams.pfRemoteClientConnectCallback = ConnectionCallback;
    xInParams.pfRemoteClientDisconnectCallback = DisconnectCallback;
    xInParams.MaxClients = ASURA_PS2_SCE_RT_MAX_NUM_CLIENTS;

	eNetError = NetHostPeerToPeer(&xInParams, &xOutParams);
	if(eNetError != NetErrorNone)
	{
		Asura_PS2_SCE_RT_CheckError(eNetError);
		return false;
	}

	// remember my connection index
	s_iConnectionIndex = xOutParams.ConnectionIndex;
	return true;
}


void Asura_PS2_SCE_RT_Server::Shutdown()
{

}


bool Asura_PS2_SCE_RT_Server::Update()
{

    return true;
}
