//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements all the functions exported by the GameUI dll
//
// $NoKeywords: $
//===========================================================================//

#ifdef WIN32
#if !defined( _X360 )
#include <windows.h>
#endif
#include <io.h>
#include <direct.h>
#elif defined( POSIX )
#include <sys/time.h>
#else
#error
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <tier0/dbg.h>

#ifdef SendMessage
#undef SendMessage
#endif
																
#include "filesystem.h"
#include "GameUI_Interface.h"
#include "Sys_Utils.h"
#include "string.h"
#include "tier0/icommandline.h"

// interface to engine
#include "EngineInterface.h"

#include "replay/ienginereplay.h"
#include "replay/ireplaysystem.h"

#include "VGuiSystemModuleLoader.h"
#include "bitmap/tgaloader.h"

#include "GameConsole.h"
#include "LoadingDialog.h"
#include "CDKeyEntryDialog.h"
#include "ModInfo.h"
#include "game/client/IGameClientExports.h"
#include "materialsystem/imaterialsystem.h"
#include "engine/imatchmaking.h"
#include "ixboxsystem.h"
#include "iachievementmgr.h"
#include "IGameUIFuncs.h"
#include <ienginevgui.h>
#include "steam/steam_api.h"
#include "BonusMapsDatabase.h"
#include "BonusMapsDialog.h"
#include "sourcevr/isourcevirtualreality.h"

// vgui2 interface
// note that GameUI project uses ..\vgui2\include, not ..\utils\vgui\include
#include "BasePanel.h"

#include <vgui/Cursor.h>
#include <KeyValues.h>
#include <vgui/ILocalize.h>
#include <vgui/IPanel.h>
#include <vgui/IScheme.h>
#include <vgui/IVGui.h>
#include <vgui/ISystem.h>
#include <vgui/ISurface.h>
#include <vgui_controls/Menu.h>
#include <vgui_controls/PHandle.h>
#include "tier3/tier3.h"
#include "tier0/vcrmode.h"
#include "matsys_controls/matsyscontrols.h"
#include "steam/steam_api.h"

#if defined( _X360 )
#include "xbox/xbox_win32stubs.h"
#endif

#include "tier0/dbg.h"
#include "engine/IEngineSound.h"

// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>

IGameUIFuncs *gameuifuncs = NULL;
IEngineVGui *enginevguifuncs = NULL;
IMatchmaking *matchmaking = NULL;
IXboxSystem *xboxsystem = NULL;		// 360 only
vgui::ISurface *enginesurfacefuncs = NULL;
IVEngineClient *engine = NULL;
IEngineSound *enginesound = NULL;
IAchievementMgr *achievementmgr = NULL;
IEngineClientReplay *g_pEngineClientReplay = NULL;
ISourceVirtualReality *g_pSourceVR = NULL;

static CSteamAPIContext g_SteamAPIContext;
CSteamAPIContext *steamapicontext = &g_SteamAPIContext;

static CBasePanel *staticPanel = NULL;

class CGameUI;
CGameUI *g_pGameUI = NULL;

class CLoadingDialog;
vgui::DHANDLE<CLoadingDialog> g_hLoadingDialog;
vgui::VPANEL g_hLoadingBackgroundDialog = NULL;

static CGameUI g_GameUI;
static WHANDLE g_hMutex = NULL;
static WHANDLE g_hWaitMutex = NULL;

static IGameClientExports *g_pGameClientExports = NULL;
IGameClientExports *GameClientExports()
{
	return g_pGameClientExports;
}

//-----------------------------------------------------------------------------
// Purpose: singleton accessor
//-----------------------------------------------------------------------------
CGameUI &GameUI()
{
	return g_GameUI;
}

EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameUI, IGameUI, GAMEUI_INTERFACE_VERSION, g_GameUI);

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CGameUI::CGameUI()
{
	g_pGameUI = this;
	m_bTryingToLoadFriends = false;
	m_iFriendsLoadPauseFrames = 0;
	m_iGameIP = 0;
	m_iGameConnectionPort = 0;
	m_iGameQueryPort = 0;
	m_bActivatedUI = false;
	m_szPreviousStatusText[0] = 0;
	m_bIsConsoleUI = false;
	m_bHasSavedThisMenuSession = false;
	m_bOpenProgressOnStart = false;
}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CGameUI::~CGameUI()
{
	g_pGameUI = NULL;
}


