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


#include "cbase.h"
#include "class_loadout_panel.h"
#include "c_tf_player.h"
#include "vgui_controls/CheckButton.h"
#include "econ_gcmessages.h"
#include "gc_clientsystem.h"
#include "tf_item_system.h"
#include "loadout_preset_panel.h"
#include "econ_item_description.h"
#include "item_style_select_dialog.h"
#include "vgui/IInput.h"
#include "vgui_controls/PanelListPanel.h"

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

extern ConVar tf_respawn_on_loadoutchanges;

ConVar tf_show_preset_explanation_in_class_loadout( "tf_show_preset_explanation_in_class_loadout", "1", FCVAR_HIDDEN | FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
ConVar tf_show_taunt_explanation_in_class_loadout( "tf_show_taunt_explanation_in_class_loadout", "1", FCVAR_HIDDEN | FCVAR_CLIENTDLL | FCVAR_ARCHIVE );

void ParticleSlider_UpdateRequest( int iLoadoutPosition, float value )
{
	CClassLoadoutPanel *pPanel = g_pClassLoadoutPanel;
	if ( !pPanel )
		return;

	CEconItemView *pHat = pPanel->GetItemInSlot( iLoadoutPosition );
	if ( !pHat )
		return;

	// does this hat even have a particle effect
	static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
	uint32 iHasEffect = 0;
	if ( !pHat->FindAttribute( pAttrDef_AttachParticleEffect, &iHasEffect ) )
		return;

	// Check for use head toggle
	static CSchemaAttributeDefHandle pAttrDef_UseHeadOrigin( "particle effect use head origin" );
	uint32 iUseHead = 0;
	if ( !pHat->FindAttribute( pAttrDef_UseHeadOrigin, &iUseHead ) || iUseHead == 0 )
		return;

	// Look for the attribute and request to change it
	static CSchemaAttributeDefHandle pAttrDef_VerticalOffset( "particle effect vertical offset" );
	uint32 iOffSet = 0;
	if ( !pHat->FindAttribute( pAttrDef_VerticalOffset, &iOffSet ) && value == 0 )
	{
		return;
	}
	else
	{
		const float& flAttrValue = (float&)iOffSet;
		if ( value == flAttrValue )
			return; // no change do nothing
	}

	// Send a message to the GC to request a change
	GCSDK::CProtoBufMsg<CMsgSetItemEffectVerticalOffset> msg( k_EMsgGCSetItemEffectVerticalOffset );
	msg.Body().set_item_id( pHat->GetItemID() );
	msg.Body().set_offset( value );
	GCClientSystem()->BSendMessage( msg );
}

void HatOffset_Callback( IConVar *pConVar, char const *pOldString, float flOldValue )
{
	ConVarRef cVarRef( pConVar );
	ParticleSlider_UpdateRequest( LOADOUT_POSITION_HEAD, cVarRef.GetFloat() );
}
ConVar tf_hat_effect_offset( "tf_hat_effect_offset", "0", FCVAR_DEVELOPMENTONLY, "Adjust the position of the unusual effect for your hat.", HatOffset_Callback );

void Misc1Offset_Callback( IConVar *pConVar, char const *pOldString, float flOldValue )
{
	ConVarRef cVarRef( pConVar );
	ParticleSlider_UpdateRequest( LOADOUT_POSITION_MISC, cVarRef.GetFloat() );
}
ConVar tf_misc1_effect_offset( "tf_misc1_effect_offset", "0", FCVAR_DEVELOPMENTONLY, "Adjust the position of the unusual effect for your hat.", Misc1Offset_Callback );

void Misc2Offset_Callback( IConVar *pConVar, char const *pOldString, float flOldValue )
{
	ConVarRef cVarRef( pConVar );
	ParticleSlider_UpdateRequest( LOADOUT_POSITION_MISC2, cVarRef.GetFloat() );
}
ConVar tf_misc2_effect_offset( "tf_misc2_effect_offset", "0", FCVAR_DEVELOPMENTONLY, "Adjust the position of the unusual effect for your hat.", Misc2Offset_Callback );


// Hacky solution to different classes wanting different slots visible in their loadouts, and in different positions
struct LoadoutPanelPositioningInstance
{
	int m_iPos[NUM_ITEM_PANELS_IN_LOADOUT];
};

bool IsTauntPanelPosition( int iButtonPos )
{
	return iButtonPos >= 9 && iButtonPos <= 16;
}

const LoadoutPanelPositioningInstance g_DefaultLoadoutPanelPositioning =
{
	{
		1,	// LOADOUT_POSITION_PRIMARY = 0,
		2,	// LOADOUT_POSITION_SECONDARY,
		3,	// LOADOUT_POSITION_MELEE,
		0,	// LOADOUT_POSITION_UTILITY,  // STAGING ONLY
		0,	// LOADOUT_POSITION_BUILDING,
		0,	// LOADOUT_POSITION_PDA,
		0,	// LOADOUT_POSITION_PDA2,
		5,	// LOADOUT_POSITION_HEAD,
		6,	// LOADOUT_POSITION_MISC,
		8,	// LOADOUT_POSITION_ACTION,
		7,	// LOADOUT_POSITION_MISC2,
		9,	// LOADOUT_POSITION_TAUNT,
		10,	// LOADOUT_POSITION_TAUNT2,
		11,	// LOADOUT_POSITION_TAUNT3,
		12,	// LOADOUT_POSITION_TAUNT4,
		13,	// LOADOUT_POSITION_TAUNT5,
		14,	// LOADOUT_POSITION_TAUNT6,
		15,	// LOADOUT_POSITION_TAUNT7,
		16,	// LOADOUT_POSITION_TAUNT8,

#ifdef STAGING_ONLY
		0,	// LOADOUT_POSITION_PDA_ADDON1,
		0,	// LOADOUT_POSITION_PDA_ADDON2,
		0,  // LOADOUT_POSITION_PDA3,
		//9,	// LOADOUT_POSITION_MISC3,
		//10,	// LOADOUT_POSITION_MISC4,
		//11,	// LOADOUT_POSITION_MISC5,
		//12,	// LOADOUT_POSITION_MISC6,
		//13,	// LOADOUT_POSITION_MISC7,
		//14,	// LOADOUT_POSITION_MISC8,
		//15,	// LOADOUT_POSITION_MISC9,
		//16,	// LOADOUT_POSITION_MISC10,
		0,	// LOADOUT_POSITION_BUILDING2,
#endif // STAGING_ONLY
	}
};

const LoadoutPanelPositioningInstance g_LoadoutPanelPositioning_Spy =
{
	{
		0,	// LOADOUT_POSITION_PRIMARY = 0,
		1,	// LOADOUT_POSITION_SECONDARY,
		2,	// LOADOUT_POSITION_MELEE,
		0,	// LOADOUT_POSITION_UTILITY,  // STAGING ONLY
		4,	// LOADOUT_POSITION_BUILDING,		// sapper
		0,	// LOADOUT_POSITION_PDA,			// disguise kit (Hidden)
		3,	// LOADOUT_POSITION_PDA2,			// Watch
		5,	// LOADOUT_POSITION_HEAD,
		6,	// LOADOUT_POSITION_MISC,
		8,	// LOADOUT_POSITION_ACTION,
		7,	// LOADOUT_POSITION_MISC2,
		9,	// LOADOUT_POSITION_TAUNT,
		10,	// LOADOUT_POSITION_TAUNT2,
		11,	// LOADOUT_POSITION_TAUNT3,
		12,	// LOADOUT_POSITION_TAUNT4,
		13,	// LOADOUT_POSITION_TAUNT5,
		14,	// LOADOUT_POSITION_TAUNT6,
		15,	// LOADOUT_POSITION_TAUNT7,
		16,	// LOADOUT_POSITION_TAUNT8,
#ifdef STAGING_ONLY
		0,	// LOADOUT_POSITION_PDA_ADDON1,
		0,	// LOADOUT_POSITION_PDA_ADDON2,
		0,  // LOADOUT_POSITION_PDA3,
		//9,	// LOADOUT_POSITION_MISC3,
		//10,	// LOADOUT_POSITION_MISC4,
		//11,	// LOADOUT_POSITION_MISC5,
		//12,	// LOADOUT_POSITION_MISC6,
		//13,	// LOADOUT_POSITION_MISC7,
		//14,	// LOADOUT_POSITION_MISC8,
		//15,	// LOADOUT_POSITION_MISC9,
		//16,	// LOADOUT_POSITION_MISC10,
		0,	// LOADOUT_POSITION_BUILDING2,
#endif // STAGING_ONLY
	}
};

const LoadoutPanelPositioningInstance g_LoadoutPanelPositioning_Engineer =
{
	{
		1,	// LOADOUT_POSITION_PRIMARY = 0,
		2,	// LOADOUT_POSITION_SECONDARY,
		3,	// LOADOUT_POSITION_MELEE,
		0,	// LOADOUT_POSITION_UTILITY,  // STAGING ONLY
		0,	// LOADOUT_POSITION_BUILDING,
		4,	// LOADOUT_POSITION_PDA,
		0,	// LOADOUT_POSITION_PDA2,
		5,	// LOADOUT_POSITION_HEAD,
		6,	// LOADOUT_POSITION_MISC,
		8,	// LOADOUT_POSITION_ACTION,
		7,	// LOADOUT_POSITION_MISC2,
		9,	// LOADOUT_POSITION_TAUNT,
		10,	// LOADOUT_POSITION_TAUNT2,
		11,	// LOADOUT_POSITION_TAUNT3,
		12,	// LOADOUT_POSITION_TAUNT4,
		13,	// LOADOUT_POSITION_TAUNT5,
		14,	// LOADOUT_POSITION_TAUNT6,
		15,	// LOADOUT_POSITION_TAUNT7,
		16,	// LOADOUT_POSITION_TAUNT8,
#ifdef STAGING_ONLY
		17,	// LOADOUT_POSITION_PDA_ADDON1,
		18,	// LOADOUT_POSITION_PDA_ADDON2,
		0,  // LOADOUT_POSITION_PDA3,
		//9,	// LOADOUT_POSITION_MISC3,
		//10,	// LOADOUT_POSITION_MISC4,
		//11,	// LOADOUT_POSITION_MISC5,
		//12,	// LOADOUT_POSITION_MISC6,
		//13,	// LOADOUT_POSITION_MISC7,
		//14,	// LOADOUT_POSITION_MISC8,
		//15,	// LOADOUT_POSITION_MISC9,
		//16,	// LOADOUT_POSITION_MISC10,
		0,	// LOADOUT_POSITION_BUILDING2,
#endif // STAGING_ONLY
	}
};

const LoadoutPanelPositioningInstance *g_VisibleLoadoutSlotsPerClass[] =
{
	&g_DefaultLoadoutPanelPositioning,			// TF_CLASS_UNDEFINED
	&g_DefaultLoadoutPanelPositioning,			// TF_CLASS_SCOUT
	&g_DefaultLoadoutPanelPositioning,			// TF_CLASS_SNIPER
	&g_DefaultLoadoutPanelPositioning,			// TF_CLASS_SOLDIER
	&g_DefaultLoadoutPanelPositioning,			// TF_CLASS_DEMOMAN
	&g_DefaultLoadoutPanelPositioning,			// TF_CLASS_MEDIC
	&g_DefaultLoadoutPanelPositioning,			// TF_CLASS_HEAVYWEAPONS
	&g_DefaultLoadoutPanelPositioning,			// TF_CLASS_PYRO
	&g_LoadoutPanelPositioning_Spy,				// TF_CLASS_SPY
	&g_LoadoutPanelPositioning_Engineer,		// TF_CLASS_ENGINEER
};

COMPILE_TIME_ASSERT( ARRAYSIZE( g_VisibleLoadoutSlotsPerClass ) == TF_LAST_NORMAL_CLASS );

//-----------------------------------------------------------------------------
// Particle Effect Slider
//-----------------------------------------------------------------------------
CLoadoutItemOptionsPanel::CLoadoutItemOptionsPanel( Panel *parent, const char *pName ) : vgui::EditablePanel( parent, pName )
{
	m_pHatParticleSlider = NULL;
	m_pHatParticleUseHeadButton = NULL;

	m_iCurrentClassIndex = -1;
	m_eItemSlot = LOADOUT_POSITION_INVALID;

	m_pListPanel = new vgui::PanelListPanel( this, "PanelListPanel" );
	m_pListPanel->SetFirstColumnWidth( 0 );
	m_pHatParticleSlider = new CCvarSlider( m_pListPanel, "HatParticleSlider" );
	m_pHatParticleSlider->AddActionSignalTarget( this );
	m_pHatParticleUseHeadButton = new vgui::CheckButton( m_pListPanel, "HatUseHeadCheckButton", "#GameUI_ParticleHatUseHead" );
	m_pHatParticleUseHeadButton->AddActionSignalTarget( this );
	m_pSetStyleButton = new CExButton( m_pListPanel, "SetStyleButton", "#TF_Item_SelectStyle" );
	m_pSetStyleButton->AddActionSignalTarget( this );
}

//-----------------------------------------------------------------------------
void CLoadoutItemOptionsPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );
	LoadControlSettings( "resource/UI/ItemOptionsPanel.res" );
}

