//////////////////////////////////////////////////////////////////////////////
//
// Asura_Lobby.h
//
// Copyright (c) 2008 Rebellion. All rights reserved.
//
// Description: This system provides a platform-independent way to create pre-
//              game lobbies, and to get them listed on a backend server so 
//              other clients can find them. Lobbies exist for the duration of 
//              a multiplayer game and could in theory exist for several 
//              rounds on different maps etc. If the project allows it, 
//              players can join once the game has started, but in that case 
//              they will enter straight into game rather than via the lobby screen.
//
// See link below for high-level documentation.
// http://asurawiki.rebellion.co.uk/twiki/bin/view/Programming/LobbySystem
//
//////////////////////////////////////////////////////////////////////////////

// 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_LOBBY_H___
#define ASURA_LOBBY_H___ 1

#include "Core/Asura_FixedArray.h"
#include "Core/Asura_String.h"

#include "Core/Memory/Asura_Mem.h"
#include "Network/Asura_Guid.h"
#include "Network/Asura_ReliableSocket.h"

// KOR - we need some data even if ASURA_SUPPORT_LOBBY is not defined, to maintain compatibility.

// default values of these are the same as the externally-searchable ones in
// PSN Matching 2.
#ifndef ASURA_LOBBY_NUM_INT_ATTRIBUTES
#define ASURA_LOBBY_NUM_INT_ATTRIBUTES (8)
#endif
#ifndef ASURA_LOBBY_MAX_SEARCH_CRITERIA
#define ASURA_LOBBY_MAX_SEARCH_CRITERIA (ASURA_LOBBY_NUM_INT_ATTRIBUTES + 2) // default 2 extra for skill filters
#endif
#ifndef ASURA_LOBBY_BINARY_ATTRIBUTE_DATA_SIZE
#define ASURA_LOBBY_BINARY_ATTRIBUTE_DATA_SIZE (64)
#endif

#define ASURA_LOBBY_ADDRESS_LENGTH (40)
#define ASURA_LOBBY_DESCRIPTION_LENGTH (40)

// Forward declarations
class Asura_NetPort;
class Asura_ReliableSocket_Client;
class Asura_ReliableSocket_Listener;
class Asura_Lobby_PlayerStatusMsg;
class Asura_Lobby_LobbyData;

// Enums
enum ASURA_LOBBY_MESSAGE_ID
{
	ASURA_MSG_LOBBY_UPDATE,
	ASURA_MSG_LOBBY_CLIENTSTATUS,
	ASURA_MSG_LOBBY_PLAYER_DATA,
	ASURA_MSG_LOBBY_PLAYER_STATUS,
	ASURA_MSG_LOBBY_REMOVE_PLAYER,
	ASURA_MSG_LOBBY_FINAL_PLAYER_LIST,
	
	ASURA_MSG_LOBBY_FIRST_PLATFORM_SPECIFIC = 0x0F00,
};

// these attributes can be used for filtering when searching for a lobby.
// Their meaning is entirely project-specific
class Asura_Lobby_Attributes
{
public:

	Asura_Lobby_Attributes();

	void SetIntAttribute( u_int uAttributeIndex, int iData ) { m_aiAttributes[uAttributeIndex] = iData; }
	int GetIntAttribute( u_int uAttributeIndex ) const { return m_aiAttributes[uAttributeIndex]; }
	void SetBinaryData( const void* pData );
	const void* GetBinaryData() const { return m_aucAttributeData; }

	void Project_ChangeByteOrder();

    inline bool operator == ( const Asura_Lobby_Attributes& xAttributes ) const
	{
		return (!Asura_Mem::Compare( m_aiAttributes, xAttributes.m_aiAttributes, ASURA_LOBBY_NUM_INT_ATTRIBUTES * sizeof(int) ) &&
				!Asura_Mem::Compare( m_aucAttributeData, m_aucAttributeData, ASURA_LOBBY_BINARY_ATTRIBUTE_DATA_SIZE));
	}

	inline bool operator != ( const Asura_Lobby_Attributes& xAttributes ) const
	{
		return (!(*this == xAttributes));
	}

private:

	// int attributes and binary data can be set by project code to be whatever you want
	Asura_FixedArray<int, ASURA_LOBBY_NUM_INT_ATTRIBUTES> m_aiAttributes;
	u_char m_aucAttributeData[ASURA_LOBBY_BINARY_ATTRIBUTE_DATA_SIZE];
};

ASURA_STATIC_ASSERT(sizeof(Asura_Lobby_Attributes) == ASURA_LOBBY_NUM_INT_ATTRIBUTES * sizeof(int) + ASURA_LOBBY_BINARY_ATTRIBUTE_DATA_SIZE);

#ifdef ASURA_SUPPORT_LOBBY

#include "Core/Memory/Asura_Mem.h"
#include "Network/Online/Asura_Platform_Lobby.h"
#include "Network/Online/Asura_Lobby_Types.h"
#ifdef ASURA_USE_MERIT_SYSTEM
#include "Network/Online/Asura_Merit_Types.h"
#endif

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////
class Asura_Message;
class Asura_Lobby;
class Asura_Lobby_ListEntry;
class Asura_Lobby_ConnectData;
class Asura_Lobby_ClientStatusMsg;
class Asura_Lobby_Attributes;
class Asura_Lobby_PlayerData;
class Asura_Lobby_SearchCriterion;
class Asura_Lobby_Team;
class Asura_Lobby_Player;

// max players define is used for sanity checking of message data and also to
// allow arrays of players to add/remove to be created on the stack.
#ifndef ASURA_LOBBY_MAX_PLAYERS
#define ASURA_LOBBY_MAX_PLAYERS (16)
#endif

#ifndef ASURA_LOBBY_MAX_TEAMS
#define ASURA_LOBBY_MAX_TEAMS (2)
#endif
#define ASURA_LOBBY_INVALID_TEAM (ASURA_LOBBY_MAX_TEAMS)

#ifndef ASURA_LOBBY_MAX_SEARCH_RESULTS
#define ASURA_LOBBY_MAX_SEARCH_RESULTS (16)
#endif

// Player project specific data (size in bytes)
#ifndef ASURA_LOBBY_PLAYER_PROJECT_DATA_SIZE
#define ASURA_LOBBY_PLAYER_PROJECT_DATA_SIZE	(4)
#endif

// Maximum number of different channels for voice chat.
// If you increase this value, please update m_ucVoiceChannels also
#define ASURA_LOBBY_MAX_VOICE_CHANNELS			(8)

#define ASURA_LOBBY_MAX_SKILL					(20)

