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


#include <stdio.h>
#include <math.h>

#include <vgui/VGUI.h>
#include <vgui/IScheme.h>
#include <KeyValues.h>
#include <vgui/ISurface.h>
#include <vgui/IPanel.h>
#include <vgui/ISystem.h>
#include <vstdlib/IKeyValuesSystem.h>
#include <vgui/IVGui.h>

#include "tier1/utlvector.h"
#include "tier1/utlrbtree.h"
#include "tier1/utldict.h"
#include "VGUI_Border.h"
#include "ScalableImageBorder.h"
#include "ImageBorder.h"
#include "vgui_internal.h"
#include "bitmap.h"
#include "filesystem.h"

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

using namespace vgui;
#define FONT_ALIAS_NAME_LENGTH 64

//-----------------------------------------------------------------------------
// Purpose: Implementation of global scheme interface
//-----------------------------------------------------------------------------
class CScheme : public IScheme
{
public:
	CScheme();

	// gets a string from the default settings section
	virtual const char *GetResourceString(const char *stringName);

	// returns a pointer to an existing border
	virtual IBorder *GetBorder(const char *borderName);

	// returns a pointer to an existing font
	virtual HFont GetFont(const char *fontName, bool proportional);

	// m_pkvColors
	virtual Color GetColor( const char *colorName, Color defaultColor);


	void Shutdown( bool full );
	void LoadFromFile( VPANEL sizingPanel, const char *filename, const char *tag, KeyValues *inKeys );

	// Gets at the scheme's name
	const char *GetName() { return tag; }
	const char *GetFileName() { return fileName; }

	char const *GetFontName( const HFont& font );

	void ReloadFontGlyphs();

	VPANEL		GetSizingPanel() { return m_SizingPanel; }

	void SpewFonts();

	bool GetFontRange( const char *fontname, int &nMin, int &nMax );
	void SetFontRange( const char *fontname, int nMin, int nMax );
	
	// Get the number of borders
	virtual int GetBorderCount() const;

	// Get the border at the given index
	virtual IBorder *GetBorderAtIndex( int iIndex );

	// Get the number of fonts
	virtual int GetFontCount() const;

	// Get the font at the given index
	virtual HFont GetFontAtIndex( int iIndex );
	
	// Get color data
	virtual const KeyValues *GetColorData() const;

private:
	const char *LookupSchemeSetting(const char *pchSetting);
	const char *GetMungedFontName( const char *fontName, const char *scheme, bool proportional);
	void LoadFonts();
	void LoadBorders();
	HFont FindFontInAliasList( const char *fontName );
	int GetMinimumFontHeightForCurrentLanguage();

	char fileName[256];
	char tag[64];

	KeyValues *m_pData;
	KeyValues *m_pkvBaseSettings;
	KeyValues *m_pkvColors;
	int		   m_nColorCount;

	struct SchemeBorder_t
	{
		IBorder *border;
		int borderSymbol;
		bool bSharedBorder;
	};
	CUtlVector<SchemeBorder_t> m_BorderList;
	IBorder  *m_pBaseBorder;	// default border to use if others not found
	KeyValues *m_pkvBorders;
#pragma pack(1)
	struct fontalias_t
	{
		CUtlSymbol _trueFontName;
		unsigned short _font : 15;
		unsigned short m_bProportional : 1;
	};
#pragma pack()
	friend struct fontalias_t;

	CUtlDict< fontalias_t, int >	m_FontAliases;
	VPANEL m_SizingPanel;
	int			m_nScreenWide;
	int			m_nScreenTall;

	struct fontrange_t
	{
		int _min;
		int _max;
	};
	CUtlDict< fontrange_t, int >	m_FontRanges;
};


//-----------------------------------------------------------------------------
// Purpose: Implementation of global scheme interface
//-----------------------------------------------------------------------------
class CSchemeManager : public ISchemeManager
{
public:
	CSchemeManager();
	~CSchemeManager();

	// loads a scheme from a file
	// first scheme loaded becomes the default scheme, and all subsequent loaded scheme are derivitives of that
	// tag is friendly string representing the name of the loaded scheme
	virtual HScheme LoadSchemeFromFile(const char *fileName, const char *tag);
	// first scheme loaded becomes the default scheme, and all subsequent loaded scheme are derivitives of that
	virtual HScheme LoadSchemeFromFileEx( VPANEL sizingPanel, const char *fileName, const char *tag);

	// reloads the schemes from the file
	virtual void ReloadSchemes();
	virtual void ReloadFonts();

	// returns a handle to the default (first loaded) scheme
	virtual HScheme GetDefaultScheme();

	// returns a handle to the scheme identified by "tag"
	virtual HScheme GetScheme(const char *tag);

	// returns a pointer to an image
	virtual IImage *GetImage(const char *imageName, bool hardwareFiltered);
	virtual HTexture GetImageID(const char *imageName, bool hardwareFiltered);

	virtual IScheme *GetIScheme( HScheme scheme );

	virtual void Shutdown( bool full );

	// gets the proportional coordinates for doing screen-size independant panel layouts
	// use these for font, image and panel size scaling (they all use the pixel height of the display for scaling)
	virtual int GetProportionalScaledValue(int normalizedValue);
	virtual int GetProportionalNormalizedValue(int scaledValue);

	// gets the proportional coordinates for doing screen-size independant panel layouts
	// use these for font, image and panel size scaling (they all use the pixel height of the display for scaling)
	virtual int GetProportionalScaledValueEx( HScheme scheme, int normalizedValue );
	virtual int GetProportionalNormalizedValueEx( HScheme scheme, int scaledValue );

	virtual bool DeleteImage( const char *pImageName );

	// gets the proportional coordinates for doing screen-size independant panel layouts
	// use these for font, image and panel size scaling (they all use the pixel height of the display for scaling)
	int GetProportionalScaledValueEx( CScheme *pScheme, int normalizedValue );
	int GetProportionalNormalizedValueEx( CScheme *pScheme, int scaledValue );

	void SpewFonts();

private:

	int GetProportionalScaledValue_( int rootWide, int rootTall, int normalizedValue );
	int GetProportionalNormalizedValue_( int rootWide, int rootTall, int scaledValue );

	// Search for already-loaded schemes
	HScheme FindLoadedScheme(const char *fileName);

	CUtlVector<CScheme *> m_Schemes;

