//////////////////////////////////////////////////////////////////////////////
//
// Asura_GameSpy_GT2.h
//
// Copyright (c) 2003 Rebellion. All rights reserved.
//
// Description:
//
//     Asura_GameSpy_GT2 class to provide Asura interface to GT2
//
//////////////////////////////////////////////////////////////////////////////

// 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_GAMESPY_GT2_H___
#define ___ASURA_GAMESPY_GT2_H___ 1

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

// asura
#include "Core/Asura_Handle.h"
#include "Core/Asura_PlatformBasics.h"
#include "Debugging/Asura_Assert.h"
#include "Network/Asura_Guid.h"
#include "Network/Asura_Network_Session.h"

// middleware
#include "gamespy/gt2/gt2.h"
#include "gamespy/natneg/natneg.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_GameSpy_GT2_MachineInfo;
class Asura_GameSpy_GT2_SessionInfo;

// C O N S T A N T S /////////////////////////////////////////////////////////
const u_short ASURA_GAMESPY_GT2_DEFAULT_PORT = 3658;	// should be 3658 for PS2

// E N U M S /////////////////////////////////////////////////////////////////
enum Asura_GameSpy_GT2_NetworkErrorType
{
	ASURA_GT2_NETERROR_NONE,
	ASURA_GT2_NETERROR_UNKNOWN,
	// start of GT2Result errors
	ASURA_GT2_NETERROR_OUTOFMEMORY,		//	GT2OutOfMemory,         // ran out of memory (e.g. failed to allocate memory for new socket object)
	ASURA_GT2_NETERROR_NETWORKERROR,      // 	GT2NetworkError,        // networking error (could be local or remote)
	ASURA_GT2_NETERROR_ADDRESSERROR,      // 	GT2AddressError,        // invalid or unreachable address
	ASURA_GT2_NETERROR_DUPLICATEADDRESS,  // 	GT2DuplicateAddress,    // a connection was attempted to an address that already has a connection on the socket
	ASURA_GT2_NETERROR_NEGOTIATIONERROR,  // 	GT2NegotiationError     // there was an error negotiating with the remote side
	// start of GT2CloseReason errors
	ASURA_GT2_NETERROR_COMMUNICATIONERROR, // GT2CommunicationError, // An invalid message was received (it was either unexpected or wrongly formatted).
	ASURA_GT2_NETERROR_SOCKETERROR,        // GT2SocketError,        // An error with the socket forced the connection to close.
	ASURA_GT2_NETERROR_TOOMANYDROPPEDPACKETS,    // GT2NotEnoughMemory     // There wasn't enough memory to store an incoming or outgoing message.
	ASURA_GT2_NUMNETERRORTYPES
};


// C L A S S E S /////////////////////////////////////////////////////////////
class Asura_GameSpy_GT2
{
	enum ClientConnectionState
	{
		CCS_NotConnected,
		CCS_WaitingForNatNeg,
		CCS_ReadyToConnect,
		CCS_WaitingForConnection,
		CCS_Connected,
		NumClientConnectionStates
	};

	public:

		static void Initialise( void );
		static void Update( void );
		static void EndOfFrame( void );
		static void Shutdown( void );
		static SOCKET GetSocket( void );
		static Asura_GameSpy_GT2_NetworkErrorType GetLastError( void ) { return s_eLastNetworkError; }

		// session manager code
		static void StartSessionManager( void );
		static void StopSessionManager( void );
//		static void ConnectToSession( u_int uSessionIndex );
		static void UpdateLANSessions( void );

		// server code
		static bool StartServerAndHostSession( bool bLanOnly );
		static void StopServer( void );
		static void SetServerLANReporting( bool bEnable );
		static void SendMessageToClient( u_int uMachine, const Asura_Message* pxMessage );
		static void SendMessageToAllClients( const Asura_Message* pxMessage );
		static void SetServerName( const Asura_UnicodeChar* wszServerName );
		static u_int GetLatencyForMachine( u_int uMachine );
		static void ServerBeginNatNeg(int iCookie);

		// client code
		static void StartClient( const char* szIPAddressToConnectTo );
		static void StopClient( void );
		static void SendMessageToServer( const Asura_Message* pxMessage );
		static u_int GetLatency( void );
		// NAT Neg defaults to false - if server browsing detects that the server 
		// is behind a NAT and we need to use the NAT Negotiation SDK to connect, 
		// set this to true, and provide a random cookie.
		static void SetUseNatNeg( bool bNatNeg, int iCookie )
		{
			s_bUseNatNeg = bNatNeg;
			s_iNatNegCookie = iCookie;
		}

		// client and server code
		static void SetPort( u_short usPort ) { s_usPort = usPort; }
		static u_short GetPort( void ) { return s_usPort; }
		static void SetPassword( const char* szPassword );
		static u_int GetPasswordHash( void ) { return s_uPasswordHash; }
		static const void* GetSessionData( void ) { return s_pucSessionData; }
		static u_int GetSessionDataSize( void ) { return s_uSessionDataSize; }
		// Project_GetVersionID is now deprecated - use Asura_GameSpy::GetGameVersion()
		static u_short Project_GetVersionID( void );
		static void GetNetworkBandwidthUsage(u_int& uSend, u_int& uReceive);
		static void GetPeakNetworkBandwidthUsage(u_int& uSend, u_int& uReceive);
		
