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

#include "BasePanel.h"
#include "OptionsDialog.h"

#include "vgui/ILocalize.h"
#include "vgui/ISurface.h"
#include "vgui/ISystem.h"
#include "vgui/IVGui.h"

#include <vgui_controls/AnalogBar.h>

#ifdef _X360
	#include "xbox/xbox_launch.h"
#endif
#include "IGameUIFuncs.h"
#include "GameUI_Interface.h"
#include "inputsystem/iinputsystem.h"
#include "EngineInterface.h"
#include "KeyValues.h"
#include "ModInfo.h"
#include "matchmaking/matchmakingbasepanel.h"
#include "vgui_controls/AnimationController.h"

#include "tier1/utlbuffer.h"
#include "filesystem.h"

using namespace vgui;

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

#define OPTION_STRING_LENGTH 64


ConVar binds_per_command( "binds_per_command", "1", 0 );

ConVar x360_resolution_widescreen_mode( "x360_resolution_widescreen_mode", "0", 0, "This is only used for reference. Changing this value does nothing" );
ConVar x360_resolution_width( "x360_resolution_width", "640", 0, "This is only used for reference. Changing this value does nothing" );
ConVar x360_resolution_height( "x360_resolution_height", "480", 0, "This is only used for reference. Changing this value does nothing" );
ConVar x360_resolution_interlaced( "x360_resolution_interlaced", "0", 0, "This is only used for reference. Changing this value does nothing" );

ConVar x360_audio_english("x360_audio_english", "0", 0, "Keeps track of whether we're forcing english in a localized language." );


enum OptionType_e
{
	OPTION_TYPE_BINARY = 0,
	OPTION_TYPE_SLIDER,
	OPTION_TYPE_CHOICE,
	OPTION_TYPE_BIND,

	OPTION_TYPE_TOTAL
};


enum SliderHomeType_e
{
	SLIDER_HOME_NONE = 0,
	SLIDER_HOME_PREV,
	SLIDER_HOME_MIN,
	SLIDER_HOME_CENTER,
	SLIDER_HOME_MAX,

	SLIDER_HOME_TYPE_TOTAL
};



struct OptionChoiceData_t
{
	char	szName[ OPTION_STRING_LENGTH ];
	char	szValue[ OPTION_STRING_LENGTH ];
};

struct OptionData_t
{
	char			szName[ OPTION_STRING_LENGTH ];
	char			szDisplayName[ OPTION_STRING_LENGTH ];

	union
	{
		char		szConvar[ OPTION_STRING_LENGTH ];	// Used by all types except binds
		char		szCommand[ OPTION_STRING_LENGTH ];	// Used exclusively by bind types
	};
	
	char			szConvar2[ OPTION_STRING_LENGTH ];	// Used for choice types that use 2 convars

	char			szConvarDef[ OPTION_STRING_LENGTH ];

	int				iPriority;

	OptionType_e	eOptionType;

	bool			bUnchangeable;
	bool			bVocalsLanguage;

	// Slider types exclusively use these
	float			fMinValue;
	float			fMaxValue;
	float			fIncValue;
	float			fSliderHomeValue;

	union
	{
		SliderHomeType_e	eSliderHomeType;	// Slider types exclusively use this int
		int					iCurrentChoice;		// Choice types exclusively use this int
		int					iNumBinds;			// Bind types exclusively use this int
	};

	CUtlVector<OptionChoiceData_t>	m_Choices;
};


class OptionsDataContainer
{
public:

	~OptionsDataContainer()
	{
		for ( int iOption = 0; iOption < m_pOptions.Count(); ++iOption )
		{
			delete (m_pOptions[ iOption ]);
			m_pOptions[ iOption ] = 0;
		}

		for ( int iOption = 0; iOption < m_pControllerOptions.Count(); ++iOption )
		{
			delete (m_pControllerOptions[ iOption ]);
			m_pControllerOptions[ iOption ] = 0;
		}
	}

public:

	CUtlVector<OptionData_t*> m_pOptions;
	CUtlVector<OptionData_t*> m_pControllerOptions;

};


static OptionsDataContainer s_OptionsDataContainer;

static CUtlVector<OptionChoiceData_t> s_DisabledOptions;


const char *UTIL_Parse( const char *data, char *token, int sizeofToken );


bool ActionsAreTheSame( const char *pchAction1, const char *pchAction2 )
{
	if ( Q_stricmp( pchAction1, pchAction2 ) == 0 )
		return true;

	if ( ( Q_stricmp( pchAction1, "+duck" ) == 0 || Q_stricmp( pchAction1, "toggle_duck" ) == 0 ) && 
		 ( Q_stricmp( pchAction2, "+duck" ) == 0 || Q_stricmp( pchAction2, "toggle_duck" ) == 0 ) )
	{
		// +duck and toggle_duck are interchangable
		return true;
	}

	if ( ( Q_stricmp( pchAction1, "+zoom" ) == 0 || Q_stricmp( pchAction1, "toggle_zoom" ) == 0 ) && 
		 ( Q_stricmp( pchAction2, "+zoom" ) == 0 || Q_stricmp( pchAction2, "toggle_zoom" ) == 0 ) )
	{
		// +zoom and toggle_zoom are interchangable
		return true;
	}

	return false;
}