//-----------------------------------------------------------------------------
void CLoadoutItemOptionsPanel::PerformLayout( void ) 
{
	BaseClass::PerformLayout();
	m_pHatParticleSlider->SetTickColor( Color( 235, 226, 202, 255 ) ); // tanlight
}

//-----------------------------------------------------------------------------
void CLoadoutItemOptionsPanel::OnCommand( const char *command )
{
	if ( FStrEq( command, "particle_button_clicked" ) )
	{
		UpdateItemOptionsUI();
		return;
	}
	else if ( FStrEq( command, "particle_use_head_clicked" ) )
	{
		// Grab current hat
		CEconItemView *pHat = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot );
		if ( !pHat )
			return;

		// does this hat even have a particle effect
		static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
		uint32 iHasEffect = 0;
		if ( !pHat->FindAttribute( pAttrDef_AttachParticleEffect, &iHasEffect ) )
			return;

		// Send a message to the GC to request a change
		GCSDK::CProtoBufMsg<CMsgSetHatEffectUseHeadOrigin> msg( k_EMsgGCSetHatEffectUseHeadOrigin );
		msg.Body().set_item_id( pHat->GetItemID() );
		msg.Body().set_use_head( m_pHatParticleUseHeadButton->IsSelected() );
		GCClientSystem()->BSendMessage( msg );
		return;
	}
	else if ( FStrEq( command, "set_style" ) )
	{
		CEconItemView *pHat = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot );
		CStyleSelectDialog *pStyle = vgui::SETUP_PANEL( new CStyleSelectDialog( GetParent(), pHat ) );
		if ( pStyle )
		{
			pStyle->Show();
		}
	}
}

