//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include "cbase.h"
#include "tf_classmenu.h"
#include "IGameUIFuncs.h" // for key bindings
#include "tf_hud_notification_panel.h"
#include "character_info_panel.h"
#include "playerspawncache.h"
#include "iclientmode.h"
#include "econ_gcmessages.h"
#include "gc_clientsystem.h"
#include "vgui/IInput.h"
#include <vgui_controls/PanelListPanel.h>
#include <vgui_controls/ScrollBarSlider.h>
#include "tf_gamerules.h"
#include "engine/IEngineSound.h"
#include "inputsystem/iinputsystem.h"

#if defined( REPLAY_ENABLED )
#include "replay/iclientreplaycontext.h"
#include "replay/ireplaysystem.h"
#include "replay/ienginereplay.h"
#include "replay/shared_defs.h"
#endif

extern IGameUIFuncs *gameuifuncs; // for key binding details

using namespace vgui;

ConVar _cl_classmenuopen( "_cl_classmenuopen", "0", 0, "internal cvar used to tell server when class menu is open" );

#if defined( REPLAY_ENABLED )
ConVar replay_replaywelcomedlgcount( "replay_replaywelcomedlgcount", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE | FCVAR_HIDDEN, "The number of times the replay help dialog has displayed." );
#endif

ConVar tf_mvm_classupgradehelpcount( "tf_mvm_classupgradehelpcount", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE | FCVAR_HIDDEN, "The number of times the player upgrade help dialog has displayed." );

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CTFClassTipsItemPanel::CTFClassTipsItemPanel( Panel *parent, const char *name, int iListItemID ) : BaseClass( parent, name )
{
	m_pTipIcon = new vgui::ImagePanel( this, "TipIcon" );
	m_pTipLabel = new CExLabel( this, "TipLabel", "" );

// 	m_pTipIcon = dynamic_cast<vgui::ImagePanel*>( FindChildByName( "TipIcon" ) );
// 	m_pTipLabel = dynamic_cast<CExLabel*>( FindChildByName( "TipLabel" ) );
}

