//////////////////////////////////////////////////////////////////////////////
//
// Asura_Matchmaking_System.h
//
// Copyright (c) 2009 Rebellion. All rights reserved.
//
// Description:
//
//     system which sits on top of Asura_Lobby_System to deal with automatic
//     skill-based matchmaking.
//     See Wiki documentation:
//     http://asurawiki.rebellion.co.uk/twiki/bin/view/Programming/MatchmakingAlgorithm
//
//////////////////////////////////////////////////////////////////////////////

// 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_MATCHMAKING_SYSTEM_H___
#define ASURA_MATCHMAKING_SYSTEM_H___ 1

#ifdef ASURA_SUPPORT_LOBBY

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////
class Asura_Lobby_SearchCriterion;
class Asura_Lobby_Attributes;

// C O N S T A N T S /////////////////////////////////////////////////////////
#ifdef ASURA_USE_MERIT_SYSTEM
static const u_int uASURA_MATCHMAKING_NUM_SKILLTHRESHOLDS = 4;
#else
// if not using merit system, must be using TrueSkill and in that case the 
// searches already sort by difference in skill.
static const u_int uASURA_MATCHMAKING_NUM_SKILLTHRESHOLDS = 1;
#endif

// E N U M S /////////////////////////////////////////////////////////////////
enum ASURA_MATCHMAKING_STATE
{
	ASURA_MATCHMAKING_STATE_NONE,
	ASURA_MATCHMAKING_STATE_SEARCHING,
	ASURA_MATCHMAKING_STATE_CONNECTING,
	ASURA_MATCHMAKING_STATE_HOSTING,
	ASURA_MATCHMAKING_STATE_CHOOSING,
	ASURA_MATCHMAKING_STATE_CREATING,
	ASURA_MATCHMAKING_STATE_WAITING,
	ASURA_MATCHMAKING_STATE_FINISHED,
	ASURA_MATCHMAKING_STATE_ERROR
};

// C L A S S E S /////////////////////////////////////////////////////////////
class Asura_Matchmaking_System
{
public:

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

	static bool IsIdle() { return s_eMatchmakingState == ASURA_MATCHMAKING_STATE_NONE; }
	static bool IsInitialised() { return s_bInitialised; }
	static bool IsFinished() { return s_eMatchmakingState == ASURA_MATCHMAKING_STATE_FINISHED; }
	static bool HasErrorOccurred() { return s_eMatchmakingState == ASURA_MATCHMAKING_STATE_ERROR; }

	static bool IsWaitingForPlayers();
	static u_int GetNumPlayersBeingWaitedFor();

	// Start() begins the matchmaking process (must be idle first)
	static void Start();
	// Cancel() aborts it
	static void Cancel();

	// ForceGameStart() allows one player (the host) to start the game even without 
	// sufficient players. This is required during development and for friendly games.
	static void ForceGameStart();

	static float GetMatchQuality( const Asura_Lobby_Attributes& xAttributes ) { return ComputeMatchQuality( xAttributes ); }
	static int GetLobbyAverageSkill( const Asura_Lobby_Attributes& xAttributes ) { return Project_GetLobbySkill( xAttributes ); }

private:

	// console cmd functions
	static bool ConsoleCmd_BeginAutomatch( bool bHelp );
	static bool ConsoleCmd_CancelAutomatch( bool bHelp );
	static bool ConsoleCmd_ForceAutomatchStart( bool bHelp );
	static bool ConsoleCmd_IsAutomatchComplete( bool bHelp );
	static bool ConsoleCmd_PrintAutomatchStatus( bool bHelp );
	// Project_PrintAutomatchStatusToConsole(); should output some localised text 
	// to the command console. This is used to display progress to the player.
	// e.g. "Waiting for %u more players".
	static void Project_PrintAutomatchStatusToConsole();

	// state machine functions
	static void ChangeState(ASURA_MATCHMAKING_STATE eState);
	static void EnterState_Searching();
	static void ExecuteState_Searching();

	static void EnterState_Connecting();
	static void ExecuteState_Connecting();

	static void EnterState_Hosting();
	static void ExecuteState_Hosting();