	static const char *s_pszSearchString;
	struct CachedBitmapHandle_t
	{
		Bitmap *pBitmap;
	};
	static bool BitmapHandleSearchFunc(const CachedBitmapHandle_t &, const CachedBitmapHandle_t &);
	CUtlRBTree<CachedBitmapHandle_t, int> m_Bitmaps;
};

const char *CSchemeManager::s_pszSearchString = NULL;

//-----------------------------------------------------------------------------
// Purpose: search function for stored bitmaps
//-----------------------------------------------------------------------------
bool CSchemeManager::BitmapHandleSearchFunc(const CachedBitmapHandle_t &lhs, const CachedBitmapHandle_t &rhs)
{
	// a NULL bitmap indicates to use the search string instead
	if (lhs.pBitmap && rhs.pBitmap)
	{
		return stricmp(lhs.pBitmap->GetName(), rhs.pBitmap->GetName()) > 0;
	}
	else if (lhs.pBitmap)
	{
		return stricmp(lhs.pBitmap->GetName(), s_pszSearchString) > 0;
	}
	return stricmp(s_pszSearchString, rhs.pBitmap->GetName()) > 0;
}



CSchemeManager g_Scheme;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CSchemeManager, ISchemeManager, VGUI_SCHEME_INTERFACE_VERSION, g_Scheme);


namespace vgui
{
vgui::ISchemeManager *g_pScheme = &g_Scheme;
} // namespace vgui

CON_COMMAND( vgui_spew_fonts, "" )
{
	g_Scheme.SpewFonts();
}
 
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSchemeManager::CSchemeManager()
{
	// 0th element is null, since that would be an invalid handle
	CScheme *nullScheme = new CScheme();
	m_Schemes.AddToTail(nullScheme);
	m_Bitmaps.SetLessFunc(&BitmapHandleSearchFunc);
}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CSchemeManager::~CSchemeManager()
{
	int i;
	for ( i = 0; i < m_Schemes.Count(); i++ )
	{
		delete m_Schemes[i];
	}
	m_Schemes.RemoveAll();

	for ( i = 0; i < m_Bitmaps.MaxElement(); i++ )
	{
		if (m_Bitmaps.IsValidIndex(i))
		{
			delete m_Bitmaps[i].pBitmap;
		}
	}
	m_Bitmaps.RemoveAll();

	Shutdown( false );
}

//-----------------------------------------------------------------------------
// Purpose: Reload the fonts in all schemes
//-----------------------------------------------------------------------------
void CSchemeManager::ReloadFonts()
{
	for (int i = 1; i < m_Schemes.Count(); i++)
	{
		m_Schemes[i]->ReloadFontGlyphs();
	}
}