#ifndef ASURA_LOBBY_FIRST_PORT
//For now this is a completely arbitrary number, based on the TCP unit test
#define ASURA_LOBBY_FIRST_PORT 27010
#endif

// E N U M S /////////////////////////////////////////////////////////////////
enum ASURA_LOBBY_STATE
{
	// explicit numbering no longer required since there are console commands
	// to query if the system is in particular states.
	ASURA_LOBBY_STATE_INVALID,
	ASURA_LOBBY_STATE_CREATING,
	ASURA_LOBBY_STATE_PRE_GAME,
	ASURA_LOBBY_STATE_JOINING_PLATFORM_EARLY_INIT,
	ASURA_LOBBY_STATE_JOINING_WAIT_FOR_CONNECTION,
	ASURA_LOBBY_STATE_JOINING_PLATFORM_LATE_INIT,
	ASURA_LOBBY_STATE_MIGRATING_DEADHOST,
	ASURA_LOBBY_STATE_MIGRATING_PREGAME,
	ASURA_LOBBY_STATE_REGISTERING,		// currently only needed for 360 arbitration.
	ASURA_LOBBY_STATE_INITIALISING_GAME,
	ASURA_LOBBY_STATE_STARTING_GAME,
	ASURA_LOBBY_STATE_PLAYING_GAME,
	ASURA_LOBBY_STATE_ENDING_GAME,
	ASURA_LOBBY_STATE_TERMINATING,
	// TODO - support migration, especially to choose best host before starting game
};

enum ASURA_LOBBY_CLIENT_MIGRATIONSTATE
{
	ASURA_LOBBY_CLIENT_MIGRATIONSTATE_NONE,
	ASURA_LOBBY_CLIENT_MIGRATIONSTATE_DISCONNECTING,
	ASURA_LOBBY_CLIENT_MIGRATIONSTATE_FINDHOST,
	ASURA_LOBBY_CLIENT_MIGRATIONSTATE_CONNECTING,
};

enum ASURA_LOBBY_HOST_MIGRATIONSTATE
{
	ASURA_LOBBY_HOST_MIGRATIONSTATE_NONE,
	ASURA_LOBBY_HOST_MIGRATIONSTATE_STARTING,
	ASURA_LOBBY_HOST_MIGRATIONSTATE_STOPPING,
};

enum ASURA_LOBBY_SEARCHSTATE
{
	ASURA_LOBBY_SEARCHSTATE_NOT_SEARCHING,
	ASURA_LOBBY_SEARCHSTATE_SEARCHING,
	ASURA_LOBBY_SEARCHSTATE_SEARCH_COMPLETE,
};

enum ASURA_LOBBY_GAMESTATE
{
	ASURA_LOBBY_GAMESTATE_INVALID,
	ASURA_LOBBY_GAMESTATE_MIGRATION, //Pre-game
	ASURA_LOBBY_GAMESTATE_REGISTERING,	// currently only used on 360 for arbitration
	ASURA_LOBBY_GAMESTATE_INITIALISING,
	ASURA_LOBBY_GAMESTATE_STARTING,

	ASURA_LOBBY_GAMESTATE_PLAYING,
	ASURA_LOBBY_GAMESTATE_ENDING,
};

enum ASURA_GAME_COMPLETION_RECORD
{
	ASURA_GAME_COMPLETION_RECORD_RANKED,
	ASURA_GAME_COMPLETION_RECORD_FRIENDLY,

	ASURA_NUM_GAME_COMPLETION_RECORDS
};

enum ASURA_LOBBY_GAME_COMPLETION_STATUS
{
	ASURA_LOBBY_GAME_COMPLETION_UNSET = -1,
	ASURA_LOBBY_GAME_COMPLETED,
	ASURA_LOBBY_GAME_NOT_COMPLETED,
};

enum ASURA_LOBBY_SEARCH_OPERATOR
{
	ASURA_LOBBY_SEARCH_OPERATOR_EQ,	// ==
	ASURA_LOBBY_SEARCH_OPERATOR_GE,	// >=
	ASURA_LOBBY_SEARCH_OPERATOR_LE,	// <=
};

// C L A S S E S /////////////////////////////////////////////////////////////

// Record of the number of games hosted, started and completed in online multiplayer.
class Asura_GameCompletionHistory
{
public:
	Asura_GameCompletionHistory()
		: m_bGetting(false)
		, m_bWriting(false)
		, m_bDataInitialised(false)
		, m_bDataChanged(false)
	{}

	// Get the completion ratio in the range (0,1) (the closer to one the better).
	// Adjusted to reduce the impact of this ratio if only a small number of games have been played.
	float GetAdjustedCompletionRatio() const;

	u_int GetNumGamesHosted()    const { return GetRecord().m_uNumGamesHosted; }
	u_int GetNumGamesStarted()   const { return GetRecord().m_uNumGamesStarted; }
	u_int GetNumGamesCompleted() const { return GetRecord().m_uNumGamesCompleted; }

	void IncrementNumGamesHosted()		{ ++GetRecord().m_uNumGamesHosted;    m_bDataChanged = true; }
	void IncrementNumGamesStarted()		{ ++GetRecord().m_uNumGamesStarted;   m_bDataChanged = true; }
	void IncrementNumGamesCompleted()	{ ++GetRecord().m_uNumGamesCompleted; m_bDataChanged = true; }

	bool IsDataInitialised() const { return m_bDataInitialised; }
	bool HasDataChanged() const { return m_bDataChanged; }
	bool IsGettingData() const { return m_bGetting; }
	bool IsWritingData() const { return m_bWriting; }

	// Read/write the data to/from the stats server.
	void Platform_GetData_Start();
	bool Platform_GetData_Update(); // Returns true when finished.

	void Platform_WriteData_Start();
	bool Platform_WriteData_Update();  // Returns true when finished.

private:
	// GameCompletionRecord
	class GameCompletionRecord
	{
	public:
		GameCompletionRecord()
			: m_uNumGamesHosted(0), m_uNumGamesStarted(0), m_uNumGamesCompleted(0)
		{}

		u_int m_uNumGamesHosted;
		u_int m_uNumGamesStarted;
		u_int m_uNumGamesCompleted;
	};

	// Get the relevant record of game completion stats.
	const GameCompletionRecord& GetRecord() const { return m_axRecord[GetIndex()]; }
	      GameCompletionRecord& GetRecord()		  { return m_axRecord[GetIndex()]; }

	// Do we want the ranked or friendly game completion record?
	ASURA_GAME_COMPLETION_RECORD GetIndex() const;

private:
	// Game completion records
	GameCompletionRecord m_axRecord[ASURA_NUM_GAME_COMPLETION_RECORDS];