//-----------------------------------------------------------------------------
void CLoadoutItemOptionsPanel::OnMessage( const KeyValues* pParams, vgui::VPANEL hFromPanel )
{
	if ( FStrEq( pParams->GetName(), "SliderDragEnd" ) )
	{
		m_pHatParticleSlider->ApplyChanges();
	}

	BaseClass::OnMessage( pParams, hFromPanel );
}

//-----------------------------------------------------------------------------
void CLoadoutItemOptionsPanel::SetItemSlot( loadout_positions_t eItemSlot, int iClassIndex )
{
	m_eItemSlot = eItemSlot;
	m_iCurrentClassIndex = iClassIndex;
	// Init the Slider based on the slot
	const char * pszConVarName = NULL;

	switch ( eItemSlot )
	{
	case LOADOUT_POSITION_HEAD :
		pszConVarName = "tf_hat_effect_offset";
		break;
	case LOADOUT_POSITION_MISC :
		pszConVarName = "tf_misc1_effect_offset";
		break;
	case LOADOUT_POSITION_MISC2 :
		pszConVarName = "tf_misc2_effect_offset";
		break;
	default:
		break;
	}
	
	if ( pszConVarName )
	{
		m_pHatParticleSlider->SetupSlider( -8, 8, pszConVarName, false );
	}
	
	m_pHatParticleSlider->SetTickColor( Color( 235, 226, 202, 255 ) ); // tanlight
	m_pHatParticleSlider->SetTickCaptions( "", "" );
	
	UpdateItemOptionsUI();
}

//-----------------------------------------------------------------------------
void CLoadoutItemOptionsPanel::UpdateItemOptionsUI()
{
	if ( m_eItemSlot == LOADOUT_POSITION_INVALID )
		return;

	m_pListPanel->RemoveAll();

	// Add controls for various item options
	AddControlsParticleEffect();
	AddControlsSetStyle();

	// Bail if no controls added
	if ( m_pListPanel->GetItemCount() == 0 )
	{
		// We should have some controls if we get to this point.
		Assert( 0 );
		SetVisible( false );
		return;
	}

	// Resize the background and list panel to contain all the controls
	int nVertPixels = m_pListPanel->ComputeVPixelsNeeded();
	int nNewTall = Min( 200, nVertPixels );
	m_pListPanel->SetTall( nNewTall );
	SetTall( nNewTall );
	InvalidateLayout( true, false );
}

//-----------------------------------------------------------------------------
void CLoadoutItemOptionsPanel::AddControlsParticleEffect( void ) const
{
	m_pHatParticleUseHeadButton->SetVisible( false );
	m_pHatParticleSlider->SetVisible( false );

	CEconItemView *pItem = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot );
	if ( pItem )
	{
		// does this hat even have a particle effect
		static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
		uint32 iValue = 0;
		if ( pItem->FindAttribute( pAttrDef_AttachParticleEffect, &iValue ) )
		{
			m_pHatParticleUseHeadButton->SetVisible( true );
			m_pListPanel->AddItem( NULL, m_pHatParticleUseHeadButton );
			m_pListPanel->AddItem( NULL, m_pHatParticleSlider );

			// Check for use head toggle
			static CSchemaAttributeDefHandle pAttrDef_UseHeadOrigin( "particle effect use head origin" );
			uint32 iUseHead = 0;
			if ( pItem->FindAttribute( pAttrDef_UseHeadOrigin, &iUseHead ) && iUseHead > 0 )
			{
				m_pHatParticleSlider->SetVisible( true );

				m_pHatParticleUseHeadButton->SetSelected( true );
				m_pHatParticleSlider->SetTickColor( Color( 235, 226, 202, 255 ) ); // tanlight
				m_pHatParticleSlider->Repaint();

				// Get offset if it exists
				static CSchemaAttributeDefHandle pAttrDef_VerticalOffset( "particle effect vertical offset" );
				uint32 iOffset = 0;
				if ( pItem->FindAttribute( pAttrDef_VerticalOffset, &iOffset ) )
				{
					m_pHatParticleSlider->SetSliderValue( (float&)iOffset );
				}
			}
			else
			{
				m_pHatParticleUseHeadButton->SetSelected( false );
			}
		}
	}
}

//-----------------------------------------------------------------------------
void CLoadoutItemOptionsPanel::AddControlsSetStyle( void ) const
{
	m_pSetStyleButton->SetVisible( false );

	CEconItemView *pItem = GetItem();
	if ( pItem && pItem->GetStaticData()->GetNumStyles() )
	{
		m_pSetStyleButton->SetVisible( true );
		m_pListPanel->AddItem( NULL, m_pSetStyleButton );
	}
}

//-----------------------------------------------------------------------------
CEconItemView* CLoadoutItemOptionsPanel::GetItem( void ) const
{ 
	return TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot );
}

CClassLoadoutPanel *g_pClassLoadoutPanel = NULL;
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CClassLoadoutPanel::CClassLoadoutPanel( vgui::Panel *parent ) 
	: CBaseLoadoutPanel( parent, "class_loadout_panel" )
	, m_pItemOptionPanelKVs( NULL )
{
	m_iCurrentClassIndex = TF_CLASS_UNDEFINED;
	m_iCurrentTeamIndex = TF_TEAM_RED;
	m_iCurrentSlotIndex = -1;
	m_pPlayerModelPanel = NULL;
	m_pSelectionPanel = NULL;
	m_pTauntHintLabel = NULL;
	m_pTauntLabel = NULL;
	m_pTauntCaratLabel = NULL;
	m_pPassiveAttribsLabel = NULL;
	m_pLoadoutPresetPanel = NULL;
	m_pPresetsExplanationPopup = NULL;
	m_pTauntsExplanationPopup = NULL;
	m_pBuildablesButton = NULL;
	
	m_pCharacterLoadoutButton = NULL;
	m_pTauntLoadoutButton = NULL;

	m_bInTauntLoadoutMode = false;

	g_pClassLoadoutPanel = this;

	m_pItemOptionPanel = new CLoadoutItemOptionsPanel( this, "ItemOptionsPanel" );
}

