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

#include "OptionsSubVideo.h"
#include "cvarslider.h"
#include "EngineInterface.h"
#include "BasePanel.h"
#include "IGameUIFuncs.h"
#include "modes.h"
#include "materialsystem/materialsystem_config.h"
#include "filesystem.h"
#include "GameUI_Interface.h"
#include "vgui_controls/CheckButton.h"
#include "vgui_controls/ComboBox.h"
#include "vgui_controls/Frame.h"
#include "vgui_controls/QueryBox.h"
#include "CvarToggleCheckButton.h"
#include "tier1/KeyValues.h"
#include "vgui/IInput.h"
#include "vgui/ILocalize.h"
#include "vgui/ISystem.h"
#include "tier0/icommandline.h"
#include "tier1/convar.h"
#include "ModInfo.h"
#include "vgui_controls/Tooltip.h"
#include "sourcevr/isourcevirtualreality.h"

#if defined( USE_SDL )
#include "SDL.h"
#endif

#include "inetchannelinfo.h"

extern IMaterialSystem *materials;

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

using namespace vgui;

//-----------------------------------------------------------------------------
// Purpose: aspect ratio mappings (for normal/widescreen combo)
//-----------------------------------------------------------------------------
struct RatioToAspectMode_t
{
	int anamorphic;
	float aspectRatio;
};
RatioToAspectMode_t g_RatioToAspectModes[] =
{
	{	0,		4.0f / 3.0f },
	{	1,		16.0f / 9.0f },
	{	2,		16.0f / 10.0f },
	{	2,		1.0f },
};

struct AAMode_t
{
	int m_nNumSamples;
	int m_nQualityLevel;
};

//-----------------------------------------------------------------------------
// Purpose: list of valid dx levels
//-----------------------------------------------------------------------------
int g_DirectXLevels[] =
{
	70,
	80,
	81,
	90,
#if DX_TO_GL_ABSTRACTION
	92,
#endif
	95,
};

//-----------------------------------------------------------------------------
// Purpose: returns the string name of a given dxlevel
//-----------------------------------------------------------------------------
void GetNameForDXLevel( int dxlevel, char *name, int bufferSize)
{
	if ( ( dxlevel >= 92 ) && ( dxlevel <= 95 ) )
	{
		Q_snprintf( name, bufferSize, "DirectX v9.0+" );
	}
	else
	{
		Q_snprintf( name, bufferSize, "DirectX v%.1f", dxlevel / 10.0f );
	}
}
	
//-----------------------------------------------------------------------------
// Purpose: returns the aspect ratio mode number for the given resolution
//-----------------------------------------------------------------------------
int GetScreenAspectMode( int width, int height )
{
	float aspectRatio = (float)width / (float)height;

	// just find the closest ratio
	float closestAspectRatioDist = 99999.0f;
	int closestAnamorphic = 0;
	for (int i = 0; i < ARRAYSIZE(g_RatioToAspectModes); i++)
	{
		float dist = fabs( g_RatioToAspectModes[i].aspectRatio - aspectRatio );
		if (dist < closestAspectRatioDist)
		{
			closestAspectRatioDist = dist;
			closestAnamorphic = g_RatioToAspectModes[i].anamorphic;
		}
	}

	return closestAnamorphic;
}

//-----------------------------------------------------------------------------
// Purpose: returns the string name of the specified resolution mode
//-----------------------------------------------------------------------------
static void GetResolutionName( vmode_t *mode, char *sz, int sizeofsz, int desktopWidth, int desktopHeight )
{
	Q_snprintf( sz, sizeofsz, "%i x %i%s", mode->width, mode->height,
				( mode->width == desktopWidth ) && ( mode->height == desktopHeight ) ? " (native)": "" );
}

//-----------------------------------------------------------------------------
// Purpose: Gamma-adjust dialog
//-----------------------------------------------------------------------------
class CGammaDialog : public vgui::Frame
{
	DECLARE_CLASS_SIMPLE( CGammaDialog, vgui::Frame );
public:
	CGammaDialog( vgui::VPANEL hParent ) : BaseClass( NULL, "OptionsSubVideoGammaDlg" )
	{
		// parent is ignored, since we want look like we're steal focus from the parent (we'll become modal below)
		SetTitle("#GameUI_AdjustGamma_Title", true);
		SetSize( 400, 260 );
		SetDeleteSelfOnClose( true );

		m_pGammaSlider = new CCvarSlider( this, "Gamma", "#GameUI_Gamma", 1.6f, 2.6f, "mat_monitorgamma" );
		m_pGammaLabel = new Label( this, "Gamma label", "#GameUI_Gamma" );
		m_pGammaEntry = new TextEntry( this, "GammaEntry" );

		Button *ok = new Button( this, "OKButton", "#vgui_ok" );
		ok->SetCommand( new KeyValues("OK") );

		LoadControlSettings( "resource/OptionsSubVideoGammaDlg.res" );
		MoveToCenterOfScreen();
		SetSizeable( false );

		m_pGammaSlider->SetTickCaptions( "#GameUI_Light", "#GameUI_Dark" );
	}

	MESSAGE_FUNC_PTR( OnGammaChanged, "SliderMoved", panel )
	{
		if (panel == m_pGammaSlider)
		{
			m_pGammaSlider->ApplyChanges();
		}
	}

	virtual void Activate()
	{
		BaseClass::Activate();
		m_flOriginalGamma = m_pGammaSlider->GetValue();
		UpdateGammaLabel();
	}

	MESSAGE_FUNC( OnOK, "OK" )
	{
		// make the gamma stick
		m_flOriginalGamma = m_pGammaSlider->GetValue();
		Close();
	}

	virtual void OnClose()
	{
		// reset to the original gamma
		m_pGammaSlider->SetValue( m_flOriginalGamma );
		m_pGammaSlider->ApplyChanges();
		BaseClass::OnClose();
	}

	void OnKeyCodeTyped(KeyCode code)
	{
		// force ourselves to be closed if the escape key it pressed
		if (code == KEY_ESCAPE)
		{
			Close();
		}
		else
		{
			BaseClass::OnKeyCodeTyped(code);
		}
	}

	MESSAGE_FUNC_PTR( OnControlModified, "ControlModified", panel )
	{
		// the HasBeenModified() check is so that if the value is outside of the range of the
		// slider, it won't use the slider to determine the display value but leave the
		// real value that we determined in the constructor
		if (panel == m_pGammaSlider && m_pGammaSlider->HasBeenModified())
		{
			UpdateGammaLabel();
		}
	}

	MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel )
	{
		if (panel == m_pGammaEntry)
		{
			char buf[64];
			m_pGammaEntry->GetText(buf, 64);

			float fValue = (float) atof(buf);
			if (fValue >= 1.0)
			{
				m_pGammaSlider->SetSliderValue(fValue);
				PostActionSignal(new KeyValues("ApplyButtonEnable"));
			}
		}
	}

	void UpdateGammaLabel()
	{
		char buf[64];
		Q_snprintf(buf, sizeof( buf ), " %.1f", m_pGammaSlider->GetSliderValue());
		m_pGammaEntry->SetText(buf);
	}


private:
	CCvarSlider			*m_pGammaSlider;
	vgui::Label			*m_pGammaLabel;
	vgui::TextEntry		*m_pGammaEntry;
	float				m_flOriginalGamma;
};