	// Flags
	bool m_bGetting;		// Currently getting data from the stats server.
	bool m_bWriting;		// Currently writing data to the stats server.

	bool m_bDataInitialised;// Have we read the data from the stats server user storage?
	bool m_bDataChanged;	// Need to write the data back to the stats server.
};


class Asura_Lobby_HostingSettings
{
public:
	u_int m_uPrivateSlots;	// used to reserve places for invitations
};

// Asura_Lobby_System manages the state of the lobby we're in and deals with 
// initialising Asura_Network prior to starting a game.
class Asura_Lobby_System
{
public:

	static void Initialise();
	static void AddConsoleCmdsAndVars();
	static void Update();
	static void Shutdown();

	static bool IsHost();
	static bool IsMigrating();
	static bool IsInitialised() { return s_bInitialised; }
	static bool IsInvitationPending() { return s_bInvitationPending; }

	static void CreateLobby( u_int uMaxPlayers, u_char ucNumTeams, const Asura_Lobby_Attributes& xAttributes );
	static void SearchForLobby( Asura_Lobby_SearchCriterion* pxSearchCriteria, u_int uNumSearchCriteria );
	static void CancelSearch();
	static void JoinLobby( u_int uIndexOfSearchResult );
	static bool LeaveLobby( bool bHelp ); // doubles up as console cmd
	static void StartGame();
	static void EndGame();

	static bool IsReadyToJoinGame();
	static bool IsReadyToLoadGame();

	// CompletedGame() should be called before calling EndGame(). The parameter should 
	// be true iff we completed a game without the local user quitting before the end.
	static void CompletedGame(bool bCompleted); 

	// Automatching functions. The automatching code sits on top of this system, so
	// it shouldn't really know anyting about it; however there is a need for the host
	// to indicate to the client whether he is in the automatch state or not, and how
	// many players are being waited for. That's all these functions are for.
	static void SetAutomatching(bool bAutomatching);
	static bool IsAutomatching();
	static void SetNumPlayersBeingWaitedFor( u_int uNumPlayers );
	static u_int GetNumPlayersBeingWaitedFor();

	static u_int GetNumSearchResults(void) { return s_uNumSearchResults; }
	static const Asura_Lobby_ListEntry& GetSearchResultByIndex(u_int uIndex);

	// communications functions
	static void ServerMessageHandler(const Asura_ReliableConnectionID& xID, u_short usMsgID, u_short usMsgBodySize, const char* pcMsgBody);
	static void ClientMessageHandler(u_short usMsgID, u_short usMsgBodySize, const char* pcMsgBody);
	static void PlayerLeft(const Asura_ReliableConnectionID& xID, ASURA_RELIABLE_SOCKET_DISCONNECT_REASON eReason);
	static bool PlayerJoined(const Asura_ReliableConnectionID& xID, u_short usMsgSize, const char* pcMsgBody);
	static void ClientDisconnected(ASURA_RELIABLE_SOCKET_DISCONNECT_REASON eReason);

	static ASURA_LOBBY_STATE GetState() { return s_eLobbyState; }
	static ASURA_LOBBY_SEARCHSTATE GetSearchState() { return s_eSearchState; }
	static int GetErrorCode() { return s_iNetworkDisconnectReason; }
	static const Asura_UnicodeChar* GetLocalPlayerName() { return s_awcLocalPlayerName; }
	static bool GetLocalPlayerReady() { return s_bLocalPlayerReady; }
#ifdef ASURA_USE_MERIT_SYSTEM
	static void SetLocalPlayerMerit(u_int uMeritLevel, int iMeritPoints)
	{
		s_xLocalPlayerMerit.SetMeritLevel(uMeritLevel);
		s_xLocalPlayerMerit.SetMeritPoints(iMeritPoints);
	}
	static const Asura_Merit& GetLocalPlayerMerit() { return s_xLocalPlayerMerit; }
	static int GetLocalPlayerSkill() { return static_cast<int>(s_xLocalPlayerMerit.GetMeritLevel()); }
#else
	static void SetLocalPlayerSkill( int iSkill ) { s_iLocalPlayerSkill = iSkill; }
	static int GetLocalPlayerSkill() { return s_iLocalPlayerSkill; }
#endif
	static void SetLocalPlayerGuid( Asura_Guid uGuid );
	static void SetPlayerGuid( const Asura_Lobby_GameConnectData& xConnectData, Asura_Guid uGuid );

	// GetLobby() exists so game setup code can get to the attributes etc.
	// Calling code must check for NULL pointer!
	static const Asura_Lobby* GetLobby() { return s_pxLobby; }

	// player teams can be updated by project code on the server
	static void SetPlayerTeamByGuid( Asura_Guid uClientGuid, u_char ucTeam );
	// player voice channels can be updated by project code on the server
	static void SetPlayerVoiceChannelsByGuid( Asura_Guid uClientGuid, u_char ucVoiceChannels );
	// players can request their desired team. This will need better handling at some point.
	static void SetLocalPlayersRequestedTeam( u_char ucTeam );
	static u_char GetLocalPlayersRequestedTeam(); 
	// team names are configurable by the project.
	static void SetTeamName( u_char ucTeam, const Asura_UnicodeChar* wszName );
	// player data is configurable by the project
	static void SetLocalPlayersProjectData( const u_char* pucData );
	// voice channels are configurable by the project
	static void ConnectLocalPlayerToVoiceChannel( u_char ucVoiceChannel );
	static void DisconnectLocalPlayerFromVoiceChannel( u_char ucVoiceChannel );
	static void SetLocalPlayersActiveVoiceChannel( u_char ucVoiceChannel );

	static bool IsLocalPlayerConnectedToVoiceChannel( u_char ucVoiceChannel ); 
	static bool IsMicrophoneAttached() { return Platform_IsMicrophoneAttached(); }

	static u_char GetLocalPlayersVoiceChannels(); 
	static u_char GetLocalPlayersActiveVoiceChannel(); 

	// disable/enable the transfer of lobby ownership (and hosting) to the best player
	// on the specified events
	static void SetMigrationEnabled( bool bOnDeadHost, bool bOnGameStart );
	static void GetMigrationEnabled( bool& bOnDeadHost, bool& bOnGameStart );

	// set this flag to determine whether we're using the ranked or unranked section of the game
	static void SetUseRankedLobbies( bool bUseRankedLobbies );
	static bool IsUsingRankedLobbies() { return s_bUseRankedLobbies; }

	// set this flag to determine whether we're allowing join-in-progress for friendly games.
	// NB Ranked games never allow join-in-progress since this is a requirement on Xbox Live.
	static void SetAllowJoinInProgress( bool bAllowJoinInProgress );
	static bool GetAllowJoinInProgress() { return s_bAllowJoinInProgress; }