CTFClassTipsItemPanel::~CTFClassTipsItemPanel()
{
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassTipsItemPanel::SetClassTip( const wchar_t *pwszText, const char *pszIcon )
{
	// Set tf_english string and .res icon path
	if ( m_pTipLabel && m_pTipIcon )
	{
		if ( pszIcon )
		{
			m_pTipIcon->SetImage( pszIcon );
		}
		else
		{
			m_pTipIcon->SetVisible( false );
// 			int iX, iY = 0;
// 			m_pTipLabel->GetPos( iX, iY );
// 			int nIconWidth = m_pTipIcon->GetWide();
// 			m_pTipLabel->SetPos( ( iX - nIconWidth ), iY );
		}
		m_pTipLabel->SetText( pwszText );
	}

	InvalidateLayout( true, true );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassTipsItemPanel::ApplySchemeSettings( IScheme* pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	LoadControlSettings( "Resource/UI/ClassTipsItem.res" );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
class CTFClassTipsListPanel : public PanelListPanel
{
private:
	DECLARE_CLASS_SIMPLE( CTFClassTipsListPanel, PanelListPanel );

public:
	CTFClassTipsListPanel( Panel *parent, const char *panelName )
		:	PanelListPanel( parent, panelName )
	{
		m_pScrollBar = GetScrollbar();

		m_pUpArrow = new CExImageButton( this, "UpArrow", "" );
		if ( m_pUpArrow )
		{
			m_pUpArrow->AddActionSignalTarget( m_pScrollBar );
			m_pUpArrow->SetCommand(new KeyValues("ScrollButtonPressed", "index", 0));
			m_pUpArrow->GetImage()->SetShouldScaleImage( true );
			m_pUpArrow->SetFgColor( Color( 255, 255, 255, 255 ) );
			m_pUpArrow->SetAlpha( 255 );
			m_pUpArrow->SetPaintBackgroundEnabled( false );
			m_pUpArrow->SetVisible( false );
			m_pUpArrow->PassMouseTicksTo( m_pScrollBar );
			m_pUpArrow->SetImageDefault( "chalkboard_scroll_up" );
		}

		m_pDownArrow = new CExImageButton( this, "DownArrow", "" );
		if ( m_pDownArrow )
		{
			m_pDownArrow->AddActionSignalTarget( m_pScrollBar );
			m_pDownArrow->SetCommand(new KeyValues("ScrollButtonPressed", "index", 1));
			m_pDownArrow->GetImage()->SetShouldScaleImage( true );
			m_pDownArrow->SetFgColor( Color( 255, 255, 255, 255 ) );
			m_pDownArrow->SetAlpha( 255 );
			m_pDownArrow->SetPaintBackgroundEnabled( false );
			m_pDownArrow->SetVisible( false );
			m_pDownArrow->PassMouseTicksTo( m_pScrollBar );
			m_pDownArrow->SetImageDefault( "chalkboard_scroll_down" );
		}

		if ( m_pScrollBar )
		{
			m_pScrollBar->SetOverriddenButtons( m_pUpArrow, m_pDownArrow );
			m_pScrollBar->SetVisible( false );
		}

		m_pLine = new vgui::ImagePanel( this, "Line" );
		m_pBox = new vgui::ImagePanel( this, "Box" );
		if ( m_pLine )
		{
			m_pLine->SetImage( "chalkboard_scroll_line" );
			m_pLine->SetShouldScaleImage( true );
		}

		if ( m_pBox )
		{
			m_pBox->SetImage( "chalkboard_scroll_box" );
			m_pBox->SetShouldScaleImage( true );
		}

		SetScrollBarImagesVisible( false );

		vgui::ivgui()->AddTickSignal( GetVPanel() );
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	~CTFClassTipsListPanel()
	{
		ivgui()->RemoveTickSignal( GetVPanel() );
	}

private:

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	int GetListHeight( void )
	{
		int nHeight = 0;

		for ( int i = FirstItem(); i < GetItemCount(); i++ )
		{
			vgui::Panel *pClassTipsItemPanel = GetItemPanel( i );
			if ( pClassTipsItemPanel )
			{
				nHeight += pClassTipsItemPanel->GetTall();
			}
		}

		return nHeight;
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	void SetScrollBarImagesVisible( bool visible )
	{
		if ( m_pDownArrow && m_pDownArrow->IsVisible() != visible )
		{
			m_pDownArrow->SetVisible( visible );
			m_pDownArrow->SetEnabled( visible );
		}

		if ( m_pUpArrow && m_pUpArrow->IsVisible() != visible )
		{
			m_pUpArrow->SetVisible( visible );
			m_pUpArrow->SetEnabled( visible );
		}

		if ( m_pLine && m_pLine->IsVisible() != visible )
		{
			m_pLine->SetVisible( visible );
		}

		if ( m_pBox && m_pBox->IsVisible() != visible )
		{
			m_pBox->SetVisible( visible );
		}
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	void OnTick()
	{
		if ( !IsVisible() )
			return;

		if ( m_pDownArrow && m_pUpArrow && m_pLine && m_pBox )
		{
			if ( m_pScrollBar && m_pScrollBar->IsVisible() && GetListHeight() > GetTall() )
			{
				SetScrollBarImagesVisible ( true );

				// set the alpha on the up arrow
				int nMin, nMax;
				m_pScrollBar->GetRange( nMin, nMax );
				int nScrollPos = m_pScrollBar->GetValue();
				int nRangeWindow = m_pScrollBar->GetRangeWindow();
				int nBottom = nMax - nRangeWindow;
				if ( nBottom < 0 )
				{
					nBottom = 0;
				}

				int nAlpha = ( nScrollPos - nMin <= 0 ) ? 90 : 255;
				m_pUpArrow->SetAlpha( nAlpha );

				// set the alpha on the down arrow
				nAlpha = ( nScrollPos >= nBottom ) ? 90 : 255;
				m_pDownArrow->SetAlpha( nAlpha );

				ScrollBarSlider *pSlider = m_pScrollBar->GetSlider();
				if ( pSlider && pSlider->GetRangeWindow() > 0 )
				{
					int x, y, w, t, min, max;
					m_pLine->GetBounds( x, y, w, t );
					pSlider->GetNobPos( min, max );
					m_pBox->SetBounds( x, y + min, w, ( max - min ) );
				}
			}
			else
			{
				// turn off our images
				SetScrollBarImagesVisible ( false );
			}
		}
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	void ApplySchemeSettings( IScheme *pScheme )
	{
		BaseClass::ApplySchemeSettings( pScheme );

		if ( m_pScrollBar )
		{
			m_pScrollBar->SetZPos( 500 );
			m_pUpArrow->SetZPos( 501 );
			m_pDownArrow->SetZPos( 501 );

			// turn off painting the vertical scrollbar
			m_pScrollBar->SetPaintBackgroundEnabled( false );
			m_pScrollBar->SetPaintBorderEnabled( false );
			m_pScrollBar->SetPaintEnabled( false );
			m_pScrollBar->SetScrollbarButtonsVisible( false );
			m_pScrollBar->GetButton(0)->SetMouseInputEnabled( false );
			m_pScrollBar->GetButton(1)->SetMouseInputEnabled( false );

			if ( m_pScrollBar->IsVisible() )
			{
				int nMin, nMax;
				m_pScrollBar->GetRange( nMin, nMax );
				m_pScrollBar->SetValue( nMin );

				int nScrollbarWide = m_pScrollBar->GetWide();

				int wide, tall;
				GetSize( wide, tall );

				if ( m_pUpArrow )
				{
					m_pUpArrow->SetBounds( wide - nScrollbarWide, 0, nScrollbarWide, nScrollbarWide );
					m_pUpArrow->GetImage()->SetSize( nScrollbarWide, nScrollbarWide );
				}

				if ( m_pLine )
				{
					m_pLine->SetBounds( wide - nScrollbarWide, nScrollbarWide, nScrollbarWide, tall - ( 2 * nScrollbarWide ) );
				}

				if ( m_pBox )
				{
					m_pBox->SetBounds( wide - nScrollbarWide, nScrollbarWide, nScrollbarWide, nScrollbarWide );
				}

				if ( m_pDownArrow )
				{
					m_pDownArrow->SetBounds( wide - nScrollbarWide, tall - nScrollbarWide, nScrollbarWide, nScrollbarWide );
					m_pDownArrow->GetImage()->SetSize( nScrollbarWide, nScrollbarWide );
				}

				SetScrollBarImagesVisible( false );
			}
		}
	}

	vgui::ScrollBar			*m_pScrollBar;
	CExImageButton			*m_pUpArrow;
	CExImageButton			*m_pDownArrow;
	vgui::ImagePanel		*m_pLine;
	vgui::ImagePanel		*m_pBox;
};

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
class CTFClassTipsPanel : public EditablePanel
{
private:
	DECLARE_CLASS_SIMPLE( CTFClassTipsPanel, EditablePanel );

public:
	CTFClassTipsPanel( Panel *parent, const char *panelName )
		:	EditablePanel( parent, panelName )
	{
		m_pClassTipsListPanel = new CTFClassTipsListPanel( this, "ClassTipsListPanel" );
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	~CTFClassTipsPanel()
	{
	}

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	void SetClass( int iClass )
	{
		const char *pPathID = IsX360() ? "MOD" : "GAME";
		m_fmtResFilename.sprintf( "classes/%s.res", g_aRawPlayerClassNames[ iClass ] );
		if ( !g_pFullFileSystem->FileExists( m_fmtResFilename.Access(), pPathID ) &&
			g_pFullFileSystem->FileExists( "classes/default.res", pPathID ) )
		{
			m_fmtResFilename.sprintf( "classes/default.res" );
		}

		if ( m_pClassTipsListPanel )
		{
			m_pClassTipsListPanel->DeleteAllItems();

			int nScrollToItem = 0;

			// Get tip count
			const wchar_t *wzTipCount = g_pVGuiLocalize->Find( CFmtStr( "ClassTips_%d_Count", iClass ) );
			int nTipCount = wzTipCount ? _wtoi( wzTipCount ) : 0;
			for ( int iTip = 1; iTip < nTipCount+1; ++iTip )
			{
				const wchar_t *pwszText = g_pVGuiLocalize->Find( CFmtStr( "#ClassTips_%d_%d", iClass, iTip ) );
				const wchar_t *pwszTextMvM = g_pVGuiLocalize->Find( CFmtStr( "#ClassTips_%d_%d_MvM", iClass, iTip ) );
				wchar_t *pwszIcon = g_pVGuiLocalize->Find( CFmtStr( "ClassTips_%d_%d_Icon", iClass, iTip ) );
				char szIcon[MAX_PATH];

				szIcon[0] = 0;
				if ( pwszIcon )
				{
					g_pVGuiLocalize->ConvertUnicodeToANSI( pwszIcon, szIcon, sizeof( szIcon ) );
				}

				// Don't load MvM tips outside the mode
				if ( pwszTextMvM )
				{
					if ( !TFGameRules()->IsMannVsMachineMode() )
						continue;

					// If we're MvM mode, remember first MvM tip
					if ( !nScrollToItem )
						nScrollToItem = iTip;
				}

				// Create a TipsItemPanel for each tip
				if ( pwszText || pwszTextMvM )
				{
					CTFClassTipsItemPanel *pClassTipsItemPanel = new CTFClassTipsItemPanel( this, "ClassTipsItemPanel", iTip );
					if ( pwszText )
					{
						pClassTipsItemPanel->SetClassTip( pwszText, szIcon );
					}
					else if ( pwszTextMvM )
					{
						pClassTipsItemPanel->SetClassTip( pwszTextMvM, szIcon );
					}

					m_pClassTipsListPanel->AddItem( NULL, pClassTipsItemPanel );
				}
			}

			if ( m_pClassTipsListPanel->GetItemCount() > 0 )
			{
				m_pClassTipsListPanel->SetFirstColumnWidth( 0 );
				m_pClassTipsListPanel->ScrollToItem( nScrollToItem );
			}
		}

		InvalidateLayout( true, true );
	}

private:

	//-----------------------------------------------------------------------------
	// Purpose: 
	//-----------------------------------------------------------------------------
	virtual void ApplySchemeSettings( IScheme *pScheme )
	{
		BaseClass::ApplySchemeSettings( pScheme );

		LoadControlSettings( "Resource/UI/ClassTipsList.res" );
	}

	CTFClassTipsListPanel	*m_pClassTipsListPanel;

	CFmtStr					m_fmtResFilename;
};

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CTFClassMenu::CTFClassMenu( IViewPort *pViewPort )
:	CClassMenu( pViewPort )
{
	MakePopup();

	m_mouseoverButtons.RemoveAll();

	m_iClassMenuKey = BUTTON_CODE_INVALID;
	m_iCurrentClassIndex = TF_CLASS_HEAVYWEAPONS;

#ifdef _X360
	m_pFooter = new CTFFooter( this, "Footer" );
#endif

	m_pTFPlayerModelPanel = NULL;
	m_pSelectAClassLabel = NULL;

	m_pClassTipsPanel = new CTFClassTipsPanel( this, "ClassTipsPanel" );

	Q_memset( m_pClassButtons, 0, sizeof( m_pClassButtons ) );

#ifndef _X360
	char tempName[MAX_PATH];
	for ( int i = 0 ; i < CLASS_COUNT_IMAGES ; ++i )
	{
		Q_snprintf( tempName, sizeof( tempName ), "countImage%d", i );
		m_ClassCountImages[i] = new CTFImagePanel( this, tempName );
	}

	m_pCountLabel = NULL;

	m_pLocalPlayerImage = new CTFImagePanel( this, "localPlayerImage" );
	m_pLocalPlayerBG = new CTFImagePanel( this, "localPlayerBG" );
	m_iLocalPlayerClass = TEAM_UNASSIGNED;

	m_pClassButtons[TF_CLASS_SCOUT] = new CExImageButton( this, "scout", "", this );
	m_pClassButtons[TF_CLASS_SOLDIER] = new CExImageButton( this, "soldier", "", this );
	m_pClassButtons[TF_CLASS_PYRO] = new CExImageButton( this, "pyro", "", this );
	m_pClassButtons[TF_CLASS_DEMOMAN] = new CExImageButton( this, "demoman", "", this );
	m_pClassButtons[TF_CLASS_MEDIC] = new CExImageButton( this, "medic", "", this );
	m_pClassButtons[TF_CLASS_HEAVYWEAPONS] = new CExImageButton( this, "heavyweapons", "", this );
	m_pClassButtons[TF_CLASS_SNIPER] = new CExImageButton( this, "sniper", "", this );
	m_pClassButtons[TF_CLASS_ENGINEER] = new CExImageButton( this, "engineer", "", this );
	m_pClassButtons[TF_CLASS_SPY] = new CExImageButton( this, "spy", "", this );
	m_pClassButtons[TF_CLASS_RANDOM] = new CExImageButton( this, "random", "", this );
#endif

	m_pEditLoadoutButton = NULL;
	m_nBaseMusicGuid = -1;

	ListenForGameEvent( "localplayer_changeteam" );
	ListenForGameEvent( "show_match_summary" );

	Q_memset( m_pMvmUpgradeImages, 0, sizeof( m_pMvmUpgradeImages ) );
	m_pMvmUpgradeImages[TF_CLASS_SCOUT] = new vgui::ImagePanel( this, "MvMUpgradeImageScout" );
	m_pMvmUpgradeImages[TF_CLASS_SOLDIER] = new vgui::ImagePanel( this, "MvMUpgradeImageSolider" );
	m_pMvmUpgradeImages[TF_CLASS_PYRO] = new vgui::ImagePanel( this, "MvMUpgradeImagePyro" );
	m_pMvmUpgradeImages[TF_CLASS_DEMOMAN] = new vgui::ImagePanel( this, "MvMUpgradeImageDemoman" );
	m_pMvmUpgradeImages[TF_CLASS_MEDIC] = new vgui::ImagePanel( this, "MvMUpgradeImageMedic" );
	m_pMvmUpgradeImages[TF_CLASS_HEAVYWEAPONS] = new vgui::ImagePanel( this, "MvMUpgradeImageHeavy" );
	m_pMvmUpgradeImages[TF_CLASS_SNIPER] = new vgui::ImagePanel( this, "MvMUpgradeImageSniper" );
	m_pMvmUpgradeImages[TF_CLASS_ENGINEER] = new vgui::ImagePanel( this, "MvMUpgradeImageEngineer" );
	m_pMvmUpgradeImages[TF_CLASS_SPY] = new vgui::ImagePanel( this, "MvMUpgradeImageSpy" );

	vgui::ivgui()->AddTickSignal( GetVPanel() );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::ApplySchemeSettings( IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	for ( int i = 0; i < TF_CLASS_MENU_BUTTONS; i++ )
	{
		m_pClassHintIcons[i] = nullptr;
	}

	// Load the .res file
	if ( ::input->IsSteamControllerActive() )
	{
		LoadControlSettings( "Resource/UI/ClassSelection_SC.res" );
		m_pCancelHintIcon = dynamic_cast< CSCHintIcon* >( FindChildByName( "CancelHintIcon" ) );
		m_pEditLoadoutHintIcon = dynamic_cast< CSCHintIcon* >( FindChildByName( "EditLoadoutHintIcon" ) );

		m_pClassHintIcons[TF_CLASS_SCOUT] = dynamic_cast< CSCHintIcon* >( FindChildByName( "ScoutHintIcon" ) );
		m_pClassHintIcons[TF_CLASS_SOLDIER] = dynamic_cast< CSCHintIcon* >( FindChildByName( "SoldierHintIcon" ) );
		m_pClassHintIcons[TF_CLASS_PYRO] = dynamic_cast< CSCHintIcon* >( FindChildByName( "PyroHintIcon" ) );
		m_pClassHintIcons[TF_CLASS_DEMOMAN] = dynamic_cast< CSCHintIcon* >( FindChildByName( "DemomanHintIcon" ) );
		m_pClassHintIcons[TF_CLASS_HEAVYWEAPONS] = dynamic_cast< CSCHintIcon* >( FindChildByName( "HeavyHintIcon" ) );
		m_pClassHintIcons[TF_CLASS_MEDIC] = dynamic_cast< CSCHintIcon* >( FindChildByName( "MedicHintIcon" ) );
		m_pClassHintIcons[TF_CLASS_SPY] = dynamic_cast< CSCHintIcon* >( FindChildByName( "SpyHintIcon" ) );
		m_pClassHintIcons[TF_CLASS_ENGINEER] = dynamic_cast< CSCHintIcon* >( FindChildByName( "EngineerHintIcon" ) );
		m_pClassHintIcons[TF_CLASS_SNIPER] = dynamic_cast< CSCHintIcon* >( FindChildByName( "SniperHintIcon" ) );
		m_pClassHintIcons[TF_CLASS_RANDOM] = dynamic_cast< CSCHintIcon* >( FindChildByName( "RandomHintIcon" ) );

		for ( int i = 0; i < TF_CLASS_MENU_BUTTONS; i++ )
		{
			if ( m_pClassHintIcons[i] )
			{
				m_pClassHintIcons[i]->SetVisible( false );
			}
		}

		SetMouseInputEnabled( false );
	}
	else
	{
		LoadControlSettings( "Resource/UI/ClassSelection.res" );
		m_pCancelHintIcon = m_pEditLoadoutHintIcon = nullptr;
		SetMouseInputEnabled( true );
	}

	m_pTFPlayerModelPanel = dynamic_cast<CTFPlayerModelPanel*>( FindChildByName("TFPlayerModel") );
	m_pSelectAClassLabel = dynamic_cast<CExLabel*>( FindChildByName( "ClassMenuSelect" ) );
	m_pEditLoadoutButton = dynamic_cast<CExButton*>( FindChildByName( "EditLoadoutButton" ) );

	const char *pTeamExtension = GetTeamNumber() == TF_TEAM_BLUE ? "blu" : "red";
	for ( int i = 0; i < ARRAYSIZE( m_pClassButtons ); ++i )
	{
		if ( !m_pClassButtons[i] )
			continue;

		if ( !IsValidTFPlayerClass( i ) && i != TF_CLASS_RANDOM )
			continue;

		m_pClassButtons[i]->SetImageSelected( CFmtStr( "class_sel_sm_%s_%s", g_aRawPlayerClassNamesShort[i], pTeamExtension ).Access() );
		if( i != TF_CLASS_RANDOM )
		{
			m_pClassButtons[i]->SetArmedSound("misc/null.wav");
		}
	}
}

void CTFClassMenu::PerformLayout()
{
	BaseClass::PerformLayout();

#ifndef _X360
	m_pCountLabel = dynamic_cast< CExLabel * >( FindChildByName( "CountLabel" ) );

	if ( m_pCountLabel )
	{
		m_pCountLabel->SizeToContents();
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CTFClassMenu::GetCurrentPlayerClass()
{
	int iClass = TF_CLASS_HEAVYWEAPONS;
	C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();

	if ( pLocalPlayer && pLocalPlayer->m_Shared.GetDesiredPlayerClassIndex() != TF_CLASS_UNDEFINED )
	{
		iClass = pLocalPlayer->m_Shared.GetDesiredPlayerClassIndex();
	}

	return iClass;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CExImageButton *CTFClassMenu::GetCurrentClassButton()
{
	const int iClass = GetCurrentPlayerClass();
	m_iCurrentClassIndex = iRemapIndexToClass[ iClass ];
	return m_pClassButtons[iClass];
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::ShowPanel( bool bShow )
{
	if ( bShow )
	{
		// Hide the other class menu
		if ( gViewPortInterface )
		{
			gViewPortInterface->ShowPanel( GetTeamNumber() == TF_TEAM_BLUE ? PANEL_CLASS_RED : PANEL_CLASS_BLUE, false );
		}

		// can't change class if you're on the losing team during the "bonus time" after a team has won the round
		if ( ( TFGameRules()->State_Get() == GR_STATE_TEAM_WIN && 
			 C_TFPlayer::GetLocalTFPlayer() && 
			 C_TFPlayer::GetLocalTFPlayer()->GetTeamNumber() != TFGameRules()->GetWinningTeam()
			 && C_TFPlayer::GetLocalTFPlayer()->GetTeamNumber() != TEAM_SPECTATOR 
			 && C_TFPlayer::GetLocalTFPlayer()->GetTeamNumber() != TEAM_UNASSIGNED
			 && GetSpectatorMode() == OBS_MODE_NONE ) ||
			 TFGameRules()->State_Get() == GR_STATE_GAME_OVER ||
			( TFGameRules()->IsInTraining() && C_TFPlayer::GetLocalTFPlayer() &&
			  ( C_TFPlayer::GetLocalTFPlayer()->GetPlayerClass() == NULL || C_TFPlayer::GetLocalTFPlayer()->GetPlayerClass()->GetClassIndex() != TF_CLASS_UNDEFINED ) ) )
		{
			SetVisible( false );

			CHudNotificationPanel *pNotifyPanel = GET_HUDELEMENT( CHudNotificationPanel );
			if ( pNotifyPanel )
			{
				if ( C_TFPlayer::GetLocalTFPlayer() )
				{
					pNotifyPanel->SetupNotifyCustom( "#TF_CantChangeClassNow", "ico_notify_flag_moving", C_TFPlayer::GetLocalTFPlayer()->GetTeamNumber() );
				}
			}

			return;
		}

		engine->CheckPoint( "ClassMenu" );

		// Force us to reload our scheme, in case Steam Controller stuff has changed.
		InvalidateLayout( true, true );

		Activate();

		m_iClassMenuKey = gameuifuncs->GetButtonCodeForBind( "changeclass" );
		m_iScoreBoardKey = gameuifuncs->GetButtonCodeForBind( "showscores" );

		SelectClass( GetCurrentPlayerClass() );
	}
	else
	{
		SetVisible( false );
		if ( m_pTFPlayerModelPanel )
		{
			m_pTFPlayerModelPanel->ClearCarriedItems();
		}
	}
}

const char *g_pszLegacyClassSelectVCDWeapons[TF_LAST_NORMAL_CLASS] =
{
	"",										// TF_CLASS_UNDEFINED = 0,
	"",										// TF_CLASS_SCOUT,				// weapons handled individually
	"",										// TF_CLASS_SNIPER,				// weapons handled individually
	"",										// TF_CLASS_SOLDIER,			// weapons handled individually
	"tf_weapon_grenadelauncher",			// TF_CLASS_DEMOMAN,
	"tf_weapon_medigun",					// TF_CLASS_MEDIC,
	"tf_weapon_minigun",					// TF_CLASS_HEAVYWEAPONS,
	"tf_weapon_flamethrower",				// TF_CLASS_PYRO,
	"",										// TF_CLASS_SPY,				// weapons handled individually
	"tf_weapon_wrench",						// TF_CLASS_ENGINEER,		
};

int g_iLegacyClassSelectWeaponSlots[TF_LAST_NORMAL_CLASS] =
{
	LOADOUT_POSITION_PRIMARY,		// TF_CLASS_UNDEFINED = 0,
	LOADOUT_POSITION_PRIMARY,		// TF_CLASS_SCOUT,			// TF_FIRST_NORMAL_CLASS
	LOADOUT_POSITION_PRIMARY,		// TF_CLASS_SNIPER,
	LOADOUT_POSITION_PRIMARY,		// TF_CLASS_SOLDIER,
	LOADOUT_POSITION_PRIMARY,		// TF_CLASS_DEMOMAN,
	LOADOUT_POSITION_SECONDARY,		// TF_CLASS_MEDIC,
	LOADOUT_POSITION_PRIMARY,		// TF_CLASS_HEAVYWEAPONS,
	LOADOUT_POSITION_PRIMARY,		// TF_CLASS_PYRO,
	LOADOUT_POSITION_MELEE,			// TF_CLASS_SPY,
	LOADOUT_POSITION_MELEE,			// TF_CLASS_ENGINEER,		
};

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::UpdateButtonSelectionStates( int iClass )
{
	// Set the correct button as selected, all other buttons as not selected
	for ( int i = 0; i < ARRAYSIZE( m_pClassButtons ); ++i )
	{
		if ( !m_pClassButtons[ i ] )
			continue;

		m_pClassButtons[ i ]->SetSelected( i == iClass );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::SelectClass( int iClass )
{
	if ( !engine->IsInGame() )
		return;
	
	if ( !m_pTFPlayerModelPanel )
		return;

	if ( !m_pClassTipsPanel )
		return;

	if ( !m_pEditLoadoutButton )
		return;

	// Update select hint icon for Steam Controller
	for ( int i = 0; i < TF_CLASS_MENU_BUTTONS; i++ )
	{
		if ( m_pClassHintIcons[i] )
		{
			m_pClassHintIcons[i]->SetVisible( i == iClass );
		}
	}

	// Were we random? If so, we'll force our class to refresh later to prevent the
	// model panel thinking the class hasn't changed.
	const bool bClassWasRandom = m_iCurrentClassIndex == TF_CLASS_RANDOM;

	// Cache current player class
	m_iCurrentClassIndex = iClass;

	UpdateButtonSelectionStates( iClass );

	bool bRandomClass = iClass == TF_CLASS_RANDOM;
	if ( !IsValidTFPlayerClass( iClass ) && !bRandomClass )
	{
		m_pTFPlayerModelPanel->SetVisible( false );
		m_pTFPlayerModelPanel->ClearCarriedItems();
		return;
	}

	m_pTFPlayerModelPanel->SetVisible( true );
	m_pTFPlayerModelPanel->ClearCarriedItems();

	if ( bRandomClass )
	{
		m_pEditLoadoutButton->SetVisible( false );
		if ( m_pEditLoadoutHintIcon )
		{
			m_pEditLoadoutHintIcon->SetVisible( false );
		}

		MDLHandle_t hModel = mdlcache->FindMDL( "models/class_menu/random_class_icon.mdl" );
		m_pTFPlayerModelPanel->SetMDL( hModel );
		m_pTFPlayerModelPanel->SetSequence( ACT_IDLE );
		m_pTFPlayerModelPanel->InvalidateLayout( true, true ); // Updates position
		m_pTFPlayerModelPanel->SetSkin( GetTeamNumber() == TF_TEAM_RED ? 0 : 1 );
		mdlcache->Release( hModel ); // counterbalance addref from within FindMDL
	}
	else
	{
		bool bIsRobot = false;
		// Check for Robot
		static CSchemaAttributeDefHandle pAttrDef_PlayerRobot( "appear as mvm robot" );
		for ( int i = 0; i < CLASS_LOADOUT_POSITION_COUNT; i++ )
		{
			CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( iClass, i );
			if ( !pItemData )
				continue;
			if ( FindAttribute( pItemData, pAttrDef_PlayerRobot ) )
			{
				bIsRobot = true;
				break;
			}
		}

		/*if ( pLocalPlayer )
		{
			int iRobot = 0;
			CALL_ATTRIB_HOOK_INT_ON_OTHER( pLocalPlayer, iRobot, appear_as_mvm_robot );
			bIsRobot = iRobot ? true : false;
		}*/
		m_pTFPlayerModelPanel->SetToPlayerClass( iClass, bIsRobot, bClassWasRandom );

		m_pEditLoadoutButton->SetVisible( true );
		if ( m_pEditLoadoutHintIcon )
		{
			m_pEditLoadoutHintIcon->SetVisible( true );
		}

		C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
		if ( pLocalPlayer )
		{
			int iTeam = GetTeamNumber();
			m_pTFPlayerModelPanel->SetTeam( iTeam );
		}

		LoadItems();
	}

	m_pClassTipsPanel->SetClass( iClass );

	enginesound->StopSoundByGuid( m_nBaseMusicGuid );
	CBroadcastRecipientFilter filter;
	char nClassMusicStr[64];
	if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
	{
		sprintf( nClassMusicStr, "music.mvm_class_menu_0%i", iClass );
	}
	else
	{
		sprintf( nClassMusicStr, "music.class_menu_0%i", iClass );
	}
	CBaseEntity::EmitSound( filter, SOUND_FROM_UI_PANEL, nClassMusicStr );
	m_nBaseMusicGuid = enginesound->GetGuidForLastSoundEmitted();
	

}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::LoadItems()
{
	const int iClass = m_pTFPlayerModelPanel->GetPlayerClass();

	m_pTFPlayerModelPanel->ClearCarriedItems();

	static CSchemaAttributeDefHandle pAttrDef_DisableFancyLoadoutAnim( "disable fancy class select anim" );
	bool bCanUseFancyClassSelectAnimation = true;

	static CSchemaAttributeDefHandle pAttrDef_ClassSelectOverrideVCD( "class select override vcd" );
	CAttribute_String attrClassSelectOverrideVCD;

	const char *pszVCD = "class_select";

	for ( int i = 0; i < CLASS_LOADOUT_POSITION_COUNT; i++ )
	{
		CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( iClass, i );
		if ( pItemData && pItemData->IsValid() )
		{
			m_pTFPlayerModelPanel->AddCarriedItem( pItemData );

			// Certain items have different shapes and would interfere with our class select animations.
			bCanUseFancyClassSelectAnimation = bCanUseFancyClassSelectAnimation
											&& !pItemData->FindAttribute( pAttrDef_DisableFancyLoadoutAnim );

			// Some items want to override the class select VCD
			if ( pItemData->FindAttribute( pAttrDef_ClassSelectOverrideVCD, &attrClassSelectOverrideVCD ) )
			{
				const char *pszClassSelectOverrideVCD = attrClassSelectOverrideVCD.value().c_str();
				if ( pszClassSelectOverrideVCD && *pszClassSelectOverrideVCD )
				{
					pszVCD = pszClassSelectOverrideVCD;
				}
			}
		}
	}

	m_pTFPlayerModelPanel->PlayVCD( bCanUseFancyClassSelectAnimation ? pszVCD : NULL, g_pszLegacyClassSelectVCDWeapons[iClass] );
	m_pTFPlayerModelPanel->HoldItemInSlot( g_iLegacyClassSelectWeaponSlots[iClass] );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::OnKeyCodePressed( KeyCode code )
{
	m_KeyRepeat.KeyDown( code );

	if ( code > KEY_0 && code <= KEY_9 )
	{
		const int iButton = code - KEY_0;
		const int iClass = iRemapIndexToClass[ iButton ];
		SelectClass( iClass );
		Go();
	}
	else if ( code > KEY_PAD_0 && code <= KEY_PAD_9 )
	{
		const int iButton = code - KEY_PAD_0;
		const int iClass = iRemapIndexToClass[ iButton ];
		SelectClass( iClass );
		Go();
	}
	else if( code == KEY_ENTER || code == KEY_SPACE || code == KEY_XBUTTON_A || code == KEY_XBUTTON_RTRIGGER || code == STEAMCONTROLLER_A )
	{
		Go();
	}
	else if ( ( m_iClassMenuKey != BUTTON_CODE_INVALID && m_iClassMenuKey == code ) ||
		code == KEY_XBUTTON_BACK || 
		code == KEY_XBUTTON_B ||
		code == STEAMCONTROLLER_B ||
		code == KEY_0 || 
		code == KEY_PAD_0 )
	{
		C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();

		if ( pLocalPlayer && ( pLocalPlayer->GetPlayerClass()->GetClassIndex() != TF_CLASS_UNDEFINED ) )
		{
			ShowPanel( false );
		}
	}
	else if( code == KEY_XBUTTON_RIGHT || code == KEY_XSTICK1_RIGHT || code == STEAMCONTROLLER_DPAD_RIGHT )
	{
		int loopCheck = 0;
		int nCurrentClass = GetRemappedMenuIndexForClass( m_iCurrentClassIndex );

		do 
		{
			loopCheck++;
			nCurrentClass++;
			nCurrentClass = ( nCurrentClass % TF_CLASS_MENU_BUTTONS );
		} while( ( m_pClassButtons[ iRemapIndexToClass[nCurrentClass] ] == NULL ) && ( loopCheck < TF_CLASS_MENU_BUTTONS ) );
		
		SelectClass(  iRemapIndexToClass[ nCurrentClass ] );
	}
	else if ( code == STEAMCONTROLLER_Y )
	{
		OnCommand( "openloadout" );
	}
	else if( code == KEY_XBUTTON_LEFT || code == KEY_XSTICK1_LEFT || code == STEAMCONTROLLER_DPAD_LEFT )
	{
		int loopCheck = 0;
		int nCurrentClass = GetRemappedMenuIndexForClass( m_iCurrentClassIndex );

		do 
		{
			loopCheck++;
			nCurrentClass--;
			if( nCurrentClass <= 0 )
			{
				nCurrentClass = GetRemappedMenuIndexForClass( TF_CLASS_RANDOM );
			}
		} while( ( m_pClassButtons[ iRemapIndexToClass[nCurrentClass] ] == NULL ) && ( loopCheck < TF_CLASS_MENU_BUTTONS ) );
		
		SelectClass(  iRemapIndexToClass[ nCurrentClass ] );
	}
	else if( code == KEY_XBUTTON_UP || code == KEY_XSTICK1_UP || code == STEAMCONTROLLER_DPAD_UP )
	{
		// Scroll class info text up
		if ( g_lastPanel )
		{
			CExRichText *pRichText = dynamic_cast< CExRichText * >( g_lastPanel->FindChildByName( "classInfo" ) );

			if ( pRichText )
			{
				PostMessage( pRichText, new KeyValues("MoveScrollBarDirect", "delta", 1) );
			}
		}
	}
	else if( code == KEY_XBUTTON_DOWN || code == KEY_XSTICK1_DOWN || code == STEAMCONTROLLER_DPAD_DOWN )
	{
		// Scroll class info text up
		if ( g_lastPanel )
		{
			CExRichText *pRichText = dynamic_cast< CExRichText * >( g_lastPanel->FindChildByName( "classInfo" ) );

			if ( pRichText )
			{
				PostMessage( pRichText, new KeyValues("MoveScrollBarDirect", "delta", -1) );
			}
		}
	}
	else
	{
		BaseClass::OnKeyCodePressed( code );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::OnKeyCodeReleased( vgui::KeyCode code )
{
	m_KeyRepeat.KeyUp( code );

	BaseClass::OnKeyCodeReleased( code );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::OnThink()
{
	vgui::KeyCode code = m_KeyRepeat.KeyRepeated();
	if ( code )
	{
		OnKeyCodePressed( code );
	}

	// Get mouse cursor position
	int aCursorPos[2];
	vgui::input()->GetCursorPos( aCursorPos[0], aCursorPos[1] );

	// Go through all buttons - if the mouse is within one, select that class
	for ( int i = 0; i < ARRAYSIZE( m_pClassButtons ); ++i )
	{
		if ( !m_pClassButtons[ i ] )
			continue;

		if ( m_iCurrentClassIndex != i && m_pClassButtons[ i ]->IsWithin( aCursorPos[0], aCursorPos[1] ) )
		{
			SelectClass( i );
		}
	}

	//Always hide the health... this needs to be done every frame because a message from the server keeps resetting this.
	C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
	if ( pLocalPlayer )
	{
		pLocalPlayer->m_Local.m_iHideHUD |= HIDEHUD_HEALTH;
	}

	BaseClass::OnThink();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::SetCancelButtonVisible( bool bVisible )
{
	SetVisibleButton( "CancelButton", bVisible );
	if ( m_pCancelHintIcon )
	{
		m_pCancelHintIcon->SetVisible( bVisible );
	}
	
	if ( m_pSelectAClassLabel )
	{
		m_pSelectAClassLabel->SetVisible( !bVisible );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::Update()
{
	C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();

	// Force them to pick a class if they haven't picked one yet.
	if ( ( pLocalPlayer && pLocalPlayer->m_Shared.GetDesiredPlayerClassIndex() != TF_CLASS_UNDEFINED ) )
	{
#ifdef _X360
		if ( m_pFooter )
		{
			m_pFooter->ShowButtonLabel( "cancel", true );
		}
#else
		SetCancelButtonVisible( true );

		if ( TFGameRules() && TFGameRules()->IsInHighlanderMode() )
		{
			SetVisibleButton( "ResetButton", true );
		}
		else
		{
			SetVisibleButton( "ResetButton", false );
		}
#endif
	}
	else
	{
#ifdef _X360
		if ( m_pFooter )
		{
			m_pFooter->ShowButtonLabel( "cancel", false );
		}
#else
		SetCancelButtonVisible( false );
		SetVisibleButton( "ResetButton", false );
#endif
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
Panel *CTFClassMenu::CreateControlByName( const char *controlName )
{
	if ( !Q_stricmp( "CIconPanel", controlName ) )
	{
		return new CIconPanel( this, "icon_panel" );
	}
	else
	{
		return BaseClass::CreateControlByName( controlName );
	}
}

//-----------------------------------------------------------------------------
// Catch the mouseover event and set the active class
//-----------------------------------------------------------------------------
void CTFClassMenu::OnShowPage( vgui::Panel *panel, const char *pagename )
{
	for ( int i = 0; i < TF_CLASS_MENU_BUTTONS; i++ )
	{
		if (m_pClassButtons[i] == panel )
		{
//			SelectClass( i );
			break;
		}
	}
}

//-----------------------------------------------------------------------------
// Draw nothing
//-----------------------------------------------------------------------------
void CTFClassMenu::PaintBackground( void )
{
}

//-----------------------------------------------------------------------------
// Do things that should be done often, eg number of players in the 
// selected class
//-----------------------------------------------------------------------------
void CTFClassMenu::OnTick( void )
{
	//When a player changes teams, their class and team values don't get here 
	//necessarily before the command to update the class menu. This leads to the cancel button 
	//being visible and people cancelling before they have a class. check for class == TF_CLASS_UNDEFINED and if so
	//hide the cancel button

	if ( !IsVisible() )
		return;

#ifndef _X360
	C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();

	// Force them to pick a class if they haven't picked one yet.
	if ( pLocalPlayer && pLocalPlayer->m_Shared.GetDesiredPlayerClassIndex() == TF_CLASS_UNDEFINED )
	{
		SetCancelButtonVisible( false );
		SetVisibleButton( "ResetButton", false );
	}

	UpdateClassCounts();

#endif

	BaseClass::OnTick();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::OnClose()
{
	C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
	if ( pLocalPlayer )
	{
		// Clear the HIDEHUD_HEALTH bit we hackily added. Turns out prediction
		//  was restoring these bits every frame. Unfortunately, prediction
		//  is off for karts which means the spell hud item would disappear if you
		//  brought up this menu and returned.
		pLocalPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_HEALTH;
	}

	ShowPanel( false );

	BaseClass::OnClose();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::SetVisible( bool state )
{
	BaseClass::SetVisible( state );

	m_KeyRepeat.Reset();

	if ( state )
	{
		engine->ServerCmd( "menuopen" );			// to the server
		engine->ClientCmd( "_cl_classmenuopen 1" );	// for other panels
		CBroadcastRecipientFilter filter;

		if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
		{
			CBaseEntity::EmitSound( filter, SOUND_FROM_UI_PANEL, "music.mvm_class_menu" );
		}
		else
		{
			CBaseEntity::EmitSound( filter, SOUND_FROM_UI_PANEL, "music.class_menu" );
		}

		CheckMvMUpgrades();
	}
	else
	{
		engine->ServerCmd( "menuclosed" );	
		engine->ClientCmd( "_cl_classmenuopen 0" );
		
		if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
		{
			CBaseEntity::StopSound( SOUND_FROM_UI_PANEL, "music.mvm_class_menu" );
		}
		else
		{
			CBaseEntity::StopSound( SOUND_FROM_UI_PANEL, "music.class_menu" );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::Go()
{
	const int iClass = m_iCurrentClassIndex;
	if ( iClass == TF_CLASS_UNDEFINED )
		return;

	// Check class limits
	if ( TFGameRules() && !TFGameRules()->CanPlayerChooseClass( C_TFPlayer::GetLocalTFPlayer(), iClass ) )
		return;

#if defined( REPLAY_ENABLED )
	// Display replay recording message if appropriate
	int &nDisplayedConnectedRecording = CPlayerSpawnCache::Instance().m_Data.m_nDisplayedConnectedRecording;
	if ( g_pReplay->IsReplayEnabled() &&
		 !g_pEngineClientReplay->IsPlayingReplayDemo() &&	// FIXME: We shouldn't need this here but for some reason the engine thinks a replay is recording during demo playback, even though replay_recording has a FCVAR_DONTRECORD flag
		 !nDisplayedConnectedRecording &&
		 replay_replaywelcomedlgcount.GetInt() <= MAX_TIMES_TO_SHOW_REPLAY_WELCOME_DLG )
	{
		wchar_t wText[256];
		wchar wKeyBind[80];
		char szText[256];

		const char *pSaveReplayKey = engine->Key_LookupBinding( "save_replay" );
		if ( !pSaveReplayKey )
		{
			pSaveReplayKey = "< not bound >";
		}
		g_pVGuiLocalize->ConvertANSIToUnicode( pSaveReplayKey, wKeyBind, sizeof( wKeyBind ) );
		g_pVGuiLocalize->ConstructString_safe( wText, g_pVGuiLocalize->Find( "#Replay_ConnectRecording" ), 1, wKeyBind );
		g_pVGuiLocalize->ConvertUnicodeToANSI( wText, szText, sizeof( szText ) );

		extern ConVar replay_msgduration_connectrecording;
		g_pClientMode->DisplayReplayMessage( szText, replay_msgduration_connectrecording.GetFloat(), false, NULL, true );

		// Don't execute this clause next time the player spawns, unless the cache has been cleared
		++nDisplayedConnectedRecording;

		// Increment (archives)
		replay_replaywelcomedlgcount.SetValue( replay_replaywelcomedlgcount.GetInt() + 1 );
	}
#endif

	// This will complete any pending replay, commit if necessary, and clear - this way when the player respawns
	// we will start with a fresh replay for the new life.
	g_pClientReplayContext->OnPlayerClassChanged();

	// Change class
	BaseClass::OnCommand( CFmtStr( "joinclass %s", g_aRawPlayerClassNames[ iClass ] ).Access() );

	CBroadcastRecipientFilter filter;

	if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
	{
		CBaseEntity::EmitSound( filter, SOUND_FROM_UI_PANEL, "music.mvm_class_select" );
	}
	else
	{
		
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFClassMenu::OnCommand( const char *command )
{
	if ( !V_strnicmp( command, "select", 6 ) )
	{
		const char *pClass = command + 6;
		const int iClass = atoi( pClass );

		// Avoid restarting the animation if the user selected on the same class
		if ( iClass != m_iCurrentClassIndex )
		{
			SelectClass( iClass );
		}
		else
		{
			// Ensure selection states, in case the user clicks a button and drags away
			UpdateButtonSelectionStates( iClass );
		}

		Go();
	}
	else if ( !V_strnicmp( command, "resetclass", 10 ) )
	{
		if ( TFGameRules() && !TFGameRules()->IsInHighlanderMode() )
			return;

		engine->ClientCmd( const_cast<char *>( command ) );
	}
	else if ( !V_strnicmp( command, "openloadout", 11 ) )
	{
		// Let this panel know when you've closed, so we can reload items
		EconUI()->AddPanelCloseListener( this );

		// Make the back button close, rather than go back to the econ root panel
		EconUI()->SetClosePanel( -m_iCurrentClassIndex );

		// Set team number, so the model's color will match
		EconUI()->SetDefaultTeam( GetTeamNumber() );

		// Go directly to the loadout for the selected class
		EconUI()->OpenEconUI( -m_iCurrentClassIndex );	
	}
	else
	{
		BaseClass::OnCommand( command );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Console command to select a class
//-----------------------------------------------------------------------------
void CTFClassMenu::Join_Class( const CCommand &args )
{
	if ( args.ArgC() > 1 )
	{
		char cmd[256];
		Q_snprintf( cmd, sizeof( cmd ), "joinclass %s", args.Arg( 1 ) );
		OnCommand( cmd );
		ShowPanel( false );
	}
}

static const char *g_sDialogVariables[] = {
	"",
	"numScout",
	"numSoldier",
	"numPyro",

	"numDemoman",
	"numHeavy",
	"numEngineer",
	
	"numMedic",
	"numSniper",
	"numSpy",
	"",
};

static const char *g_sClassImagesBlue[] = {
	"",
	"class_sel_sm_scout_blu",
	"class_sel_sm_soldier_blu",
	"class_sel_sm_pyro_blu",

	"class_sel_sm_demo_blu",
	"class_sel_sm_heavy_blu",
	"class_sel_sm_engineer_blu",

	"class_sel_sm_medic_blu",
	"class_sel_sm_sniper_blu",
	"class_sel_sm_spy_blu",

	"class_sel_sm_scout_blu",
};

static const char *g_sClassImagesRed[] = {
	"",
	"class_sel_sm_scout_red",
	"class_sel_sm_soldier_red",
	"class_sel_sm_pyro_red",
	
	"class_sel_sm_demo_red",
	"class_sel_sm_heavy_red",
	"class_sel_sm_engineer_red",
	
	"class_sel_sm_medic_red",
	"class_sel_sm_sniper_red",
	"class_sel_sm_spy_red",

	"class_sel_sm_scout_red",
};

int g_ClassDefinesRemap[] = {
	0,
	TF_CLASS_SCOUT,	
	TF_CLASS_SOLDIER,
	TF_CLASS_PYRO,

	TF_CLASS_DEMOMAN,
	TF_CLASS_HEAVYWEAPONS,
	TF_CLASS_ENGINEER,

	TF_CLASS_MEDIC,
	TF_CLASS_SNIPER,
	TF_CLASS_SPY,
	TF_CLASS_CIVILIAN,
};

void CTFClassMenu::UpdateNumClassLabels( int iTeam )
{
#ifndef _X360
	int nTotalCount = 0;

	// count how many of each class there are
	if ( !g_TF_PR )
		return;

	if ( iTeam < FIRST_GAME_TEAM || iTeam >= TF_TEAM_COUNT ) // invalid team number
		return;

	C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();

	bool bSpectator = pLocalPlayer && pLocalPlayer->GetTeamNumber() == TEAM_SPECTATOR;

	int iLocalPlayerClass = TF_CLASS_UNDEFINED;
	if ( pLocalPlayer )
	{
		iLocalPlayerClass = pLocalPlayer->m_Shared.GetDesiredPlayerClassIndex();
	}

	if ( iLocalPlayerClass == TF_CLASS_UNDEFINED )
	{
		m_iLocalPlayerClass = iLocalPlayerClass;

		if ( m_pLocalPlayerImage && m_pLocalPlayerImage->IsVisible() )
		{
			m_pLocalPlayerImage->SetVisible( false );
		}

		if ( m_pLocalPlayerBG && m_pLocalPlayerBG->IsVisible() )
		{
			m_pLocalPlayerBG->SetVisible( false );
		}
	}

	for( int i = TF_FIRST_NORMAL_CLASS ; i <= TF_LAST_NORMAL_CLASS ; i++ )
	{
		if ( bSpectator == true )
		{
			SetDialogVariable( g_sDialogVariables[i], "" );
			continue;
		}

		int classCount = g_TF_PR->GetCountForPlayerClass( iTeam, g_ClassDefinesRemap[i], false );
		int iClassLimit = TFGameRules()->GetClassLimit( g_ClassDefinesRemap[i] );

		if ( iClassLimit != NO_CLASS_LIMIT )
		{
			if ( classCount >= iClassLimit )
			{
				if ( classCount > 0 )
				{
					wchar_t	wTemp[32];
					wchar_t wzCount[10];
					_snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", classCount );
					g_pVGuiLocalize->ConstructString_safe( wTemp, g_pVGuiLocalize->Find("TF_ClassLimitHit"), 1, wzCount );
					SetDialogVariable( g_sDialogVariables[i], wTemp );
				}
				else
				{
					SetDialogVariable( g_sDialogVariables[i], g_pVGuiLocalize->Find("TF_ClassLimitHit_None") );
				}
			}
			else
			{
				wchar_t	wTemp[32];
				wchar_t wzCount[10];
				_snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", classCount );
				wchar_t wzMax[10];
				_snwprintf( wzMax, ARRAYSIZE( wzMax ), L"%d", iClassLimit );
				g_pVGuiLocalize->ConstructString_safe( wTemp, g_pVGuiLocalize->Find("TF_ClassLimitUnder"), 2, wzCount, wzMax );
				SetDialogVariable( g_sDialogVariables[i], wTemp );
			}
		}
		else if ( classCount > 0 )
		{
			SetDialogVariable( g_sDialogVariables[i], classCount );
		}
		else
		{
			SetDialogVariable( g_sDialogVariables[i], "" );
		}

		if ( g_ClassDefinesRemap[i] == iLocalPlayerClass )
		{
			// take 1 off the count for the images since the local player has their own image already
			if ( classCount > 0 )
			{
				classCount--;
			}

			if ( m_pLocalPlayerImage )
			{
				if ( !m_pLocalPlayerImage->IsVisible() )
				{
					m_pLocalPlayerImage->SetVisible( true );
				}

				if ( m_iLocalPlayerClass != iLocalPlayerClass )
				{
					m_iLocalPlayerClass = iLocalPlayerClass;
					m_pLocalPlayerImage->SetImage( iTeam == TF_TEAM_BLUE ? g_sClassImagesBlue[i] : g_sClassImagesRed[i] );
				}
			}

			if ( m_pLocalPlayerBG && !m_pLocalPlayerBG->IsVisible() )
			{
				m_pLocalPlayerBG->SetVisible( true );
			}
		}

		if ( nTotalCount < CLASS_COUNT_IMAGES )
		{
			for ( int j = 0 ; j < classCount ; ++j )
			{
				CTFImagePanel *pImage = m_ClassCountImages[nTotalCount];
				if ( pImage )
				{
					pImage->SetVisible( true );
					pImage->SetImage( iTeam == TF_TEAM_BLUE ? g_sClassImagesBlue[i] : g_sClassImagesRed[i] );
				}

				nTotalCount++;
				if ( nTotalCount >= CLASS_COUNT_IMAGES )
				{
					break;
				}
			}
		}
	}
	
	if ( nTotalCount == 0 )
	{
		// no classes for our team yet
		if ( m_pCountLabel && m_pCountLabel->IsVisible() )
		{
			m_pCountLabel->SetVisible( false );
		}
	}
	else
	{
		if ( m_pCountLabel && !m_pCountLabel->IsVisible() )
		{
			m_pCountLabel->SetVisible( true );
		}
	}

	// turn off any unused images
	while ( nTotalCount < CLASS_COUNT_IMAGES )
	{
		CTFImagePanel *pImage = m_ClassCountImages[nTotalCount];
		if ( pImage )
		{
			pImage->SetVisible( false );
		}

		nTotalCount++;
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::FireGameEvent( IGameEvent *event )
{
	const char *pszEventName = event->GetName();

	// when we are changing levels
	if ( FStrEq( pszEventName, "localplayer_changeteam" ) )
	{
		if ( IsVisible() )
		{
			C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
			if ( pLocalPlayer )
			{
				int iTeam = pLocalPlayer->GetTeamNumber();
				if ( iTeam != GetTeamNumber() )
				{
					ShowPanel( false );

					if ( iTeam == TF_TEAM_BLUE )
					{
						gViewPortInterface->ShowPanel( PANEL_CLASS_BLUE, true );
					}
					else
					{
						gViewPortInterface->ShowPanel( PANEL_CLASS_RED, true );
					}
				}
			}
		}
	}
	else if ( FStrEq( pszEventName, "show_match_summary" ) )
	{
		if ( IsVisible() )
		{
			ShowPanel( false );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFClassMenu::OnEconUIClosed()
{
	// Reload items on model panel, in case anything's changed
	LoadItems();
}

int g_nNumUpgradeIconsForLastHint = 0;

//-----------------------------------------------------------------------------
void CTFClassMenu::CheckMvMUpgrades()
{
	// Set MvM Icons invisible
	for ( int icons = 0; icons < ARRAYSIZE( m_pMvmUpgradeImages ); ++icons )
	{
		if ( m_pMvmUpgradeImages[icons] == NULL )
			continue;
		m_pMvmUpgradeImages[icons]->SetVisible( false );
	}

	if ( !TFGameRules() || !TFGameRules()->IsMannVsMachineMode() )
		return;

	CMannVsMachineStats *pStats = MannVsMachineStats_GetInstance();
	if ( !pStats )
		return;

	CUtlVector< CUpgradeInfo > *upgrades = pStats->GetLocalPlayerUpgrades();

	int nShowUpgradingHint = -1;
	int nNumUpgradeIconsForHint = 0;

	for ( int i = 0; i < upgrades->Count(); ++i )
	{
		vgui::Panel *pUpgradeImage = m_pMvmUpgradeImages[upgrades->Element(i).m_iPlayerClass];

		if ( !pUpgradeImage )
			continue;

		if ( !pUpgradeImage->IsVisible() )
		{
			pUpgradeImage->SetVisible( true );
			nNumUpgradeIconsForHint++;

			// Only show the hint if we've shown it 3 or less times ever
			if ( nShowUpgradingHint == -1 && tf_mvm_classupgradehelpcount.GetInt() < 3 )
			{
				int nY;
				pUpgradeImage->GetPos( nShowUpgradingHint, nY );
				nShowUpgradingHint += pUpgradeImage->GetWide() / 2;
			}
		}
	}

	// Only show the hint if there are more upgrade icon than the last time we openned the menu
	if ( nShowUpgradingHint != -1 && g_nNumUpgradeIconsForLastHint < nNumUpgradeIconsForHint )
	{
		CExplanationPopup *pPopup = dynamic_cast< CExplanationPopup* >( FindChildByName("StartExplanation") );
		if ( pPopup )
		{
			pPopup->SetCalloutInParentsX( nShowUpgradingHint );
			pPopup->Popup();

			g_nNumUpgradeIconsForLastHint = nNumUpgradeIconsForHint;
			tf_mvm_classupgradehelpcount.SetValue( tf_mvm_classupgradehelpcount.GetInt() + 1 );
		}
	}
}