//-----------------------------------------------------------------------------
// Purpose: advanced keyboard settings dialog
//-----------------------------------------------------------------------------
class COptionsSubVideoAdvancedDlg : public vgui::Frame
{
	DECLARE_CLASS_SIMPLE( COptionsSubVideoAdvancedDlg, vgui::Frame );
public:
	COptionsSubVideoAdvancedDlg( vgui::Panel *parent ) : BaseClass( parent , "OptionsSubVideoAdvancedDlg" )
	{
		SetTitle("#GameUI_VideoAdvanced_Title", true);
		SetSize( 260, 400 );

		m_pDXLevel = new ComboBox(this, "dxlabel", 6, false );
		const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard();
		KeyValues *pKeyValues = new KeyValues( "config" );
		materials->GetRecommendedConfigurationInfo( 0, pKeyValues );
		m_pDXLevel->DeleteAllItems();
		for (int i = 0; i < ARRAYSIZE(g_DirectXLevels); i++)
		{
			// don't allow choice of lower dxlevels than the default, 
			// unless we're already at that lower level or have it forced
			if (!CommandLine()->CheckParm("-dxlevel") &&
				g_DirectXLevels[i] != config.dxSupportLevel &&
				g_DirectXLevels[i] < pKeyValues->GetInt("ConVar.mat_dxlevel"))
				continue;

			KeyValues *pTempKV = new KeyValues("config");
			if (g_DirectXLevels[i] == pKeyValues->GetInt("ConVar.mat_dxlevel")
				|| materials->GetRecommendedConfigurationInfo( g_DirectXLevels[i], pTempKV ))
			{
				// add the configuration in the combo
				char szDXLevelName[64];
				GetNameForDXLevel( g_DirectXLevels[i], szDXLevelName, sizeof(szDXLevelName) );
				m_pDXLevel->AddItem( szDXLevelName, new KeyValues("dxlevel", "dxlevel", g_DirectXLevels[i]) );
			}

			pTempKV->deleteThis();
		}
		pKeyValues->deleteThis();

		m_pModelDetail = new ComboBox( this, "ModelDetail", 6, false );
		m_pModelDetail->AddItem("#gameui_low", NULL);
		m_pModelDetail->AddItem("#gameui_medium", NULL);
		m_pModelDetail->AddItem("#gameui_high", NULL);

		m_pTextureDetail = new ComboBox( this, "TextureDetail", 6, false );
		m_pTextureDetail->AddItem("#gameui_low", NULL);
		m_pTextureDetail->AddItem("#gameui_medium", NULL);
		m_pTextureDetail->AddItem("#gameui_high", NULL);
		m_pTextureDetail->AddItem("#gameui_ultra", NULL);

		// Build list of MSAA and CSAA modes, based upon those which are supported by the device
		//
		// The modes that we've seen in the wild to date are as follows (in perf order, fastest to slowest)
		//
		//								2x	4x	6x	8x	16x	8x	16xQ
		//		Texture/Shader Samples	1	1	1	1	1	1	1
		//		Stored Color/Z Samples	2	4	6	4	4	8	8
		//		Coverage Samples		2	4	6	8	16	8	16
		//		MSAA or CSAA			M	M	M	C	C	M	C
		//
		//	The CSAA modes are nVidia only (added in the G80 generation of GPUs)
		//
		m_nNumAAModes = 0;
		m_pAntialiasingMode = new ComboBox( this, "AntialiasingMode", 10, false );
		m_pAntialiasingMode->AddItem("#GameUI_None", NULL);
		m_nAAModes[m_nNumAAModes].m_nNumSamples = 1;
		m_nAAModes[m_nNumAAModes].m_nQualityLevel = 0;
		m_nNumAAModes++;

		if ( materials->SupportsMSAAMode(2) )
		{
			m_pAntialiasingMode->AddItem("#GameUI_2X", NULL);
			m_nAAModes[m_nNumAAModes].m_nNumSamples = 2;
			m_nAAModes[m_nNumAAModes].m_nQualityLevel = 0;
			m_nNumAAModes++;
		}

		if ( materials->SupportsMSAAMode(4) )
		{
			m_pAntialiasingMode->AddItem("#GameUI_4X", NULL);
			m_nAAModes[m_nNumAAModes].m_nNumSamples = 4;
			m_nAAModes[m_nNumAAModes].m_nQualityLevel = 0;
			m_nNumAAModes++;
		}

		if ( materials->SupportsMSAAMode(6) )
		{
			m_pAntialiasingMode->AddItem("#GameUI_6X", NULL);
			m_nAAModes[m_nNumAAModes].m_nNumSamples = 6;
			m_nAAModes[m_nNumAAModes].m_nQualityLevel = 0;
			m_nNumAAModes++;
		}

		if ( materials->SupportsCSAAMode(4, 2) )							// nVidia CSAA			"8x"
		{
			m_pAntialiasingMode->AddItem("#GameUI_8X_CSAA", NULL);
			m_nAAModes[m_nNumAAModes].m_nNumSamples = 4;
			m_nAAModes[m_nNumAAModes].m_nQualityLevel = 2;
			m_nNumAAModes++;
		}

		if ( materials->SupportsCSAAMode(4, 4) )							// nVidia CSAA			"16x"
		{
			m_pAntialiasingMode->AddItem("#GameUI_16X_CSAA", NULL);
			m_nAAModes[m_nNumAAModes].m_nNumSamples = 4;
			m_nAAModes[m_nNumAAModes].m_nQualityLevel = 4;
			m_nNumAAModes++;
		}

		if ( materials->SupportsMSAAMode(8) )
		{
			m_pAntialiasingMode->AddItem("#GameUI_8X", NULL);
			m_nAAModes[m_nNumAAModes].m_nNumSamples = 8;
			m_nAAModes[m_nNumAAModes].m_nQualityLevel = 0;
			m_nNumAAModes++;
		}

		if ( materials->SupportsCSAAMode(8, 2) )							// nVidia CSAA			"16xQ"
		{
			m_pAntialiasingMode->AddItem("#GameUI_16XQ_CSAA", NULL);
			m_nAAModes[m_nNumAAModes].m_nNumSamples = 8;
			m_nAAModes[m_nNumAAModes].m_nQualityLevel = 2;
			m_nNumAAModes++;
		}

		m_pFilteringMode = new ComboBox( this, "FilteringMode", 6, false );
		m_pFilteringMode->AddItem("#GameUI_Bilinear", NULL);
		m_pFilteringMode->AddItem("#GameUI_Trilinear", NULL);
		m_pFilteringMode->AddItem("#GameUI_Anisotropic2X", NULL);
		m_pFilteringMode->AddItem("#GameUI_Anisotropic4X", NULL);
		m_pFilteringMode->AddItem("#GameUI_Anisotropic8X", NULL);
		m_pFilteringMode->AddItem("#GameUI_Anisotropic16X", NULL);

		m_pShadowDetail = new ComboBox( this, "ShadowDetail", 6, false );
		m_pShadowDetail->AddItem("#gameui_low", NULL);
		m_pShadowDetail->AddItem("#gameui_medium", NULL);
		if ( materials->SupportsShadowDepthTextures() )
		{
			m_pShadowDetail->AddItem("#gameui_high", NULL);
		}

		ConVarRef mat_dxlevel( "mat_dxlevel" );

		m_pHDR = new ComboBox( this, "HDR", 6, false );
		m_pHDR->AddItem("#GameUI_hdr_level0", NULL);
		m_pHDR->AddItem("#GameUI_hdr_level1", NULL);

		if ( materials->SupportsHDRMode( HDR_TYPE_INTEGER ) )
		{
			m_pHDR->AddItem("#GameUI_hdr_level2", NULL);
		}
#if 0
		if ( materials->SupportsHDRMode( HDR_TYPE_FLOAT ) )
		{
			m_pHDR->AddItem("#GameUI_hdr_level3", NULL);
		}
#endif

		m_pHDR->SetEnabled( mat_dxlevel.GetInt() >= 80 );

		m_pWaterDetail = new ComboBox( this, "WaterDetail", 6, false );
		m_pWaterDetail->AddItem("#gameui_noreflections", NULL);
		m_pWaterDetail->AddItem("#gameui_reflectonlyworld", NULL);
		m_pWaterDetail->AddItem("#gameui_reflectall", NULL);

		m_pVSync = new ComboBox( this, "VSync", 2, false );
		m_pVSync->AddItem("#gameui_disabled", NULL);
		m_pVSync->AddItem("#gameui_enabled", NULL);

		m_pMulticore = new ComboBox( this, "Multicore", 2, false );
		m_pMulticore->AddItem("#gameui_disabled", NULL);
		m_pMulticore->AddItem("#gameui_enabled", NULL);

		m_pShaderDetail = new ComboBox( this, "ShaderDetail", 6, false );
		m_pShaderDetail->AddItem("#gameui_low", NULL);
		m_pShaderDetail->AddItem("#gameui_high", NULL);

		m_pColorCorrection = new ComboBox( this, "ColorCorrection", 2, false );
		m_pColorCorrection->AddItem("#gameui_disabled", NULL);
		m_pColorCorrection->AddItem("#gameui_enabled", NULL);

		m_pMotionBlur = new ComboBox( this, "MotionBlur", 2, false );
		m_pMotionBlur->AddItem("#gameui_disabled", NULL);
		m_pMotionBlur->AddItem("#gameui_enabled", NULL);

		LoadControlSettings( "resource/OptionsSubVideoAdvancedDlg.res" );
		MoveToCenterOfScreen();
		SetSizeable( false );

		m_pDXLevel->SetEnabled(false);
		
		m_pColorCorrection->SetEnabled( mat_dxlevel.GetInt() >= 90 );
		m_pMotionBlur->SetEnabled( mat_dxlevel.GetInt() >= 90 );
		
		if ( g_pCVar->FindVar( "fov_desired" ) == NULL )
		{
			Panel *pFOV = FindChildByName( "FovSlider" );
			if ( pFOV )
			{
				pFOV->SetVisible( false );
			}

			pFOV = FindChildByName( "FovLabel" );
			if ( pFOV )
			{
				pFOV->SetVisible( false );
			}

			pFOV = FindChildByName( "FovValueLabel" );
			if ( pFOV )
			{
				pFOV->SetVisible( false );
			}
		}
		
		MarkDefaultSettingsAsRecommended();

		m_bUseChanges = false;
	}