	// attributes can be changed by the host once a lobby has been created: e.g. Map rotation
	static void SetLobbyAttributes( const Asura_Lobby_Attributes& xAttributes );

	static const char* GetGameAddress() { return (s_bPlatformInitialisingGameDone) ? s_acGameAddress : NULL; }
	
	// can override the default listen port. If this is done, there must exist a means to communicate
	// the port to clients who want to join, e.g. via a matchmaking search result property. Currently 
	// this is only used on PC with the LAN implementation (to allow multiple instances on a single machine).
	static void SetTCPListenPort(u_short uListenTCPPort) { s_uListenTCPPort = uListenTCPPort; }
	static u_short GetTCPListenPort() { return s_uListenTCPPort; }
	static u_short GetBoundTCPPort(); // get the port that was bound by the system, useful if specifying 0.

	static Asura_GameCompletionHistory& GetGameCompletionHistory() { return s_xGameCompletionHistory; }

private:
	static void UpdateDebugOutput();

	static void ChangeState( ASURA_LOBBY_STATE eNewState );
	static void Terminate( int iNetworkDisconnectionReason );
	static void Platform_Initialise();	// should setup local player name.
#ifdef ASURA_USE_MERIT_SYSTEM
	static void Platform_InitialiseMerit();
#endif
	static void Platform_Shutdown();	// called when lobby system stops being used.
	static void Platform_Terminate();	// called when connection is terminated but lobby system still active
	static void Platform_Update();		// called every frame

	// indep. state functions
	static void EnterState_Creating();
	static void EnterState_PreGame();
	static void EnterState_Joining_PlatformEarlyInit();
	static void EnterState_Joining_WaitForConnection();
	static void EnterState_Joining_PlatformLateInit();
	static void EnterState_Migrating();
	static void EnterState_Registering();
	static void EnterState_InitialisingGame();
	static void EnterState_StartingGame();
	static void EnterState_PlayingGame();
	static void EnterState_EndingGame();
	static void EnterState_Terminating();

	static void ExecuteState_Creating();
	static void ExecuteState_PreGame();
	static void ExecuteState_Joining_PlatformEarlyInit();
	static void ExecuteState_Joining_WaitForConnection();
	static void ExecuteState_Joining_PlatformLateInit();
	static void ExecuteState_Migrating();
	static void ExecuteState_Registering();
	static void ExecuteState_InitialisingGame();
	static void ExecuteState_StartingGame();
	static void ExecuteState_PlayingGame();
	static void ExecuteState_EndingGame();
	static void ExecuteState_Terminating();

	static void ExitState_Terminating();

	// search state functions
	static void ChangeSearchState( ASURA_LOBBY_SEARCHSTATE eNewState );
	static void EnterSearchState_Searching();
	static void ExecuteSearchState_Searching();

	// migration functions
	static void ChangeHostMigrationState( ASURA_LOBBY_HOST_MIGRATIONSTATE eNewState );
	static void EnterHostMigrationState_Stopping();
	static void EnterHostMigrationState_Starting();
	static void ExecuteHostMigrationState_Stopping();
	static void ExecuteHostMigrationState_Starting();

	static void ChangeClientMigrationState( ASURA_LOBBY_CLIENT_MIGRATIONSTATE eNewState );
	static void EnterClientMigrationState_Disconnecting();
	static void ExecuteClientMigrationState_Disconnecting();
	static void ExecuteClientMigrationState_FindHost();
	static void ExecuteClientMigrationState_Connecting();


	// helper functions
	static bool SetupHostPort();
	static void UpdateNetworkConnection();
	static void ProcessStatusUpdate( const Asura_Lobby_LobbyData* pxLobbyData );
	static void GenerateDataUpdates();
	static void SendClientUpdateToServer();
	static void ProcessClientStatus( Asura_ReliableConnectionID xID, const Asura_Lobby_ClientStatusMsg* pxData );
	static void AddLocalPlayerToLobby();
	static void SetLocalPlayerDataChanged() { s_bLocalPlayerDataChanged = true; }

	// platform functions
	static void Platform_CreateLobby(bool bHost);// lobby creation can be done on both server and client.
	static bool Platform_UpdateLobbyCreation(); // returns true when creation is finished
	static bool Platform_UpdateSessionRegistration(); // returns true when registration is finished, sets s_bRegistered if successful.
	static bool Platform_UpdateStartingSession(); // returns true when session is ready to start.
	static bool Platform_UpdateEndingSession(); // returns true when session is properly finished.
	static void Platform_EndGame();	// called by EndGame(). On 360 this lets us do the stats update before score data is destroyed.
	static void Platform_BeginSearch();
	static void Platform_CancelSearch();
	static bool Platform_SearchHasFinished();
	// Platform_PrepareToJoinLobby() - called repeatedly during ASURA_LOBBY_STATE_JOINING_PLATFORM_EARLY_INIT
	// state - when ready to join, it returns true and platform code fills out the address appropriately.
	static bool Platform_PrepareToJoinLobby(  u_int uIndex, Asura_NetAddress& xAddress, Asura_NetPort& xPort );
	static bool Platform_IsPlayerTalking( const Asura_Lobby_Player& xPlayer );
	static bool Platform_IsMicrophoneAttached();
	static void Platform_DoPlayerListAction( u_int uIndex );
	static bool Platform_ConsoleCmd_LobbyInviteFriends( bool bHelp );	// opens OS dialog to invite friends.
	static bool Platform_UpdateInitialisingGame();
	static bool Platform_GetMigrationAddress( Asura_NetAddress& xAddress, Asura_NetPort& xPort );
	static void Platform_ServerStartMigration( bool bNewHost );
	static bool Platform_ServerEndMigration();
	static bool Platform_ClientEndMigration();
	static void Platform_UpdateDebugOutput( u_int& uX, u_int& uY );
	//TODO: Can this be removed in favour of an integrated migration system?
	static bool Platform_ClientMessageHandler( u_short usMsgID, u_short usMsgBodySize, const char* pcMsgBody );

	// Command Console functions for menu interface
	
	// player access functions - for all players
	static bool GetPlayerName( u_int uPlayerIndex, bool bInterrogate, bool bHelp );
	static bool GetPlayerReadyStatus( u_int uPlayerIndex, bool bInterrogate, bool bHelp );
	static bool GetPlayerNotReadyStatus( u_int uPlayerIndex, bool bInterrogate, bool bHelp );
	static bool GetPlayerIsFriend( u_int uPlayerIndex, bool bInterrogate, bool bHelp );
	static bool GetPlayerIsTalking( u_int uPlayerIndex, bool bInterrogate, bool bHelp );
	static bool GetPlayerMicrophoneAttached( u_int uPlayerIndex, bool bInterrogate, bool bHelp );
	static bool DoPlayerListAction( u_int uPlayerIndex, bool bInterrogate, bool bHelp );