//-----------------------------------------------------------------------------
// Purpose: Initialization
//-----------------------------------------------------------------------------
void CGameUI::Initialize( CreateInterfaceFn factory )
{
	ConnectTier1Libraries( &factory, 1 );
	ConnectTier2Libraries( &factory, 1 );
	ConVar_Register( FCVAR_CLIENTDLL );
	ConnectTier3Libraries( &factory, 1 );

	enginesound = (IEngineSound *)factory(IENGINESOUND_CLIENT_INTERFACE_VERSION, NULL);
	engine = (IVEngineClient *)factory( VENGINE_CLIENT_INTERFACE_VERSION, NULL );

	steamapicontext->Init();

	ConVarRef var( "gameui_xbox" );
	m_bIsConsoleUI = var.IsValid() && var.GetBool();

	vgui::VGui_InitInterfacesList( "GameUI", &factory, 1 );
	vgui::VGui_InitMatSysInterfacesList( "GameUI", &factory, 1 );

	// load localization file
	g_pVGuiLocalize->AddFile( "Resource/gameui_%language%.txt", "GAME", true );

	// load mod info
	ModInfo().LoadCurrentGameInfo();

	// load localization file for kb_act.lst
	g_pVGuiLocalize->AddFile( "Resource/valve_%language%.txt", "GAME", true );

	enginevguifuncs = (IEngineVGui *)factory( VENGINE_VGUI_VERSION, NULL );
	enginesurfacefuncs = (vgui::ISurface *)factory(VGUI_SURFACE_INTERFACE_VERSION, NULL);
	gameuifuncs = (IGameUIFuncs *)factory( VENGINE_GAMEUIFUNCS_VERSION, NULL );
	matchmaking = (IMatchmaking *)factory( VENGINE_MATCHMAKING_VERSION, NULL );
	xboxsystem = (IXboxSystem *)factory( XBOXSYSTEM_INTERFACE_VERSION, NULL );
	g_pEngineClientReplay = (IEngineClientReplay *)factory( ENGINE_REPLAY_CLIENT_INTERFACE_VERSION, NULL );

	if ( ModInfo().SupportsVR() && CommandLine()->CheckParm( "-vr" ) )
	{
		g_pSourceVR = (ISourceVirtualReality *)factory( SOURCE_VIRTUAL_REALITY_INTERFACE_VERSION, NULL );
	}

	// NOTE: g_pEngineReplay intentionally not checked here
	if ( !enginesurfacefuncs || !gameuifuncs || !enginevguifuncs || !xboxsystem || (IsX360() && !matchmaking) )
	{
		Error( "CGameUI::Initialize() failed to get necessary interfaces\n" );
	}

	vgui::VPANEL rootpanel = enginevguifuncs->GetPanel( PANEL_GAMEUIDLL );

	// setup base panel
	staticPanel = new CBasePanel();
	staticPanel->SetBounds(0, 0, 400, 300 );
	staticPanel->SetPaintBorderEnabled( false );
	staticPanel->SetPaintBackgroundEnabled( true );
	staticPanel->SetPaintEnabled( false );
	staticPanel->SetVisible( true );
	staticPanel->SetMouseInputEnabled( false );
	staticPanel->SetKeyBoardInputEnabled( false );
	staticPanel->SetParent(rootpanel);
}