COptionsDialogXbox::COptionsDialogXbox( vgui::Panel *parent, bool bControllerOptions ) : BaseClass( parent, "OptionsDialog" )
{
#ifdef _X360
	// Get out current resolution and stuff it into convars for later reference
	XVIDEO_MODE videoMode;
	XGetVideoMode( &videoMode );
	x360_resolution_widescreen_mode.SetValue( videoMode.fIsWideScreen );
	x360_resolution_width.SetValue( static_cast<int>( videoMode.dwDisplayWidth ) );
	x360_resolution_height.SetValue( static_cast<int>( videoMode.dwDisplayHeight ) );
	x360_resolution_interlaced.SetValue( videoMode.fIsInterlaced );
#endif

	//Figure out which way duck is bound, and set the option_duck_method convar the correct way.
	const char *pDuckKey = engine->Key_LookupBinding( "+duck" );
	ButtonCode_t code = g_pInputSystem->StringToButtonCode( pDuckKey );
	const char *pDuckMode = engine->Key_BindingForKey( code );

	// NOW. If duck key is bound to +DUCK, set the convar to 0. Else, set it to 1.
	ConVarRef varOption( "option_duck_method" );

	if( pDuckMode != NULL )
	{
		if( !Q_stricmp(pDuckMode,"+duck") )
		{
			varOption.SetValue( 0 );
		}
		else
		{
			varOption.SetValue( 1 );
		}
	}

	SetSize( 32, 32 );
	SetDeleteSelfOnClose( true );
	SetTitleBarVisible( false );
	SetCloseButtonVisible( false );
	SetSizeable( false );

	m_pFooter = new CFooterPanel( parent, "OptionsFooter" );
	m_pFooter->SetStandardDialogButtons();

	m_bControllerOptions = bControllerOptions;

	m_bOptionsChanged = false;

	// Store the old vocal language setting
	m_bOldForceEnglishAudio = x360_audio_english.GetBool();

	// Get the correct options list
	if ( !m_bControllerOptions )
		m_pOptions = &s_OptionsDataContainer.m_pOptions;
	else
		m_pOptions = &s_OptionsDataContainer.m_pControllerOptions;

	if ( m_pOptions->Count() == 0 )
	{
		// Populate it if it's hasn't been filled
		ReadOptionsFromFile( "scripts/mod_options.360.txt" );
		ReadOptionsFromFile( "scripts/options.360.txt" );
		SortOptions();
	}

	m_pSelectedOption = NULL;

	m_iSelection = 0;
	m_iScroll = 0;

	m_iXAxisState = 0;
	m_iYAxisState = 0;
	m_fNextChangeTime = 0.0f;

	m_pOptionsSelectionLeft = SETUP_PANEL( new Panel( this, "OptionsSelectionLeft" ) );
	m_pOptionsSelectionLeft2 = SETUP_PANEL( new Panel( this, "OptionsSelectionLeft2" ) );
	m_pOptionsUpArrow = new vgui::Label( this, "UpArrow", "" );
	m_pOptionsDownArrow = new vgui::Label( this, "DownArrow", "" );

	for ( int iLabel = 0; iLabel < OPTIONS_MAX_NUM_ITEMS; ++iLabel )
	{
		char szLabelName[ 64 ];

		Q_snprintf( szLabelName, sizeof( szLabelName), "OptionLabel%i", iLabel );
		m_pOptionLabels[ iLabel ] = new vgui::Label( this, szLabelName, "" );

		Q_snprintf( szLabelName, sizeof( szLabelName), "ValueLabel%i", iLabel );
		m_pValueLabels[ iLabel ] = new vgui::Label( this, szLabelName, "" );

		Q_snprintf( szLabelName, sizeof( szLabelName), "ValueBar%i", iLabel );
		m_pValueBars[ iLabel ] = new AnalogBar( this, szLabelName );
	}

	// Faster repeats for sideways 
	m_KeyRepeat.SetKeyRepeatTime( KEY_XBUTTON_LEFT, 0.08 );
	m_KeyRepeat.SetKeyRepeatTime( KEY_XBUTTON_RIGHT, 0.08 );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void COptionsDialogXbox::InitializeSliderDefaults( void )
{
	for ( int iOption = 0; iOption < m_pOptions->Count(); ++iOption )
	{
		OptionData_t *pOption = (*m_pOptions)[ iOption ];

		if ( pOption->eOptionType != OPTION_TYPE_SLIDER )
			continue;

		if( pOption->eSliderHomeType != SLIDER_HOME_PREV )
			continue;

		const char *pszConvarName = pOption->szConvar;
		if ( pOption->szConvarDef && pOption->szConvarDef[0] )
		{
			// They've specified a different convar to use as the default 
			pszConvarName = pOption->szConvarDef;
		}

		ConVarRef varOption( pszConvarName );
		pOption->fSliderHomeValue = varOption.GetFloat();
	}
}

COptionsDialogXbox::~COptionsDialogXbox()
{
	if ( m_bOldForceEnglishAudio != x360_audio_english.GetBool() )
	{
#ifdef _X360
		XboxLaunch()->SetForceEnglish( x360_audio_english.GetBool() );
#endif
		PostMessage( BasePanel()->GetVPanel(), new KeyValues( "command", "command", "QuitRestartNoConfirm" ), 0.0f );
	}

	delete m_pFooter;
	m_pFooter = NULL;
}


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

	m_nButtonGap = inResourceData->GetInt( "footer_buttongap", -1 );
}

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

	KeyValues *pControlSettings = BasePanel()->GetConsoleControlSettings()->FindKey( "OptionsDialog.res" );
	LoadControlSettings( "null", NULL, pControlSettings );

	if ( m_pFooter )
	{
		KeyValues *pFooterControlSettings = BasePanel()->GetConsoleControlSettings()->FindKey( "OptionsFooter.res" );
		m_pFooter->LoadControlSettings( "null", NULL, pFooterControlSettings );
	}

	m_SelectedColor = pScheme->GetColor( "SectionedListPanel.SelectedBgColor", Color(255, 255, 255, 255) );

	m_pOptionsSelectionLeft->SetBgColor( m_SelectedColor );
	m_pOptionsSelectionLeft->SetAlpha( 96 );

	m_pOptionsSelectionLeft2->SetBgColor( Color(0,0,0,96) );

	int iX, iY;
	m_pOptionsSelectionLeft->GetPos( iX, m_iSelectorYStart );
	m_iOptionSpacing = (m_pOptionLabels[ 0 ])->GetTall(); 

	m_hLabelFont = pScheme->GetFont( "MenuLarge" );
	m_hButtonFont = pScheme->GetFont( "GameUIButtons" );

	// Decide how many items will fit
	int iTall = GetTall();
	m_iNumItems = ( iTall - 70 ) / m_iOptionSpacing;

	if ( m_iNumItems > m_pOptions->Count() )
	{
		// There's more space in the dialog than needed, shrink it down
		m_iNumItems = m_pOptions->Count();
		iTall = m_iNumItems * m_iOptionSpacing + 70;
		SetTall( iTall );
	}

	MoveToCenterOfScreen();

	// Adjust sizes for the number of items
	vgui::Panel *pPanel = FindChildByName( "OptionsBackgroundLeft" );
	pPanel->SetTall( iTall - 70 );
	pPanel = FindChildByName( "OptionsBackgroundRight" );
	pPanel->SetTall( iTall - 70 );

	m_pOptionsUpArrow->GetPos( iX, iY );
	m_pOptionsUpArrow->SetPos( iX, iTall - 32 );
	m_pOptionsDownArrow->GetPos( iX, iY );
	m_pOptionsDownArrow->SetPos( iX, iTall - 32 );

	m_pValueBars[ 0 ]->SetFgColor( Color( 255, 255, 255, 200 ) );
	m_pValueBars[ 0 ]->SetBgColor( Color( 255, 255, 255, 32 ) );
	m_pValueBars[ 0 ]->SetHomeColor( Color( m_SelectedColor[0], m_SelectedColor[1], m_SelectedColor[2], 96 ) );

	// Get proper setting for items for orginal in res file
	vgui::Panel *(pPanelList[3]) = { m_pOptionLabels[ 0 ], m_pValueLabels[ 0 ], m_pValueBars[ 0 ] };

	for ( int iPanelList = 0; iPanelList < 3; ++iPanelList )
	{
		pPanel = pPanelList[ iPanelList ];

		int  iZ, iWide;
		bool bVisible;
		pPanel->GetPos( iX, iY );
		iZ = ipanel()->GetZPos( pPanel->GetVPanel() );
		iWide = pPanel->GetWide();
		iTall = pPanel->GetTall();
		bVisible = pPanel->IsVisible();

		for ( int iLabel = 1; iLabel < m_iNumItems; ++iLabel )
		{
			if ( iPanelList == 0 )
			{
				pPanel = m_pOptionLabels[ iLabel ];
				m_pOptionLabels[ iLabel ]->SetFont( m_pOptionLabels[ 0 ]->GetFont() );
			}
			else if ( iPanelList == 1 )
			{
				pPanel = m_pValueLabels[ iLabel ];
				m_pValueLabels[ iLabel ]->SetFont( m_pValueLabels[ 0 ]->GetFont() );
			}
			else if ( iPanelList == 2 )
			{
				pPanel = m_pValueBars[ iLabel ];
				m_pValueBars[ iLabel ]->SetFgColor( m_pValueBars[ 0 ]->GetFgColor() );
				m_pValueBars[ iLabel ]->SetBgColor( m_pValueBars[ 0 ]->GetBgColor() );
				m_pValueBars[ iLabel ]->SetHomeColor( m_pValueBars[ 0 ]->GetHomeColor() );
			}

			pPanel->SetPos( iX, iY + iLabel * m_iOptionSpacing );
			pPanel->SetZPos( iZ );
			pPanel->SetWide( iWide );
			pPanel->SetTall( iTall );
			pPanel->SetVisible( bVisible );
		}
	}

	// Make unused items invisible
	for ( int iLabel = m_iNumItems; iLabel < OPTIONS_MAX_NUM_ITEMS; ++iLabel )
	{
		m_pOptionLabels[ iLabel ]->SetVisible( false );
		m_pValueLabels[ iLabel ]->SetVisible( false );
		m_pValueBars[ iLabel ]->SetVisible( false );
	}

	InitializeSliderDefaults();
	UpdateScroll();
	DeactivateSelection();
	UpdateFooter();

	// Don't fade the background for options so that brightness can be easily adjusted
	if ( !m_bControllerOptions )
		vgui::GetAnimationController()->RunAnimationCommand( BasePanel(), "m_flBackgroundFillAlpha", 0.01f, 0.0f, 1.0f, AnimationController::INTERPOLATOR_LINEAR );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void COptionsDialogXbox::OnClose( void )
{
	CMatchmakingBasePanel *pBase = BasePanel()->GetMatchmakingBasePanel();
	if ( pBase )
	{
		pBase->ShowFooter( true );
	}

	ConVarRef varOption( "option_duck_method" );

	char szCommand[ 256 ];

	for ( int iCode = 0; iCode < BUTTON_CODE_LAST; ++iCode )
	{
		ButtonCode_t code = static_cast<ButtonCode_t>( iCode );

		const char *pDuckKeyName = gameuifuncs->GetBindingForButtonCode( code );

		// Check if there's a binding for this key
		if ( !pDuckKeyName || !pDuckKeyName[0] )
			continue;

		// If we use this binding, display the key in our list
		if ( ActionsAreTheSame( pDuckKeyName, "+duck" ) )
		{
			if( varOption.GetBool() )
			{
				// Bind DUCK key to toggle_duck
				Q_snprintf( szCommand, sizeof( szCommand ), "bind \"%s\" \"%s\"", g_pInputSystem->ButtonCodeToString( code ), "toggle_duck" );
				engine->ClientCmd_Unrestricted( szCommand );
			}
			else
			{
				// Bind DUCK key to +DUCK
				Q_snprintf( szCommand, sizeof( szCommand ), "bind \"%s\" \"%s\"", g_pInputSystem->ButtonCodeToString( code ), "+DUCK" );
				engine->ClientCmd_Unrestricted( szCommand );
			}	
		}
	}

	// Save these settings!
	if ( m_bOptionsChanged )
		engine->ClientCmd_Unrestricted( "host_writeconfig" );

	BasePanel()->RunCloseAnimation( "CloseOptionsDialog_OpenMainMenu" );
	if ( !m_bControllerOptions )
		vgui::GetAnimationController()->RunAnimationCommand( BasePanel(), "m_flBackgroundFillAlpha", 120.0f, 0.0f, 1.0f, AnimationController::INTERPOLATOR_LINEAR );

	BaseClass::OnClose();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void COptionsDialogXbox::OnKeyCodePressed( vgui::KeyCode code )
{
	if ( IsX360() )
	{
		if ( GetAlpha() != 255 )
		{
			// inhibit key activity during transitions
			return;
		}
	}

	if ( !m_bSelectionActive )
		HandleInactiveKeyCodePressed( code );
	else if ( m_pSelectedOption->eOptionType != OPTION_TYPE_BIND )
		HandleActiveKeyCodePressed( code );
	else
		HandleBindKeyCodePressed( code );
}

void COptionsDialogXbox::OnCommand(const char *command)
{
	m_KeyRepeat.Reset();

	if ( !Q_stricmp( command, "DefaultControls" ) )
	{
		vgui::surface()->PlaySound( "UI/buttonclick.wav" );
		FillInDefaultBindings();
	}
	else if ( !Q_stricmp( command, "RefreshOptions" ) )
	{
		UncacheChoices();
		UpdateScroll();
	}
	else if ( !Q_stricmp( command, "AcceptVocalsLanguageChange" ) )
	{
		OnClose();
	}
	else if ( !Q_stricmp( command, "CancelVocalsLanguageChange" ) )
	{
		vgui::surface()->PlaySound( "UI/buttonclick.wav" );
		x360_audio_english.SetValue( m_bOldForceEnglishAudio );
		OnCommand( "RefreshOptions" );
		OnClose();
	}
	else if ( !Q_stricmp( command, "ReleaseModalWindow" ) )
	{
		vgui::surface()->RestrictPaintToSinglePanel(NULL);
	}
	else
	{
		BaseClass::OnCommand(command);
	}
}

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

	BaseClass::OnKeyCodeReleased( code );
}

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

	BaseClass::OnThink();
}