	// player access functions - team-based
	template<typename T> static void AddTeamConsoleCmdsAndVars();
	template<typename T> static bool GetPlayerNameByTeam( u_int uPlayerIndex, bool bInterrogate, bool bHelp );
	template<typename T> static bool GetPlayerReadyStatusByTeam( u_int uPlayerIndex, bool bInterrogate, bool bHelp );
	template<typename T> static bool GetPlayerNotReadyStatusByTeam( u_int uPlayerIndex, bool bInterrogate, bool bHelp );
	template<typename T> static bool GetPlayerIsFriendByTeam( u_int uPlayerIndex, bool bInterrogate, bool bHelp );
	template<typename T> static bool GetPlayerIsTalkingByTeam( u_int uPlayerIndex, bool bInterrogate, bool bHelp );
	template<typename T> static bool DoPlayerListActionByTeam( u_int uPlayerIndex, bool bInterrogate, bool bHelp );
	template<typename T> static bool GetTeamName( u_int uListboxEntry, bool bInterrogate, bool bHelp );

	static bool ToggleReadyStatus( bool bHelp );
	static bool OpenFriendsList( bool bHelp );
	static bool ConsoleCmd_CancelInvitation( bool bHelp );
	static bool ConsoleCmd_UseRankedMatch( bool bUseRankedMatch, bool bInterrogate, bool bHelp );
	static bool ConsoleCmd_AllowJoinInProgress( bool bAllowJoinInProgress, bool bInterrogate, bool bHelp );
	static bool ConsoleCmd_IsHost( bool bHelp );
	static bool ConsoleCmd_IsJoiningGameInProgress( bool bHelp ); 
	static bool ConsoleCmd_IsMigrating( bool bHelp );
	static bool ConsoleCmd_IsStartingGame( bool bHelp );
	static bool ConsoleCmd_IsEndingGame( bool bHelp );
	static bool ConsoleCmd_IsCreating( bool bHelp );
	static bool ConsoleCmd_IsJoining( bool bHelp );
	static bool ConsoleCmd_IsPreGame( bool bHelp );
	static bool ConsoleCmd_IsSearchComplete( bool bHelp );
	static bool ConsoleCmd_IsError( bool bHelp );
	static bool ConsoleCmd_IsTerminating( bool bHelp );
	static bool ConsoleCmd_PrintErrorCode( bool bHelp );
	// Project_PrintErrorCodeToConsole() must call Asura_CommandConsole::Printf() 
	// to display either text or numerical error information as appropriate, e.g. 
	// for ASURA_NDR_TIMEOUT or ASURA_NDR_SERVERQUIT, have a nice message and for 
	// other errors display "error code: 0x%x" so QA can report the error code.
	static void Project_PrintErrorCodeToConsole();

	// Project_GetInviteText() simply allows each project to specify the subject
	// line and body for invitation messages. Currently only used on PS3. Size limit
	// for subject is only 18 UTF8 characters, body can be up to 512.
	static void Project_GetInviteText( const Asura_UnicodeChar*& wszSubject, const Asura_UnicodeChar*& wszBody );
	
	template<typename T> static const T* CheckAndExtractData( const void* pData, const u_int uDataSize );

	// static class data
	static Asura_Lobby* s_pxLobby;

	static bool s_bInitialised;
	static bool s_bInvitationPending;	// set by platform code when invite received.
	static bool s_bInvitedToGame;		// true once joining process is underway and remains true until game is left
	static bool s_bUseRankedLobbies;
	static bool s_bAllowJoinInProgress;
	static bool s_bJoiningGameInProgress;
	static bool s_bLocalPlayerReady;
	static bool s_bLocalPlayerHasMicrophone;
	static bool s_bLocalClientReceivedFirstUpdate;
	static bool s_bPlayerJoinedSinceLastUpdate;
	static bool s_bRegistered;
	static bool s_bShowDebugOutput;
	static bool s_bMigrateOnDeadHost;
	static bool s_bMigrateOnGameStart;
	static bool s_bPlatformCreateDone;

	static u_char s_ucLocalPlayerRequestedTeam;
	static u_char s_aucProjectData[ASURA_LOBBY_PLAYER_PROJECT_DATA_SIZE];
	static u_char s_ucVoiceChannels;
	static u_char s_ucActiveVoiceChannel; 

	// local player name is initialised by Platform_Initialise().
	// Some platforms might require it to be updateable (e.g. Steam) but we'll
	// cross that bridge when we come to it...
	static Asura_UnicodeChar s_awcLocalPlayerName[ASURA_LOBBY_PLAYER_NAME_BUFFER_SIZE];
#ifdef ASURA_USE_MERIT_SYSTEM
	static Asura_Merit s_xLocalPlayerMerit;
#else
	static int s_iLocalPlayerSkill;
#endif
	static Asura_Lobby_HostingSettings s_xHostingSettings;

	// global state
	static ASURA_LOBBY_STATE s_eLobbyState;
	static float s_fStateTimer;
	static float s_fUpdateMessageTimer;
	static int s_iNetworkDisconnectReason;
	static float s_fUpdateTimestamp;
	static bool s_bLocalPlayerDataChanged;
	
	// lobby search
	static ASURA_LOBBY_SEARCHSTATE s_eSearchState;
	static Asura_Lobby_SearchCriterion* s_pxSearchCriteria;
	static u_int s_uNumSearchCriteria;
	static Asura_Lobby_ListEntry* s_pxSearchResults;
	static u_int s_uNumSearchResults;
	static u_int s_uIndexOfLobbyToJoin;
	static u_int s_uNumTeams;

	// migration
	static ASURA_LOBBY_CLIENT_MIGRATIONSTATE s_eClientMigrationState;
	static ASURA_LOBBY_HOST_MIGRATIONSTATE s_eHostMigrationState;
	static float s_fHostMigrationTimeout;

	// Game completion records
	static Asura_GameCompletionHistory s_xGameCompletionHistory;
	static ASURA_LOBBY_GAME_COMPLETION_STATUS s_eGameCompletionStatus;

	// game information
	static char s_acGameAddress[ASURA_LOBBY_ADDRESS_LENGTH];
	static bool s_bPlatformInitialisingGameDone;

	//TODO: Rename
	static Asura_ReliableSocket_Listener* s_pxHostSocket;
	static Asura_ReliableSocket_Client* s_pxClientSocket;
	static u_short s_uListenTCPPort;
};