	static void EnterState_Choosing();
	static void ExecuteState_Choosing();

	static void EnterState_Creating();
	static void ExecuteState_Creating();

	static void EnterState_Waiting();
	static void ExecuteState_Waiting();

	static void EnterState_Finished();

	// utility functions
	static void PerformSearch();
	static u_int PickBestLobby();
	static float ComputeMatchQuality( u_int uLobbyIndex );
	static float ComputeMatchQuality( const Asura_Lobby_Attributes& xAttributes );
	static bool TimeLimitExpired( u_int uStartTime, u_int uTimeLimit );
	static void ReduceMatchQualityThreshold();
	static u_int GetSufficientPlayers();
	static bool LobbyHasSufficientPlayers();

	// functions that must be supplied by project-specific code.
	//
	// Project_SetSearchFilters():
	// The skill threshold is used to set min skill and max skill for returned results, 
	// where min/max skill = playerskill +/- uSkillThreshold. the number of filters set
	// should be returned in uNumFilters, and must be <= ASURA_LOBBY_MAX_SEARCH_CRITERIA.
	static void Project_SetSearchFilters( u_int uSkillThreshold, Asura_Lobby_SearchCriterion* axFilters, u_int& uNumFilters );
	// Project_GetLobbySkill():
	// Just extract the lobby attribute specified by the project which represents skill.
	static int Project_GetLobbySkill( const Asura_Lobby_Attributes& xAttributes );
	// 
	// Project_CreateLobby():
	// must call Asura_Lobby_System::CreateLobby() with suitable parameters.
	static void Project_CreateLobby();

	// static class data
	static bool s_bInitialised;
	static ASURA_MATCHMAKING_STATE s_eMatchmakingState;
	static u_int s_uSkillThreshold;			// if applicable, allowed difference between lobbyskill and player skill.
	static u_int s_uCurrentSkillthresholdIndex;
	static u_int s_uBestLobby;				// index into lobby system search results
	static u_int s_uHostingStartTimestamp;	// MsecsSinceStart when we started hosting
	static u_int s_uHostingStateEnterTimestamp;	// MsecsSinceStart when we entered the hosting state
	static u_int s_uSearchResultsTimestamp;	// MsecsSinceStart when we received the search results
	static float s_fMatchQualityThreshold;	// current minimum allowed MatchQuality.
	static bool* s_pbLobbyAttempted;		// array of bools indicating which lobby system search results we've tried to connect to.
	static bool s_bConnectingStateHasJoinedLobby; // Connection state has called JoinLobby() on the lobby system, will be false while waiting for lobby system to finish important states (like Terminate)

	// static class data representing data which should be configurable per
	// project and ideally intialised using data from online storage e.g. TSS
	// on PS3 and TMS on 360
	static u_int s_uMinPlayers;						// min players allowed in order to start a match
	static u_int s_uMaxPlayers;						// max players allowed in a match
	static float s_fInitialMatchQualityThreshold;	// e.g. 0.9f
	static float s_fMatchQualityThresholdIncrement;	// e.g. 0.1f - each iteration reduces s_fMatchQualityThreshold by this amount.
	// array of increasing skill thresholds: if nothing turns up with the first search, retry with next larest value etc.
	// e.g. if using 3 and your skill is 4 you get games between 1 and 7
	static u_int s_auSkillThresholds[uASURA_MATCHMAKING_NUM_SKILLTHRESHOLDS];
	static u_int s_uSearchResultsStaleTime;			// e.g. 10000 msecs - search results trashed after this time
	static u_int s_uTimeToWaitForSufficientPlayers;	// e.g. 120000 msecs - after this time, host searches for another game to join
	static float s_fMaxPlayersTime;					// e.g. 30.0f - time spent waiting for there to be s_uMaxPlayers, then lerp until...
	static float s_fMinPlayersTime;					// e.g. 30.0f - time at end of time spent waiting for only s_uMinPlayers.
};

#endif // #ifdef ASURA_SUPPORT_LOBBY

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

#endif // #ifndef ASURA_MATCHMAKING_SYSTEM_H___