void COptionsDialogXbox::HandleInactiveKeyCodePressed( vgui::KeyCode code )
{
	m_KeyRepeat.KeyDown( code );

	OptionType_e eOptionType = (*m_pOptions)[ m_iSelection ]->eOptionType;
	switch( code )
	{
		// Change the selected value
		case KEY_XBUTTON_A:
		case STEAMCONTROLLER_A:
			if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable )
			{
				// Don't allow more binds if it's already maxed out
				if ( eOptionType == OPTION_TYPE_BIND && ( (*m_pOptions)[ m_iSelection ]->iNumBinds < binds_per_command.GetInt() || binds_per_command.GetInt() == 1 ) )
				{
					ActivateSelection();
					vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" );
				}
				else if ( eOptionType == OPTION_TYPE_BINARY || eOptionType == OPTION_TYPE_CHOICE )
				{
					ActivateSelection();
					ChangeValue( 1 );
					DeactivateSelection();
				}
			}
			else if ( !(*m_pOptions)[ m_iSelection ]->bVocalsLanguage )
			{
				BasePanel()->ShowMessageDialog( MD_OPTION_CHANGE_FROM_X360_DASHBOARD, this );
			}
			break;

		// To the main menu
		case KEY_XBUTTON_B:
		case STEAMCONTROLLER_B:
			if ( m_bOldForceEnglishAudio != x360_audio_english.GetBool() )
			{
				// Pop up a dialog to confirm changing the language
				vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" );
				BasePanel()->ShowMessageDialog( MD_SAVE_BEFORE_LANGUAGE_CHANGE, this );
			}
			else
			{
				OnClose();
			}
			break;

		// Move the selection up and down
		case KEY_XBUTTON_UP:
		case KEY_XSTICK1_UP:
		case STEAMCONTROLLER_DPAD_UP:
			ChangeSelection( -1 );
			break;

		case KEY_XBUTTON_DOWN:
		case KEY_XSTICK1_DOWN:
		case STEAMCONTROLLER_DPAD_DOWN:
			ChangeSelection( 1 );
			break;

		// Quickly change in the negative direction
		case KEY_XBUTTON_LEFT:
		case KEY_XSTICK1_LEFT:
		case STEAMCONTROLLER_DPAD_LEFT:
			if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable )
			{
				if ( eOptionType != OPTION_TYPE_BIND )
				{
					ActivateSelection();
					ChangeValue( -1 );
					DeactivateSelection();
				}
			}
			else if ( !(*m_pOptions)[ m_iSelection ]->bVocalsLanguage )
			{
				BasePanel()->ShowMessageDialog( MD_OPTION_CHANGE_FROM_X360_DASHBOARD, this );
			}
			break;

		// Quickly change in the positive direction
		case KEY_XBUTTON_RIGHT:
		case KEY_XSTICK1_RIGHT:
		case STEAMCONTROLLER_DPAD_RIGHT:
			if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable )
			{
				if ( eOptionType != OPTION_TYPE_BIND )
				{
					ActivateSelection();
					ChangeValue( 1 );
					DeactivateSelection();
				}
			}
			else if ( !(*m_pOptions)[ m_iSelection ]->bVocalsLanguage )
			{
				BasePanel()->ShowMessageDialog( MD_OPTION_CHANGE_FROM_X360_DASHBOARD, this );
			}
			break;

		case KEY_XBUTTON_X:
		case STEAMCONTROLLER_X:
			if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable )
			{
				if ( eOptionType == OPTION_TYPE_BIND )
				{
					if ( (*m_pOptions)[ m_iSelection ]->iNumBinds != 0 )
					{
						ActivateSelection();
						m_bOptionsChanged = true;
						vgui::surface()->PlaySound( "UI/buttonclick.wav" );
						UnbindOption( m_pSelectedOption, GetSelectionLabel() );
						DeactivateSelection();
					}
				}
			}
			break;

		case KEY_XBUTTON_Y:
		case STEAMCONTROLLER_Y:
			if ( m_bControllerOptions )
			{
				m_bOptionsChanged = true;
				vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" );
				BasePanel()->ShowMessageDialog( MD_DEFAULT_CONTROLS_CONFIRM, this );
			}
			else
			{
				BasePanel()->OnChangeStorageDevice();
			}
			break;

		default:
			break;
	}
}