	virtual void Activate()
	{
		BaseClass::Activate();

		input()->SetAppModalSurface(GetVPanel());

		if (!m_bUseChanges)
		{
			// reset the data
			OnResetData();
		}
	}

	void SetComboItemAsRecommended( vgui::ComboBox *combo, int iItem )
	{
		// get the item text
		wchar_t text[512];
		combo->GetItemText(iItem, text, sizeof(text));

		// append the recommended flag
		wchar_t newText[512];
		_snwprintf( newText, sizeof(newText) / sizeof(wchar_t), L"%ls *", text );

		// reset
		combo->UpdateItem(iItem, newText, NULL);
	}

	int FindMSAAMode( int nAASamples, int nAAQuality )
	{
		// Run through the AA Modes supported by the device
        for ( int nAAMode = 0; nAAMode < m_nNumAAModes; nAAMode++ )
		{
			// If we found the mode that matches what we're looking for, return the index
			if ( ( m_nAAModes[nAAMode].m_nNumSamples == nAASamples) && ( m_nAAModes[nAAMode].m_nQualityLevel == nAAQuality) )
			{
				return nAAMode;
			}
		}

		return 0;	// Didn't find what we're looking for, so no AA
	}

	MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel )
	{
		if ( panel == m_pDXLevel && RequiresRestart() )
		{
			// notify the user that this will require a disconnect
			QueryBox *box = new QueryBox("#GameUI_SettingRequiresDisconnect_Title", "#GameUI_SettingRequiresDisconnect_Info", this);
			box->AddActionSignalTarget( this );
			box->SetCancelCommand(new KeyValues("ResetDXLevelCombo"));
			box->DoModal();
		}
	}

	MESSAGE_FUNC( OnGameUIHidden, "GameUIHidden" )	// called when the GameUI is hidden
	{
		Close();
	}

	MESSAGE_FUNC( ResetDXLevelCombo, "ResetDXLevelCombo" )
	{
		ConVarRef mat_dxlevel( "mat_dxlevel" );
		for (int i = 0; i < m_pDXLevel->GetItemCount(); i++)
		{
			KeyValues *kv = m_pDXLevel->GetItemUserData(i);
			if ( kv->GetInt("dxlevel") == mat_dxlevel.GetInt( ) )
			{
				m_pDXLevel->ActivateItem( i );
				break;
			}
		}

		// Reset HDR too
		if ( m_pHDR->IsEnabled() )
		{
			ConVarRef mat_hdr_level("mat_hdr_level");
			Assert( mat_hdr_level.IsValid() );
			m_pHDR->ActivateItem( clamp( mat_hdr_level.GetInt(), 0, 2 ) );
		}
	}

	MESSAGE_FUNC( OK_Confirmed, "OK_Confirmed" )
	{
		m_bUseChanges = true;
		Close();
	}

	void MarkDefaultSettingsAsRecommended()
	{
		// Pull in data from dxsupport.cfg database (includes fine-grained per-vendor/per-device config data)
		KeyValues *pKeyValues = new KeyValues( "config" );
		materials->GetRecommendedConfigurationInfo( 0, pKeyValues );	

		// Read individual values from keyvalues which came from dxsupport.cfg database
		int nSkipLevels = pKeyValues->GetInt( "ConVar.mat_picmip", 0 );
		int nAnisotropicLevel = pKeyValues->GetInt( "ConVar.mat_forceaniso", 1 );
		int nForceTrilinear = pKeyValues->GetInt( "ConVar.mat_trilinear", 0 );
		int nAASamples = pKeyValues->GetInt( "ConVar.mat_antialias", 0 );
		int nAAQuality = pKeyValues->GetInt( "ConVar.mat_aaquality", 0 );
		int nRenderToTextureShadows = pKeyValues->GetInt( "ConVar.r_shadowrendertotexture", 0 );
		int nShadowDepthTextureShadows = pKeyValues->GetInt( "ConVar.r_flashlightdepthtexture", 0 );
#ifndef _X360
		int nWaterUseRealtimeReflection = pKeyValues->GetInt( "ConVar.r_waterforceexpensive", 0 );
#endif
		int nWaterUseEntityReflection = pKeyValues->GetInt( "ConVar.r_waterforcereflectentities", 0 );
		int nMatVSync = pKeyValues->GetInt( "ConVar.mat_vsync", 1 );
		int nRootLOD = pKeyValues->GetInt( "ConVar.r_rootlod", 0 );
		int nReduceFillRate = pKeyValues->GetInt( "ConVar.mat_reducefillrate", 0 );
		int nDXLevel = pKeyValues->GetInt( "ConVar.mat_dxlevel", 0 );
		int nColorCorrection = pKeyValues->GetInt( "ConVar.mat_colorcorrection", 0 );
		int nMotionBlur = pKeyValues->GetInt( "ConVar.mat_motion_blur_enabled", 0 );
		// It doesn't make sense to retrieve this convar from dxsupport, because we'll then have materialsystem setting this config at loadtime. (Also, it only has very minimal support for CPU related configuration.)
		//int nMulticore = pKeyValues->GetInt( "ConVar.mat_queue_mode", 0 );
		int nMulticore = GetCPUInformation()->m_nPhysicalProcessors >= 2;
		
		// Only recommend a dxlevel if there is more than one available
		if ( m_pDXLevel->GetItemCount() > 1 )
		{
			for (int i = 0; i < m_pDXLevel->GetItemCount(); i++)
			{
				KeyValues *kv = m_pDXLevel->GetItemUserData(i);
				if (kv->GetInt("dxlevel") == pKeyValues->GetInt("ConVar.mat_dxlevel"))
				{
					SetComboItemAsRecommended( m_pDXLevel, i );
					break;
				}
			}
		}
	
		SetComboItemAsRecommended( m_pModelDetail, 2 - nRootLOD );
		SetComboItemAsRecommended( m_pTextureDetail, 2 - nSkipLevels );

		switch ( nAnisotropicLevel )
		{
		case 2:
			SetComboItemAsRecommended( m_pFilteringMode, 2 );
			break;
		case 4:
			SetComboItemAsRecommended( m_pFilteringMode, 3 );
			break;
		case 8:
			SetComboItemAsRecommended( m_pFilteringMode, 4 );
			break;
		case 16:
			SetComboItemAsRecommended( m_pFilteringMode, 5 );
			break;
		case 0:
		default:
			if ( nForceTrilinear != 0 )
			{
				SetComboItemAsRecommended( m_pFilteringMode, 1 );
			}
			else
			{
				SetComboItemAsRecommended( m_pFilteringMode, 0 );
			}
			break;
		}

		// Map desired mode to list item number
		int nMSAAMode = FindMSAAMode( nAASamples, nAAQuality );
		SetComboItemAsRecommended( m_pAntialiasingMode, nMSAAMode );

		if ( nShadowDepthTextureShadows )
			SetComboItemAsRecommended( m_pShadowDetail, 2 );	// Shadow depth mapping (in addition to RTT shadows)
		else if ( nRenderToTextureShadows )
			SetComboItemAsRecommended( m_pShadowDetail, 1 );	// RTT shadows
		else
			SetComboItemAsRecommended( m_pShadowDetail, 0 );	// Blobbies

		SetComboItemAsRecommended( m_pShaderDetail, nReduceFillRate ? 0 : 1 );
		
#ifndef _X360
		if ( nWaterUseRealtimeReflection )
#endif
		{
			if ( nWaterUseEntityReflection )
			{
				SetComboItemAsRecommended( m_pWaterDetail, 2 );
			}
			else
			{
				SetComboItemAsRecommended( m_pWaterDetail, 1 );
			}
		}
#ifndef _X360
		else
		{
			SetComboItemAsRecommended( m_pWaterDetail, 0 );
		}
#endif

		SetComboItemAsRecommended( m_pVSync, nMatVSync != 0 );

		SetComboItemAsRecommended( m_pMulticore, nMulticore != 0 );

		SetComboItemAsRecommended( m_pHDR, nDXLevel >= 90 ? 2 : 0 );

		SetComboItemAsRecommended( m_pColorCorrection, nColorCorrection );

		SetComboItemAsRecommended( m_pMotionBlur, nMotionBlur );

		pKeyValues->deleteThis();
	}

	void ApplyChangesToConVar( const char *pConVarName, int value )
	{
		Assert( cvar->FindVar( pConVarName ) );
		char szCmd[256];
		Q_snprintf( szCmd, sizeof(szCmd), "%s %d\n", pConVarName, value );
		engine->ClientCmd_Unrestricted( szCmd );
	}

	virtual void ApplyChanges()
	{
		if ( !m_bUseChanges )
			return;
		
		KeyValues *pActiveItem = m_pDXLevel->GetActiveItemUserData();
		if ( pActiveItem )
		{
			ApplyChangesToConVar( "mat_dxlevel", pActiveItem->GetInt( "dxlevel" ) );
		}
		
		ApplyChangesToConVar( "r_rootlod", 2 - m_pModelDetail->GetActiveItem());
		ApplyChangesToConVar( "mat_picmip", 2 - m_pTextureDetail->GetActiveItem());

		// reset everything tied to the filtering mode, then the switch sets the appropriate one
		ApplyChangesToConVar( "mat_trilinear", false );
		ApplyChangesToConVar( "mat_forceaniso", 1 );
		switch ( m_pFilteringMode->GetActiveItem() )
		{
		case 0:
			break;
		case 1:
			ApplyChangesToConVar( "mat_trilinear", true );
			break;
		case 2:
			ApplyChangesToConVar( "mat_forceaniso", 2 );
			break;
		case 3:
			ApplyChangesToConVar( "mat_forceaniso", 4 );
			break;
		case 4:
			ApplyChangesToConVar( "mat_forceaniso", 8 );
			break;
		case 5:
			ApplyChangesToConVar( "mat_forceaniso", 16 );
			break;
		default:
			// Trilinear.
			ApplyChangesToConVar( "mat_forceaniso", 1 );
			break;
		}

		// Set the AA convars according to the menu item chosen
		int nActiveAAItem = m_pAntialiasingMode->GetActiveItem();
		ApplyChangesToConVar( "mat_antialias", m_nAAModes[nActiveAAItem].m_nNumSamples );
		ApplyChangesToConVar( "mat_aaquality", m_nAAModes[nActiveAAItem].m_nQualityLevel );

		if( m_pHDR->IsEnabled() )
		{
			ConVarRef mat_hdr_level("mat_hdr_level");
			Assert( mat_hdr_level.IsValid() );
			mat_hdr_level.SetValue(m_pHDR->GetActiveItem());
		}

		if ( m_pShadowDetail->GetActiveItem() == 0 )						// Blobby shadows
		{
			ApplyChangesToConVar( "r_shadowrendertotexture", 0 );			// Turn off RTT shadows
			ApplyChangesToConVar( "r_flashlightdepthtexture", 0 );			// Turn off shadow depth textures
		}
		else if ( m_pShadowDetail->GetActiveItem() == 1 )					// RTT shadows only
		{
			ApplyChangesToConVar( "r_shadowrendertotexture", 1 );			// Turn on RTT shadows
			ApplyChangesToConVar( "r_flashlightdepthtexture", 0 );			// Turn off shadow depth textures
		}
		else if ( m_pShadowDetail->GetActiveItem() == 2 )					// Shadow depth textures
		{
			ApplyChangesToConVar( "r_shadowrendertotexture", 1 );			// Turn on RTT shadows
			ApplyChangesToConVar( "r_flashlightdepthtexture", 1 );			// Turn on shadow depth textures
		}

		ApplyChangesToConVar( "mat_reducefillrate", ( m_pShaderDetail->GetActiveItem() > 0 ) ? 0 : 1 );

		switch ( m_pWaterDetail->GetActiveItem() )
		{
		default:
		case 0:
#ifndef _X360
			ApplyChangesToConVar( "r_waterforceexpensive", false );
#endif
			ApplyChangesToConVar( "r_waterforcereflectentities", false );
			break;
		case 1:
#ifndef _X360
			ApplyChangesToConVar( "r_waterforceexpensive", true );
#endif
			ApplyChangesToConVar( "r_waterforcereflectentities", false );
			break;
		case 2:
#ifndef _X360
			ApplyChangesToConVar( "r_waterforceexpensive", true );
#endif
			ApplyChangesToConVar( "r_waterforcereflectentities", true );
			break;
		}

		ApplyChangesToConVar( "mat_vsync", m_pVSync->GetActiveItem() );	 

		int iMC = m_pMulticore->GetActiveItem();
		ApplyChangesToConVar( "mat_queue_mode", (iMC == 0) ? 0 : -1 );	 

		ApplyChangesToConVar( "mat_colorcorrection", m_pColorCorrection->GetActiveItem() );

		ApplyChangesToConVar( "mat_motion_blur_enabled", m_pMotionBlur->GetActiveItem() );
		
		CCvarSlider *pFOV = (CCvarSlider *)FindChildByName( "FOVSlider" );
		if ( pFOV ) 
		{
			pFOV->ApplyChanges();
		}
	}

	virtual void OnResetData()
	{
		ConVarRef mat_dxlevel( "mat_dxlevel" );
		ConVarRef r_rootlod( "r_rootlod" );
		ConVarRef mat_picmip( "mat_picmip" );
		ConVarRef mat_trilinear( "mat_trilinear" );
		ConVarRef mat_forceaniso( "mat_forceaniso" );
		ConVarRef mat_antialias( "mat_antialias" );
		ConVarRef mat_aaquality( "mat_aaquality" );
		ConVarRef mat_vsync( "mat_vsync" );
		ConVarRef mat_queue_mode( "mat_queue_mode" );
		ConVarRef r_flashlightdepthtexture( "r_flashlightdepthtexture" );
#ifndef _X360
		ConVarRef r_waterforceexpensive( "r_waterforceexpensive" );
#endif
		ConVarRef r_waterforcereflectentities( "r_waterforcereflectentities" );
		ConVarRef mat_reducefillrate("mat_reducefillrate" );
		ConVarRef mat_hdr_level( "mat_hdr_level" );
		ConVarRef mat_colorcorrection( "mat_colorcorrection" );
		ConVarRef mat_motion_blur_enabled( "mat_motion_blur_enabled" );
		ConVarRef r_shadowrendertotexture( "r_shadowrendertotexture" );

		ResetDXLevelCombo();

		m_pModelDetail->ActivateItem( 2 - clamp(r_rootlod.GetInt(), 0, 2) );
		m_pTextureDetail->ActivateItem( 2 - clamp(mat_picmip.GetInt(), -1, 2) );

		if ( r_flashlightdepthtexture.GetBool() )		// If we're doing flashlight shadow depth texturing...
		{
			r_shadowrendertotexture.SetValue( 1 );		// ...be sure render to texture shadows are also on
			m_pShadowDetail->ActivateItem( 2 );
		}
		else if ( r_shadowrendertotexture.GetBool() )	// RTT shadows, but not shadow depth texturing
		{
			m_pShadowDetail->ActivateItem( 1 );
		}
		else	// Lowest shadow quality
		{
			m_pShadowDetail->ActivateItem( 0 );
		}

		m_pShaderDetail->ActivateItem( mat_reducefillrate.GetBool() ? 0 : 1 );
		m_pHDR->ActivateItem(clamp(mat_hdr_level.GetInt(), 0, 2));

		switch (mat_forceaniso.GetInt())
		{
		case 2:
			m_pFilteringMode->ActivateItem( 2 );
			break;
		case 4:
			m_pFilteringMode->ActivateItem( 3 );
			break;
		case 8:
			m_pFilteringMode->ActivateItem( 4 );
			break;
		case 16:
			m_pFilteringMode->ActivateItem( 5 );
			break;
		case 0:
		default:
			if (mat_trilinear.GetBool())
			{
				m_pFilteringMode->ActivateItem( 1 );
			}
			else
			{
				m_pFilteringMode->ActivateItem( 0 );
			}
			break;
		}

		// Map convar to item on AA drop-down
		int nAASamples = mat_antialias.GetInt();
		int nAAQuality = mat_aaquality.GetInt();
		int nMSAAMode = FindMSAAMode( nAASamples, nAAQuality );
		m_pAntialiasingMode->ActivateItem( nMSAAMode );
	
		m_pAntialiasingMode->SetEnabled( m_nNumAAModes > 1 );

#ifndef _X360
		if ( r_waterforceexpensive.GetBool() )
#endif
		{
			if ( r_waterforcereflectentities.GetBool() )
			{
				m_pWaterDetail->ActivateItem( 2 );
			}
			else
			{
				m_pWaterDetail->ActivateItem( 1 );
			}
		}
#ifndef _X360
		else
		{
			m_pWaterDetail->ActivateItem( 0 );
		}
#endif

		m_pVSync->ActivateItem( mat_vsync.GetInt() );

		int iMC = mat_queue_mode.GetInt();

		// We (Rick!) have now switched -2 to mean enabled. So this comment has been rendered obsolete:
		//  -- For testing, we have -2, the legacy default setting as meaning multicore is disabled.
		//  -- After that, we'll switch -2 to mean it's enabled.
		m_pMulticore->ActivateItem( (iMC == 0) ? 0 : 1 );

		m_pColorCorrection->ActivateItem( mat_colorcorrection.GetInt() );

		m_pMotionBlur->ActivateItem( mat_motion_blur_enabled.GetInt() );

		// get current hardware dx support level
		char dxVer[64];
		GetNameForDXLevel( mat_dxlevel.GetInt(), dxVer, sizeof( dxVer ) );
		SetControlString("dxlabel", dxVer);

		// get installed version
		char szVersion[64];
		szVersion[0] = 0;
		system()->GetRegistryString( "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\DirectX\\Version", szVersion, sizeof(szVersion) );
		int os = 0, majorVersion = 0, minorVersion = 0, subVersion = 0;
		sscanf(szVersion, "%d.%d.%d.%d", &os, &majorVersion, &minorVersion, &subVersion);
		Q_snprintf(dxVer, sizeof(dxVer), "DirectX v%d.%d", majorVersion, minorVersion);
		SetControlString("dxinstalledlabel", dxVer);
	}

	virtual void OnCommand( const char *command )
	{
		if ( !stricmp(command, "OK") )
		{
			if ( RequiresRestart() )
			{
				// Bring up the confirmation dialog
				QueryBox *box = new QueryBox("#GameUI_SettingRequiresDisconnect_Title", "#GameUI_SettingRequiresDisconnect_Info");
				box->AddActionSignalTarget( this );
				box->SetOKCommand(new KeyValues("OK_Confirmed"));
				box->SetCancelCommand(new KeyValues("ResetDXLevelCombo"));
				box->DoModal();
				box->MoveToFront();
				return;
			}

			m_bUseChanges = true;
			Close();
		}
		else
		{
			BaseClass::OnCommand( command );
		}
	}

	void OnKeyCodeTyped(KeyCode code)
	{
		// force ourselves to be closed if the escape key it pressed
		if (code == KEY_ESCAPE)
		{
			Close();
		}
		else
		{
			BaseClass::OnKeyCodeTyped(code);
		}
	}

	bool RequiresRestart()
	{
		if ( GameUI().IsInLevel() )
		{
			if ( GameUI().IsInBackgroundLevel() )
				return false;
			if ( !GameUI().IsInMultiplayer() )
				return false;

			ConVarRef mat_dxlevel( "mat_dxlevel" );
			KeyValues *pUserData = m_pDXLevel->GetActiveItemUserData();
			Assert( pUserData );
			if ( pUserData && mat_dxlevel.GetInt() != pUserData->GetInt("dxlevel") )
			{
				return true;
			}

			// HDR changed?
			if ( m_pHDR->IsEnabled() )
			{
				ConVarRef mat_hdr_level("mat_hdr_level");
				Assert( mat_hdr_level.IsValid() );
				if ( mat_hdr_level.GetInt() != m_pHDR->GetActiveItem() )
					return true;
			}
		}
		return false;
	}