CClassLoadoutPanel::~CClassLoadoutPanel()
{
	if ( m_pItemOptionPanelKVs )
	{
		m_pItemOptionPanelKVs->deleteThis();
		m_pItemOptionPanelKVs = NULL;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
{
	LoadControlSettings( "Resource/UI/ClassLoadoutPanel.res" );

	BaseClass::ApplySchemeSettings( pScheme );

	m_pPlayerModelPanel = dynamic_cast<CTFPlayerModelPanel*>( FindChildByName("classmodelpanel") );
	m_pTauntHintLabel = dynamic_cast<vgui::Label*>( FindChildByName("TauntHintLabel") );
	m_pTauntLabel = dynamic_cast<CExLabel*>( FindChildByName("TauntLabel") );
	m_pTauntCaratLabel = dynamic_cast<CExLabel*>( FindChildByName("TauntCaratLabel") );
	m_pBuildablesButton = dynamic_cast<CExButton*>( FindChildByName("BuildablesButton") );
	m_pCharacterLoadoutButton = dynamic_cast<CExImageButton*>( FindChildByName("CharacterLoadoutButton") );
	m_pTauntLoadoutButton = dynamic_cast<CExImageButton*>( FindChildByName("TauntLoadoutButton") );
	m_pPassiveAttribsLabel = dynamic_cast<CExLabel*>( FindChildByName("PassiveAttribsLabel") );
	m_pLoadoutPresetPanel = dynamic_cast<CLoadoutPresetPanel*>( FindChildByName( "loadout_preset_panel" ) );
	m_pPresetsExplanationPopup = dynamic_cast<CExplanationPopup*>( FindChildByName( "PresetsExplanation" ) );
	m_pTauntsExplanationPopup = dynamic_cast<CExplanationPopup*>( FindChildByName( "TauntsExplanation" ) );
	m_pTopLinePanel = FindChildByName( "TopLine" );
	if ( m_pPassiveAttribsLabel )
	{
		m_pPassiveAttribsLabel->SetMouseInputEnabled( false );
	}

	m_pMouseOverTooltip->SetPositioningStrategy( IPTTP_BOTTOM_SIDE );

	m_aDefaultColors[LOADED][FG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDefaultColorFg", Color( 255, 255, 255, 255 ) );
	m_aDefaultColors[LOADED][FG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetArmedColorFg", Color( 255, 255, 255, 255 ) );
	m_aDefaultColors[LOADED][FG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDepressedColorFg", Color( 255, 255, 255, 255 ) );

	m_aDefaultColors[LOADED][BG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDefaultColorBg", Color( 255, 255, 255, 255 ) );
	m_aDefaultColors[LOADED][BG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetArmedColorBg", Color( 255, 255, 255, 255 ) );
	m_aDefaultColors[LOADED][BG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDepressedColorBg", Color( 255, 255, 255, 255 ) );

	m_aDefaultColors[NOTLOADED][FG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.TextColor", Color( 255, 255, 255, 255 ) );
	m_aDefaultColors[NOTLOADED][FG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.ArmedTextColor", Color( 255, 255, 255, 255 ) );
	m_aDefaultColors[NOTLOADED][FG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.DepressedTextColor", Color( 255, 255, 255, 255 ) );

	m_aDefaultColors[NOTLOADED][BG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.BgColor", Color( 255, 255, 255, 255 ) );
	m_aDefaultColors[NOTLOADED][BG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.ArmedBgColor", Color( 255, 255, 255, 255 ) );
	m_aDefaultColors[NOTLOADED][BG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.DepressedBgColor", Color( 255, 255, 255, 255 ) );
}


void CClassLoadoutPanel::ApplySettings( KeyValues *inResourceData )
{
	BaseClass::ApplySettings( inResourceData );

	KeyValues *pItemKV = inResourceData->FindKey( "itemoptionpanels_kv" );
	if ( pItemKV )
	{
		if ( m_pItemOptionPanelKVs )
		{
			m_pItemOptionPanelKVs->deleteThis();
		}
		m_pItemOptionPanelKVs = new KeyValues("itemoptionpanels_kv");
		pItemKV->CopySubkeys( m_pItemOptionPanelKVs );
	}

#ifdef STAGING_ONLY
	// PDA Panels
	if ( m_pItemModelPanels.Count() > LOADOUT_POSITION_PDA_ADDON2 )
	{
		for ( int i = LOADOUT_POSITION_PDA_ADDON1; i <= LOADOUT_POSITION_PDA_ADDON2; i++ )
		{
			int wide = m_pItemModelPanels[i]->GetWide();
			int tall = m_pItemModelPanels[i]->GetTall();

			m_pItemModelPanels[i]->SetSize( wide / 2, tall / 2 );
			m_pItemModelPanels[i]->InvalidateLayout();
		}
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::PerformLayout( void ) 
{
	BaseClass::PerformLayout();

	// This is disabled by default in res file. IF we turn it on again, uncomment this.
	/*if ( m_pPassiveAttribsLabel )
	{
		m_pPassiveAttribsLabel->SetVisible( !m_bInTauntLoadoutMode );
	}*/

	if ( m_pTauntHintLabel )
	{
		m_pTauntHintLabel->SetVisible( m_bInTauntLoadoutMode );
		
		const char *key = engine->Key_LookupBinding( "taunt" );
		if ( !key )
		{
			key = "< not bound >";
		}
		SetDialogVariable( "taunt", key );
	}

	if ( m_pTauntLabel )
	{
		m_pTauntLabel->SetVisible( m_bInTauntLoadoutMode );
	}
	if ( m_pTauntCaratLabel )
	{
		m_pTauntCaratLabel->SetVisible( m_bInTauntLoadoutMode );
	}
	if ( m_pCharacterLoadoutButton )
	{
		UpdatePageButtonColor( m_pCharacterLoadoutButton, !m_bInTauntLoadoutMode );
	}
	if ( m_pTauntLoadoutButton )
	{
		UpdatePageButtonColor( m_pTauntLoadoutButton, m_bInTauntLoadoutMode );
	}

	FOR_EACH_VEC( m_vecItemOptionButtons, i )
	{
		m_vecItemOptionButtons[i]->SetVisible( false );
	}
	
	for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
	{
		// Viewing a class loadout. Layout the buttons & the class image.
		if ( i >= NUM_ITEM_PANELS_IN_LOADOUT )
		{
			m_pItemModelPanels[i]->SetVisible( false );
			continue;
		}

		int iButtonPos = 0;
		if ( m_iCurrentClassIndex != TF_CLASS_UNDEFINED )
		{
			iButtonPos = g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[i];
		}

		bool bIsVisible = false;
		if ( iButtonPos > 0 )
		{
			bIsVisible = m_bInTauntLoadoutMode ? IsTauntPanelPosition( iButtonPos ) : !IsTauntPanelPosition( iButtonPos );
		}
		m_pItemModelPanels[i]->SetVisible( bIsVisible );

		if ( bIsVisible )
		{
			if ( m_bInTauntLoadoutMode )
			{
				iButtonPos -= g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[LOADOUT_POSITION_TAUNT];
			}
			else
			{
				iButtonPos--;
			}
			
#ifdef STAGING_ONLY
			// Override for the PDA AddOnSlots
			if ( i == LOADOUT_POSITION_PDA_ADDON1 )
			{
				int iYPos, iXPos;
				m_pItemModelPanels[ LOADOUT_POSITION_PDA ]->GetPos( iXPos, iYPos );
				int iWide, iTall;
				m_pItemModelPanels[ LOADOUT_POSITION_PDA ]->GetSize( iWide, iTall );

				m_pItemModelPanels[i]->SetPos( iXPos + iWide + XRES(1), iYPos );
				m_pItemModelPanels[i]->SetSize( iWide / 2.1, iTall / 2.1 );
				continue;
			}
			else if ( i == LOADOUT_POSITION_PDA_ADDON2 )
			{
				int iYPos, iXPos;
				m_pItemModelPanels[LOADOUT_POSITION_PDA]->GetPos( iXPos, iYPos );
				int iWide, iTall;
				m_pItemModelPanels[LOADOUT_POSITION_PDA]->GetSize( iWide, iTall );

				m_pItemModelPanels[i]->SetPos( iXPos + iWide + XRES(1), iYPos + iTall - (iTall / 2.1 ) );
				m_pItemModelPanels[i]->SetSize( iWide / 2.1, iTall / 2.1 );
				continue;
			}
#endif

			m_pItemModelPanels[i]->SetNoItemText( ItemSystem()->GetItemSchema()->GetLoadoutStringsForDisplay( EEquipType_t::EQUIP_TYPE_CLASS )[i] );

			int iCenter = GetWide() * 0.5;
			int iColumnHeight = 4;
			int iColumn = iButtonPos / iColumnHeight;
			int iYButtonPos = iButtonPos % iColumnHeight;
			
			int iOffset = iColumn == 0 ? m_iItemXPosOffcenterA : m_iItemXPosOffcenterB + ((iColumn - 1) * 200);
			int	iXPos = iCenter + iOffset;
			int	iYPos = m_iItemYPos + (m_iItemYDelta * iYButtonPos);
			m_pItemModelPanels[i]->SetPos( iXPos, iYPos );

			// Update position and visibility of the item option buttons
			if ( i < m_vecItemOptionButtons.Count() )
			{
				// Place the button just inside the item model panel
				CExButton* pItemOptionsPanel = m_vecItemOptionButtons[iButtonPos];
				int iButtonWide = m_pItemModelPanels[i]->GetWide();
				int iMyWide = pItemOptionsPanel->GetWide();
				int iOptionsXPos = iColumn == 0 
								 ? iXPos + iButtonWide - iMyWide
								 : iXPos;
				pItemOptionsPanel->SetPos( iOptionsXPos, iYPos );

				CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i );
				// Enable or disable the item options button for this item model panel
				pItemOptionsPanel->SetVisible( !m_bInTauntLoadoutMode && AnyOptionsAvailableForItem( pItemData ) );
				pItemOptionsPanel->SetCommand( CFmtStr( "options%d", i ) );
			}
		}

		m_pItemModelPanels[ i ]->SetSelected( false );
	}

	if ( m_pLoadoutPresetPanel )
	{
		m_pLoadoutPresetPanel->SetPos( ( ScreenWidth() - m_pLoadoutPresetPanel->GetWide() ) / 2, m_iItemYPos );
	}

	LinkModelPanelControllerNavigation( true );
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::OnKeyCodePressed( vgui::KeyCode code )
{
	// See if the preset control uses this key
	if( m_pLoadoutPresetPanel->HandlePresetKeyPressed( code ) )
	{
		return;
	}

	ButtonCode_t nButtonCode = GetBaseButtonCode( code );

	if (nButtonCode == KEY_XBUTTON_LEFT || 
		nButtonCode == KEY_XSTICK1_LEFT ||
		nButtonCode == KEY_XSTICK2_LEFT ||
		nButtonCode == STEAMCONTROLLER_DPAD_LEFT ||
		code == KEY_LEFT ||
		nButtonCode == KEY_XBUTTON_RIGHT || 
		nButtonCode == KEY_XSTICK1_RIGHT ||
		nButtonCode == KEY_XSTICK2_RIGHT ||
		nButtonCode == STEAMCONTROLLER_DPAD_RIGHT ||
		code == KEY_RIGHT ||
		nButtonCode == KEY_XBUTTON_UP || 
		nButtonCode == KEY_XSTICK1_UP ||
		nButtonCode == KEY_XSTICK2_UP ||
		nButtonCode == STEAMCONTROLLER_DPAD_UP ||
		code == KEY_UP ||
		nButtonCode == KEY_XBUTTON_DOWN || 
		nButtonCode == KEY_XSTICK1_DOWN ||
		nButtonCode == KEY_XSTICK2_DOWN ||
		nButtonCode == STEAMCONTROLLER_DPAD_DOWN ||
		code == KEY_DOWN )
	{
		// just eat all navigation keys so we don't 
		// end up with undesirable navigation behavior bubbling from 
		// one item model panel to another
	}
	else if( nButtonCode == KEY_XBUTTON_A || code == KEY_ENTER || nButtonCode == STEAMCONTROLLER_A )
	{
		// show the current loadout slot
		int nSelected = GetFirstSelectedItemIndex( true );
		if( nSelected != -1 )
		{
			OnCommand( VarArgs("change%d", nSelected ) );
		}
	}
	else
	{
		BaseClass::OnKeyCodePressed( code );
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::OnNavigateTo( const char* panelName )
{
	CItemModelPanel *pChild = dynamic_cast<CItemModelPanel *>( FindChildByName( panelName ) );
	if( !pChild )
		return;

	pChild->SetSelected( true );
	SetBorderForItem( pChild, false );
	pChild->RequestFocus();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::OnNavigateFrom( const char* panelName )
{
	CItemModelPanel *pChild = dynamic_cast<CItemModelPanel *>( FindChildByName( panelName ) );
	if( !pChild )
		return;

	pChild->SetSelected( false );
	SetBorderForItem( pChild, false );
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::OnShowPanel( bool bVisible, bool bReturningFromArmory )
{
	if ( bVisible )
	{
		// always start in character loadout page
		SetLoadoutPage( CHARACTER_LOADOUT_PAGE );

		if ( m_pSelectionPanel )
		{
			m_pSelectionPanel->SetVisible( false );
			m_pSelectionPanel->MarkForDeletion();
			m_pSelectionPanel = NULL;
		}

		m_iCurrentSlotIndex = TF_WPN_TYPE_PRIMARY;
		if( m_pItemModelPanels.Count() && m_pItemModelPanels[0] )
		{
			m_pItemModelPanels[0]->SetSelected( true );
			SetBorderForItem( m_pItemModelPanels[0], false );
		}

		m_bLoadoutHasChanged = false;

		if ( tf_show_preset_explanation_in_class_loadout.GetBool() && m_pPresetsExplanationPopup )
		{
			m_pPresetsExplanationPopup->Popup();
			tf_show_preset_explanation_in_class_loadout.SetValue( 0 );
		}
		else if ( tf_show_taunt_explanation_in_class_loadout.GetBool() && m_pTauntsExplanationPopup )
		{
			m_pTauntsExplanationPopup->Popup();
			tf_show_taunt_explanation_in_class_loadout.SetValue( 0 );
		}

		ClearItemOptionsMenu();
	}
	else
	{
		if ( m_pPlayerModelPanel )
		{
			m_pPlayerModelPanel->ClearCarriedItems();
		}
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::PostShowPanel( bool bVisible )
{
	if ( bVisible )
	{
		if ( m_pPlayerModelPanel )
		{
			m_pPlayerModelPanel->SetVisible( true );
		}

		if ( m_pBuildablesButton )
		{
			m_pBuildablesButton->SetVisible( m_iCurrentClassIndex == TF_CLASS_ENGINEER );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::SetClass( int iClass )
{
	m_iCurrentClassIndex = iClass;

	if ( m_pLoadoutPresetPanel )
	{
		m_pLoadoutPresetPanel->SetClass( m_iCurrentClassIndex );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::SetTeam( int iTeam )
{
	Assert( IsValidTFTeam( iTeam ) );
	m_iCurrentTeamIndex = iTeam;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CClassLoadoutPanel::GetNumRelevantSlots() const
{
	return m_pItemModelPanels.Count();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CEconItemView *CClassLoadoutPanel::GetItemInSlot( int iSlot )
{
	if( iSlot >= 0 && iSlot < m_pItemModelPanels.Count() )
	{
		return m_pItemModelPanels[iSlot]->GetItem();
	}

	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::FireGameEvent( IGameEvent *event )
{
	// If we're not visible, ignore all events
	if ( !IsVisible() )
		return;

	BaseClass::FireGameEvent( event );

	// We need to update ourselves after the base has done it, so our item models have been updated
	const char *type = event->GetName();
	if ( Q_strcmp( "inventory_updated", type ) == 0 )
	{
		if ( m_pPlayerModelPanel )
		{
			m_pPlayerModelPanel->HoldItemInSlot( m_iCurrentSlotIndex );
		}
	}
}

void CClassLoadoutPanel::AddNewItemPanel( int iPanelIndex )
{
	BaseClass::AddNewItemPanel( iPanelIndex );

	m_vecItemOptionButtons[ m_vecItemOptionButtons.AddToTail() ] = new CExButton( this,
																				  CFmtStr( "item_options_button%d", iPanelIndex ),
																				  "+",
																				  this,
																				  CFmtStr( "options%d", iPanelIndex ) );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::UpdateModelPanels( void )
{
	// Search for a Robot Costume
	bool bIsRobot = false;
	static CSchemaAttributeDefHandle pAttrDef_PlayerRobot( "appear as mvm robot" );
	// For now, fill them out with the local player's currently wielded items
	for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
	{
		CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i );
		if ( !pItemData )
			continue;
		if ( FindAttribute( pItemData, pAttrDef_PlayerRobot ) )
		{
			bIsRobot = true;
			break;
		}
	}

	// We're showing the loadout for a specific class.
	TFPlayerClassData_t *pData = GetPlayerClassData( m_iCurrentClassIndex );
	if ( m_pPlayerModelPanel )
	{
		m_pPlayerModelPanel->ClearCarriedItems();
		m_pPlayerModelPanel->SetToPlayerClass( m_iCurrentClassIndex, bIsRobot );
		m_pPlayerModelPanel->SetTeam( m_iCurrentTeamIndex );
	}

	// For now, fill them out with the local player's currently wielded items
	for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
	{
		CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i );
		m_pItemModelPanels[i]->SetItem( pItemData );
		m_pItemModelPanels[i]->SetShowQuantity( true );
		m_pItemModelPanels[i]->SetSelected( false );
		SetBorderForItem( m_pItemModelPanels[i], false );

		if ( m_pPlayerModelPanel && pItemData && pItemData->IsValid() )
		{
			m_pPlayerModelPanel->AddCarriedItem( pItemData );
		}
	}

	if ( m_pPlayerModelPanel )
	{
		m_pPlayerModelPanel->HoldItemInSlot( m_iCurrentSlotIndex );
	}

	SetDialogVariable( "loadoutclass", g_pVGuiLocalize->Find( pData->m_szLocalizableName ) );

	UpdatePassiveAttributes();

	// Now layout again to position our item buttons 
	InvalidateLayout();

	if ( m_pItemOptionPanel->IsVisible() )
	{
		m_pItemOptionPanel->UpdateItemOptionsUI();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::OnItemPanelMouseReleased( vgui::Panel *panel )
{
	CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel );

	if ( pItemPanel && IsVisible() )
	{
		for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
		{
			if ( m_pItemModelPanels[i] == pItemPanel  )
			{
				OnCommand( VarArgs("change%d", i) );
				return;
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::OnSelectionReturned( KeyValues *data )
{
	if ( data )
	{
		uint64 ulIndex = data->GetUint64( "itemindex", INVALID_ITEM_ID );

		// ulIndex implies do nothing (escape key)
		if ( ulIndex != 0 )
		{
			TFInventoryManager()->EquipItemInLoadout( m_iCurrentClassIndex, m_iCurrentSlotIndex, ulIndex );

			m_bLoadoutHasChanged = true;

			UpdateModelPanels();

			// Send the preset panel a msg so it can save the change
			KeyValues *pLoadoutChangedMsg = new KeyValues( "LoadoutChanged" );
			pLoadoutChangedMsg->SetInt( "slot", m_iCurrentSlotIndex );
			pLoadoutChangedMsg->SetUint64( "itemid", ulIndex );
			PostMessage( m_pLoadoutPresetPanel, pLoadoutChangedMsg );
		}
	}

	PostMessage( GetParent(), new KeyValues("SelectionEnded") );

	// It'll have deleted itself, so we don't need to clean it up
	m_pSelectionPanel = NULL;
	OnCancelSelection();

	// find the selected item and give it the focus
	CItemModelPanel *pSelection = GetFirstSelectedItemModelPanel( true );
	if( !pSelection )
	{
		m_pItemModelPanels[0]->SetSelected( true );
		pSelection = m_pItemModelPanels[0];
	}

	pSelection->RequestFocus();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::OnCancelSelection( void )
{
	if ( m_pSelectionPanel )
	{
		m_pSelectionPanel->SetVisible( false );
		m_pSelectionPanel->MarkForDeletion();
		m_pSelectionPanel = NULL;
	}

	if ( m_pPlayerModelPanel )
	{
		m_pPlayerModelPanel->SetVisible( true );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::RespawnPlayer()
{
	if ( tf_respawn_on_loadoutchanges.GetBool() )
	{
		// Tell the GC to tell server that we should respawn if we're in a respawn room
		GCSDK::CGCMsg< MsgGCEmpty_t > msg( k_EMsgGCRespawnPostLoadoutChange );
		GCClientSystem()->BSendMessage( msg );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Apply KVs to the item option buttons
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::ApplyKVsToItemPanels( void )
{
	BaseClass::ApplyKVsToItemPanels();

	if ( m_pItemOptionPanelKVs )
	{
		for ( int i = 0; i < m_vecItemOptionButtons.Count(); i++ )
		{
			m_vecItemOptionButtons[i]->ApplySettings( m_pItemOptionPanelKVs );
			m_vecItemOptionButtons[i]->InvalidateLayout();
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::OnClosing( void )
{
	if ( m_pPlayerModelPanel )
	{
		m_pPlayerModelPanel->ClearCarriedItems();
	}

	if ( m_bLoadoutHasChanged )
	{
		RespawnPlayer();

		m_bLoadoutHasChanged = false;
	}
}

extern const char *g_szItemBorders[AE_MAX_TYPES][5];
extern ConVar cl_showbackpackrarities;
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::SetBorderForItem( CItemModelPanel *pItemPanel, bool bMouseOver )
{
	if ( !pItemPanel )
		return;

	const char *pszBorder = NULL;

	if ( pItemPanel->IsGreyedOut() )
	{
		pszBorder = "EconItemBorder";
	}
	else
	{
		int iRarity = 0;
		if ( pItemPanel->HasItem() && cl_showbackpackrarities.GetBool() )
		{
			iRarity = pItemPanel->GetItem()->GetItemQuality();

			uint8 nRarity = pItemPanel->GetItem()->GetItemDefinition()->GetRarity();
			if ( ( nRarity != k_unItemRarity_Any ) && ( iRarity != AE_SELFMADE ) )
			{
				// translate this quality to rarity
				iRarity = nRarity + AE_RARITY_DEFAULT;
			}

			if ( iRarity > 0 )
			{
				if ( bMouseOver || pItemPanel->IsSelected() )
				{
					pszBorder = g_szItemBorders[iRarity][1];
				}
				else
				{
					pszBorder = g_szItemBorders[iRarity][0];
				}
			}
		}
		
		
		if ( iRarity == 0 )
		{
			if ( bMouseOver || pItemPanel->IsSelected() )
			{
				pszBorder = "LoadoutItemMouseOverBorder";
			}
			else
			{
				pszBorder = "EconItemBorder";
			}
		}
	}

	vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
	pItemPanel->SetBorder( pScheme->GetBorder( pszBorder ) );
}

//-----------------------------------------------------------------------------
// Purpose: Clear the item options menu and reset the button that summoned it
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::ClearItemOptionsMenu( void )
{
	SetOptionsButtonText( m_pItemOptionPanel->GetItemSlot(), "+" );
	m_pItemOptionPanel->SetItemSlot( LOADOUT_POSITION_INVALID, m_iCurrentClassIndex );
	m_pItemOptionPanel->SetVisible( false );
}

//-----------------------------------------------------------------------------
// Purpose: Safely set the text for a button
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::SetOptionsButtonText( int nIndex, const char* pszText )
{
	if ( nIndex >= 0 && nIndex < m_vecItemOptionButtons.Count() )
	{
		m_vecItemOptionButtons[ m_pItemOptionPanel->GetItemSlot() ]->SetText( pszText );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Return if the passed in item has any options
//-----------------------------------------------------------------------------
bool CClassLoadoutPanel::AnyOptionsAvailableForItem( const CEconItemView *pItem )
{
	if ( !pItem )
		return false;
		
	// Styles!
	if ( pItem->GetStaticData()->GetNumSelectableStyles() > 1 )
		return true;

	// Unusual particle effect! For Cosmetics only
	static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
	if ( pItem->FindAttribute( pAttrDef_AttachParticleEffect ) && pItem->GetItemDefinition()->GetLoadoutSlot( 0 ) >= LOADOUT_POSITION_HEAD )
		return true;

	return false;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::SetLoadoutPage( classloadoutpage_t loadoutPage )
{
	ClearItemOptionsMenu();
	switch ( loadoutPage )
	{
		case CHARACTER_LOADOUT_PAGE:
		{
			m_bInTauntLoadoutMode = false;
		}
		break;
		case TAUNT_LOADOUT_PAGE:
		{
			m_bInTauntLoadoutMode = true;
		}
		break;
		default:
		{
			// Unhandled loadout page
			Assert( 0 );
		}
	}
	InvalidateLayout();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::OnCommand( const char *command )
{
	if ( FStrEq( command, "characterloadout" ) )
	{
		SetLoadoutPage( CHARACTER_LOADOUT_PAGE );
		return;
	}
	else if ( FStrEq( command, "tauntloadout" ) )
	{
		SetLoadoutPage( TAUNT_LOADOUT_PAGE );
		return;
	}
	else if ( !V_strnicmp( command, "change", 6 ) )
	{
		const char *pszNum = command+6;
		if ( pszNum && pszNum[0] )
		{
			int iSlot = atoi(pszNum);
			if ( iSlot >= 0 && iSlot < CLASS_LOADOUT_POSITION_COUNT && m_iCurrentClassIndex != TF_CLASS_UNDEFINED )
			{
				if ( m_iCurrentSlotIndex != iSlot )
				{
					m_iCurrentSlotIndex = iSlot;
				}

				// Create the selection screen. It removes itself on close.
				m_pSelectionPanel = new CEquipSlotItemSelectionPanel( this, m_iCurrentClassIndex, iSlot );
				m_pSelectionPanel->ShowPanel( 0, true );

				if ( m_pPlayerModelPanel )
				{
					m_pPlayerModelPanel->SetVisible( false );
				}

				ClearItemOptionsMenu();

				PostMessage( GetParent(), new KeyValues("SelectionStarted") );
			}
		}

		return;
	}
	else if ( !V_strnicmp( command, "options", 7 ) )
	{
		const char *pszNum = command + 7;
		if( pszNum && pszNum[0] )
		{
			int iSlot = atoi( pszNum );
			//iSlot = g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[iSlot - 1];
			if ( iSlot >= 0 && iSlot < m_vecItemOptionButtons.Count() && m_iCurrentClassIndex != TF_CLASS_UNDEFINED )
			{
				// Change the button we're coming from to be a "+"
				SetOptionsButtonText( m_pItemOptionPanel->GetItemSlot(), "+" );

				// Update the current slot index for callback from the setstyle button.
				// It will send us a message to change the item the player model is holding
				// and we need this to be updated for that.
				m_iCurrentSlotIndex = iSlot;
				

				// Did they just toggle?
				if ( m_pItemOptionPanel->GetItemSlot() == iSlot )
				{
					m_pItemOptionPanel->SetVisible( !m_pItemOptionPanel->IsVisible() );
				}
				else
				{
					// Set the options panel to have the data for this slot
					m_pItemOptionPanel->SetItemSlot( (loadout_positions_t)iSlot, m_iCurrentClassIndex );
					m_pItemOptionPanel->SetVisible( true );
					// Figure out if this is on the left or right
					int iColumnHeight = 4;
					int iColumn = iSlot / iColumnHeight;
					PinCorner_e myCornerToPin = iColumn == 0 ? PIN_TOPLEFT : PIN_TOPRIGHT;
					PinCorner_e siblingCornerPinTo = iColumn == 0 ? PIN_TOPRIGHT : PIN_TOPLEFT;
					// Pin to the appropriate side
					int iButtonPos = g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[ iSlot ] - 1;
					m_pItemOptionPanel->PinToSibling( m_vecItemOptionButtons[ iButtonPos ]->GetName(), myCornerToPin, siblingCornerPinTo );
					m_pItemOptionPanel->UpdateItemOptionsUI();
				}

				// Change the button we're going to to be "-" if we're visible, "+" if we're not
				SetOptionsButtonText( m_pItemOptionPanel->GetItemSlot(), m_pItemOptionPanel->IsVisible() ? "-" : "+" );
			}
			return;
		}
	}
	BaseClass::OnCommand( command );
}

//-----------------------------------------------------------------------------
void CClassLoadoutPanel::OnMessage( const KeyValues* pParams, vgui::VPANEL hFromPanel )
{
	BaseClass::OnMessage( pParams, hFromPanel );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
struct passive_attrib_to_print_t
{
	const CEconItemAttributeDefinition *m_pAttrDef;
	attrib_value_t m_value;
};

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
class CAttributeIterator_AddPassiveAttribsToPassiveList : public CEconItemSpecificAttributeIterator
{
public:
	CAttributeIterator_AddPassiveAttribsToPassiveList( CUtlVector<passive_attrib_to_print_t> *pList, bool bForceAdd )
		: m_pList( pList )
		, m_bForceAdd( bForceAdd )
	{
		Assert( m_pList );
	}

	virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
	{
		Assert( pAttrDef );

		if ( pAttrDef->IsHidden() )
			return true;

		if ( !m_bForceAdd )
		{
			const char *pDesc = pAttrDef->GetArmoryDescString();
			if ( !pDesc || !pDesc[0] )
				return true;

			// If we have the "on_wearer" key, we're a passive attribute
			if ( !Q_stristr(pDesc, "on_wearer") )
				return true;
		}

		// Now see if we're already in the list
		FOR_EACH_VEC( (*m_pList), i )
		{
			passive_attrib_to_print_t& passiveAttr = (*m_pList)[i];

			Assert( passiveAttr.m_pAttrDef );

			// We match if our class is the same -- this is a case-sensitive compare!
			if ( Q_strcmp( passiveAttr.m_pAttrDef->GetAttributeClass(), pAttrDef->GetAttributeClass() ) )
				continue;

			// We've found a matching attribute. Collate our values and stomp over the earlier value.
			passiveAttr.m_value = CollateAttributeValues( passiveAttr.m_pAttrDef, passiveAttr.m_value, pAttrDef, value );

			return true;
		}

		// We didn't find it. Add it to the list.
		passive_attrib_to_print_t newPassiveAttr = { pAttrDef, value };
		m_pList->AddToTail( newPassiveAttr );

		return true;
	}

	// Other types are ignored.

private:
	CUtlVector<passive_attrib_to_print_t> *m_pList;
	bool m_bForceAdd;
};

//-----------------------------------------------------------------------------
void CClassLoadoutPanel::UpdatePassiveAttributes( void )
{
	if ( !m_pPassiveAttribsLabel )
		return;

	// We build a list of attributes & associated values by looping through all equipped items.
	// This way we can identify & collate attributes based on the same definition index.
	CUtlVector<passive_attrib_to_print_t> vecAttribsToPrint;

	// Loop through all equipped items
	for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
	{
		CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i );
		if ( pItemData && pItemData->IsValid() )
		{
			CAttributeIterator_AddPassiveAttribsToPassiveList attrItPassives( &vecAttribsToPrint, false );
			pItemData->IterateAttributes( &attrItPassives );
		}
	}

	// Then add any set bonuses
	if ( steamapicontext && steamapicontext->SteamUser() )
	{
		CSteamID localSteamID = steamapicontext->SteamUser()->GetSteamID();
		CUtlVector<const CEconItemSetDefinition *> pActiveSets;
		TFInventoryManager()->GetActiveSets( &pActiveSets, localSteamID, m_iCurrentClassIndex );

		FOR_EACH_VEC( pActiveSets, set )
		{
			CAttributeIterator_AddPassiveAttribsToPassiveList attrItSetPassives( &vecAttribsToPrint, true );
			pActiveSets[set]->IterateAttributes( &attrItSetPassives );
		}
	}

	// Now build the text
	wchar_t wszPassiveDesc[4096];
	wszPassiveDesc[0] = '\0';
	m_pPassiveAttribsLabel->GetTextImage()->ClearColorChangeStream();

	wchar_t *pHeader = g_pVGuiLocalize->Find( "#TF_PassiveAttribs" );
	if ( pHeader )
	{
		V_wcscpy_safe( wszPassiveDesc, pHeader );
		V_wcscat_safe( wszPassiveDesc, L"\n" );
	}

	if ( vecAttribsToPrint.Count() )
	{
		FOR_EACH_VEC( vecAttribsToPrint, i )
		{
			CEconAttributeDescription AttrDesc( GLocalizationProvider(), vecAttribsToPrint[i].m_pAttrDef, vecAttribsToPrint[i].m_value );
			AddAttribPassiveText( AttrDesc, wszPassiveDesc, ARRAYSIZE(wszPassiveDesc) );
		}
	}
	else
	{
		vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
		Color col = pScheme->GetColor( GetColorNameForAttribColor( ATTRIB_COL_NEUTRAL ), Color(255,255,255,255) );
		m_pPassiveAttribsLabel->GetTextImage()->AddColorChange( col, Q_wcslen( wszPassiveDesc ) );

		wchar_t *pNone = g_pVGuiLocalize->Find( "#TF_PassiveAttribs_None" );
		if ( pNone )
		{
			V_wcscat_safe( wszPassiveDesc, pNone );
		}
	}

	m_pPassiveAttribsLabel->SetText( wszPassiveDesc );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::AddAttribPassiveText( const CEconAttributeDescription& AttrDesc, INOUT_Z_CAP(iNumPassiveChars) wchar_t *out_wszPassiveDesc, int iNumPassiveChars )
{
	vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
	Assert( pScheme );

	if ( !AttrDesc.GetDescription().IsEmpty() )
	{
		// Insert the color change at the current position
		Color col = pScheme->GetColor( GetColorNameForAttribColor( AttrDesc.GetDefaultColor() ), Color(255,255,255,255) );
		m_pPassiveAttribsLabel->GetTextImage()->AddColorChange( col, Q_wcslen( out_wszPassiveDesc ) );

		// Now append the text of the attribute
		V_wcsncat( out_wszPassiveDesc, AttrDesc.GetDescription().Get(), iNumPassiveChars );
		V_wcsncat( out_wszPassiveDesc, L"\n", iNumPassiveChars );
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClassLoadoutPanel::UpdatePageButtonColor( CExImageButton *pPageButton, bool bIsActive )
{
	if ( pPageButton )
	{
		int iLoaded = bIsActive ? LOADED : NOTLOADED;
		pPageButton->SetDefaultColor( m_aDefaultColors[iLoaded][FG][DEFAULT], m_aDefaultColors[iLoaded][BG][DEFAULT] );
		pPageButton->SetArmedColor( m_aDefaultColors[iLoaded][FG][ARMED], m_aDefaultColors[iLoaded][BG][ARMED] );
		pPageButton->SetDepressedColor( m_aDefaultColors[iLoaded][FG][DEPRESSED], m_aDefaultColors[iLoaded][BG][DEPRESSED] );
	}
}