void COptionsDialogXbox::HandleActiveKeyCodePressed( vgui::KeyCode code )
{
	// Only binds types should become active!
	DeactivateSelection();
}

void COptionsDialogXbox::HandleBindKeyCodePressed( vgui::KeyCode code )
{
	// Don't let stick movements be bound
	if ( ( code >= KEY_XSTICK1_RIGHT && code <= KEY_XSTICK1_UP ) || 
		 ( code >= KEY_XSTICK2_RIGHT && code <= KEY_XSTICK2_UP ) )
		return;

	if ( code == KEY_XBUTTON_START )
	{
		vgui::surface()->PlaySound( "UI/buttonrollover.wav" );
		UpdateValue( m_pSelectedOption, GetSelectionLabel() );
	}
	else
		ChangeValue( static_cast<int>( code ) );

	DeactivateSelection();
}

void COptionsDialogXbox::ActivateSelection( void )
{
	m_bSelectionActive = true;
	m_pSelectedOption = (*m_pOptions)[ m_iSelection ];

	if ( !m_pSelectedOption || m_pSelectedOption->eOptionType != OPTION_TYPE_BIND )
		return;

	m_KeyRepeat.Reset();

	m_pOptionsSelectionLeft->SetAlpha( 255 );

	int iLabel = GetSelectionLabel();

	m_pOptionLabels[ iLabel ]->SetFgColor( Color( 0, 0, 0, 255 ) );

	m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont );
	m_pValueLabels[ iLabel ]->SetText( "#GameUI_SetNewButton" );

	UpdateFooter();
}

void COptionsDialogXbox::DeactivateSelection( void )
{
	m_bSelectionActive = false;

	if ( !m_pSelectedOption || m_pSelectedOption->eOptionType != OPTION_TYPE_BIND )
		return;

	m_pOptionsSelectionLeft->SetAlpha( 96 );

	int iLabel = GetSelectionLabel();

	m_pOptionLabels[ iLabel ]->SetFgColor( Color( 255, 255, 255, 255 ) );
	m_pValueLabels[ iLabel ]->SetFgColor( Color( 255, 255, 255, 255 ) );

	UpdateFooter();
}

void COptionsDialogXbox::ChangeSelection( int iChange )
{
	m_iSelection += iChange;

	if ( m_iSelection < 0 )
	{
		m_iSelection = 0;
		vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
	}
	else if ( m_iSelection >= m_pOptions->Count() )
	{
		m_iSelection = m_pOptions->Count() - 1;
		vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
	}
	else
	{
		vgui::surface()->PlaySound( "UI/buttonrollover.wav" );
	}

	// Make sure the selected item in in the window
	if ( m_iSelection < m_iScroll )
	{
		m_iScroll = m_iSelection;
		UpdateScroll();
	}
	else if ( GetSelectionLabel() >= m_iNumItems )
	{
		m_iScroll = m_iSelection - m_iNumItems + 1;
		UpdateScroll();
	}

	UpdateFooter();

	UpdateSelection();
}

void COptionsDialogXbox::UpdateFooter( void )
{
	m_pFooter->ClearButtons();

	if ( !m_bSelectionActive )
	{
		if ( !(*m_pOptions)[ m_iSelection ]->bUnchangeable )
		{
			switch ( (*m_pOptions)[ m_iSelection ]->eOptionType )
			{
				case OPTION_TYPE_BINARY:
					m_pFooter->AddNewButtonLabel( "#GameUI_Modify", "#GameUI_Icons_DPAD" );
					break;

				case OPTION_TYPE_SLIDER:
					m_pFooter->AddNewButtonLabel( "#GameUI_Modify", "#GameUI_Icons_DPAD" );
					break;

				case OPTION_TYPE_CHOICE:
					m_pFooter->AddNewButtonLabel( ( (*m_pOptions)[ m_iSelection ]->m_Choices.Count() == 2 ) ? ( "#GameUI_Toggle" ) : ( "#GameUI_Modify" ), "#GameUI_Icons_DPAD" );
					break;

				case OPTION_TYPE_BIND:
				{
					if ( (*m_pOptions)[ m_iSelection ]->iNumBinds < binds_per_command.GetInt() || binds_per_command.GetInt() == 1 )
						m_pFooter->AddNewButtonLabel( "#GameUI_Modify", "#GameUI_Icons_A_BUTTON" );
					if ( (*m_pOptions)[ m_iSelection ]->iNumBinds != 0 )
						m_pFooter->AddNewButtonLabel( "#GameUI_ClearButton", "#GameUI_Icons_X_BUTTON" );
					break;
				}
			}
		}

		if ( m_bControllerOptions )
			m_pFooter->AddNewButtonLabel( "#GameUI_DefaultButtons", "#GameUI_Icons_Y_BUTTON" );
		else
			m_pFooter->AddNewButtonLabel( "#GameUI_Console_StorageChange", "#GameUI_Icons_Y_BUTTON" );

		m_pFooter->AddNewButtonLabel( "#GameUI_Close", "#GameUI_Icons_B_BUTTON" );
	}
	else if ( m_pSelectedOption )
	{
		switch ( m_pSelectedOption->eOptionType )
		{
			case OPTION_TYPE_BIND:
				m_pFooter->AddNewButtonLabel( "#GameUI_Cancel", "#GameUI_Icons_START" );			
				break;
		}
	}

	if ( m_nButtonGap > 0 )
	{
		m_pFooter->SetButtonGap( m_nButtonGap );
	}
	else
	{
		m_pFooter->UseDefaultButtonGap();
	}

	CMatchmakingBasePanel *pBase = BasePanel()->GetMatchmakingBasePanel();
	if ( pBase )
	{
		pBase->ShowFooter( false );
	}
}

void COptionsDialogXbox::UpdateSelection( void )
{
	int iYPos = m_iSelectorYStart + m_iOptionSpacing * ( GetSelectionLabel() );

	int iX, iY;
	m_pOptionsSelectionLeft->GetPos( iX, iY );
	m_pOptionsSelectionLeft->SetPos( iX, iYPos );
	m_pOptionsSelectionLeft2->GetPos( iX, iY );
	m_pOptionsSelectionLeft2->SetPos( iX, iYPos + 2 );
}

void COptionsDialogXbox::UpdateScroll( void )
{
	for ( int iLabel = 0; iLabel < m_iNumItems; ++iLabel )
	{
		int iOption = m_iScroll + iLabel;

		if ( iOption >= m_pOptions->Count() )
			break;

		OptionData_t *pOption = (*m_pOptions)[ iOption ];

		m_pOptionLabels[ iLabel ]->SetText( pOption->szDisplayName );

		UpdateValue( pOption, iLabel );

		if ( pOption->bUnchangeable )
		{
			m_pOptionLabels[ iLabel ]->SetFgColor( Color( 128, 128, 128, 255 ) );
			m_pValueLabels[ iLabel ]->SetFgColor( Color( 128, 128, 128, 255 ) );
		}
		else
		{
			m_pOptionLabels[ iLabel ]->SetFgColor( Color( 200, 200, 200, 255 ) );
			m_pValueLabels[ iLabel ]->SetFgColor( Color( 200, 200, 200, 255 ) );
		}

	}

	// Draw arrows if there's more items to scroll to
	m_pOptionsUpArrow->SetAlpha( ( m_iScroll > 0 ) ? ( 255 ) : ( 64 ) );
	m_pOptionsDownArrow->SetAlpha( ( m_iScroll + m_iNumItems < m_pOptions->Count() ) ? ( 255 ) : ( 64 ) );
}