// Asura_Lobby represents a lobby that either we or another client have
// created and registered with the matchmaking backend.
// Asura_Lobby_Data is the info not specific to any player and should have
// a fixed size so it can included in network messages.
// Asura_Lobby_PlayerData is non-changing information about each player in the lobby
// and should also have a fixed size. It inherits from a platform version of the 
// class so platforms can automatically include whatever specific info they need.
// Asura_Lobby_PlayerStatusMsg is the information about each player which can 
// change, and includes a Guid to identify the player when in-game.
class Asura_Lobby_LobbyData : public Asura_Platform_Lobby_LobbyData
{
public:

	Asura_Lobby_LobbyData()
	{
		m_ucGameState = ASURA_LOBBY_GAMESTATE_INVALID;
		m_ucMaxPlayers = 0;
		m_ucNumTeams = 0;
		m_ucAutomatchingData = 0;
		m_xHostID.Invalidate();
	}

	Asura_ReliableConnectionID m_xHostID;
	u_char m_ucGameState; // from ASURA_LOBBY_GAMESTATE enum
	u_char m_ucMaxPlayers;
	u_char m_ucNumTeams;
	u_char m_ucAutomatchingData;
	Asura_Lobby_Attributes m_xAttributes;
};

class Asura_Lobby_ConnectData : public Asura_Platform_Lobby_PlayerData
{
public:
	Asura_Lobby_ConnectData()
	: m_cHostScore(0)
	{}

	void Fill();

	// public data
	Asura_UnicodeChar m_awcName[ASURA_LOBBY_PLAYER_NAME_BUFFER_SIZE];
	char m_cHostScore;
#ifdef ASURA_USE_MERIT_SYSTEM
	Asura_Merit m_xMerit;
#endif

protected:
	void Platform_Fill();

private:
#ifdef ASURA_USE_MERIT_SYSTEM
	void Platform_InitialiseMerit();
#endif
};

//Sends initial player data from host to other clients
class Asura_Lobby_PlayerData : public Asura_Platform_Lobby_PlayerData
{
public:
 	Asura_Lobby_PlayerData() 
		: Asura_Platform_Lobby_PlayerData()
		, m_uClientGuid(ASURA_GUID_UNREGISTERED)
#ifdef ASURA_USE_MERIT_SYSTEM
		, m_xMerit()
#endif
		, m_bInvited( false )
		, m_bReady( false )
		, m_bRegistered( false )
		, m_bIsMicrophoneAttached( false )
		, m_ucTeam( 0 )
		, m_ucVoiceChannels( 1 )	// Default to lobby connected to channel 0
		, m_cHostScore( 0 )
		, m_xID()
	{ 
 		m_awcName[0] = 0; 
 		Asura_Mem::Set( m_aucProjectData, 0, ASURA_LOBBY_PLAYER_PROJECT_DATA_SIZE ); 
 	}

	Asura_Lobby_PlayerData( Asura_ReliableConnectionID xID, const Asura_Lobby_ConnectData& xConnectData, bool bInvited, u_char ucTeam )
		: Asura_Platform_Lobby_PlayerData( xConnectData )
		, m_uClientGuid( ASURA_GUID_UNREGISTERED )
		, m_bInvited( bInvited )
		, m_bReady( false )
		, m_bRegistered( false )
		, m_bIsMicrophoneAttached( false )
		, m_ucTeam( ucTeam )
		, m_ucVoiceChannels( 1 )	// Default to lobby connected to channel 0
		, m_cHostScore( xConnectData.m_cHostScore )
		, m_xID( xID )
	{
		Asura_Mem::Set( m_aucProjectData, 0, ASURA_LOBBY_PLAYER_PROJECT_DATA_SIZE ); 
		Asura_PrintfToBfr(m_awcName, L"%s", ASURA_LOBBY_PLAYER_NAME_BUFFER_SIZE, true, xConnectData.m_awcName);
#ifdef ASURA_USE_MERIT_SYSTEM
		m_xMerit = xConnectData.m_xMerit;
#endif

		//Platform_Initialise( xConnectData );
	}

	Asura_Guid m_uClientGuid;
	Asura_UnicodeChar m_awcName[ASURA_LOBBY_PLAYER_NAME_BUFFER_SIZE];
#ifdef ASURA_USE_MERIT_SYSTEM
	Asura_Merit m_xMerit;
#endif
	bool m_bInvited;
	bool m_bReady;
	bool m_bRegistered;
	bool m_bIsMicrophoneAttached;
	u_char m_ucTeam;
	u_char m_aucProjectData[ASURA_LOBBY_PLAYER_PROJECT_DATA_SIZE]; 
	u_char m_ucVoiceChannels;
	char m_cHostScore;
	Asura_ReliableConnectionID m_xID;
};

class Asura_Lobby_Player
{
public:
	Asura_Lobby_Player( Asura_Lobby& xLobbyParent )
	: m_xData()
	, m_xLobbyParent( xLobbyParent )
	, m_fTimer(0.0f)
	, m_ucPlayerFlags(0)
	{}

	void Initialise( const Asura_Lobby_PlayerData* pxData );
	void UpdateData( const Asura_Lobby_ClientStatusMsg* pxData );
	void UpdateData( const Asura_Lobby_PlayerStatusMsg* pxData );

	const Asura_Platform_Lobby_PlayerData& GetPlatformData() const	{ return m_xData; }
	const Asura_ReliableConnectionID& GetID() const					{ return m_xData.m_xID; }
	const Asura_Lobby_PlayerData* GetDataPtr() const				{ return &m_xData; }

	// query functions
	Asura_Guid GetClientGuid() const						{ return m_xData.m_uClientGuid; }
	bool WasInvited() const									{ return m_xData.m_bInvited; }
	const Asura_UnicodeChar* GetName() const				{ return m_xData.m_awcName; }
	bool GetReadyStatus() const;
	bool HasRegistered() const								{ return m_xData.m_bRegistered; }
	bool IsMicrophoneAttached() const						{ return m_xData.m_bIsMicrophoneAttached; }
	u_char GetTeamIndex() const								{ return m_xData.m_ucTeam; }
	const u_char* GetProjectData() const					{ return &m_xData.m_aucProjectData[0]; }
	u_char  GetVoiceChannels() const						{ return m_xData.m_ucVoiceChannels; }

#ifdef ASURA_USE_MERIT_SYSTEM
	const Asura_Merit& GetMerit() const						{ return m_xData.m_xMerit; }
#endif
#ifdef ASURA_USE_DEBUG_TEXT
	char DebugGetHostScore()	const						{ return m_xData.m_cHostScore; }
#endif