//-----------------------------------------------------------------------------
// Converts the handle into an interface
//-----------------------------------------------------------------------------
IScheme *CSchemeManager::GetIScheme( HScheme scheme )
{
	if ( scheme >= (unsigned long)m_Schemes.Count() )
	{
		AssertOnce( !"Invalid scheme requested." );
		return NULL;
	}
	else
	{
		return m_Schemes[scheme];
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSchemeManager::Shutdown( bool full )
{
	// Full shutdown kills the null scheme
	for( int i = full ? 0 : 1; i < m_Schemes.Count(); i++ )
	{
		m_Schemes[i]->Shutdown( full );
	}

	if ( full )
	{
		m_Schemes.RemoveAll();
	}
}

//-----------------------------------------------------------------------------
// Purpose: loads a scheme from disk
//-----------------------------------------------------------------------------
HScheme CSchemeManager::FindLoadedScheme(const char *fileName)
{
	// Find the scheme in the list of already loaded schemes
	for (int i = 1; i < m_Schemes.Count(); i++)
	{
		char const *schemeFileName = m_Schemes[i]->GetFileName();
		if (!stricmp(schemeFileName, fileName))
			return i;
	}

	return 0;
}

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CScheme::CScheme()
{
	fileName[ 0 ] = 0;
	tag[ 0 ] = 0;

	m_pData = NULL;
	m_pkvBaseSettings = NULL;
	m_pkvColors = NULL;
	m_nColorCount = 0;

	m_pBaseBorder = NULL;	// default border to use if others not found
	m_pkvBorders = NULL;
	m_SizingPanel = 0;
	m_nScreenWide = -1;
	m_nScreenTall = -1;
}

// first scheme loaded becomes the default scheme, and all subsequent loaded scheme are derivitives of that
HScheme  CSchemeManager::LoadSchemeFromFileEx( VPANEL sizingPanel, const char *fileName, const char *tag)
{
	// Look to see if we've already got this scheme...
	HScheme hScheme = FindLoadedScheme(fileName);
	if (hScheme != 0)
	{
		CScheme *pScheme = static_cast< CScheme * >( GetIScheme( hScheme ) );
		if ( IsPC() && pScheme )
		{
			pScheme->ReloadFontGlyphs();
		}
		return hScheme;
	}

	KeyValues *data;
	data = new KeyValues("Scheme");

	data->UsesEscapeSequences( true );	// VGUI uses this
	
	// Look first in game directory
	bool result = data->LoadFromFile( g_pFullFileSystem, fileName, "GAME" );
	if ( !result )
	{
		// look in any directory
		result = data->LoadFromFile( g_pFullFileSystem, fileName, NULL );
	}

	if (!result)
	{
		data->deleteThis();
		return 0;
	}
	
	if ( IsX360() )
	{
		data->ProcessResolutionKeys( g_pSurface->GetResolutionKey() );
	}
	if ( IsPC() )
	{
		ConVarRef cl_hud_minmode( "cl_hud_minmode", true );
		if ( cl_hud_minmode.IsValid() && cl_hud_minmode.GetBool() )
		{
			data->ProcessResolutionKeys( "_minmode" );
		}
	}
	if( g_pIVgui->GetVRMode() )
	{
		data->ProcessResolutionKeys( "_vrmode" );
	}

	CScheme *newScheme = new CScheme();
	newScheme->LoadFromFile( sizingPanel, fileName, tag, data );

	return m_Schemes.AddToTail(newScheme);
}

//-----------------------------------------------------------------------------
// Purpose: loads a scheme from disk
//-----------------------------------------------------------------------------
HScheme CSchemeManager::LoadSchemeFromFile(const char *fileName, const char *tag)
{
	return LoadSchemeFromFileEx( 0, fileName, tag );
}

//-----------------------------------------------------------------------------
// Purpose: Table of scheme file entries, translation from old goldsrc schemes to new src schemes
//-----------------------------------------------------------------------------
struct SchemeEntryTranslation_t
{
	const char *pchNewEntry;
	const char *pchOldEntry;
	const char *pchDefaultValue;
};
SchemeEntryTranslation_t g_SchemeTranslation[] =
{
	{ "Border.Bright",					"BorderBright",		"200 200 200 196" },	// the lit side of a control
	{ "Border.Dark"						"BorderDark",		"40 40 40 196" },		// the dark/unlit side of a control
	{ "Border.Selection"				"BorderSelection",	"0 0 0 196" },			// the additional border color for displaying the default/selected button

	{ "Button.TextColor",				"ControlFG",		"White" },
	{ "Button.BgColor",					"ControlBG",		"Blank" },
	{ "Button.ArmedTextColor",			"ControlFG" },
	{ "Button.ArmedBgColor",			"ControlBG" },
	{ "Button.DepressedTextColor",		"ControlFG" },
	{ "Button.DepressedBgColor",		"ControlBG" },
	{ "Button.FocusBorderColor",		"0 0 0 255" },

	{ "CheckButton.TextColor",			"BaseText" },
	{ "CheckButton.SelectedTextColor",	"BrightControlText" },
	{ "CheckButton.BgColor",			"CheckBgColor" },
	{ "CheckButton.Border1",  			"CheckButtonBorder1" },
	{ "CheckButton.Border2",  			"CheckButtonBorder2" },
	{ "CheckButton.Check",				"CheckButtonCheck" },

	{ "ComboBoxButton.ArrowColor",		"LabelDimText" },
	{ "ComboBoxButton.ArmedArrowColor",	"MenuButton/ArmedArrowColor" },
	{ "ComboBoxButton.BgColor",			"MenuButton/ButtonBgColor" },
	{ "ComboBoxButton.DisabledBgColor",	"ControlBG" },

	{ "Frame.TitleTextInsetX",			NULL,		"32" },
	{ "Frame.ClientInsetX",				NULL,		"8" },
	{ "Frame.ClientInsetY",				NULL,		"6" },
	{ "Frame.BgColor",					"BgColor" },
	{ "Frame.OutOfFocusBgColor",		"BgColor" },
	{ "Frame.FocusTransitionEffectTime",NULL,		"0" },
	{ "Frame.TransitionEffectTime",		NULL,		"0" },
	{ "Frame.AutoSnapRange",			NULL,		"8" },
	{ "FrameGrip.Color1",				"BorderBright" },
	{ "FrameGrip.Color2",				"BorderSelection" },
	{ "FrameTitleButton.FgColor",		"TitleButtonFgColor" },
	{ "FrameTitleButton.BgColor",		"TitleButtonBgColor" },
	{ "FrameTitleButton.DisabledFgColor",	"TitleButtonDisabledFgColor" },
	{ "FrameTitleButton.DisabledBgColor",	"TitleButtonDisabledBgColor" },
	{ "FrameSystemButton.FgColor",		"TitleBarBgColor" },
	{ "FrameSystemButton.BgColor",		"TitleBarBgColor" },
	{ "FrameSystemButton.Icon",			"TitleBarIcon" },
	{ "FrameSystemButton.DisabledIcon",	"TitleBarDisabledIcon" },
	{ "FrameTitleBar.Font",				NULL,		"Default" },
	{ "FrameTitleBar.TextColor",		"TitleBarFgColor" },
	{ "FrameTitleBar.BgColor",			"TitleBarBgColor" },
	{ "FrameTitleBar.DisabledTextColor","TitleBarDisabledFgColor" },
	{ "FrameTitleBar.DisabledBgColor",	"TitleBarDisabledBgColor" },

	{ "GraphPanel.FgColor",				"BrightControlText" },
	{ "GraphPanel.BgColor",				"WindowBgColor" },

	{ "Label.TextDullColor",			"LabelDimText" },
	{ "Label.TextColor",				"BaseText" },
	{ "Label.TextBrightColor",			"BrightControlText" },
	{ "Label.SelectedTextColor",		"BrightControlText" },
	{ "Label.BgColor",					"LabelBgColor" },
	{ "Label.DisabledFgColor1",			"DisabledFgColor1" },
	{ "Label.DisabledFgColor2",			"DisabledFgColor2" },

	{ "ListPanel.TextColor",				"WindowFgColor" },
	{ "ListPanel.TextBgColor",				"Menu/ArmedBgColor" },
	{ "ListPanel.BgColor",					"ListBgColor" },
	{ "ListPanel.SelectedTextColor",		"ListSelectionFgColor" },
	{ "ListPanel.SelectedBgColor",			"Menu/ArmedBgColor" },
	{ "ListPanel.SelectedOutOfFocusBgColor","SelectionBG2" },
	{ "ListPanel.EmptyListInfoTextColor",	"LabelDimText" },
	{ "ListPanel.DisabledTextColor",		"LabelDimText" },
	{ "ListPanel.DisabledSelectedTextColor","ListBgColor" },

	{ "Menu.TextColor",					"Menu/FgColor" },
	{ "Menu.BgColor",					"Menu/BgColor" },
	{ "Menu.ArmedTextColor",			"Menu/ArmedFgColor" },
	{ "Menu.ArmedBgColor",				"Menu/ArmedBgColor" },
	{ "Menu.TextInset",					NULL,		"6" },

	{ "Panel.FgColor",					"FgColor" },
	{ "Panel.BgColor",					"BgColor" },

	{ "ProgressBar.FgColor",				"BrightControlText" },
	{ "ProgressBar.BgColor",				"WindowBgColor" },

	{ "PropertySheet.TextColor",			"FgColorDim" },
	{ "PropertySheet.SelectedTextColor",	"BrightControlText" },
	{ "PropertySheet.TransitionEffectTime",	NULL,		"0" },

	{ "RadioButton.TextColor",			"FgColor" },
	{ "RadioButton.SelectedTextColor",	"BrightControlText" },

	{ "RichText.TextColor",				"WindowFgColor" },
	{ "RichText.BgColor",				"WindowBgColor" },
	{ "RichText.SelectedTextColor",		"SelectionFgColor" },
	{ "RichText.SelectedBgColor",		"SelectionBgColor" },

	{ "ScrollBar.Wide",					NULL,		"19" },

	{ "ScrollBarButton.FgColor",			"DimBaseText" },
	{ "ScrollBarButton.BgColor",			"ControlBG" },
	{ "ScrollBarButton.ArmedFgColor",		"BaseText" },
	{ "ScrollBarButton.ArmedBgColor",		"ControlBG" },
	{ "ScrollBarButton.DepressedFgColor",	"BaseText" },
	{ "ScrollBarButton.DepressedBgColor",	"ControlBG" },

	{ "ScrollBarSlider.FgColor",				"ScrollBarSlider/ScrollBarSliderFgColor" },
	{ "ScrollBarSlider.BgColor",				"ScrollBarSlider/ScrollBarSliderBgColor" },

	{ "SectionedListPanel.HeaderTextColor",	"SectionTextColor" },
	{ "SectionedListPanel.HeaderBgColor",	"BuddyListBgColor" },
	{ "SectionedListPanel.DividerColor",	"SectionDividerColor" },
	{ "SectionedListPanel.TextColor",		"BuddyButton/FgColor1" },
	{ "SectionedListPanel.BrightTextColor",	"BuddyButton/ArmedFgColor1" },
	{ "SectionedListPanel.BgColor",			"BuddyListBgColor" },
	{ "SectionedListPanel.SelectedTextColor",			"BuddyButton/ArmedFgColor1" },
	{ "SectionedListPanel.SelectedBgColor",				"BuddyButton/ArmedBgColor" },
	{ "SectionedListPanel.OutOfFocusSelectedTextColor",	"BuddyButton/ArmedFgColor2" },
	{ "SectionedListPanel.OutOfFocusSelectedBgColor",	"SelectionBG2" },

	{ "Slider.NobColor",			"SliderTickColor" },
	{ "Slider.TextColor",			"Slider/SliderFgColor" },
	{ "Slider.TrackColor",			"SliderTrackColor"},
	{ "Slider.DisabledTextColor1",	"DisabledFgColor1" },
	{ "Slider.DisabledTextColor2",	"DisabledFgColor2" },

	{ "TextEntry.TextColor",		"WindowFgColor" },
	{ "TextEntry.BgColor",			"WindowBgColor" },
	{ "TextEntry.CursorColor",		"TextCursorColor" },
	{ "TextEntry.DisabledTextColor","WindowDisabledFgColor" },
	{ "TextEntry.DisabledBgColor",	"ControlBG" },
	{ "TextEntry.SelectedTextColor","SelectionFgColor" },
	{ "TextEntry.SelectedBgColor",	"SelectionBgColor" },
	{ "TextEntry.OutOfFocusSelectedBgColor",	"SelectionBG2" },
	{ "TextEntry.FocusEdgeColor",	"BorderSelection" },

	{ "ToggleButton.SelectedTextColor",	"BrightControlText" },

	{ "Tooltip.TextColor",			"BorderSelection" },
	{ "Tooltip.BgColor",			"SelectionBG" },

	{ "TreeView.BgColor",			"ListBgColor" },

	{ "WizardSubPanel.BgColor",		"SubPanelBgColor" },
};

//-----------------------------------------------------------------------------
// Purpose: loads a scheme from from disk into memory
//-----------------------------------------------------------------------------
void CScheme::LoadFromFile( VPANEL sizingPanel, const char *inFilename, const char *inTag, KeyValues *inKeys )
{
	COM_TimestampedLog( "CScheme::LoadFromFile( %s )", inFilename );

	Q_strncpy(fileName, inFilename, sizeof(fileName) );
	
	m_SizingPanel = sizingPanel;

	m_pData = inKeys;
	m_pkvBaseSettings = m_pData->FindKey("BaseSettings", true);
	m_pkvColors = m_pData->FindKey("Colors", true);

	// override the scheme name with the tag name
	KeyValues *name = m_pData->FindKey("Name", true);
	name->SetString("Name", inTag);

	if ( inTag )
	{
		Q_strncpy( tag, inTag, sizeof( tag ) );
	}
	else
	{
		Assert( "You need to name the scheme!" );
		Q_strncpy( tag, "default", sizeof( tag ) );
	}

	// translate format from goldsrc scheme to new scheme
	for (int i = 0; i < ARRAYSIZE(g_SchemeTranslation); i++)
	{
		if (!m_pkvBaseSettings->FindKey(g_SchemeTranslation[i].pchNewEntry, false))
		{
			const char *pchColor;

			if (g_SchemeTranslation[i].pchOldEntry)
			{
				pchColor = LookupSchemeSetting(g_SchemeTranslation[i].pchOldEntry);
			}
			else
			{
				pchColor = g_SchemeTranslation[i].pchDefaultValue;
			}

			Assert( pchColor );

			m_pkvBaseSettings->SetString(g_SchemeTranslation[i].pchNewEntry, pchColor);
		}
	}

	// need to copy tag before loading fonts
	LoadFonts();
	LoadBorders();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CScheme::GetFontRange( const char *fontname, int &nMin, int &nMax )
{
	int i = m_FontRanges.Find( fontname );
	if ( i != m_FontRanges.InvalidIndex() )
	{
		nMin = m_FontRanges[i]._min;
		nMax = m_FontRanges[i]._max;
		return true;
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CScheme::SetFontRange( const char *fontname, int nMin, int nMax )
{
	int i = m_FontRanges.Find( fontname );
	if ( i != m_FontRanges.InvalidIndex() )
	{
		m_FontRanges[i]._min = nMin;
		m_FontRanges[i]._max = nMax;
		return;
	}

	// not already in our list
	int iNew = m_FontRanges.Insert( fontname );
	
	m_FontRanges[iNew]._min = nMin;
	m_FontRanges[iNew]._max = nMax;
}

//-----------------------------------------------------------------------------
// Purpose: adds all the font specifications to the surface
//-----------------------------------------------------------------------------
void CScheme::LoadFonts()
{
	bool bValid = false;
	char language[64];
	memset( language, 0, sizeof( language ) );

	// get our language
	if ( IsPC() )
	{
		bValid = vgui::g_pSystem->GetRegistryString( "HKEY_CURRENT_USER\\Software\\Valve\\Source\\Language", language, sizeof( language ) - 1 );
	}
	else
	{
		Q_strncpy( language, XBX_GetLanguageString(), sizeof( language ) );
		bValid = true;
	}

	if ( !bValid )
	{
		Q_strncpy( language, "english", sizeof( language ) );
	}

	// add our custom fonts
	for (KeyValues *kv = m_pData->FindKey("CustomFontFiles", true)->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey())
	{
		const char *fontFile = kv->GetString();
		if (fontFile && *fontFile)
		{
			g_pSurface->AddCustomFontFile( NULL, fontFile );
		}
		else
		{
			// we have a block to read
			int nRangeMin = 0, nRangeMax = 0;
			const char *pszName = NULL;
			bool bUseRange = false;

			for ( KeyValues *pData = kv->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() )
			{
				const char *pszKey = pData->GetName();
				if ( !Q_stricmp( pszKey, "font" ) )
				{
					fontFile = pData->GetString();
				}
				else if ( !Q_stricmp( pszKey, "name" ) )
				{
					pszName = pData->GetString();
				}
				else
				{
					// we must have a language
					if ( Q_stricmp( language, pszKey ) == 0 ) // matches the language we're running?
					{
						// get the range
						KeyValues *pRange = pData->FindKey( "range" );
						if ( pRange )
						{
							bUseRange = true;
							sscanf( pRange->GetString(), "%x %x", &nRangeMin, &nRangeMax );

							if ( nRangeMin > nRangeMax )
							{
								int nTemp = nRangeMin;
								nRangeMin = nRangeMax;
								nRangeMax = nTemp;
							}
						}
					}
				}
			}

			if ( fontFile && *fontFile )
			{
				g_pSurface->AddCustomFontFile( pszName, fontFile );

				if ( bUseRange )
				{
					SetFontRange( pszName, nRangeMin, nRangeMax );
				}
			}
		}
	}

	// add bitmap fonts
	for (KeyValues *kv = m_pData->FindKey("BitmapFontFiles", true)->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey())
	{
		const char *fontFile = kv->GetString();
		if (fontFile && *fontFile)
		{
			bool bSuccess = g_pSurface->AddBitmapFontFile( fontFile );
			if ( bSuccess )
			{
				// refer to the font by a user specified symbol
				g_pSurface->SetBitmapFontName( kv->GetName(), fontFile );
			}
		}
	}

	// create the fonts
	for (KeyValues *kv = m_pData->FindKey("Fonts", true)->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey())
	{
		for ( int i = 0; i < 2; i++ )
		{
			// create the base font
			bool proportionalFont = static_cast<bool>( i );
			const char *fontName = GetMungedFontName( kv->GetName(), tag, proportionalFont ); // first time it adds a normal font, and then a proportional one
			HFont font = g_pSurface->CreateFont();

			int j = m_FontAliases.Insert( fontName );
			m_FontAliases[j]._trueFontName = kv->GetName();
			m_FontAliases[j]._font = font;
			m_FontAliases[j].m_bProportional = proportionalFont;
		}
	}

	// load in the font glyphs
	ReloadFontGlyphs();
}

//-----------------------------------------------------------------------------
// Purpose: Reloads all scheme fonts
//-----------------------------------------------------------------------------
void CScheme::ReloadFontGlyphs()
{
	COM_TimestampedLog( "ReloadFontGlyphs(): Start" );

	// get our current resolution
	if ( m_SizingPanel != 0 )
	{
		g_pIPanel->GetSize( m_SizingPanel, m_nScreenWide, m_nScreenTall );
	}
	else
	{
		g_pSurface->GetScreenSize( m_nScreenWide, m_nScreenTall );
	}

	// check our language; some have minimum sizes
	int minimumFontHeight = GetMinimumFontHeightForCurrentLanguage();

	// add the data to all the fonts
	KeyValues *fonts = m_pData->FindKey("Fonts", true);
	FOR_EACH_DICT_FAST( m_FontAliases, i )
	{
		KeyValues *kv = fonts->FindKey( m_FontAliases[i]._trueFontName.String(), true );
	
		// walk through creating adding the first matching glyph set to the font
		for (KeyValues *fontdata = kv->GetFirstSubKey(); fontdata != NULL; fontdata = fontdata->GetNextKey())
		{
			// skip over fonts not meant for this resolution
			int fontYResMin = 0, fontYResMax = 0;
			sscanf(fontdata->GetString("yres", ""), "%d %d", &fontYResMin, &fontYResMax);
			if (fontYResMin)
			{
				if (!fontYResMax)
				{
					fontYResMax = fontYResMin;
				}
				// check the range
				if (m_nScreenTall < fontYResMin || m_nScreenTall > fontYResMax)
					continue;
			}

			int flags = 0;
			if (fontdata->GetInt( "italic" ))
			{
				flags |= ISurface::FONTFLAG_ITALIC;
			}
			if (fontdata->GetInt( "underline" ))
			{
				flags |= ISurface::FONTFLAG_UNDERLINE;
			}
			if (fontdata->GetInt( "strikeout" ))
			{
				flags |= ISurface::FONTFLAG_STRIKEOUT;
			}
			if (fontdata->GetInt( "symbol" ))
			{
				flags |= ISurface::FONTFLAG_SYMBOL;
			}
			if (fontdata->GetInt( "antialias" ) && g_pSurface->SupportsFeature(ISurface::ANTIALIASED_FONTS))
			{
				flags |= ISurface::FONTFLAG_ANTIALIAS;
			}
			if (fontdata->GetInt( "dropshadow" ) && g_pSurface->SupportsFeature(ISurface::DROPSHADOW_FONTS))
			{
				flags |= ISurface::FONTFLAG_DROPSHADOW;
			}
			if (fontdata->GetInt( "outline" ) && g_pSurface->SupportsFeature(ISurface::OUTLINE_FONTS))
			{
				flags |= ISurface::FONTFLAG_OUTLINE;
			}
			if (fontdata->GetInt( "custom" ))
			{
				flags |= ISurface::FONTFLAG_CUSTOM;
			}
			if (fontdata->GetInt( "bitmap" ))
			{
				flags |= ISurface::FONTFLAG_BITMAP;
			}
			if (fontdata->GetInt( "rotary" ))
			{
				flags |= ISurface::FONTFLAG_ROTARY;
			}
			if (fontdata->GetInt( "additive" ))
			{
				flags |= ISurface::FONTFLAG_ADDITIVE;
			}

			int tall = fontdata->GetInt( "tall" );
			int blur = fontdata->GetInt( "blur" );
			int scanlines = fontdata->GetInt( "scanlines" );
			float scalex = fontdata->GetFloat( "scalex", 1.0f );
			float scaley = fontdata->GetFloat( "scaley", 1.0f );

			// only grow this font if it doesn't have a resolution filter specified
			if ( ( !fontYResMin && !fontYResMax ) && m_FontAliases[i].m_bProportional )
			{
				tall = g_Scheme.GetProportionalScaledValueEx( this, tall );
				blur = g_Scheme.GetProportionalScaledValueEx( this, blur );
				scanlines = g_Scheme.GetProportionalScaledValueEx( this, scanlines ); 
				scalex = g_Scheme.GetProportionalScaledValueEx( this, scalex * 10000.0f ) * 0.0001f;
				scaley = g_Scheme.GetProportionalScaledValueEx( this, scaley * 10000.0f ) * 0.0001f;
			}

			// clip the font size so that fonts can't be too big
			if ( tall > 127 )
			{
				tall = 127;
			}

			// check our minimum font height
			if ( tall < minimumFontHeight )
			{
				tall = minimumFontHeight;
			}
			
			if ( flags & ISurface::FONTFLAG_BITMAP )
			{
				// add the new set
				g_pSurface->SetBitmapFontGlyphSet(
					m_FontAliases[i]._font,
					g_pSurface->GetBitmapFontName( fontdata->GetString( "name" ) ), 
					scalex,
					scaley,
					flags);
			}
			else
			{
				int nRangeMin, nRangeMax;

				if ( GetFontRange( fontdata->GetString( "name" ), nRangeMin, nRangeMax ) )
				{
					// add the new set
					g_pSurface->SetFontGlyphSet(
						m_FontAliases[i]._font,
						fontdata->GetString( "name" ), 
						tall, 
						fontdata->GetInt( "weight" ), 
						blur,
						scanlines,
						flags,
						nRangeMin,
						nRangeMax);					
				}
				else
				{
					// add the new set
					g_pSurface->SetFontGlyphSet(
						m_FontAliases[i]._font,
						fontdata->GetString( "name" ), 
						tall, 
						fontdata->GetInt( "weight" ), 
						blur,
						scanlines,
						flags);
				}
			}

			// don't add any more
			break;
		}
	}

	COM_TimestampedLog( "ReloadFontGlyphs(): End" );
}

//-----------------------------------------------------------------------------
// Purpose: create the Border objects from the scheme data
//-----------------------------------------------------------------------------
void CScheme::LoadBorders()
{
	m_pkvBorders = m_pData->FindKey("Borders", true);
	{for ( KeyValues *kv = m_pkvBorders->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey())
	{
		if (kv->GetDataType() == KeyValues::TYPE_STRING)
		{
			// it's referencing another border, ignore for now
		}
		else
		{
			int i = m_BorderList.AddToTail();

			IBorder *border = NULL;
			const char *pszBorderType = kv->GetString( "bordertype", NULL );
			if ( pszBorderType && pszBorderType[0] )
			{
				if ( !stricmp(pszBorderType,"image") )
				{
					border = new ImageBorder();
				}
				else if ( !stricmp(pszBorderType,"scalable_image") )
				{
					border = new ScalableImageBorder();
				}
				else
				{
					Assert(0);
					// Fall back to the base border type. See below.
					pszBorderType = NULL;
				}
			}

			if ( !pszBorderType || !pszBorderType[0] )
			{
				border = new Border();
			}

			border->SetName(kv->GetName());
			border->ApplySchemeSettings(this, kv);

			m_BorderList[i].border = border;
			m_BorderList[i].bSharedBorder = false;
			m_BorderList[i].borderSymbol = kv->GetNameSymbol();
		}
	}}

	// iterate again to get the border references
	for ( KeyValues *kv = m_pkvBorders->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey())
	{
		if (kv->GetDataType() == KeyValues::TYPE_STRING)
		{
			// it's referencing another border, find it
			Border *border = (Border *)GetBorder(kv->GetString());
//			const char *str = kv->GetName();
			Assert(border);

			// add an entry that just references the existing border
			int i = m_BorderList.AddToTail();
			m_BorderList[i].border = border;
			m_BorderList[i].bSharedBorder = true;
			m_BorderList[i].borderSymbol = kv->GetNameSymbol();
		}
	}
	
	m_pBaseBorder = GetBorder("BaseBorder");
}

void CScheme::SpewFonts( void )
{
	Msg( "Scheme: %s (%s)\n", GetName(), GetFileName() );
	FOR_EACH_DICT_FAST( m_FontAliases, i )
	{
		const fontalias_t& FontAlias = m_FontAliases[ i ];
		uint32 Font = FontAlias._font;
		const char *szFontName = g_pSurface->GetFontName( Font );
		const char *szFontFamilyName = g_pSurface->GetFontFamilyName( Font );
		const char *szTrueFontName = FontAlias._trueFontName.String();
		const char *szFontAlias = m_FontAliases.GetElementName( i );

		Msg( "  %2d: HFont:0x%8.8x, %s, %s, font:%s, tall:%d(%d). %s\n", 
			i, 
			Font,
			szTrueFontName ? szTrueFontName : "??", 
			szFontAlias ? szFontAlias : "??",
			szFontName ? szFontName : "??", 
			g_pSurface->GetFontTall( Font ),
			g_pSurface->GetFontTallRequested( Font ),
			szFontFamilyName ? szFontFamilyName : "" );
	}
}

//-----------------------------------------------------------------------------
// Purpose: reloads the scheme from the file
//-----------------------------------------------------------------------------
void CSchemeManager::ReloadSchemes()
{
	int count = m_Schemes.Count();
	Shutdown( false );
	
	// reload the scheme
	for (int i = 1; i < count; i++)
	{
		LoadSchemeFromFile(m_Schemes[i]->GetFileName(), m_Schemes[i]->GetName());
	}
}

//-----------------------------------------------------------------------------
// Purpose: kills all the schemes
//-----------------------------------------------------------------------------
void CScheme::Shutdown( bool full )
{
	for (int i = 0; i < m_BorderList.Count(); i++)
	{
		// delete if it's not shared
		if (!m_BorderList[i].bSharedBorder)
		{
			IBorder *border = m_BorderList[i].border;
			delete border;
		}
	}

	m_pBaseBorder = NULL;
	m_BorderList.RemoveAll();
	m_pkvBorders = NULL;

	if (full && m_pData)
	{
		m_pData->deleteThis();
		m_pData = NULL;
		delete this;
	}
}

//-----------------------------------------------------------------------------
// Purpose: returns a handle to the default (first loaded) scheme
//-----------------------------------------------------------------------------
HScheme CSchemeManager::GetDefaultScheme()
{
	return 1;
}

//-----------------------------------------------------------------------------
// Purpose: returns a handle to the scheme identified by "tag"
//-----------------------------------------------------------------------------
HScheme CSchemeManager::GetScheme(const char *tag)
{
	for (int i=1;i<m_Schemes.Count();i++)
	{
		if ( !stricmp(tag,m_Schemes[i]->GetName()) )
		{
			return i;
		}
	}
	return 1; // default scheme
}

int CSchemeManager::GetProportionalScaledValue_( int rootWide, int rootTall, int normalizedValue )
{
	int proH, proW;
	g_pSurface->GetProportionalBase( proW, proH );
	double scale = (double)rootTall / (double)proH;

	return (int)( normalizedValue * scale );
}

int CSchemeManager::GetProportionalNormalizedValue_( int rootWide, int rootTall, int scaledValue )
{
	int proH, proW;
	g_pSurface->GetProportionalBase( proW, proH );
	float scale = (float)rootTall / (float)proH;

	return (int)( scaledValue / scale );
}

//-----------------------------------------------------------------------------
// Purpose: converts a value into proportional mode
//-----------------------------------------------------------------------------
int CSchemeManager::GetProportionalScaledValue(int normalizedValue)
{
	int wide, tall;
	g_pSurface->GetScreenSize( wide, tall );
	return GetProportionalScaledValue_( wide, tall, normalizedValue );
}

//-----------------------------------------------------------------------------
// Purpose: converts a value out of proportional mode
//-----------------------------------------------------------------------------
int CSchemeManager::GetProportionalNormalizedValue(int scaledValue)
{
	int wide, tall;
	g_pSurface->GetScreenSize( wide, tall );
	return GetProportionalNormalizedValue_( wide, tall, scaledValue );
}

// gets the proportional coordinates for doing screen-size independant panel layouts
// use these for font, image and panel size scaling (they all use the pixel height of the display for scaling)
int CSchemeManager::GetProportionalScaledValueEx( CScheme *pScheme, int normalizedValue )
{
	VPANEL sizing = pScheme->GetSizingPanel();
	if ( !sizing )
	{
		return GetProportionalScaledValue( normalizedValue );
	}

	int w, h;
	g_pIPanel->GetSize( sizing, w, h );
	return GetProportionalScaledValue_( w, h, normalizedValue );
}

int CSchemeManager::GetProportionalNormalizedValueEx( CScheme *pScheme, int scaledValue )
{
	VPANEL sizing = pScheme->GetSizingPanel();
	if ( !sizing )
	{
		return GetProportionalNormalizedValue( scaledValue );
	}

	int w, h;
	g_pIPanel->GetSize( sizing, w, h );
	return GetProportionalNormalizedValue_( w, h, scaledValue );
}

int CSchemeManager::GetProportionalScaledValueEx( HScheme scheme, int normalizedValue )
{
	IScheme *pscheme = GetIScheme( scheme );
	if ( !pscheme )
	{
		Assert( 0 );
		return GetProportionalScaledValue( normalizedValue );
	}

	CScheme *p = static_cast< CScheme * >( pscheme );
	return GetProportionalScaledValueEx( p, normalizedValue );
}

int CSchemeManager::GetProportionalNormalizedValueEx( HScheme scheme, int scaledValue )
{
	IScheme *pscheme = GetIScheme( scheme );
	if ( !pscheme )
	{
		Assert( 0 );
		return GetProportionalNormalizedValue( scaledValue );
	}

	CScheme *p = static_cast< CScheme * >( pscheme );
	return GetProportionalNormalizedValueEx( p, scaledValue );
}

void CSchemeManager::SpewFonts( void )
{
	for ( int i = 1; i < m_Schemes.Count(); i++ )
	{
		m_Schemes[i]->SpewFonts();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
const char *CScheme::GetResourceString(const char *stringName)
{
	return m_pkvBaseSettings->GetString(stringName);
}

//-----------------------------------------------------------------------------
// Purpose: returns a pointer to an image
//-----------------------------------------------------------------------------
IImage *CSchemeManager::GetImage(const char *imageName, bool hardwareFiltered)
{
	if ( !imageName || strlen(imageName) <= 0 ) // frame icons and the like are in the scheme file and may not be defined, so if this is null then fail silently
	{
		return NULL; 
	}

	// set up to search for the bitmap
	CachedBitmapHandle_t searchBitmap;
	searchBitmap.pBitmap = NULL;

	// Prepend 'vgui/'. Resource files try to load images assuming they live in the vgui directory.
	// Used to do this in Bitmap::Bitmap, moved so that the s_pszSearchString is searching for the
	// filename with 'vgui/' already added.
	char szFileName[MAX_PATH];

	//if ( Q_IsAbsolutePath(imageName) )
	//{
	//	Q_strncpy( szFileName, imageName, sizeof(szFileName) );
	//}
	//else
	{
		if ( Q_stristr( imageName, ".pic" ) )
		{
			Q_snprintf( szFileName, sizeof(szFileName), "%s", imageName );
		}
		else
		{
			Q_snprintf( szFileName, sizeof(szFileName), "vgui/%s", imageName );
		}
	}

	s_pszSearchString = szFileName;
	int i = m_Bitmaps.Find( searchBitmap );
	if (m_Bitmaps.IsValidIndex( i ) )
	{
		return m_Bitmaps[i].pBitmap;
	}

	// couldn't find the image, try and load it
	CachedBitmapHandle_t hBitmap = { new Bitmap( szFileName, hardwareFiltered ) };
	m_Bitmaps.Insert( hBitmap );
	return hBitmap.pBitmap;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
HTexture CSchemeManager::GetImageID(const char *imageName, bool hardwareFiltered)
{
	IImage *img = GetImage(imageName, hardwareFiltered);
	return ((Bitmap *)img)->GetID();
}

//-----------------------------------------------------------------------------
// Delete a managed image
//-----------------------------------------------------------------------------
bool CSchemeManager::DeleteImage( const char *pImageName )
{
	if ( !pImageName )
	{
		// nothing to do
		return false;
	}

	// set up to search for the bitmap
	CachedBitmapHandle_t searchBitmap;
	searchBitmap.pBitmap = NULL;

	// Prepend 'vgui/'. Resource files try to load images assuming they live in the vgui directory.
	// Used to do this in Bitmap::Bitmap, moved so that the s_pszSearchString is searching for the
	// filename with 'vgui/' already added.
	char szFileName[256];
	if ( Q_stristr( pImageName, ".pic" ) )
	{
		Q_snprintf( szFileName, sizeof(szFileName), "%s", pImageName );
	}
	else
	{
		Q_snprintf( szFileName, sizeof(szFileName), "vgui/%s", pImageName );
	}
	s_pszSearchString = szFileName;

	int i = m_Bitmaps.Find( searchBitmap );
	if ( !m_Bitmaps.IsValidIndex( i ) )
	{
		// not found
		return false;
	}
		
	// no way to know if eviction occured, assume it does
	m_Bitmaps[i].pBitmap->Evict();
	delete  m_Bitmaps[i].pBitmap;	
	m_Bitmaps.RemoveAt( i );

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: returns a pointer to an existing border
//-----------------------------------------------------------------------------
IBorder *CScheme::GetBorder(const char *borderName)
{
	int symbol = KeyValuesSystem()->GetSymbolForString(borderName);
	for (int i = 0; i < m_BorderList.Count(); i++)
	{
		if (m_BorderList[i].borderSymbol == symbol)
		{
			return m_BorderList[i].border;
		}
	}

	return m_pBaseBorder;
}

//-----------------------------------------------------------------------------
// Purpose: Get the number of borders
//-----------------------------------------------------------------------------
int CScheme::GetBorderCount() const
{
	return m_BorderList.Count();
}

//-----------------------------------------------------------------------------
// Purpose: Get the border at the given index
//-----------------------------------------------------------------------------
IBorder *CScheme::GetBorderAtIndex( int iIndex )
{
	if ( !m_BorderList.IsValidIndex( iIndex ) )
		return NULL;

	return m_BorderList[ iIndex ].border;
}

//-----------------------------------------------------------------------------
// Finds a font in the alias list
//-----------------------------------------------------------------------------
HFont CScheme::FindFontInAliasList( const char *fontName )
{
	int i = m_FontAliases.Find( fontName );
	if ( i != m_FontAliases.InvalidIndex() )
	{
		return m_FontAliases[i]._font;
	}

	// No dice
	return 0;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : font - 
// Output : char const
//-----------------------------------------------------------------------------
char const *CScheme::GetFontName( const HFont& font )
{
	for (int i = m_FontAliases.Count(); --i >= 0; )
	{
		HFont fnt = (HFont)m_FontAliases[i]._font;
		if ( fnt == font )
			return m_FontAliases[i]._trueFontName.String();
	}

	return "<Unknown font>";
}

//-----------------------------------------------------------------------------
// Purpose: returns a pointer to an existing font, proportional=false means use default
//-----------------------------------------------------------------------------
HFont CScheme::GetFont( const char *fontName, bool proportional )
{
	// First look in the list of aliases...
	return FindFontInAliasList( GetMungedFontName( fontName, tag, proportional ) );
}

//-----------------------------------------------------------------------------
// Purpose:Get the number of fonts
//-----------------------------------------------------------------------------
int CScheme::GetFontCount() const
{
	return m_FontAliases.Count();
}

//-----------------------------------------------------------------------------
// Purpose: Get the font at the given index
//-----------------------------------------------------------------------------
HFont CScheme::GetFontAtIndex( int iIndex )
{
	if ( !m_FontAliases.IsValidIndex( iIndex ) )
		return INVALID_FONT;
	
	return m_FontAliases[ iIndex ]._font;
}

//-----------------------------------------------------------------------------
// Purpose: returns a char string of the munged name this font is stored as in the font manager
//-----------------------------------------------------------------------------
const char *CScheme::GetMungedFontName( const char *fontName, const char *scheme, bool proportional )
{
	static char mungeBuffer[ 64 ];
	if ( scheme )
	{
		Q_snprintf( mungeBuffer, sizeof( mungeBuffer ), "%s%s-%s", fontName, scheme, proportional ? "p" : "no" );
	}
	else
	{ 
		Q_snprintf( mungeBuffer, sizeof( mungeBuffer ), "%s-%s", fontName, proportional ? "p" : "no" ); // we don't want the "(null)" snprintf appends
	}
	return mungeBuffer;
}

//-----------------------------------------------------------------------------
// Purpose: Gets a color from the scheme file
//-----------------------------------------------------------------------------
Color CScheme::GetColor(const char *colorName, Color defaultColor)
{
	const char *pchT = LookupSchemeSetting(colorName);
	if (!pchT)
		return defaultColor;

	int r = 0, g = 0, b = 0, a = 0;
	if (sscanf(pchT, "%d %d %d %d", &r, &g, &b, &a) >= 3)
		return Color(r, g, b, a);

	return defaultColor;
}

//-----------------------------------------------------------------------------
// Purpose: Get the color at the given index
//-----------------------------------------------------------------------------
const KeyValues *CScheme::GetColorData() const
{
	return m_pkvColors;
}

//-----------------------------------------------------------------------------
// Purpose: recursively looks up a setting
//-----------------------------------------------------------------------------
const char *CScheme::LookupSchemeSetting(const char *pchSetting)
{
	// try parse out the color
	int r, g, b, a = 0;
	int res = sscanf(pchSetting, "%d %d %d %d", &r, &g, &b, &a);
	if (res >= 3)
	{
		return pchSetting;
	}

	// check the color area first
	const char *colStr = m_pkvColors->GetString(pchSetting, NULL);
	if (colStr)
		return colStr;

	// check base settings
	colStr = m_pkvBaseSettings->GetString(pchSetting, NULL);
	if (colStr)
	{
		return LookupSchemeSetting(colStr);
	}

	return pchSetting;
}

//-----------------------------------------------------------------------------
// Purpose: gets the minimum font height for the current language
//-----------------------------------------------------------------------------
int CScheme::GetMinimumFontHeightForCurrentLanguage()
{
	char language[64];
	bool bValid;
	if ( IsPC() )
	{
		bValid = vgui::g_pSystem->GetRegistryString( "HKEY_CURRENT_USER\\Software\\Valve\\Source\\Language", language, sizeof(language)-1 );
	}
	else
	{
		Q_strncpy( language, XBX_GetLanguageString(), sizeof( language ) );
		bValid = true;
	}

	if ( bValid )
	{
		if (!stricmp(language, "korean")
			|| !stricmp(language, "tchinese")
			|| !stricmp(language, "schinese")
			|| !stricmp(language, "japanese"))
		{
			// the bitmap-based fonts for these languages simply don't work with a pt. size of less than 9 (13 pixels)
			return 13;
		}

		if ( !stricmp(language, "thai" ) )
		{
			// thai has problems below 18 pts
			return 18;
		}
	}

	// no special minimum height required
	return 0;
}