private:
	bool m_bUseChanges;
	vgui::ComboBox *m_pModelDetail, *m_pTextureDetail, *m_pAntialiasingMode, *m_pFilteringMode;
	vgui::ComboBox *m_pShadowDetail, *m_pHDR, *m_pWaterDetail, *m_pVSync, *m_pMulticore, *m_pShaderDetail;
	vgui::ComboBox *m_pColorCorrection;
	vgui::ComboBox *m_pMotionBlur;
	vgui::ComboBox *m_pDXLevel;

	int m_nNumAAModes;
	AAMode_t m_nAAModes[16];
};

#if defined( USE_SDL )

//-----------------------------------------------------------------------------
// Purpose: Get display index we will go fullscreen on.
//-----------------------------------------------------------------------------
static int getSDLDisplayIndex()
{
	static ConVarRef sdl_displayindex( "sdl_displayindex" );

	Assert( sdl_displayindex.IsValid() );
	return sdl_displayindex.IsValid() ? sdl_displayindex.GetInt() : 0;
}

//-----------------------------------------------------------------------------
// Purpose: Get display index we are currently fullscreen on. (or -1 if none).
//-----------------------------------------------------------------------------
static int getSDLDisplayIndexFullscreen()
{
	static ConVarRef sdl_displayindex_fullscreen( "sdl_displayindex_fullscreen" );

	Assert( sdl_displayindex_fullscreen.IsValid() );
	return sdl_displayindex_fullscreen.IsValid() ? sdl_displayindex_fullscreen.GetInt() : -1;
}