	bool HasDataChanged() const								{ return m_bDataChanged; }
	void ClearDataChanged()									{ m_bDataChanged = false; }

	bool IsNewData() const									{ return m_bDataNew; }
	void ClearNewData()										{ m_bDataNew = false; }

	float GetTimer() const									{ return m_fTimer; }
	void SetTimer( float fTime )							{ m_fTimer = fTime; }
	void UpdateTimer();

	bool IsLocalPlayer() const								{ return m_bLocalPlayer; }
	bool IsBetterHost( const Asura_Lobby_Player* xOther ) const;
	bool IsABestHost() const;

	// Set() functions for mutable data
	void SetClientGuid( Asura_Guid uGuid )					{ m_xData.m_uClientGuid = uGuid;				m_bDataChanged = true; }
	void SetReadyStatus( bool bReady )						{ m_xData.m_bReady = bReady;					m_bDataChanged = true; }
	void SetRegistered( bool bRegistered )					{ m_xData.m_bRegistered = bRegistered;			m_bDataChanged = true; }
	void SetMicrophoneAttached(bool bAttached)				{ m_xData.m_bIsMicrophoneAttached = bAttached;	m_bDataChanged = true; }
	void SetVoiceChannels( const u_char ucVoiceChannels )	{ m_xData.m_ucVoiceChannels = ucVoiceChannels;	m_bDataChanged = true; }
	void SetTeam( u_char ucTeam );
	void SetProjectData( const u_char* pucData );

	void SetConnectionID( Asura_ReliableConnectionID xID );

	bool operator==( const Asura_Platform_Lobby_GameConnectData& xOther ) const	{ return ( m_xData == xOther ); }
	bool operator==( const Asura_Platform_Lobby_PlayerData& xOther ) const		{ return ( m_xData == xOther ); }

protected:
	char GetHostScore()	const								{ return m_xData.m_cHostScore; }

private:
	Asura_Lobby_PlayerData m_xData;
	Asura_Lobby& m_xLobbyParent;
	
	float m_fTimer;

	union
	{
		u_char m_ucPlayerFlags;
		struct
		{
			u_int	m_bLocalPlayer	: 1,
					m_bDataChanged	: 1,
					m_bDataNew		: 1,
					PLAYER_UNUSED	: 6;
		};
	};
};

class Asura_Lobby
{
public:
	Asura_Lobby(u_int uMaxPlayers, const Asura_Lobby_Attributes& xAttributes);
	~Asura_Lobby();

	// player list management functions
	Asura_Lobby_Player* AddPlayer( const Asura_Lobby_PlayerData& xPlayerData );
	void UpdatePlayer( const Asura_Lobby_PlayerStatusMsg* pxPlayerStatus );
	bool RemovePlayer( const Asura_ReliableConnectionID& xID );
	bool RemovePlayer( u_int uPlayerIndex );
	void AddPlayerToTeam( u_int uPlayerIndex, u_char ucTeam );
	void RemovePlayerFromTeam( u_int uPlayerIndex, u_char ucTeam );

	// query functions
	ASURA_LOBBY_GAMESTATE GetGameState() const { return static_cast<ASURA_LOBBY_GAMESTATE>(m_xData.m_ucGameState); }
	bool IsAutomatching() const;
	u_int GetAutomatchPlayersAwaited() const;
	u_int GetMaxPlayers() const { return m_xData.m_ucMaxPlayers; }
	u_int GetNumPlayers() const { return m_uNumPlayers; }
	Asura_ReliableConnectionID GetHostID() const { return m_xData.m_xHostID; }
	const Asura_UnicodeChar* GetPlayerNameByIndex(u_int uIndex) const;
	const Asura_Lobby_Attributes& GetAttributes() const { return m_xData.m_xAttributes; }

	bool FindPlayerIndex( Asura_ReliableConnectionID xID, u_int& uIndex ) const;
	Asura_Lobby_Player& GetPlayerByIndex(u_int uIndex) const;

	Asura_Lobby_Player* FindPlayer( const Asura_Platform_Lobby_GameConnectData& xConnectData ) const;
	Asura_Lobby_Player* FindPlayer( const Asura_Platform_Lobby_PlayerData& xConnectData ) const;
	Asura_Lobby_Player* FindPlayer( Asura_Guid uClientGuid ) const;
	Asura_Lobby_Player* FindPlayer( Asura_ReliableConnectionID xID ) const;
	Asura_Lobby_Player* FindLocalPlayer() const;

	//Returns the player which should be chosen as host during a migration. where
	//Players have equal scores, the player with the highest index will be chosen.
	//Other systems used in migration *must* conform to this behaviour (i.e., PS3
	//priority lists)
	const Asura_Lobby_Player& FindUniqueBestHost() const;

	// team stuff
	u_char GetNumTeams() const { return m_xData.m_ucNumTeams; }
	u_int GetNumPlayersForTeam( u_char ucTeam ) const;
	u_int GetTeamPlayerIndex( u_char ucTeam, u_int uIndexInTeam ) const; // returns non-team player index.
	const Asura_Lobby_Player& GetTeamPlayerByIndex( u_char ucTeam, u_int uIndexInTeam ) const;
	const Asura_UnicodeChar* GetTeamName(u_char ucTeam) const;

	// migration related functions
	void InvalidateAllPlayers();
	void RemoveInvalidPlayers();
	void RemoveAllPlayers();

	// data access functions for messaging
	const Asura_Lobby_LobbyData* GetDataPtr() const { return &m_xData; }
	void SetData( const void* pData );
	void SetGameState(ASURA_LOBBY_GAMESTATE eState);
	void SetNumTeams( u_char ucNumTeams );
	void SetHostID( Asura_ReliableConnectionID xID );
	void SetAttributes( const Asura_Lobby_Attributes& xAttributes ); 
	
	void SetTeamName( u_char ucTeam, const Asura_UnicodeChar* wszName );
	void SetAutomatching(bool bAutomatching);
	void SetAutomatchNumPlayersAwaited(u_int uPlayers);

	void ClearDataChangedFlag() { m_bDataChanged = false; }
	bool HasDataChanged()		{ return m_bDataChanged; }

	void ResetAllClientGuids();

	// platform public functions (to be called by platform code)
	void Platform_FillLobbyData();

	// static functions
	static void AddConsoleCmdsAndVars();

private:
	// helpers
	void UpdateTeamMappingsForPlayerRemoval( u_int uIndexOfPlayerBeingRemoved );

	// platform functions
	void Platform_AddPlayer( const Asura_Lobby_Player& xPlayer );
	void Platform_RemovePlayer( const Asura_Lobby_Player& xPlayer );