void CGameUI::PostInit()
{
	if ( IsX360() )
	{
		enginesound->PrecacheSound( "UI/buttonrollover.wav", true, true );
		enginesound->PrecacheSound( "UI/buttonclick.wav", true, true );
		enginesound->PrecacheSound( "UI/buttonclickrelease.wav", true, true );
		enginesound->PrecacheSound( "player/suit_denydevice.wav", true, true );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Sets the specified panel as the background panel for the loading
//		dialog.  If NULL, default background is used.  If you set a panel,
//		it should be full-screen with an opaque background, and must be a VGUI popup.
//-----------------------------------------------------------------------------
void CGameUI::SetLoadingBackgroundDialog( vgui::VPANEL panel )
{
	g_hLoadingBackgroundDialog = panel;
}

void CGameUI::BonusMapUnlock( const char *pchFileName, const char *pchMapName )
{
	if ( !pchFileName || pchFileName[ 0 ] == '\0' || 
		 !pchMapName || pchMapName[ 0 ] == '\0' )
	{
		if ( !g_pBonusMapsDialog )
			return;

		g_pBonusMapsDialog->SetSelectedBooleanStatus( "lock", false );
		return;
	}

	if ( BonusMapsDatabase()->SetBooleanStatus( "lock", pchFileName, pchMapName, false ) )
	{
		BonusMapsDatabase()->RefreshMapData();

		if ( !g_pBonusMapsDialog )
		{
			// It unlocked without the bonus maps menu open, so flash the menu item
			CBasePanel *pBasePanel = BasePanel();
			if ( pBasePanel )
			{
				if ( GameUI().IsConsoleUI() )
				{
					if ( Q_stricmp( pchFileName, "scripts/advanced_chambers" ) == 0 )
					{
						pBasePanel->SetMenuItemBlinkingState( "OpenNewGameDialog", true );
					}
				}
				else
				{
					pBasePanel->SetMenuItemBlinkingState( "OpenBonusMapsDialog", true );
				}
			}

			BonusMapsDatabase()->SetBlink( true );
		}
		else
			g_pBonusMapsDialog->RefreshData();	// Update the open dialog
	}
}

void CGameUI::BonusMapComplete( const char *pchFileName, const char *pchMapName )
{
	if ( !pchFileName || pchFileName[ 0 ] == '\0' || 
		 !pchMapName || pchMapName[ 0 ] == '\0' )
	{
		if ( !g_pBonusMapsDialog )
			return;

		g_pBonusMapsDialog->SetSelectedBooleanStatus( "complete", true );
		BonusMapsDatabase()->RefreshMapData();
		g_pBonusMapsDialog->RefreshData();
		return;
	}

	if ( BonusMapsDatabase()->SetBooleanStatus( "complete", pchFileName, pchMapName, true ) )
	{
		BonusMapsDatabase()->RefreshMapData();

		// Update the open dialog
		if ( g_pBonusMapsDialog )
			g_pBonusMapsDialog->RefreshData();
	}
}

void CGameUI::BonusMapChallengeUpdate( const char *pchFileName, const char *pchMapName, const char *pchChallengeName, int iBest )
{
	if ( !pchFileName || pchFileName[ 0 ] == '\0' || 
		 !pchMapName || pchMapName[ 0 ] == '\0' || 
		 !pchChallengeName || pchChallengeName[ 0 ] == '\0' )
	{
		return;
	}
	else
	{
		if ( BonusMapsDatabase()->UpdateChallengeBest( pchFileName, pchMapName, pchChallengeName, iBest ) )
		{
			// The challenge best changed, so write it to the file
			BonusMapsDatabase()->WriteSaveData();
			BonusMapsDatabase()->RefreshMapData();

			// Update the open dialog
			if ( g_pBonusMapsDialog )
				g_pBonusMapsDialog->RefreshData();
		}
	}
}

void CGameUI::BonusMapChallengeNames( char *pchFileName, char *pchMapName, char *pchChallengeName )
{
	if ( !pchFileName || !pchMapName || !pchChallengeName )
		return;

	BonusMapsDatabase()->GetCurrentChallengeNames( pchFileName, pchMapName, pchChallengeName );
}

void CGameUI::BonusMapChallengeObjectives( int &iBronze, int &iSilver, int &iGold )
{
	BonusMapsDatabase()->GetCurrentChallengeObjectives( iBronze, iSilver, iGold );
}

void CGameUI::BonusMapDatabaseSave( void )
{
	BonusMapsDatabase()->WriteSaveData();
}

int CGameUI::BonusMapNumAdvancedCompleted( void )
{
	return BonusMapsDatabase()->NumAdvancedComplete();
}

void CGameUI::BonusMapNumMedals( int piNumMedals[ 3 ] )
{
	BonusMapsDatabase()->NumMedals( piNumMedals );
}

//-----------------------------------------------------------------------------
// Purpose: connects to client interfaces
//-----------------------------------------------------------------------------
void CGameUI::Connect( CreateInterfaceFn gameFactory )
{
	g_pGameClientExports = (IGameClientExports *)gameFactory(GAMECLIENTEXPORTS_INTERFACE_VERSION, NULL);

	achievementmgr = engine->GetAchievementMgr();

	if (!g_pGameClientExports)
	{
		Error("CGameUI::Initialize() failed to get necessary interfaces\n");
	}

	m_GameFactory = gameFactory;
}

//-----------------------------------------------------------------------------
// Purpose: Callback function; sends platform Shutdown message to specified window
//-----------------------------------------------------------------------------
int __stdcall SendShutdownMsgFunc(WHANDLE hwnd, int lparam)
{
	Sys_PostMessage(hwnd, Sys_RegisterWindowMessage("ShutdownValvePlatform"), 0, 1);
	return 1;
}

//-----------------------------------------------------------------------------
// Purpose: Searches for GameStartup*.mp3 files in the sound/ui folder and plays one
//-----------------------------------------------------------------------------
void CGameUI::PlayGameStartupSound()
{
	if ( IsX360() )
		return;

	if ( CommandLine()->FindParm( "-nostartupsound" ) )
		return;

	FileFindHandle_t fh;

	CUtlVector<char *> fileNames;
	char path[ 512 ];

	bool bHolidayFound = false;

	// only want to run the holiday check for TF2
	const char *pGameName = CommandLine()->ParmValue( "-game", "hl2" );
	if ( ( Q_stricmp( pGameName, "tf" ) == 0 ) || ( Q_stricmp( pGameName, "tf_beta" ) == 0 ) )
	{
		// check for a holiday sound file
		const char *pszHoliday = NULL;
	
		if ( GameClientExports() )
		{
			pszHoliday = GameClientExports()->GetHolidayString();
			if ( pszHoliday && pszHoliday[0] )
			{
				Q_snprintf( path, sizeof( path ), "sound/ui/holiday/gamestartup_%s*.mp3", pszHoliday );
				Q_FixSlashes( path );

				char const *fn = g_pFullFileSystem->FindFirstEx( path, "MOD", &fh );
				{
					if ( fn )
					{
						bHolidayFound = true;
					}
				}
			}
		}
	}

	// only want to do this if we haven't found a holiday file
	if ( !bHolidayFound )
	{
		Q_snprintf( path, sizeof( path ), "sound/ui/gamestartup*.mp3" );
		Q_FixSlashes( path );
	}

	char const *fn = g_pFullFileSystem->FindFirstEx( path, "MOD", &fh );
	if ( fn )
	{
		do
		{
			char ext[ 10 ];
			Q_ExtractFileExtension( fn, ext, sizeof( ext ) );

			if ( !Q_stricmp( ext, "mp3" ) )
			{
				char temp[ 512 ];
				if ( bHolidayFound )
				{
					Q_snprintf( temp, sizeof( temp ), "ui/holiday/%s", fn );
				}
				else
				{
					Q_snprintf( temp, sizeof( temp ), "ui/%s", fn );
				}

				char *found = new char[ strlen( temp ) + 1 ];
				Q_strncpy( found, temp, strlen( temp ) + 1 );

				Q_FixSlashes( found );
				fileNames.AddToTail( found );
			}
	
			fn = g_pFullFileSystem->FindNext( fh );

		} while ( fn );

		g_pFullFileSystem->FindClose( fh );
	}

	// did we find any?
	if ( fileNames.Count() > 0 )
	{
#ifdef WIN32
		SYSTEMTIME SystemTime;
		GetSystemTime( &SystemTime );
		int index = SystemTime.wMilliseconds % fileNames.Count();
#else
		struct timeval tm;
		gettimeofday( &tm, NULL );
		int index = tm.tv_usec/1000 % fileNames.Count();
#endif

		if ( fileNames.IsValidIndex( index ) && fileNames[index] )
		{
			// Play the Saxxy music if we're in saxxy mode.
#if defined( SAXXYMAINMENU_ENABLED )
			bool bIsTF = false;
			const char *pGameDir = engine->GetGameDirectory();
			if ( pGameDir )
			{
				// Is the game TF?
				const int nStrLen = V_strlen( pGameDir );
				bIsTF = nStrLen
					&& nStrLen >= 2 &&
					pGameDir[nStrLen-2] == 't' &&
					pGameDir[nStrLen-1] == 'f';
			}

			// escape chars "*#" make it stream, and be affected by snd_musicvolume
			const char *pSoundFile = bIsTF ? "ui/holiday/gamestartup_saxxy.mp3" : fileNames[index];
#else
			const char *pSoundFile = fileNames[index];
#endif

			char found[ 512 ];
			Q_snprintf( found, sizeof( found ), "play *#%s", pSoundFile );

			engine->ClientCmd_Unrestricted( found );
		}

		fileNames.PurgeAndDeleteElements();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Called to setup the game UI
//-----------------------------------------------------------------------------
void CGameUI::Start()
{
	// determine Steam location for configuration
	if ( !FindPlatformDirectory( m_szPlatformDir, sizeof( m_szPlatformDir ) ) )
		return;

	if ( IsPC() )
	{
		// setup config file directory
		char szConfigDir[512];
		Q_strncpy( szConfigDir, m_szPlatformDir, sizeof( szConfigDir ) );
		Q_strncat( szConfigDir, "config", sizeof( szConfigDir ), COPY_ALL_CHARACTERS );

		Msg( "Steam config directory: %s\n", szConfigDir );

		g_pFullFileSystem->AddSearchPath(szConfigDir, "CONFIG");
		g_pFullFileSystem->CreateDirHierarchy("", "CONFIG");

		// user dialog configuration
		vgui::system()->SetUserConfigFile("InGameDialogConfig.vdf", "CONFIG");

		g_pFullFileSystem->AddSearchPath( "platform", "PLATFORM" );
	}

	// localization
	g_pVGuiLocalize->AddFile( "Resource/platform_%language%.txt");
	g_pVGuiLocalize->AddFile( "Resource/vgui_%language%.txt");

	Sys_SetLastError( SYS_NO_ERROR );

	if ( IsPC() )
	{
		if ( !IsPosix() )
		{
			// Alfred says this is really, really old code that does some wacky crap that only
			//  happened in the first version of HL and it's the only game that does this and
			//  it was a steam testing type thing and we don't need to do it on Posix, etc.

			g_hMutex = Sys_CreateMutex( "ValvePlatformUIMutex" );
			g_hWaitMutex = Sys_CreateMutex( "ValvePlatformWaitMutex" );
			if ( g_hMutex == 0 || g_hWaitMutex == 0 || Sys_GetLastError() == SYS_ERROR_INVALID_HANDLE )
			{
				// error, can't get handle to mutex
				if (g_hMutex)
				{
					Sys_ReleaseMutex(g_hMutex);
				}
				if (g_hWaitMutex)
				{
					Sys_ReleaseMutex(g_hWaitMutex);
				}
				g_hMutex = NULL;
				g_hWaitMutex = NULL;
				Error("Steam Error: Could not access Steam, bad mutex\n");
				return;
			}
			unsigned int waitResult = Sys_WaitForSingleObject(g_hMutex, 0);
			if (!(waitResult == SYS_WAIT_OBJECT_0 || waitResult == SYS_WAIT_ABANDONED))
			{
				// mutex locked, need to deactivate Steam (so we have the Friends/ServerBrowser data files)
				// get the wait mutex, so that Steam.exe knows that we're trying to acquire ValveTrackerMutex
				waitResult = Sys_WaitForSingleObject(g_hWaitMutex, 0);
#ifdef WIN32
				if (waitResult == SYS_WAIT_OBJECT_0 || waitResult == SYS_WAIT_ABANDONED)
				{
					Sys_EnumWindows(SendShutdownMsgFunc, 1);
				}
#endif
			}
		}
			
		// Delay playing the startup music until the first frame
		m_bPlayGameStartupSound = true;

		// now we are set up to check every frame to see if we can friends/server browser
		m_bTryingToLoadFriends = true;
		m_iFriendsLoadPauseFrames = 1;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Validates the user has a cdkey in the registry
//-----------------------------------------------------------------------------
void CGameUI::ValidateCDKey()
{
	// this check is disabled, since we have no plans for an offline version of hl2
#if 0
	//!! hack, write out a regkey for now so developers don't have to type it in
	//!! undo this before release
	vgui::system()->SetRegistryString("HKEY_CURRENT_USER\\Software\\Valve\\Source\\Settings\\EncryptedCDKey", "QOgi:JXrJj<Eb8abkESf4Pg;OfofJwDzRsyH>AdjtyPnV[FB");

	// see what's in the registry
	if (!CCDKeyEntryDialog::IsValidWeakCDKeyInRegistry())
	{
		m_hCDKeyEntryDialog = new CCDKeyEntryDialog(NULL, false);
		m_hCDKeyEntryDialog->Activate();
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Finds which directory the platform resides in
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CGameUI::FindPlatformDirectory(char *platformDir, int bufferSize)
{
	platformDir[0] = '\0';

	if ( platformDir[0] == '\0' )
	{
		// we're not under steam, so setup using path relative to game
		if ( IsPC() )
		{
#ifdef WIN32
			if ( ::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), platformDir, bufferSize ) )
			{
				char *lastslash = strrchr(platformDir, '\\'); // this should be just before the filename
				if ( lastslash )
				{
					*lastslash = 0;
					Q_strncat(platformDir, "\\platform\\", bufferSize, COPY_ALL_CHARACTERS );
					return true;
				}
			}
#else
			if ( getcwd( platformDir, bufferSize ) )
			{
				V_AppendSlash( platformDir, bufferSize );
				Q_strncat(platformDir, "platform", bufferSize, COPY_ALL_CHARACTERS );
				V_AppendSlash( platformDir, bufferSize );
				return true;
			}
#endif			
		}
		else
		{
			// xbox fetches the platform path from exisiting platform search path
			// path to executeable is not correct for xbox remote configuration
			if ( g_pFullFileSystem->GetSearchPath( "PLATFORM", false, platformDir, bufferSize ) )
			{
				char *pSeperator = strchr( platformDir, ';' );
				if ( pSeperator )
					*pSeperator = '\0';
				return true;
			}
		}

		Error( "Unable to determine platform directory\n" );
		return false;
	}

	return (platformDir[0] != 0);
}

//-----------------------------------------------------------------------------
// Purpose: Called to Shutdown the game UI system
//-----------------------------------------------------------------------------
void CGameUI::Shutdown()
{
	// notify all the modules of Shutdown
	g_VModuleLoader.ShutdownPlatformModules();

	// unload the modules them from memory
	g_VModuleLoader.UnloadPlatformModules();

	ModInfo().FreeModInfo();
	
	// release platform mutex
	// close the mutex
	if (g_hMutex)
	{
		Sys_ReleaseMutex(g_hMutex);
	}
	if (g_hWaitMutex)
	{
		Sys_ReleaseMutex(g_hWaitMutex);
	}
	
	BonusMapsDatabase()->WriteSaveData();

	steamapicontext->Clear();
	

	ConVar_Unregister();
	DisconnectTier3Libraries();
	DisconnectTier2Libraries();
	DisconnectTier1Libraries();
}

//-----------------------------------------------------------------------------
// Purpose: just wraps an engine call to activate the gameUI
//-----------------------------------------------------------------------------
void CGameUI::ActivateGameUI()
{
	engine->ExecuteClientCmd("gameui_activate");
}

//-----------------------------------------------------------------------------
// Purpose: just wraps an engine call to hide the gameUI
//-----------------------------------------------------------------------------
void CGameUI::HideGameUI()
{
	engine->ExecuteClientCmd("gameui_hide");
}

//-----------------------------------------------------------------------------
// Purpose: Toggle allowing the engine to hide the game UI with the escape key
//-----------------------------------------------------------------------------
void CGameUI::PreventEngineHideGameUI()
{
	engine->ExecuteClientCmd("gameui_preventescape");
}

//-----------------------------------------------------------------------------
// Purpose: Toggle allowing the engine to hide the game UI with the escape key
//-----------------------------------------------------------------------------
void CGameUI::AllowEngineHideGameUI()
{
	engine->ExecuteClientCmd("gameui_allowescape");
}

//-----------------------------------------------------------------------------
// Purpose: Activate the game UI
//-----------------------------------------------------------------------------
void CGameUI::OnGameUIActivated()
{
	m_bActivatedUI = true;

	// hide/show the main panel to Activate all game ui
	staticPanel->SetVisible( true );

	// pause the server in single player
	if ( engine->GetMaxClients() <= 1 )
	{
		engine->ClientCmd_Unrestricted( "setpause" );
	}

	SetSavedThisMenuSession( false );

	// notify taskbar
	BasePanel()->OnGameUIActivated();

	if ( GameClientExports() )
	{
		const char *pGameName = CommandLine()->ParmValue( "-game", "hl2" );
		// only want to run this for TF2
		if ( ( Q_stricmp( pGameName, "tf" ) == 0 ) || ( Q_stricmp( pGameName, "tf_beta" ) == 0 ) )
		{
			GameClientExports()->OnGameUIActivated();
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Hides the game ui, in whatever state it's in
//-----------------------------------------------------------------------------
void CGameUI::OnGameUIHidden()
{
	if ( GameClientExports() )
	{
		const char *pGameName = CommandLine()->ParmValue( "-game", "hl2" );
		// only want to run this for TF2
		if ( ( Q_stricmp( pGameName, "tf" ) == 0 ) || ( Q_stricmp( pGameName, "tf_beta" ) == 0 ) )
		{
			GameClientExports()->OnGameUIHidden();
		}
	}

	// unpause the game when leaving the UI
	if ( engine->GetMaxClients() <= 1 )
	{
		engine->ClientCmd_Unrestricted("unpause");
	}

	BasePanel()->OnGameUIHidden();
}

//-----------------------------------------------------------------------------
// Purpose: paints all the vgui elements
//-----------------------------------------------------------------------------
void CGameUI::RunFrame()
{
	if ( IsX360() && m_bOpenProgressOnStart )
	{
		StartProgressBar();
		m_bOpenProgressOnStart = false;
	}

	// resize the background panel to the screen size
	int wide, tall;
	vgui::surface()->GetScreenSize(wide, tall);
	staticPanel->SetSize(wide,tall);

	// Run frames
	g_VModuleLoader.RunFrame();
	BasePanel()->RunFrame();

	// Play the start-up music the first time we run frame
	if ( IsPC() && m_bPlayGameStartupSound )
	{
		PlayGameStartupSound();
		m_bPlayGameStartupSound = false;
	}

	if ( IsPC() && ( ( IsPosix() && m_bTryingToLoadFriends ) || 
					( m_bTryingToLoadFriends && m_iFriendsLoadPauseFrames-- < 1 && g_hMutex && g_hWaitMutex ) ) )
	{
		// try and load Steam platform files
		unsigned int waitResult = Sys_WaitForSingleObject(g_hMutex, 0);
		if ( IsPosix() || ( waitResult == SYS_WAIT_OBJECT_0 || waitResult == SYS_WAIT_ABANDONED ))
		{
			// we got the mutex, so load Friends/Serverbrowser
			// clear the loading flag
			m_bTryingToLoadFriends = false;
			g_VModuleLoader.LoadPlatformModules(&m_GameFactory, 1, false);

			// release the wait mutex
			if ( !IsPosix() )
				Sys_ReleaseMutex(g_hWaitMutex);

			// notify the game of our game name
			const char *fullGamePath = engine->GetGameDirectory();
			const char *pathSep = strrchr( fullGamePath, '/' );
			if ( !pathSep )
			{
				pathSep = strrchr( fullGamePath, '\\' );
			}
			if ( pathSep )
			{
				KeyValues *pKV = new KeyValues("ActiveGameName" );
				pKV->SetString( "name", pathSep + 1 );
				pKV->SetInt( "appid", engine->GetAppID() );
				KeyValues *modinfo = new KeyValues("ModInfo");
				if ( modinfo->LoadFromFile( g_pFullFileSystem, "gameinfo.txt" ) )
				{
					pKV->SetString( "game", modinfo->GetString( "game", "" ) );
				}
				modinfo->deleteThis();
				
				g_VModuleLoader.PostMessageToAllModules( pKV );
			}

			// notify the ui of a game connect if we're already in a game
			if (m_iGameIP)
			{
				SendConnectedToGameMessage();
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Called when the game connects to a server
//-----------------------------------------------------------------------------
void CGameUI::OLD_OnConnectToServer(const char *game, int IP, int port)
{
	// Nobody should use this anymore because the query port and the connection port can be different.
	// Use OnConnectToServer2 instead.
	Assert( false );
	OnConnectToServer2( game, IP, port, port );
}

//-----------------------------------------------------------------------------
// Purpose: Called when the game connects to a server
//-----------------------------------------------------------------------------
void CGameUI::OnConnectToServer2(const char *game, int IP, int connectionPort, int queryPort)
{
	m_iGameIP = IP;
	m_iGameConnectionPort = connectionPort;
	m_iGameQueryPort = queryPort;

	SendConnectedToGameMessage();
}


void CGameUI::SendConnectedToGameMessage()
{
	KeyValues *kv = new KeyValues( "ConnectedToGame" );
	kv->SetInt( "ip", m_iGameIP );
	kv->SetInt( "connectionport", m_iGameConnectionPort );
	kv->SetInt( "queryport", m_iGameQueryPort );

	g_VModuleLoader.PostMessageToAllModules( kv );
}

//-----------------------------------------------------------------------------
// Purpose: Called when the game disconnects from a server
//-----------------------------------------------------------------------------
void CGameUI::OnDisconnectFromServer( uint8 eSteamLoginFailure )
{
	m_iGameIP = 0;
	m_iGameConnectionPort = 0;
	m_iGameQueryPort = 0;

	g_VModuleLoader.PostMessageToAllModules(new KeyValues("DisconnectedFromGame"));

	if ( eSteamLoginFailure == STEAMLOGINFAILURE_NOSTEAMLOGIN )
	{
		if ( g_hLoadingDialog )
		{
			g_hLoadingDialog->DisplayNoSteamConnectionError();
		}
	}
	else if ( eSteamLoginFailure == STEAMLOGINFAILURE_VACBANNED )
	{
		if ( g_hLoadingDialog )
		{
			g_hLoadingDialog->DisplayVACBannedError();
		}
	}
	else if ( eSteamLoginFailure == STEAMLOGINFAILURE_LOGGED_IN_ELSEWHERE )
	{
		if ( g_hLoadingDialog )
		{
			g_hLoadingDialog->DisplayLoggedInElsewhereError();
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: activates the loading dialog on level load start
//-----------------------------------------------------------------------------
void CGameUI::OnLevelLoadingStarted( bool bShowProgressDialog )
{
	g_VModuleLoader.PostMessageToAllModules( new KeyValues( "LoadingStarted" ) );

	// notify
	BasePanel()->OnLevelLoadingStarted();

	if ( bShowProgressDialog )
	{
		StartProgressBar();
	}

	// Don't play the start game sound if this happens before we get to the first frame
	m_bPlayGameStartupSound = false;
}

//-----------------------------------------------------------------------------
// Purpose: closes any level load dialog
//-----------------------------------------------------------------------------
void CGameUI::OnLevelLoadingFinished(bool bError, const char *failureReason, const char *extendedReason)
{
	StopProgressBar( bError, failureReason, extendedReason );

	// notify all the modules
	g_VModuleLoader.PostMessageToAllModules( new KeyValues( "LoadingFinished" ) );

	// hide the UI
	HideGameUI();

	// notify
	BasePanel()->OnLevelLoadingFinished();
}

//-----------------------------------------------------------------------------
// Purpose: Updates progress bar
// Output : Returns true if screen should be redrawn
//-----------------------------------------------------------------------------
bool CGameUI::UpdateProgressBar(float progress, const char *statusText)
{
	// if either the progress bar or the status text changes, redraw the screen
	bool bRedraw = false;

	if ( ContinueProgressBar( progress ) )
	{
		bRedraw = true;
	}
		
	if ( SetProgressBarStatusText( statusText ) )
	{
		bRedraw = true;
	}

	return bRedraw;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CGameUI::StartProgressBar()
{
	if ( !g_hLoadingDialog.Get() )
	{
		g_hLoadingDialog = new CLoadingDialog(staticPanel);
	}

	// open a loading dialog
	m_szPreviousStatusText[0] = 0;
	g_hLoadingDialog->SetProgressPoint(0.0f);
	g_hLoadingDialog->Open();
}

//-----------------------------------------------------------------------------
// Purpose: returns true if the screen should be updated
//-----------------------------------------------------------------------------
bool CGameUI::ContinueProgressBar( float progressFraction )
{
	if (!g_hLoadingDialog.Get())
		return false;

	g_hLoadingDialog->Activate();
	return g_hLoadingDialog->SetProgressPoint(progressFraction);
}

//-----------------------------------------------------------------------------
// Purpose: stops progress bar, displays error if necessary
//-----------------------------------------------------------------------------
void CGameUI::StopProgressBar(bool bError, const char *failureReason, const char *extendedReason)
{
	if (!g_hLoadingDialog.Get() && bError)
	{
		g_hLoadingDialog = new CLoadingDialog(staticPanel);
	}

	if (!g_hLoadingDialog.Get())
		return;

	if ( !IsX360() && bError )
	{
		// turn the dialog to error display mode
		g_hLoadingDialog->DisplayGenericError(failureReason, extendedReason);
	}
	else
	{
		// close loading dialog
		g_hLoadingDialog->Close();
		g_hLoadingDialog = NULL;
	}
	// should update the background to be in a transition here
}

//-----------------------------------------------------------------------------
// Purpose: sets loading info text
//-----------------------------------------------------------------------------
bool CGameUI::SetProgressBarStatusText(const char *statusText)
{
	if (!g_hLoadingDialog.Get())
		return false;

	if (!statusText)
		return false;

	if (!stricmp(statusText, m_szPreviousStatusText))
		return false;

	g_hLoadingDialog->SetStatusText(statusText);
	Q_strncpy(m_szPreviousStatusText, statusText, sizeof(m_szPreviousStatusText));
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CGameUI::SetSecondaryProgressBar(float progress /* range [0..1] */)
{
	if (!g_hLoadingDialog.Get())
		return;

	g_hLoadingDialog->SetSecondaryProgress(progress);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CGameUI::SetSecondaryProgressBarText(const char *statusText)
{
	if (!g_hLoadingDialog.Get())
		return;

	g_hLoadingDialog->SetSecondaryProgressText(statusText);
}

//-----------------------------------------------------------------------------
// Purpose: Returns prev settings
//-----------------------------------------------------------------------------
bool CGameUI::SetShowProgressText( bool show )
{
	if (!g_hLoadingDialog.Get())
		return false;

	return g_hLoadingDialog->SetShowProgressText( show );
}


//-----------------------------------------------------------------------------
// Purpose: returns true if we're currently playing the game
//-----------------------------------------------------------------------------
bool CGameUI::IsInLevel()
{
	const char *levelName = engine->GetLevelName();
	if (levelName && levelName[0] && !engine->IsLevelMainMenuBackground())
	{
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: returns true if we're at the main menu and a background level is loaded
//-----------------------------------------------------------------------------
bool CGameUI::IsInBackgroundLevel()
{
	const char *levelName = engine->GetLevelName();
	if (levelName && levelName[0] && engine->IsLevelMainMenuBackground())
	{
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: returns true if we're in a multiplayer game
//-----------------------------------------------------------------------------
bool CGameUI::IsInMultiplayer()
{
	return (IsInLevel() && engine->GetMaxClients() > 1);
}

//-----------------------------------------------------------------------------
// Purpose: returns true if we're playing back a replay
//-----------------------------------------------------------------------------
bool CGameUI::IsInReplay()
{
	return g_pEngineClientReplay && g_pEngineClientReplay->IsPlayingReplayDemo();
}

//-----------------------------------------------------------------------------
// Purpose: returns true if we're console ui
//-----------------------------------------------------------------------------
bool CGameUI::IsConsoleUI()
{
	return m_bIsConsoleUI;
}

//-----------------------------------------------------------------------------
// Purpose: returns true if we've saved without closing the menu
//-----------------------------------------------------------------------------
bool CGameUI::HasSavedThisMenuSession()
{
	return m_bHasSavedThisMenuSession;
}

void CGameUI::SetSavedThisMenuSession( bool bState )
{
	m_bHasSavedThisMenuSession = bState;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CGameUI::ShowNewGameDialog( int chapter )
{
	char val[32];
	Q_snprintf( val, sizeof(val), "%d", chapter);
	staticPanel->OnOpenNewGameDialog(val);
}

//-----------------------------------------------------------------------------
// Purpose: Makes the loading background dialog visible, if one has been set
//-----------------------------------------------------------------------------
void CGameUI::ShowLoadingBackgroundDialog()
{
	if ( g_hLoadingBackgroundDialog )
	{
		vgui::ipanel()->SetParent( g_hLoadingBackgroundDialog, staticPanel->GetVPanel() );
		vgui::ipanel()->PerformApplySchemeSettings( g_hLoadingBackgroundDialog );
		vgui::ipanel()->SetVisible( g_hLoadingBackgroundDialog, true );		
		vgui::ipanel()->MoveToFront( g_hLoadingBackgroundDialog );
		vgui::ipanel()->SendMessage( g_hLoadingBackgroundDialog, new KeyValues( "activate" ), staticPanel->GetVPanel() );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Hides the loading background dialog, if one has been set
//-----------------------------------------------------------------------------
void CGameUI::HideLoadingBackgroundDialog()
{
	if ( g_hLoadingBackgroundDialog )
	{
		vgui::ipanel()->SetParent( g_hLoadingBackgroundDialog, NULL );
		vgui::ipanel()->SetVisible( g_hLoadingBackgroundDialog, false );		
		vgui::ipanel()->MoveToBack( g_hLoadingBackgroundDialog );
		vgui::ipanel()->SendMessage( g_hLoadingBackgroundDialog, new KeyValues( "deactivate" ), staticPanel->GetVPanel() );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Returns whether a loading background dialog has been set
//-----------------------------------------------------------------------------
bool CGameUI::HasLoadingBackgroundDialog()
{
	return ( 0 != g_hLoadingBackgroundDialog );
}

//-----------------------------------------------------------------------------
// Purpose: Xbox 360 calls from engine to GameUI 
//-----------------------------------------------------------------------------
void CGameUI::SessionNotification( const int notification, const int param )
{
	BasePanel()->SessionNotification( notification, param );
}
void CGameUI::SystemNotification( const int notification )
{
	BasePanel()->SystemNotification( notification );
}
void CGameUI::ShowMessageDialog( const uint nType, vgui::Panel *pOwner )
{
	BasePanel()->ShowMessageDialog( nType, pOwner );
}
void CGameUI::CloseMessageDialog( const uint nType )
{
	BasePanel()->CloseMessageDialog( nType );
}
void CGameUI::UpdatePlayerInfo( uint64 nPlayerId, const char *pName, int nTeam, byte cVoiceState, int nPlayersNeeded, bool bHost )
{
	BasePanel()->UpdatePlayerInfo( nPlayerId, pName, nTeam, cVoiceState, nPlayersNeeded, bHost );
}
void CGameUI::SessionSearchResult( int searchIdx, void *pHostData, XSESSION_SEARCHRESULT *pResult, int ping )
{
	BasePanel()->SessionSearchResult( searchIdx, pHostData, pResult, ping );
}
void CGameUI::OnCreditsFinished( void )
{
	BasePanel()->OnCreditsFinished();
}
bool CGameUI::ValidateStorageDevice( int *pStorageDeviceValidated )
{
	return BasePanel()->ValidateStorageDevice( pStorageDeviceValidated );
}

void CGameUI::SetProgressOnStart()
{
	m_bOpenProgressOnStart = true;
}

void CGameUI::OnConfirmQuit( void )
{
	BasePanel()->OnOpenQuitConfirmationDialog();
}

bool CGameUI::IsMainMenuVisible( void )
{
	CBasePanel *pBasePanel = BasePanel();
	if ( pBasePanel )
		return (pBasePanel->IsVisible() && pBasePanel->GetMenuAlpha() > 0 );
	return false;
}

// Client DLL is providing us with a panel that it wants to replace the main menu with
void CGameUI::SetMainMenuOverride( vgui::VPANEL panel )
{
	CBasePanel *pBasePanel = BasePanel();
	if ( pBasePanel )
	{
		pBasePanel->SetMainMenuOverride( panel );
	}
}

// Client DLL is telling us that a main menu command was issued, probably from its custom main menu panel
void CGameUI::SendMainMenuCommand( const char *pszCommand )
{
	CBasePanel *pBasePanel = BasePanel();
	if ( pBasePanel )
	{
		pBasePanel->RunMenuCommand( pszCommand );
	}
}