#endif // USE_SDL

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
COptionsSubVideo::COptionsSubVideo(vgui::Panel *parent) : PropertyPage(parent, NULL)
{
	m_bRequireRestart = false;

	m_bDisplayedVRModeMessage = false;

	m_pGammaButton = new Button( this, "GammaButton", "#GameUI_AdjustGamma" );
	m_pGammaButton->SetCommand(new KeyValues("OpenGammaDialog"));
	m_pMode = new ComboBox(this, "Resolution", 8, false);
	m_pAspectRatio = new ComboBox( this, "AspectRatio", 6, false );
	m_pVRMode = new ComboBox( this, "VRMode", 2, false );
	m_pAdvanced = new Button( this, "AdvancedButton", "#GameUI_AdvancedEllipsis" );
	m_pAdvanced->SetCommand(new KeyValues("OpenAdvanced"));
	m_pBenchmark = new Button( this, "BenchmarkButton", "#GameUI_LaunchBenchmark" );
	m_pBenchmark->SetCommand(new KeyValues("LaunchBenchmark"));
	m_pThirdPartyCredits = new URLButton(this, "ThirdPartyVideoCredits", "#GameUI_ThirdPartyTechCredits");
//	m_pThirdPartyCredits->SetCommand(new KeyValues("OpenThirdPartyVideoCreditsDialog"));
	m_pThirdPartyCredits->SetVisible(false);
	m_pHDContent = new CheckButton( this, "HDContentButton", "#GameUI_HDContent" );

	char pszAspectName[3][64];
	const wchar_t *unicodeText = g_pVGuiLocalize->Find("#GameUI_AspectNormal");
	g_pVGuiLocalize->ConvertUnicodeToANSI(unicodeText, pszAspectName[0], 32);
	unicodeText = g_pVGuiLocalize->Find("#GameUI_AspectWide16x9");
	g_pVGuiLocalize->ConvertUnicodeToANSI(unicodeText, pszAspectName[1], 32);
	unicodeText = g_pVGuiLocalize->Find("#GameUI_AspectWide16x10");
	g_pVGuiLocalize->ConvertUnicodeToANSI(unicodeText, pszAspectName[2], 32);

#ifndef ANDROID
	int iNormalItemID = m_pAspectRatio->AddItem( pszAspectName[0], NULL );
	int i16x9ItemID = m_pAspectRatio->AddItem( pszAspectName[1], NULL );
	int i16x10ItemID = m_pAspectRatio->AddItem( pszAspectName[2], NULL );

	const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard();

	int iAspectMode = GetScreenAspectMode( config.m_VideoMode.m_Width, config.m_VideoMode.m_Height );
	switch ( iAspectMode )
	{
	default:
	case 0:
		m_pAspectRatio->ActivateItem( iNormalItemID );
		break;
	case 1:
		m_pAspectRatio->ActivateItem( i16x9ItemID );
		break;
	case 2:
		m_pAspectRatio->ActivateItem( i16x10ItemID );
		break;
	}
#else
	int iNormalItemID = m_pAspectRatio->AddItem( "lemonparty.org", NULL );
	m_pAspectRatio->ActivateItem( iNormalItemID );

	m_pGammaButton->SetEnabled(false);
#endif

	char pszVRModeName[2][64];
	unicodeText = g_pVGuiLocalize->Find("#GameUI_Disabled");
	g_pVGuiLocalize->ConvertUnicodeToANSI(unicodeText, pszVRModeName[0], 32);
	unicodeText = g_pVGuiLocalize->Find("#GameUI_Enabled");
	g_pVGuiLocalize->ConvertUnicodeToANSI(unicodeText, pszVRModeName[1], 32);

	m_pVRMode->AddItem( pszVRModeName[0], NULL );
	m_pVRMode->AddItem( pszVRModeName[1], NULL );

	// Multimonitor under Direct3D requires you to destroy and recreate the device, 
	// which is an operation we don't support as it currently stands. The user can 
	// pass -adapter N to use a different device.
#if defined( USE_SDL ) && defined( DX_TO_GL_ABSTRACTION )
	int numVideoDisplays = SDL_GetNumVideoDisplays();

	m_pWindowed = new vgui::ComboBox( this, "DisplayModeCombo", 5 + numVideoDisplays, false );

	if ( numVideoDisplays <= 1 )
	{
		m_pWindowed->AddItem( "#GameUI_Fullscreen", NULL );
		m_pWindowed->AddItem( "#GameUI_Windowed", NULL );
	}
	else
	{
		// Add something like this:
		//   Full Screen (0)
		//   Full Screen (1)
		//   Windowed
		wchar_t *fullscreenText = g_pVGuiLocalize->Find( "#GameUI_Fullscreen" );

		for ( int i = 0; i < numVideoDisplays; i++ )
		{
			wchar_t ItemText[ 256 ];

			V_swprintf_safe( ItemText, L"%ls (%d)", fullscreenText, i );
			m_pWindowed->AddItem( ItemText, NULL );
		}

		m_pWindowed->AddItem( "#GameUI_Windowed", NULL );
	}

#else
	m_pWindowed = new vgui::ComboBox( this, "DisplayModeCombo", 6, false );

	m_pWindowed->AddItem( "#GameUI_Fullscreen", NULL );
	m_pWindowed->AddItem( "#GameUI_Windowed", NULL );
#endif

#ifdef ANDROID
	m_pWindowed->SetEnabled( false );
#endif

	LoadControlSettings("Resource\\OptionsSubVideo.res");

	// Moved down here so we can set the Drop down's
	// menu state after the default (disabled) value is loaded
	PrepareResolutionList();

	// only show the benchmark button if they have the benchmark map
	if ( !g_pFullFileSystem->FileExists("maps/test_hardware.bsp") )
	{
		m_pBenchmark->SetVisible( false );
	}

	if ( ModInfo().HasHDContent() )
	{
		m_pHDContent->SetVisible( true );
	}
	
	// if VR mode isn't available, disable the dropdown
	if( !g_pSourceVR )
	{
		// if sourcevr.dll is missing entirely that means VR mode is not
		// supported in this game. Hide the mode dropdown and its label 
		m_pVRMode->SetVisible( false );

		Panel *label = FindChildByName( "VRModeLabel" );
		if( label )
			label->SetVisible( false );
	}
	else if( !g_pSourceVR->IsHmdConnected() )
	{
		m_pVRMode->ActivateItem( 0 );
		m_pVRMode->SetEnabled( false );
		m_pVRMode->GetTooltip()->SetText( "#GameUI_NoVRTooltip" );
		EnableOrDisableWindowedForVR();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Generates resolution list
//-----------------------------------------------------------------------------
void COptionsSubVideo::PrepareResolutionList()
{
	// get the currently selected resolution
	char sz[256];
	m_pMode->GetText(sz, 256);
	int currentWidth = 0, currentHeight = 0;
	sscanf( sz, "%i x %i", &currentWidth, &currentHeight );

	// Clean up before filling the info again.
	m_pMode->DeleteAllItems();
#ifndef ANDROID
	m_pAspectRatio->SetItemEnabled(1, false);
	m_pAspectRatio->SetItemEnabled(2, false);
#endif
	// get full video mode list
	vmode_t *plist = NULL;
	int count = 0;
	gameuifuncs->GetVideoModes( &plist, &count );

	const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard();

	// Windowed is the last item in the combobox.
	bool bWindowed = ( m_pWindowed->GetActiveItem() >= ( m_pWindowed->GetItemCount() - 1 ) );
	int desktopWidth, desktopHeight;
	gameuifuncs->GetDesktopResolution( desktopWidth, desktopHeight );

#if defined( USE_SDL )
	bool bFullScreenWithMultipleDisplays = ( !bWindowed && ( SDL_GetNumVideoDisplays() > 1 ) );
	if ( bFullScreenWithMultipleDisplays )
	{
		SDL_Rect rect;
#if defined( DX_TO_GL_ABSTRACTION )
		int displayIndex = m_pWindowed->GetActiveItem();
#else
		int displayIndex = materials->GetCurrentAdapter();
#endif

		if ( !SDL_GetDisplayBounds( displayIndex, &rect ) )
		{
			desktopWidth = rect.w;
			desktopHeight = rect.h;
		}
	}

	// If we are switching to fullscreen, and this isn't the mode we're currently in, then
	//	fake things out so the native fullscreen resolution is selected. Stuck this in
	//	because I assume most people will go fullscreen at native resolution, and it's sometimes
	//	difficult to find the native resolution with all the aspect ratio options.
	bool bNewFullscreenDisplay = ( !bWindowed && ( getSDLDisplayIndexFullscreen() != m_pWindowed->GetActiveItem() ) );
	if ( bNewFullscreenDisplay )
	{
		currentWidth = desktopWidth;
		currentHeight = desktopHeight;
	}
#endif

	// iterate all the video modes adding them to the dropdown
	bool bFoundWidescreen = false;
	int selectedItemID = -1;
	for (int i = 0; i < count; i++, plist++)
	{
#if !defined( USE_SDL )
		// don't show modes bigger than the desktop for windowed mode
		if ( bWindowed )
#endif
		{
			if ( plist->width > desktopWidth || plist->height > desktopHeight )
			{
				// Filter out sizes larger than our desktop.
				continue;
			}
		}

		GetResolutionName( plist, sz, sizeof( sz ), desktopWidth, desktopHeight );

		int itemID = -1;

		int iAspectMode = GetScreenAspectMode( plist->width, plist->height );
#ifndef ANDROID
		if ( iAspectMode > 0 )
		{
			m_pAspectRatio->SetItemEnabled( iAspectMode, true );
			bFoundWidescreen = true;
		}

		// filter the list for those matching the current aspect
		if ( iAspectMode == m_pAspectRatio->GetActiveItem() )
		{
			itemID = m_pMode->AddItem( sz, NULL);
		}
#else
		float aspect = (float)plist->width / plist->height;
		float native_aspect = (float)desktopWidth / desktopHeight;

		if( fabs(native_aspect - aspect) < 0.01f )
			itemID = m_pMode->AddItem( sz, NULL);
#endif

		// try and find the bestplistplistplist match for the resolution to be selected
		if ( plist->width == currentWidth && plist->height == currentHeight )
		{
			selectedItemID = itemID;
		}
		else if ( selectedItemID == -1 && plist->width == config.m_VideoMode.m_Width && plist->height == config.m_VideoMode.m_Height )
		{
			selectedItemID = itemID;
		}
	}

	// disable ratio selection if we can't display widescreen.
#ifndef ANDROID
	m_pAspectRatio->SetEnabled( bFoundWidescreen );
#endif

	m_nSelectedMode = selectedItemID;

	if ( selectedItemID != -1 )
	{
		m_pMode->ActivateItem( selectedItemID );
	}
	else
	{
		int Width = config.m_VideoMode.m_Width;
		int Height = config.m_VideoMode.m_Height;

#if defined( USE_SDL )
		// If we are switching to a new display, or the size is greater than the desktop, then
		//	display the desktop width and height.
		if ( bNewFullscreenDisplay || ( Width > desktopWidth ) || ( Height > desktopHeight ) )
		{
			Width = desktopWidth;
			Height = desktopHeight;
		}
#endif

		Q_snprintf( sz, ARRAYSIZE( sz ), "%d x %d", Width, Height );
		m_pMode->SetText( sz );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
COptionsSubVideo::~COptionsSubVideo()
{
	if (m_hOptionsSubVideoAdvancedDlg.Get())
	{
		m_hOptionsSubVideoAdvancedDlg->MarkForDeletion();
	}
}


FILE *FOpenGameHDFile( const char *pchMode )
{
	const char *pGameDir = engine->GetGameDirectory();
	char szModSteamInfPath[ 1024 ];
	V_ComposeFileName( pGameDir, "game_hd.txt", szModSteamInfPath, sizeof( szModSteamInfPath ) );

	FILE *fp = fopen( szModSteamInfPath, pchMode );
	return fp;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool COptionsSubVideo::BUseHDContent()
{
	FILE *fp = FOpenGameHDFile( "rb" );
	if ( fp )
	{
		fclose(fp);
		return true;
	}
	return false;

}


//-----------------------------------------------------------------------------
// Purpose: hint the engine to load HD content if possible, logic must match with engine/common.cpp BLoadHDContent
//-----------------------------------------------------------------------------
void COptionsSubVideo::SetUseHDContent( bool bUse )
{
	if ( bUse )
	{
		FILE *fp = FOpenGameHDFile( "wb+" );
		if ( fp )
		{
			fprintf( fp, "If this file exists on disk HD content will be loaded.\n" );
			fclose( fp );
		}
	}
	else
	{
		const char *pGameDir = engine->GetGameDirectory();
		char szModSteamInfPath[ 1024 ];
		V_ComposeFileName( pGameDir, "game_hd.txt", szModSteamInfPath, sizeof( szModSteamInfPath ) );
		_unlink( szModSteamInfPath );
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void COptionsSubVideo::OnResetData()
{
	m_bRequireRestart = false;

	const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard();

    // reset UI elements
#if defined( USE_SDL ) && defined( DX_TO_GL_ABSTRACTION )
	int ItemIndex;

	if ( config.Windowed() )
	{
		// Last item in the combobox is Windowed.
		ItemIndex = ( m_pWindowed->GetItemCount() - 1 );
	}
	else
	{
		// Check which fullscreen displayindex is currently selected, and pick it.
		ItemIndex = getSDLDisplayIndex();

		if ( ( ItemIndex < 0 ) || ItemIndex >= ( m_pWindowed->GetItemCount() - 1 ) )
		{
			Assert( 0 );
			ItemIndex = 0;
		}
	}

    m_pWindowed->ActivateItem( ItemIndex );
#else
    m_pWindowed->ActivateItem( config.Windowed() ? 1 : 0 );
#endif

	// reset gamma control
#ifdef ANDROID
	m_pGammaButton->SetEnabled( false );
#else
	m_pGammaButton->SetEnabled( !config.Windowed() );
#endif

	m_pHDContent->SetSelected( BUseHDContent() );

    SetCurrentResolutionComboItem();

	bool bVREnabled = config.m_nVRModeAdapter != -1;
	m_pVRMode->ActivateItem( bVREnabled ? 1 : 0 );
	EnableOrDisableWindowedForVR();

}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void COptionsSubVideo::SetCurrentResolutionComboItem()
{
	vmode_t *plist = NULL;
	int count = 0;
	gameuifuncs->GetVideoModes( &plist, &count );

	const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard();

    int resolution = -1;
    for ( int i = 0; i < count; i++, plist++ )
	{
		if ( plist->width == config.m_VideoMode.m_Width && 
			 plist->height == config.m_VideoMode.m_Height )
		{
            resolution = i;
			break;
		}
	}

    if (resolution != -1)
	{
		char sz[256];
		int desktopWidth, desktopHeight;

		gameuifuncs->GetDesktopResolution( desktopWidth, desktopHeight );

#if defined( USE_SDL )
		SDL_Rect rect;
#if defined( DX_TO_GL_ABSTRACTION )
		int displayIndex = getSDLDisplayIndex();
#else
		int displayIndex = materials->GetCurrentAdapter();
#endif

		if ( !SDL_GetDisplayBounds( displayIndex, &rect ) )
		{
			desktopWidth = rect.w;
			desktopHeight = rect.h;
		}
#endif

		GetResolutionName( plist, sz, sizeof(sz), desktopWidth, desktopHeight );
        m_pMode->SetText(sz);
	}
}

//-----------------------------------------------------------------------------
// Purpose: restarts the game
//-----------------------------------------------------------------------------
void COptionsSubVideo::OnApplyChanges()
{
	if ( RequiresRestart() )
	{
		INetChannelInfo *nci = engine->GetNetChannelInfo();
		if ( nci )
		{
			// Only retry if we're not running the server
			const char *pAddr = nci->GetAddress();
			if ( pAddr )
			{
				if ( Q_strncmp(pAddr,"127.0.0.1",9) && Q_strncmp(pAddr,"localhost",9) )
				{
					engine->ClientCmd_Unrestricted( "retry\n" );
				}
				else
				{
					engine->ClientCmd_Unrestricted( "disconnect\n" );
				}
			}
		}
	}

	// apply advanced options
	if (m_hOptionsSubVideoAdvancedDlg.Get())
	{
		m_hOptionsSubVideoAdvancedDlg->ApplyChanges();
	}

	// resolution
	char sz[256];
	if ( m_nSelectedMode == -1 )
	{
		m_pMode->GetText( sz, 256 );
	}
	else
	{
		m_pMode->GetItemText( m_nSelectedMode, sz, 256 );
	}

	int width = 0, height = 0;
	sscanf( sz, "%i x %i", &width, &height );

	// windowed
	bool bConfigChanged = false;
	bool windowed = ( m_pWindowed->GetActiveItem() == ( m_pWindowed->GetItemCount() - 1 ) ) ? true : false;
	const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard();

	bool bVRMode = m_pVRMode->GetActiveItem() != 0;
	if( ( -1 != config.m_nVRModeAdapter ) != bVRMode )
	{
		// let engine fill in mat_vrmode_adapter 
		char szCmd[256];
		Q_snprintf( szCmd, sizeof(szCmd), "mat_enable_vrmode %d\n", bVRMode ? 1 : 0 );
		engine->ClientCmd_Unrestricted( szCmd );

		// force windowed. VR mode ignores this flag and desktop mode needs to be in a window always
		windowed = bVRMode;
	}


	// make sure there is a change
	if ( config.m_VideoMode.m_Width != width
		|| config.m_VideoMode.m_Height != height
		|| config.Windowed() != windowed )
	{
		bConfigChanged = true;
	}

#if defined( USE_SDL )
	if ( !windowed )
	{
		SDL_Rect rect;
		int displayIndexTarget = m_pWindowed->GetActiveItem();
		int displayIndexCurrent = getSDLDisplayIndexFullscreen();

		// Handle going fullscreen from display X to display Y.
		if ( displayIndexCurrent != displayIndexTarget )
		{
			static ConVarRef sdl_displayindex( "sdl_displayindex" );

			if ( sdl_displayindex.IsValid() )
			{
				// Set the displayindex we want to go fullscreen on now.
				sdl_displayindex.SetValue( displayIndexTarget );
				bConfigChanged = true;
			}
		}

		if ( !SDL_GetDisplayBounds( displayIndexTarget, &rect ) )
		{
			// If we are going non-native fullscreen, tweak the resolution to have the same aspect ratio as the display.
			if ( ( width != rect.w ) || ( height != rect.h ) )
			{
				// TODO: We may want a convar to allow folks to mess with their aspect ratio?
				height = ( width * rect.h ) / rect.w;
				bConfigChanged = true;
			}
		}
	}
#endif // USE_SDL

	if ( bConfigChanged )
	{
		// set mode
		char szCmd[ 256 ];
		Q_snprintf( szCmd, sizeof( szCmd ), "mat_setvideomode %i %i %i\n", width, height, windowed ? 1 : 0 );
		engine->ClientCmd_Unrestricted( szCmd );
	}

	if ( ModInfo().HasHDContent() )
	{
		if ( BUseHDContent() != m_pHDContent->IsSelected() )
		{
			SetUseHDContent( m_pHDContent->IsSelected() );
			// Bring up the confirmation dialog
			MessageBox *box = new MessageBox("#GameUI_OptionsRestartRequired_Title", "#GameUI_HDRestartRequired_Info");
			box->DoModal();
			box->MoveToFront();
		}
	}

	// apply changes
	engine->ClientCmd_Unrestricted( "mat_savechanges\n" );

}

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

	if ( m_pGammaButton )
	{
#ifdef ANDROID
		m_pGammaButton->SetEnabled( false );
#else
		const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard();
		m_pGammaButton->SetEnabled( !config.Windowed() );
#endif
	}
}

//-----------------------------------------------------------------------------
// Purpose: enables apply button on data changing
//-----------------------------------------------------------------------------
void COptionsSubVideo::OnTextChanged(Panel *pPanel, const char *pszText)
{
	if (pPanel == m_pMode)
    {
		const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard();

		m_nSelectedMode = m_pMode->GetActiveItem();

		int w = 0, h = 0;
		sscanf(pszText, "%i x %i", &w, &h);
        if ( config.m_VideoMode.m_Width != w || config.m_VideoMode.m_Height != h )
        {
            OnDataChanged();
        }
    }
#ifndef ANDROID
	else if (pPanel == m_pAspectRatio)
	{
		PrepareResolutionList();
	}
#endif
	else if (pPanel == m_pWindowed)
	{
		PrepareResolutionList();
		OnDataChanged();
	}
	else if ( pPanel == m_pVRMode )
	{
		if ( !m_bDisplayedVRModeMessage )
		{
			bool bVRNowEnabled = m_pVRMode->GetActiveItem() == 1;
			bool bVRWasEnabled = materials->GetCurrentConfigForVideoCard().m_nVRModeAdapter != -1;
			if( bVRWasEnabled != bVRNowEnabled )
			{
				m_bDisplayedVRModeMessage = true;
				MessageBox *box = new MessageBox( "#GameUI_VRMode", "#GameUI_VRModeRelaunchMsg", this );
				box->MoveToFront();
				box->DoModal();
			}
		}

		EnableOrDisableWindowedForVR();
	}
}


//-----------------------------------------------------------------------------
// Purpose: enables windowed combo box
//-----------------------------------------------------------------------------
void		COptionsSubVideo::EnableOrDisableWindowedForVR()
{
	bool bCanBeEnabled = g_pSourceVR && g_pSourceVR->IsHmdConnected();
	bool bVRNowEnabled = m_pVRMode->GetActiveItem() == 1;
	bool bVRWasEnabled = materials->GetCurrentConfigForVideoCard().m_nVRModeAdapter != -1;
	if( bCanBeEnabled && ( bVRNowEnabled || bVRWasEnabled ) )
	{
		m_pWindowed->SetEnabled( false );
		m_pWindowed->ActivateItem( m_pWindowed->GetItemCount() - 1 );
		m_pWindowed->GetTooltip()->SetText( "#GameUI_WindowedTooltip" );
	}
	else
	{
#ifdef ANDROID
		m_pWindowed->SetEnabled( false );
#else
		m_pWindowed->SetEnabled( true );
#endif
	}

}


//-----------------------------------------------------------------------------
// Purpose: enables apply button
//-----------------------------------------------------------------------------
void COptionsSubVideo::OnDataChanged()
{
	PostActionSignal(new KeyValues("ApplyButtonEnable"));

}

//-----------------------------------------------------------------------------
// Purpose: Checks to see if the changes requires a restart to take effect
//-----------------------------------------------------------------------------
bool COptionsSubVideo::RequiresRestart()
{
	if ( m_hOptionsSubVideoAdvancedDlg.Get() 
		&& m_hOptionsSubVideoAdvancedDlg->RequiresRestart() )
	{
		return true;
	}

	// make sure there is a change
	return m_bRequireRestart;
}

//-----------------------------------------------------------------------------
// Purpose: Opens advanced video mode options dialog
//-----------------------------------------------------------------------------
void COptionsSubVideo::OpenAdvanced()
{
	if ( !m_hOptionsSubVideoAdvancedDlg.Get() )
	{
		m_hOptionsSubVideoAdvancedDlg = new COptionsSubVideoAdvancedDlg( BasePanel()->FindChildByName( "OptionsDialog" ) ); // we'll parent this to the OptionsDialog directly
	}

	m_hOptionsSubVideoAdvancedDlg->Activate();
}

//-----------------------------------------------------------------------------
// Purpose: Opens gamma-adjusting dialog
//-----------------------------------------------------------------------------
void COptionsSubVideo::OpenGammaDialog()
{
	if ( !m_hGammaDialog.Get() )
	{
		m_hGammaDialog = new CGammaDialog( GetVParent() );
	}

	m_hGammaDialog->Activate();
}

//-----------------------------------------------------------------------------
// Purpose: Opens benchmark dialog
//-----------------------------------------------------------------------------
void COptionsSubVideo::LaunchBenchmark()
{
	BasePanel()->OnOpenBenchmarkDialog();
}

//-----------------------------------------------------------------------------
// Purpose: third-party audio credits dialog
//-----------------------------------------------------------------------------
class COptionsSubVideoThirdPartyCreditsDlg : public vgui::Frame
{
	DECLARE_CLASS_SIMPLE(COptionsSubVideoThirdPartyCreditsDlg, vgui::Frame);
public:
	COptionsSubVideoThirdPartyCreditsDlg(vgui::VPANEL hParent) : BaseClass(NULL, NULL)
	{
		// parent is ignored, since we want look like we're steal focus from the parent (we'll become modal below)
		int w = 500;
		int h = 200;
		if (ipanel()->IsProportional(hParent))
		{
			SetProportional(true);
			w = scheme()->GetProportionalScaledValueEx(GetScheme(), w);
			h = scheme()->GetProportionalScaledValueEx(GetScheme(), h);
		}

		SetTitle("#GameUI_ThirdPartyVideo_Title", true);
		SetSize(w, h);
		LoadControlSettings("resource/OptionsSubVideoThirdPartyDlg.res");
		MoveToCenterOfScreen();
		SetSizeable(false);
		SetDeleteSelfOnClose(true);
	}

	virtual void Activate()
	{
		BaseClass::Activate();

		input()->SetAppModalSurface(GetVPanel());
	}

	void OnKeyCodeTyped(KeyCode code)
	{
		// force ourselves to be closed if the escape key it pressed
		if (code == KEY_ESCAPE)
		{
			Close();
		}
		else
		{
			BaseClass::OnKeyCodeTyped(code);
		}
	}
};


//-----------------------------------------------------------------------------
// Purpose: Open third party audio credits dialog
//-----------------------------------------------------------------------------
void COptionsSubVideo::OpenThirdPartyVideoCreditsDialog()
{
	if (!m_OptionsSubVideoThirdPartyCreditsDlg.Get())
	{
		m_OptionsSubVideoThirdPartyCreditsDlg = new COptionsSubVideoThirdPartyCreditsDlg(GetVParent());
	}
	m_OptionsSubVideoThirdPartyCreditsDlg->Activate();
}