void COptionsDialogXbox::UncacheChoices( void )
{
	for ( int iOption = 0; iOption < m_pOptions->Count(); ++iOption )
	{
		OptionData_t *pOption = (*m_pOptions)[ iOption ];

		if ( pOption->eOptionType != OPTION_TYPE_CHOICE )
			continue;

		pOption->iCurrentChoice = -1;
	}
}

void COptionsDialogXbox::GetChoiceFromConvar( OptionData_t *pOption )
{
	if ( pOption->iCurrentChoice < 0 )
	{
		// Don't have a proper choice yet, so see if the convars value matches one of our choices
		if ( pOption->szConvar2[ 0 ] == '\0' )
		{
			ConVarRef varOption( pOption->szConvar );
			const char *pchValue = varOption.GetString();

			// Only one convar to check
			for ( int iChoice = 0; iChoice < pOption->m_Choices.Count(); ++iChoice )
			{
				if ( Q_stricmp( pOption->m_Choices[ iChoice ].szValue, pchValue ) == 0 )
				{
					pOption->iCurrentChoice = iChoice;
					break;
				}

				// We need to compare values in case we have "0" & "0.00000". 
				if ( (pchValue[0] >= '0' && pchValue[0] <= '9') || pchValue[0] == '-' )
				{
					float flVal = atof(pchValue);
					float flChoiceVal = atof(pOption->m_Choices[ iChoice ].szValue);
					if ( flVal == flChoiceVal )
					{
						pOption->iCurrentChoice = iChoice;
						break;
					}
				}
			}
		}
		else
		{
			// Two convars to contend with
			ConVarRef varOption( pOption->szConvar );
			ConVarRef varOption2( pOption->szConvar2 );

			const char *pchValue = varOption.GetString();
			const char *pchValue2 = varOption2.GetString();

			for ( int iChoice = 0; iChoice < pOption->m_Choices.Count(); ++iChoice )
			{
				char szOptionValue[ OPTION_STRING_LENGTH ];
				Q_strncpy( szOptionValue, pOption->m_Choices[ iChoice ].szValue, sizeof( szOptionValue ) );

				char *pchOptionValue2 = const_cast<char*>( Q_strnchr( szOptionValue, ';', sizeof( szOptionValue ) ) );

				AssertMsg( pchOptionValue2, "Option uses 2 convars but an option doesn't have 2 ; separated values!" );

				pchOptionValue2[ 0 ] = '\0';	// Set this as the end of the other value
				++pchOptionValue2;				// Point at next char

				if ( Q_stricmp( szOptionValue, pchValue ) == 0 && Q_stricmp( pchOptionValue2, pchValue2 ) == 0 )
				{
					pOption->iCurrentChoice = iChoice;
					break;
				}
			}
		}
	}
}

#if !defined(_X360)
// BUGBUG: This won't compile because it includes windows.h and this interface function is #defined to something else
// see below system()->GetCurrentTime
#undef GetCurrentTime
#endif

void COptionsDialogXbox::ChangeValue( float fChange )
{
	switch ( m_pSelectedOption->eOptionType )
	{
		case OPTION_TYPE_BINARY:
		{
			ConVarRef varOption( m_pSelectedOption->szConvar );
			varOption.SetValue( !varOption.GetBool() );

			UpdateValue( m_pSelectedOption, GetSelectionLabel() );

			m_bOptionsChanged = true;
			vgui::surface()->PlaySound( "UI/buttonclick.wav" );

			break;
		}

		case OPTION_TYPE_SLIDER:
		{
			if ( system()->GetCurrentTime() > m_fNextChangeTime )
			{
				ConVarRef varOption( m_pSelectedOption->szConvar );

				float fNumSegments = m_pValueBars[ 0 ]->GetTotalSegmentCount();
				if ( fNumSegments <= 0.0f )
					fNumSegments = 1.0f;

				float fIncValue;

				if ( m_pSelectedOption->fIncValue == 0.0f )
					fIncValue = ( m_pSelectedOption->fMaxValue - m_pSelectedOption->fMinValue ) * ( 1.0f / fNumSegments );
				else
					fIncValue = m_pSelectedOption->fIncValue * ( m_pSelectedOption->fMaxValue - m_pSelectedOption->fMinValue ) * ( 1.0f / fNumSegments );

				fIncValue *= fChange;

				float fOldValue = varOption.GetFloat();
				float fValue = clamp( fOldValue + fIncValue, m_pSelectedOption->fMinValue, m_pSelectedOption->fMaxValue );

				if ( fOldValue != fValue )
				{
					m_bOptionsChanged = true;
					vgui::surface()->PlaySound( "UI/buttonclick.wav" );

					varOption.SetValue( fValue );
					UpdateValue( m_pSelectedOption, GetSelectionLabel() );
				}
				else
				{
					// Play the invalid sound
					vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
				}
			}

			break;
		}

		case OPTION_TYPE_CHOICE:
		{
			GetChoiceFromConvar( m_pSelectedOption );

			if ( m_pSelectedOption->iCurrentChoice >= 0 )
			{
				m_pSelectedOption->iCurrentChoice += fChange;

				while ( m_pSelectedOption->iCurrentChoice >= m_pSelectedOption->m_Choices.Count() )
					m_pSelectedOption->iCurrentChoice -= m_pSelectedOption->m_Choices.Count();

				while ( m_pSelectedOption->iCurrentChoice < 0 )
					m_pSelectedOption->iCurrentChoice += m_pSelectedOption->m_Choices.Count();

				if ( m_pSelectedOption->szConvar2[ 0 ] == '\0' )
				{
					// Only one convar to set
					ConVarRef varOption( m_pSelectedOption->szConvar );
					varOption.SetValue( m_pSelectedOption->m_Choices[ m_pSelectedOption->iCurrentChoice ].szValue );
				}
				else
				{
					// Two convars to deal with
					char szOptionValue[ OPTION_STRING_LENGTH ];
					Q_strncpy( szOptionValue, m_pSelectedOption->m_Choices[ m_pSelectedOption->iCurrentChoice ].szValue, sizeof( szOptionValue ) );

					char *pchOptionValue2 = const_cast<char*>( Q_strnchr( szOptionValue, ';', sizeof( szOptionValue ) ) );

					AssertMsg( pchOptionValue2, "Option uses to convars but an option doesn't have 2 ; separated values!" );

					pchOptionValue2[ 0 ] = '\0';	// Set this as the end of the other value
					++pchOptionValue2;				// Point at next char

					ConVarRef varOption( m_pSelectedOption->szConvar );
					ConVarRef varOption2( m_pSelectedOption->szConvar2 );

					varOption.SetValue( szOptionValue );
					varOption2.SetValue( pchOptionValue2 );
				}
			}
			else
			{
				DevWarning( "ConVar \"%s\" set to value that's not a choice used by \"%s\" option.", m_pSelectedOption->szConvar, m_pSelectedOption->szDisplayName );
			}

			UpdateValue( m_pSelectedOption, GetSelectionLabel() );

			m_bOptionsChanged = true;
			vgui::surface()->PlaySound( "UI/buttonclick.wav" );

			break;
		}

		case OPTION_TYPE_BIND:
		{
			// If we only allow one bind replace the previous
			if ( binds_per_command.GetInt() == 1 )
				UnbindOption( m_pSelectedOption, GetSelectionLabel() );

			ButtonCode_t code = static_cast<ButtonCode_t>( static_cast<int>( fChange ) );

			char szCommand[ 256 ];
			Q_snprintf( szCommand, sizeof( szCommand ), "bind \"%s\" \"%s\"", g_pInputSystem->ButtonCodeToString( code ), m_pSelectedOption->szCommand );
			engine->ClientCmd_Unrestricted( szCommand );

			// After binding we need to update all bindings so they display the correct keys
			UpdateAllBinds( code );

			m_bOptionsChanged = true;
			vgui::surface()->PlaySound( "UI/buttonclick.wav" );
			
			break;
		}
	}
}