	private:
		
		// server functions
		static void ProcessMachineConnection( GT2Connection pxConnection, u_short usNumPlayers, u_int uPing );
		static bool ProcessEnumHostsRequest( u_int uIP, u_short usPort, u_char* pucData, u_int uDataLength );
		static void SendEnumHostsResponse( u_int uIP, u_short usPort );

		// server callbacks
		static void ServerCallback_Connection( GT2Socket pxSocket, 
											   GT2Connection pxConnection, 
											   unsigned int uIP, 
											   unsigned short usPort, 
											   int iLatency, 
											   GT2Byte* pucMessage, 
											   int iMessageLen ); 

		static void ServerCallback_ClientDisconnected( GT2Connection pxConnection,
													   GT2CloseReason eReason );

		//Callback prototype for your progress function
		static void NatNegProgress_ServerCallback(NegotiateState eState, void *pUserdata);

		//Callback prototype for your negotiation completed function
		static void NatNegComplete_ServerCallback(NegotiateResult eResult, SOCKET xGameSocket, struct sockaddr_in* pxRemoteAddr, void *pUserdata);

		// client functions
		static void ProcessEnumHostsResponse( u_int uIP, u_short uPort, const u_char* pucData, int iDataSize );
		static void SendEnumHostsBroadcast( void );
		static void ConnectToHost( const char* szFullAddress );
		
		// client callbacks
		static void ClientCallback_Connected( GT2Connection pxConnection,
											  GT2Result eResult,
											  GT2Byte* pucMessage,
											  int iMessageLen );

		static void ClientCallback_Disconnected( GT2Connection pxConnection,
												 GT2CloseReason eReason );

		// general callbacks
		static void SocketErrorCallback( GT2Socket pxSocket, int iErrorCode );
		static void PingCallback( GT2Connection pxConnection, int iLatency );
		static void MessageReceivedCallback( GT2Connection pxConnection,
											 GT2Byte * pucData,
											 int iDataLen, 
											 GT2Bool bReliable );
		static GT2Bool UnrecognizedMessageCallback(	GT2Socket pxSocket,
													unsigned int uIP,
													unsigned short usPort,
													GT2Byte* pucMessage,
													int iLen ); 
		static void SendDumpCallback( GT2Socket pxSocket,
									  GT2Connection pxConnection,
									  unsigned int uIp,
									  unsigned short usPort,
									  GT2Bool iReset,
									  const GT2Byte * pucMessage,
									  int iLen );
		static void ReceiveDumpCallback( GT2Socket pxSocket,
										 GT2Connection pxConnection,
										 unsigned int uIp,
										 unsigned short usPort,
										 GT2Bool iReset,
										 const GT2Byte * pucMessage,
										 int iLen );

		//Callback prototype for your progress function
		static void NatNegProgress_ClientCallback(NegotiateState eState, void *pUserdata);

		//Callback prototype for your negotiation completed function
		static void NatNegComplete_ClientCallback(NegotiateResult eResult, SOCKET xGameSocket, struct sockaddr_in* pxRemoteAddr, void *pUserdata);


		// server utility functions
		static Asura_GameSpy_GT2_MachineInfo* GetMachineInfo( GT2Connection pxConnection );
		static Asura_GameSpy_GT2_MachineInfo* GetMachineInfoFromIndex( u_int uMachine );

		// general utility functions
		static bool Platform_Initialise( void );
		static void Platform_Shutdown( void );
		static bool Platform_CanUseLANBroadcast( void );
		static u_int Project_GetApplicationGuid( void );
		static void Project_RefreshSessionData( void );

		static void SendAsuraMessage( GT2Connection pxConnection, const Asura_Message* pxMessage );
		static bool ShouldAddMessageToQueue(u_int uMsgID);
		static void UpdateBandwidthStats( void );
		static void SetNetworkError( Asura_GameSpy_GT2_NetworkErrorType eError ) { s_eLastNetworkError = eError; }

		// static data - general
		static GT2Socket s_pxSocket;
		static u_short s_usPort;
		static u_int s_uPasswordHash;
		static Asura_GameSpy_GT2_MachineInfo* s_pxMachineInfo;	// ptr to one machine info on client, and array on server.
		static u_int s_uMaxNumberOfMachines;
		static Asura_GameSpy_GT2_NetworkErrorType s_eLastNetworkError;
			
		// session data is to replace ApplicationDesc in DirectPlay. Should move to independent code.
		static u_char* s_pucSessionData;
		static u_int s_uSessionDataSize;

		// bandwidth usage
		static float s_fTimeSinceLastBandwidthCalculation;
		static u_int s_uSendBPS;
		static u_int s_uReceiveBPS;
		static u_int s_uPeakSendBPS;
		static u_int s_uPeakReceiveBPS;

		// static data - server
		static bool s_bServerLanReportingOn;

		// static data - client
		static bool s_bUseNatNeg;
		static int s_iNatNegCookie;
		static ClientConnectionState s_eClientConnectionState;
		static char s_acServerAddress[32];
		static u_int s_uNumberOfActiveSessions;
		static Asura_GameSpy_GT2_SessionInfo** s_ppxSessionsStorage;
		static float s_fLastSessionRequestBroadcastTime;
};


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

#endif // #ifndef ___ASURA_GAMESPY_GT2_H___
