// HTNetClient.h: interface for the HTNetClient class.
//
//////////////////////////////////////////////////////////////////////

#pragma once

#include <dplay8.h>
#include <dpaddr.h>
#include <dplobby8.h>
#include <dxerr8.h>
#include "dxutil.h"
#include "simpleclientserver.h"

struct DPHostEnumInfo
{
	DWORD                 dwRef;
	DPN_APPLICATION_DESC* pAppDesc;
	IDirectPlay8Address* pHostAddr;
	IDirectPlay8Address* pDeviceAddr;
	TCHAR                szSession[MAX_PATH];
	DWORD                dwLastPollTime;
	DWORD						dwRTTms;
	BOOL                 bValid;
};
typedef vector<DPHostEnumInfo> HostListType;

extern GUID g_guidApp;

typedef enum { HTCS_NOT_CONNECTED,
					HTCS_CONNECTING,
					HTCS_CONNECTED,
					HTCS_CONNECTION_FAILED,
					HTCS_CONNECTION_TERMINATED
				} HTClientStatus_type;

#define HTC_PLAYER_LOCK()				EnterCriticalSection( &m_csPlayerData );
#define HTC_PLAYER_UNLOCK()			LeaveCriticalSection( &m_csPlayerData );

#include <stack>
#include <string>

struct HTMSG_GAME_EVENT
{
	int eventID;
	DWORD player1;
	DWORD player2;
	int x,y;
};


typedef stack<HTMSG_CHAT_FULL> MessageQueue;
typedef stack<HTMSG_GAME_EVENT> EventQueue;

class HTNetClient  
{
	// Game information -- be nice and don't modify these values if you're
	// not part of this class. (yah, yah, encapsulation, I know -- no time
	// and too lazy for elegant design =P )
	public:
		bool								m_SetupMsgReady;						// Flag to indicated received setup info
		HTMSG_SETUP_GAME				m_recvSetupMsg;						// Received game setup info
		HTMSG_UPDATE_PLAYER			m_recvPlayerStatus[MAX_PLAYERS];	// Most recent player info
		MessageQueue					m_recvMessages;
		EventQueue						m_recvEvents;
		
		bool								m_PlayerSlotValid[MAX_PLAYERS];	// Whether player slot is valid (#0 = local player = always valid)
		bool								m_PlayerSlotUpdated[MAX_PLAYERS];	// Whether player slot is valid (#0 = local player = always valid)
		int								m_nActivePlayers;						// Number of active players

		HTMSG_CREATE_PLAYER			m_PlayerInfo[MAX_PLAYERS];			// Player information

		CRITICAL_SECTION				m_csPlayerData;						// To lock/unlock player data

		void NetUpdatePlayer(HTMSG_UPDATE_PLAYER* pPlayerInfo);
		void DestroyPlayer(HTMSG_DESTROY_PLAYER* pPlayerInfo);
		void CreatePlayer(HTMSG_CREATE_PLAYER* pPlayerInfo);
		void SendServerUpdate(InputStatus_type input);

		int GetNumQueuedChatMessages();
		HTMSG_CHAT_FULL GetNextChatMessage();
		void PushChatMessage(HTMSG_CHAT_FULL* msg);

		int GetNumQueuedEvents();
		HTMSG_GAME_EVENT GetNextEvent();
		void PushEvent(HTMSG_GAME_EVENT* event);

		void SendServerCommand(HT_COMMAND_ID_TYPE cmd);
		void SendChatMesg(const char* msg);

		inline HTMSG_UPDATE_PLAYER getPlayerStatus(int playerIdx)
		{	
			HTC_PLAYER_LOCK();
			HTMSG_UPDATE_PLAYER ret = m_recvPlayerStatus[playerIdx];
			HTC_PLAYER_UNLOCK();
			return ret;
		}
		inline void resetPlayerUpdatedFlag(int playerIdx)
		{
			HTC_PLAYER_LOCK();
			m_PlayerSlotUpdated[playerIdx] = false;
			HTC_PLAYER_UNLOCK();
		}
		// outName must have at least MAX_PLAYER_NAME spaces allocated to receive the name
		inline void getPlayerName(int playerIdx, char* outName)
		{
			HTC_PLAYER_LOCK();
			if (m_PlayerSlotValid[playerIdx])
				strncpy(outName,m_PlayerInfo[playerIdx].playerName,MAX_PLAYER_NAME);
			HTC_PLAYER_UNLOCK();
		}

	// Client information
	protected:
		//bool currentlySearching;
		bool initialized;
		TCHAR playerName[MAX_PLAYER_NAME];
		TCHAR serverName[MAX_PATH];

		HTClientStatus_type m_ConnectionState;

	// Networking stuff
	public:
		void Disconnect();
		int GetNextModelNum(TankObject* t);
		bool AbortNetworkConnect();
		HTNetClient();
		~HTNetClient();
	
		bool Init();

		void SetSearching(bool search);
		bool IsSearching(void) { return m_bSearchingForSessions; };

		void  SetPlayerName(char* newname);
		char* GetPlayerName() { return playerName; }

		int   GetPlayerIdx(DPNID pid);

		void  SetServerName(char* newname);
		char* GetServerName() { return serverName; }

		int   GetNumFoundServers();
		char* GetFoundServerName(int servernum);
		int   GetFoundServerPing(int servernum);
		int	GetFoundServerNumPlayers(int servernum);
		int	GetFoundServerMaxPlayres(int servernum);

		// Returns whether or not the server list changed
		bool UpdateServerList();

		bool StartConnectingToServer(int servernum);
		HTClientStatus_type GetConnectionState() { return m_ConnectionState; };

	protected:

		static HRESULT WINAPI DPMessageForwarder( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
		//static HRESULT WINAPI DPLobbyMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );

		HRESULT WINAPI DPMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
		//HRESULT WINAPI CDPLobbyMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );

		void NoteDPlayEnumResponse( PDPNMSG_ENUM_HOSTS_RESPONSE pEnumHostsResponse );
		void RemoveOldServerEntries();

		// Starts Direct play to be searching for local servers
		HRESULT WINAPI StartSearchingForServers();
		void StopSearchingForServers();
		
		HostListType				m_hostList;
		DWORD                   m_dwHostExpireInterval;
		CRITICAL_SECTION        m_csHostList;
		bool                    m_bSearchingForSessions;
		bool                    m_bHostListChanged;
		
		IDirectPlay8Client*     m_pDPClient;
		//IDirectPlay8LobbiedApplication* m_pLobbiedApp;

		TCHAR                   m_strAppName[MAX_PATH];
		//TCHAR                   m_strLocalPlayerName[MAX_PATH];

		DPNHANDLE               m_hEnumAsyncOp;
		BOOL                    m_bConnecting;
		DPNHANDLE               m_hConnectAsyncOp;
		//HANDLE                  m_hConnectCompleteEvent;
		//HANDLE                  m_hLobbyConnectionEvent;
		HRESULT                 m_hrConnectComplete;
		BOOL                    m_bHaveConnectionSettingsFromLobby;
		DPNHANDLE               m_hLobbyClient;
		
};