void COptionsDialogXbox::UnbindOption( OptionData_t *pOption, int iLabel )
{
	if ( pOption->eOptionType != OPTION_TYPE_BIND )
		return;

	for ( int iCode = 0; iCode < BUTTON_CODE_LAST; ++iCode )
	{
		ButtonCode_t code = static_cast<ButtonCode_t>( iCode );

		const char *pBinding = gameuifuncs->GetBindingForButtonCode( code );

		// Check if there's a binding for this key
		if ( !pBinding || !pBinding[0] )
			continue;

		// If we use this binding, display the key in our list
		if ( ActionsAreTheSame( pBinding, pOption->szCommand ) )
		{
			char szCommand[ 256 ];
			Q_snprintf( szCommand, sizeof( szCommand ), "unbind %s", g_pInputSystem->ButtonCodeToString( code ) );
			engine->ClientCmd_Unrestricted( szCommand );
		}
	}

	pOption->iNumBinds = 0;

	if ( iLabel < 0 || iLabel >= m_iNumItems )
		return;

	m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont );
	m_pValueLabels[ iLabel ]->SetText( "" );
}

void COptionsDialogXbox::UpdateValue( OptionData_t *pOption, int iLabel )
{
	if ( pOption->bVocalsLanguage )
	{
		// Can't change vocal language while in game
		pOption->bUnchangeable = GameUI().IsInLevel();
	}

	switch ( pOption->eOptionType )
	{
		case OPTION_TYPE_BINARY:
		{
			ConVarRef varOption( pOption->szConvar );

			m_pValueBars[ iLabel ]->SetVisible( false );
			m_pValueLabels[ iLabel ]->SetVisible( true );
			m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont );
			m_pValueLabels[ iLabel ]->SetText( ( varOption.GetBool() ) ? ( "#GameUI_Enable" ) : ( "#GameUI_Disable" ) );
			break;
		}

		case OPTION_TYPE_SLIDER:
		{
			ConVarRef varOption( pOption->szConvar );

			m_pValueLabels[ iLabel ]->SetVisible( false );
			m_pValueBars[ iLabel ]->SetVisible( true );

			// If it's very close to the home value set it as the home value
			float fNumSegments = m_pValueBars[ 0 ]->GetTotalSegmentCount();
			if ( fNumSegments <= 0.0f )
				fNumSegments = 1.0f;

			float fIncValue;

			if ( pOption->fIncValue == 0.0f )
				fIncValue = ( pOption->fMaxValue - pOption->fMinValue ) * ( 1.0f / fNumSegments );
			else
				fIncValue = pOption->fIncValue * ( pOption->fMaxValue - pOption->fMinValue ) * ( 1.0f / fNumSegments );

			float fNormalizeHome = fabsf( ( pOption->fMinValue - pOption->fSliderHomeValue ) / ( pOption->fMaxValue - pOption->fMinValue ) );
			if ( pOption->fIncValue < 0.0f )
				fNormalizeHome = 1.0f - fNormalizeHome;
			m_pValueBars[ iLabel ]->SetHomeValue( fNormalizeHome );

			float fNormalizeValue = fabsf( ( pOption->fMinValue - varOption.GetFloat() ) / ( pOption->fMaxValue - pOption->fMinValue ) );
			if ( pOption->fIncValue < 0.0f )
				fNormalizeValue = 1.0f - fNormalizeValue;

			// atof accuracy for convar isn't so good, so snap to the home value if we're near it.
			if ( fabsf( fNormalizeValue - fNormalizeHome ) < fabsf( fIncValue * 0.1f ) )
				fNormalizeValue = fNormalizeHome;

			m_pValueBars[ iLabel ]->SetAnalogValue( fNormalizeValue );

			break;
		}

		case OPTION_TYPE_CHOICE:
		{
			GetChoiceFromConvar( pOption );

			m_pValueBars[ iLabel ]->SetVisible( false );
			m_pValueLabels[ iLabel ]->SetVisible( true );
			m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont );
			if ( pOption->iCurrentChoice >= 0 )
			{
				m_pValueLabels[ iLabel ]->SetText( pOption->m_Choices[ pOption->iCurrentChoice ].szName );
			}
			else
			{
				if ( pOption->bUnchangeable )
				{
					if ( pOption->szConvar2[ 0 ] == '\0' )
					{
						ConVarRef varOption( pOption->szConvar );
						m_pValueLabels[ iLabel ]->SetText( varOption.GetString() );
					}
					else
					{
						// This is used for displaying the resolution settings
						ConVarRef varOption( pOption->szConvar );
						ConVarRef varOption2( pOption->szConvar2 );
						char szBuff[ 256 ];
						Q_snprintf( szBuff, sizeof( szBuff ), "%sx%s%s", varOption.GetString(), varOption2.GetString(), ( x360_resolution_interlaced.GetBool() ) ? ( "i" ) : ( "p" ) );
						m_pValueLabels[ iLabel ]->SetText( szBuff );
					}
				}
				else
				{
					DevWarning( "ConVar \"%s\" set to value that's not a choice used by \"%s\" option.", pOption->szConvar, pOption->szDisplayName );
					m_pValueLabels[ iLabel ]->SetText( "#GameUI_NoOptionsYet" );
				}
			}

			break;
		}

		case OPTION_TYPE_BIND:
		{
			UpdateBind( pOption, iLabel );
			break;
		}
	}
}

void COptionsDialogXbox::UpdateBind( OptionData_t *pOption, int iLabel, ButtonCode_t codeIgnore, ButtonCode_t codeAdd )
{
	int iNumBinds = 0;
	char szBinds[ OPTION_STRING_LENGTH ];

	char szBuff[ 512 ];
	wchar_t szWideBuff[ 64 ];

	for ( int iCode = 0; iCode < BUTTON_CODE_LAST; ++iCode )
	{
		ButtonCode_t code = static_cast<ButtonCode_t>( iCode );

		// Don't show this key in our list
		if ( code == codeIgnore )
			continue;

		bool bUseThisKey = ( codeAdd == code );

		if ( !bUseThisKey )
		{
			// If this binding is being replaced only allow the new binding to show
			if ( binds_per_command.GetInt() == 1 && codeAdd != BUTTON_CODE_INVALID )
				continue;

			// Only check against bind name if we haven't already forced this binding to be used
			const char *pBinding = gameuifuncs->GetBindingForButtonCode( code );

			if ( !pBinding )
				continue;

			bUseThisKey = ActionsAreTheSame( pBinding, pOption->szCommand );
		}

		// Don't use this bind in out list
		if ( !bUseThisKey )
			continue;

		// Turn localized string into icon character
		Q_snprintf( szBuff, sizeof( szBuff ), "#GameUI_Icons_%s", g_pInputSystem->ButtonCodeToString( static_cast<ButtonCode_t>( iCode ) ) );
		g_pVGuiLocalize->ConstructString( szWideBuff, sizeof( szWideBuff ), g_pVGuiLocalize->Find( szBuff ), 0 );
		g_pVGuiLocalize->ConvertUnicodeToANSI( szWideBuff, szBuff, sizeof( szBuff ) );

		// Add this icon to our list of keys to display
		szBinds[ iNumBinds ] = szBuff[ 0 ];
		++iNumBinds;
	}

	if ( iNumBinds == 0 )
	{
		// No keys for this bind
		pOption->iNumBinds = 0;
		m_pValueBars[ iLabel ]->SetVisible( false );
		m_pValueLabels[ iLabel ]->SetVisible( true );
		m_pValueLabels[ iLabel ]->SetFont( m_hLabelFont );
		m_pValueLabels[ iLabel ]->SetText( "" );
	}
	else
	{
		// Show icons for list of keys
		szBinds[ iNumBinds ] = '\0';
		pOption->iNumBinds = iNumBinds;
		m_pValueBars[ iLabel ]->SetVisible( false );
		m_pValueLabels[ iLabel ]->SetVisible( true );
		m_pValueLabels[ iLabel ]->SetFont( m_hButtonFont );
		m_pValueLabels[ iLabel ]->SetText( szBinds );
	}
}