	Asura_Lobby_LobbyData m_xData;
	u_int m_uNumPlayers;
	Asura_Lobby_Player* m_apxPlayers[ASURA_LOBBY_MAX_PLAYERS];
	Asura_Lobby_Team* m_apxTeams[ASURA_LOBBY_MAX_TEAMS];

	bool m_bDataChanged;

	// static data
	static u_int s_uNumTeams;
};

// Asura_Lobby_Team is a helper class for Asura_Lobby
class Asura_Lobby_Team
{
public:
	Asura_Lobby_Team( u_int uMaxPlayers )
	{
		m_wszTeamName = L"";
		m_uMaxPlayers = uMaxPlayers;
		m_puPlayerMapping = new u_int[uMaxPlayers];
		m_uNumPlayersInTeam = 0;
	}
	~Asura_Lobby_Team()
	{
		delete[] m_puPlayerMapping;
	}

	void AddPlayer( u_int uPlayerIndex );
	void RemovePlayer( u_int uPlayerIndex );
	void UpdateMappingsForPlayerRemoval( u_int uIndexOfPlayerBeingRemoved );
	void SetTeamName( const Asura_UnicodeChar* wszName ) { m_wszTeamName = wszName; }

	const Asura_UnicodeChar* GetTeamName() const { return m_wszTeamName; }
	u_int GetNumPlayers() const { return m_uNumPlayersInTeam; }
	u_int GetPlayerMapping( u_int uTeamPlayerIndex ) const;

private:
	const Asura_UnicodeChar* m_wszTeamName;
	u_int m_uMaxPlayers;
	u_int* m_puPlayerMapping;	// maps index of player in team to index of player in lobby
	u_int m_uNumPlayersInTeam;
};

//Sends player updates from host to clients
class Asura_Lobby_PlayerStatusMsg
{
public:

	Asura_Lobby_PlayerStatusMsg( const Asura_Lobby_Player& xPlayer )
	{
		m_uGuid = xPlayer.GetClientGuid();
		m_bReady = xPlayer.GetReadyStatus();
		m_bRegistered = xPlayer.HasRegistered();
		m_ucTeam = xPlayer.GetTeamIndex();
		Asura_Mem::Copy( m_aucProjectData, xPlayer.GetProjectData(), ASURA_LOBBY_PLAYER_PROJECT_DATA_SIZE ); 
		m_ucVoiceChannels = xPlayer.GetVoiceChannels();
		m_bIsMicrophoneAttached = xPlayer.IsMicrophoneAttached();
		m_xID = xPlayer.GetID();
	}

	Asura_Guid GetClientGuid() const		{ return m_uGuid; }
	bool GetReadyStatus() const				{ return m_bReady; }
	bool HasRegistered() const				{ return m_bRegistered; }
	u_char GetTeam() const					{ return m_ucTeam; }
	const u_char* GetProjectData() const	{ return &m_aucProjectData[0]; } 
	u_char GetVoiceChannels() const			{ return m_ucVoiceChannels; }
	Asura_ReliableConnectionID GetID() const	{ return m_xID; }
	bool IsMicrophoneAttached() const		{ return m_bIsMicrophoneAttached; }

private:

	Asura_Guid m_uGuid;
	bool m_bReady;
	bool m_bRegistered;
	u_char m_ucTeam;
	u_char m_aucProjectData[ASURA_LOBBY_PLAYER_PROJECT_DATA_SIZE];
	u_char m_ucVoiceChannels; 
	bool m_bIsMicrophoneAttached;
	Asura_ReliableConnectionID m_xID;
};

//Sends clients static request data to the host
class Asura_Lobby_ClientStatusMsg
{
public:

	Asura_Lobby_ClientStatusMsg( bool bReady, u_char ucRequestedTeam, bool bRegistered,
								 const u_char* pucData, u_char ucVoiceChannels,
								 bool bIsMicrophoneAttached )
	: m_bReady(bReady)
	, m_ucRequestedTeam(ucRequestedTeam)
	, m_bRegistered(bRegistered)
	, m_ucVoiceChannels( ucVoiceChannels )
	, m_bIsMicrophoneAttached( bIsMicrophoneAttached )
	{
		if (pucData) { Asura_Mem::Copy(m_aucProjectData, pucData, ASURA_LOBBY_PLAYER_PROJECT_DATA_SIZE); }
	}

	bool IsReady() const { return m_bReady; }
	u_char GetRequestedTeam() const { return m_ucRequestedTeam; }
	bool HasRegistered() const { return m_bRegistered; }
	const u_char* GetProjectData() const { return &m_aucProjectData[0]; }
	u_char GetVoiceChannels() const { return m_ucVoiceChannels; }
	bool IsMicrophoneAttached() const { return m_bIsMicrophoneAttached; }

private:

	bool m_bReady;
	u_char m_ucRequestedTeam;
	bool m_bRegistered;
	u_char m_aucProjectData[ASURA_LOBBY_PLAYER_PROJECT_DATA_SIZE];
	u_char m_ucVoiceChannels;
	bool m_bIsMicrophoneAttached;
};


// Asura_Lobby_ListEntry keeps a copy of the data relevant to each 
// lobby that is used for matchmaking.
class Asura_Lobby_ListEntry : public Asura_Platform_Lobby_ListEntry
{
public:
	Asura_UnicodeChar m_awcDescription[ASURA_LOBBY_DESCRIPTION_LENGTH];

	// possibly need to add public/private slots here
	u_int m_uNumPlayers;
	u_int m_uMaxPlayers;

	// TODO: platform/project code needs to set up attributes so they are 
	// correct on joining.
	Asura_Lobby_Attributes m_xAttributes;
};

class Asura_Lobby_SearchCriterion
{
public:

	Asura_Lobby_SearchCriterion()
	{
		m_uAttributeID = 0;
		m_eOperator = ASURA_LOBBY_SEARCH_OPERATOR_EQ;
		m_iValue = 0;
	}

	void Set(u_int uAttributeID, ASURA_LOBBY_SEARCH_OPERATOR eOperator, int iValue)
	{
		m_uAttributeID = uAttributeID;
		m_eOperator = eOperator;
		m_iValue = iValue;
	}

	u_int GetAttributeID() const { return m_uAttributeID; }
	ASURA_LOBBY_SEARCH_OPERATOR GetSearchOperator() const { return m_eOperator; }
	int GetValue() const { return m_iValue; }

private:

	u_int m_uAttributeID;
	ASURA_LOBBY_SEARCH_OPERATOR m_eOperator;
	int m_iValue;
};

#endif // #ifdef ASURA_SUPPORT_LOBBY

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

#endif // #ifndef ASURA_LOBBY_H___