void COptionsDialogXbox::UpdateAllBinds( ButtonCode_t code )
{
	// Loop through all the items
	for ( int iLabel = 0; iLabel < m_iNumItems; ++iLabel )
	{
		int iOption = m_iScroll + iLabel;

		if ( iOption >= m_pOptions->Count() )
			break;

		OptionData_t *pOption = (*m_pOptions)[ iOption ];

		if ( pOption->eOptionType == OPTION_TYPE_BIND )
		{
			if ( pOption == m_pSelectedOption )
			{
				// We just added a key to this binding so add it to the list
				UpdateBind( pOption, iLabel, BUTTON_CODE_INVALID, code );
			}
			else
			{
				// We just added a key so don't let it show up for any other bindings
				UpdateBind( pOption, iLabel, code );
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Read in defaults from game's default config file and populate list 
//			using those defaults
//-----------------------------------------------------------------------------
void COptionsDialogXbox::FillInDefaultBindings( void )
{
	CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
	if ( !g_pFullFileSystem->ReadFile( "cfg/config.360.cfg", NULL, buf ) )
		return;

	// Clear out all current bindings
	for ( int iOption = 0; iOption < m_pOptions->Count(); ++iOption )
		UnbindOption( (*m_pOptions)[ iOption ], iOption - m_iScroll );

	const char *data = (const char*)buf.Base();

	// loop through all the binding
	while ( data != NULL )
	{
		char cmd[64];
		data = UTIL_Parse( data, cmd, sizeof(cmd) );
		if ( strlen( cmd ) <= 0 )
			break;

		if ( !stricmp(cmd, "bind") )
		{
			// Key name
			char szKeyName[256];
			data = UTIL_Parse( data, szKeyName, sizeof(szKeyName) );
			if ( strlen( szKeyName ) <= 0 )
				break; // Error

			char szBinding[256];
			data = UTIL_Parse( data, szBinding, sizeof(szBinding) );
			if ( strlen( szKeyName ) <= 0 )  
				break; // Error

			// Bind it
			char szCommand[ 256 ];
			Q_snprintf( szCommand, sizeof( szCommand ), "bind \"%s\" \"%s\"", szKeyName, szBinding );
			engine->ClientCmd_Unrestricted( szCommand );

			// Loop through all the items
			for ( int iLabel = 0; iLabel < m_iNumItems; ++iLabel )
			{
				int iOption = m_iScroll + iLabel;

				if ( iOption >= m_pOptions->Count() )
					break;

				OptionData_t *pOption = (*m_pOptions)[ iOption ];

				// Check if this bind is for this option
				if ( pOption->eOptionType == OPTION_TYPE_BIND && ActionsAreTheSame( pOption->szCommand, szBinding ) )
				{
					char szBuff[ 512 ];
					wchar_t szWideBuff[ 64 ];
					char szBinds[ OPTION_STRING_LENGTH ];
					if ( pOption->iNumBinds > 0 )
						m_pValueLabels[ iLabel ]->GetText( szBinds, sizeof( szBinds ) );

					// Turn localized string into icon character
					Q_snprintf( szBuff, sizeof( szBuff ), "#GameUI_Icons_%s", szKeyName );
					g_pVGuiLocalize->ConstructString( szWideBuff, sizeof( szWideBuff ), g_pVGuiLocalize->Find( szBuff ), 0 );
					g_pVGuiLocalize->ConvertUnicodeToANSI( szWideBuff, szBuff, sizeof( szBuff ) );

					// Add this icon to our list of keys to display
					szBinds[ pOption->iNumBinds ] = szBuff[ 0 ];
					++pOption->iNumBinds;

					// Show icons for list of keys
					szBinds[ pOption->iNumBinds ] = '\0';
					m_pValueBars[ iLabel ]->SetVisible( false );
					m_pValueLabels[ iLabel ]->SetVisible( true );
					m_pValueLabels[ iLabel ]->SetFont( m_hButtonFont );
					m_pValueLabels[ iLabel ]->SetText( szBinds );
				}
			}
		}
	}

	// Reset any options with default convar values
	for ( int iLabel = 0; iLabel < m_iNumItems; ++iLabel )
	{
		int iOption = m_iScroll + iLabel;

		if ( iOption >= m_pOptions->Count() )
			break;

		OptionData_t *pOption = (*m_pOptions)[ iOption ];

		if ( pOption->szConvarDef && pOption->szConvarDef[0] )
		{
			ConVarRef varDefault( pOption->szConvarDef );
			ConVarRef varOption( pOption->szConvar );
			varOption.SetValue( varDefault.GetFloat() );
			pOption->iCurrentChoice = -1;
			UpdateValue( pOption, iLabel );
		}
	}
}

bool COptionsDialogXbox::ShouldSkipOption( KeyValues *pKey )
{
	// Skip the option if not in developer mode and this is a developer only option
	ConVarRef developer( "developer" );
	if ( !developer.GetBool() && ( pKey->GetInt( "dev", 0 ) != 0 ) )
		return true;

	// Skip the option if it doesn't match the controller/non-controller list
	if ( m_bControllerOptions != ( pKey->GetInt( "control", 0 ) != 0 ) )
		return true;

	// Skip portal options if this mod doesn't have portals (defined in gameinfo.txt)
	if ( !ModInfo().HasPortals() && ( pKey->GetInt( "portals", 0 ) != 0 ) )
		return true;

	// Skip multiplayer only options for single player (or combo) games
	if ( ModInfo().IsSinglePlayerOnly() && !ModInfo().IsMultiplayerOnly() && ( pKey->GetInt( "multiplayer", 0 ) != 0 ) )
		return true;

	// Skip difficulty options for games that don't want them
	if ( !( ModInfo().IsSinglePlayerOnly() && !ModInfo().NoDifficulty() ) && ( pKey->GetInt( "difficulty", 0 ) != 0 ) )
		return true;

	// Skip voice options for single player games
	if ( ModInfo().IsSinglePlayerOnly() && !ModInfo().IsMultiplayerOnly() && ( pKey->GetInt( "voice", 0 ) != 0 ) )
		return true;

	// Skip if it's the vocal language option but we're not in german or french
	if ( pKey->GetInt( "vocalslanguage", 0 ) != 0 )
	{
		if ( !XBX_IsLocalized() )
		{
			return true;
		}
	}

	// Don't create options if there's already an entry by the same name
	char szName[ OPTION_STRING_LENGTH ];
	Q_strncpy( szName, pKey->GetName(), sizeof( szName ) );

	for ( int iOption = 0; iOption < m_pOptions->Count(); ++iOption )
	{
		OptionData_t *pOption = (*m_pOptions)[ iOption ];
		if ( Q_strcmp( pOption->szName, szName ) == 0 )
			return true;
	}

	for ( int iOption = 0; iOption < s_DisabledOptions.Count(); ++iOption )
	{
		OptionChoiceData_t *pOption = &(s_DisabledOptions[ iOption ]);
		if ( Q_strcmp( pOption->szName, szName ) == 0 )
			return true;
	}

	return false;
}

void COptionsDialogXbox::ReadOptionsFromFile( const char *pchFileName )
{
	KeyValues *pOptionKeys = new KeyValues( "options_x360" );
	pOptionKeys->LoadFromFile( g_pFullFileSystem, pchFileName, NULL );

	KeyValues *pKey = NULL;
	for ( pKey = pOptionKeys->GetFirstTrueSubKey(); pKey; pKey = pKey->GetNextTrueSubKey() )
	{
		// Skip disabled options
		if ( pKey->GetInt( "disable", 0 ) != 0 )
		{
			// Remember disabled options so we don't create another with the same name
			int iDisabledOption = s_DisabledOptions.AddToTail();
			OptionChoiceData_t *pDisabledOption = &(s_DisabledOptions[ iDisabledOption ]);
			Q_strncpy( pDisabledOption->szName, pKey->GetName(), sizeof( pDisabledOption->szName ) );

			continue;
		}

		if ( ShouldSkipOption( pKey ) )
			continue;

		int iOption = m_pOptions->AddToTail();
		OptionData_t **pNewOption = &((*m_pOptions)[ iOption ]);
		*pNewOption = new OptionData_t;

		// Get common values
		Q_strncpy( (*pNewOption)->szName, pKey->GetName(), sizeof( (*pNewOption)->szName ) );
		Q_strncpy( (*pNewOption)->szDisplayName, pKey->GetString( "name", "" ), sizeof( (*pNewOption)->szDisplayName ) );

		Q_strncpy( (*pNewOption)->szConvar, pKey->GetString( "convar", "" ), sizeof( (*pNewOption)->szConvar ) );
		if ( (*pNewOption)->szConvar[ 0 ] == '\0' )
			Q_strncpy( (*pNewOption)->szCommand, pKey->GetString( "command", "" ), sizeof( (*pNewOption)->szCommand ) );

		Q_strncpy( (*pNewOption)->szConvarDef, pKey->GetString( "convar_def", "" ), sizeof( (*pNewOption)->szConvarDef ) );

		(*pNewOption)->iPriority = pKey->GetInt( "priority", 0 );
		(*pNewOption)->bUnchangeable = ( pKey->GetInt( "unchangeable", 0 ) != 0 );
		(*pNewOption)->bVocalsLanguage = ( pKey->GetInt( "vocalslanguage", 0 ) != 0 );

		// Determine the type
		char szType[ OPTION_STRING_LENGTH ];
		Q_strncpy( szType, pKey->GetString( "type", "binary" ), sizeof( szType ) );

		if ( Q_stricmp( szType, "binary" ) == 0 )
			(*pNewOption)->eOptionType = OPTION_TYPE_BINARY;
		else if ( Q_stricmp( szType, "slider" ) == 0 )
			(*pNewOption)->eOptionType = OPTION_TYPE_SLIDER;
		else if ( Q_stricmp( szType, "choice" ) == 0 )
			(*pNewOption)->eOptionType = OPTION_TYPE_CHOICE;
		else if ( Q_stricmp( szType, "bind" ) == 0 )
			(*pNewOption)->eOptionType = OPTION_TYPE_BIND;

		// Get type specific values
		switch ( (*pNewOption)->eOptionType )
		{
			case OPTION_TYPE_SLIDER:
			{
				(*pNewOption)->fMinValue = pKey->GetFloat( "minvalue", 0.0f );
				(*pNewOption)->fMaxValue = pKey->GetFloat( "maxvalue", 1.0f );
				(*pNewOption)->fIncValue = pKey->GetFloat( "incvalue", 0.0f );

				char szSliderHomeType[ OPTION_STRING_LENGTH ];
				Q_strncpy( szSliderHomeType, pKey->GetString( "sliderhome", "none" ), sizeof( szSliderHomeType ) );

				if ( Q_stricmp( szSliderHomeType, "prev" ) == 0 )
					(*pNewOption)->eSliderHomeType = SLIDER_HOME_PREV;
				else if ( Q_stricmp( szSliderHomeType, "min" ) == 0 )
					(*pNewOption)->eSliderHomeType = SLIDER_HOME_MIN;
				else if ( Q_stricmp( szSliderHomeType, "center" ) == 0 )
					(*pNewOption)->eSliderHomeType = SLIDER_HOME_CENTER;
				else if ( Q_stricmp( szSliderHomeType, "max" ) == 0 )
					(*pNewOption)->eSliderHomeType = SLIDER_HOME_MAX;
				else
					(*pNewOption)->eSliderHomeType = SLIDER_HOME_NONE;

				switch ( (*pNewOption)->eSliderHomeType )
				{
					case SLIDER_HOME_MIN:
						(*pNewOption)->fSliderHomeValue = (*pNewOption)->fMinValue;
						break;

					case SLIDER_HOME_CENTER:
						(*pNewOption)->fSliderHomeValue = ( (*pNewOption)->fMaxValue + (*pNewOption)->fMinValue ) * 0.5f;
						break;

					case SLIDER_HOME_MAX:
						(*pNewOption)->fSliderHomeValue = (*pNewOption)->fMaxValue;
						break;

					default:
						(*pNewOption)->fSliderHomeValue = 0.0f;
				}

				break;
			}

			case OPTION_TYPE_CHOICE:
			{
				Q_strncpy( (*pNewOption)->szConvar2, pKey->GetString( "convar2", "" ), sizeof( (*pNewOption)->szConvar ) );

				if ( (*pNewOption)->bVocalsLanguage )
				{
					(*pNewOption)->iCurrentChoice = -1;
					int iChoice = (*pNewOption)->m_Choices.AddToTail();
					OptionChoiceData_t *pNewOptionChoice = &((*pNewOption)->m_Choices[ iChoice ]);

					Q_strncpy( pNewOptionChoice->szName, "#GameUI_Language_English", sizeof( pNewOptionChoice->szName ) );
					Q_strncpy( pNewOptionChoice->szValue, "1", sizeof( pNewOptionChoice->szValue ) );

					iChoice = (*pNewOption)->m_Choices.AddToTail();
					pNewOptionChoice = &((*pNewOption)->m_Choices[ iChoice ]);

					char szLanguage[ 256 ];
					Q_snprintf( szLanguage, sizeof( szLanguage ), "#GameUI_Language_%s", XBX_GetLanguageString() );
					Q_strncpy( pNewOptionChoice->szName, szLanguage, sizeof( pNewOptionChoice->szName ) );
					Q_strncpy( pNewOptionChoice->szValue, "0", sizeof( pNewOptionChoice->szValue ) );
				}
				else
				{
					(*pNewOption)->iCurrentChoice = -1;
					KeyValues *pChoicesKey = pKey->FindKey( "choices" );

					if ( pChoicesKey )
					{
						KeyValues *pSubKey = NULL;
						for ( pSubKey = pChoicesKey->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey() )
						{
							int iChoice = (*pNewOption)->m_Choices.AddToTail();
							OptionChoiceData_t *pNewOptionChoice = &((*pNewOption)->m_Choices[ iChoice ]);

							Q_strncpy( pNewOptionChoice->szName, pSubKey->GetName(), sizeof( pNewOptionChoice->szName ) );
							Q_strncpy( pNewOptionChoice->szValue, pSubKey->GetString(), sizeof( pNewOptionChoice->szValue ) );
						}

						if ( (*pNewOption)->m_Choices.Count() < 2 )
						{
							DevWarning( "Option \"%s\" is type CHOICE but has less than 2 choices!", (*pNewOption)->szDisplayName );
						}
					}
				}
				break;
			}
		}
	}
}

int __cdecl SortByPriority( OptionData_t * const *pLeft, OptionData_t * const *pRight )
{
	return (*pLeft)->iPriority - (*pRight)->iPriority;
}

void COptionsDialogXbox::SortOptions( void )
{
	m_pOptions->Sort( SortByPriority );
}