//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//
//
// hud.cpp
//
// implementation of CHud class
//
#include "cbase.h"
#include "hud_macros.h"
#include "history_resource.h"
#include "iinput.h"
#include "clientmode.h"
#include "in_buttons.h"
#include <vgui_controls/Controls.h>
#include <vgui/ISurface.h>
#include <keyvalues.h>
#include "itextmessage.h"
#include "mempool.h"
#include <keyvalues.h>
#include "filesystem.h"
#include <vgui_controls/AnimationController.h>
#include <vgui/ISurface.h>
#include "hud_lcd.h"
#if defined( CSTRIKE15 )
	#include "c_cs_player.h"
	#include "cs_gamerules.h"
#endif

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

static 	CClassMemoryPool< CHudTexture >	 g_HudTextureMemoryPool( 128 );

//-----------------------------------------------------------------------------
// Purpose: Parses the weapon txt files to get the sprites needed.
//-----------------------------------------------------------------------------
void LoadHudTextures( CUtlDict< CHudTexture *, int >& list, char *szFilenameWithoutExtension, const unsigned char *pICEKey )
{
	KeyValues *pTemp, *pTextureSection;

	KeyValues *pKeyValuesData = ReadEncryptedKVFile( filesystem, szFilenameWithoutExtension, pICEKey );
	if ( pKeyValuesData )
	{
		pTextureSection = pKeyValuesData->FindKey( "TextureData" );
		if ( pTextureSection  )
		{
			// Read the sprite data
			pTemp = pTextureSection->GetFirstSubKey();
			while ( pTemp )
			{
				CHudTexture *tex = new CHudTexture();
				// Key Name is the sprite name
				Q_strncpy( tex->szShortName, pTemp->GetName(), sizeof( tex->szShortName ) );

				if ( pTemp->GetString( "font", NULL ) )
				{
					// it's a font-based icon
					tex->bRenderUsingFont = true;
					tex->cCharacterInFont = *( pTemp->GetString( "character", "" ));
					Q_strncpy( tex->szTextureFile, pTemp->GetString( "font" ), sizeof( tex->szTextureFile ) );
				}
				else
				{
					tex->bRenderUsingFont = false;
					Q_strncpy( tex->szTextureFile, pTemp->GetString( "file" ), sizeof( tex->szTextureFile ) );
					tex->rc.left	= pTemp->GetInt( "x", 0 );
					tex->rc.top		= pTemp->GetInt( "y", 0 );
					tex->rc.right	= pTemp->GetInt( "width", 0 )	+ tex->rc.left;
					tex->rc.bottom	= pTemp->GetInt( "height", 0 )	+ tex->rc.top;
				}

				list.Insert( tex->szShortName, tex );

				pTemp = pTemp->GetNextKey();
			}
		}
	}

	// Failed for some reason. Delete the Key data and abort.
	pKeyValuesData->deleteThis();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : * - 
//			list - 
//-----------------------------------------------------------------------------
void FreeHudTextureList( CUtlDict< CHudTexture *, int >& list )
{
	int c = list.Count();
	for ( int i = 0; i < c; i++ )
	{
		CHudTexture *tex = list[ i ];
		delete tex;
	}
	list.RemoveAll();
}

// Globally-used fonts
vgui::HFont g_hFontTrebuchet24 = vgui::INVALID_FONT;


//=======================================================================================================================
// Hud Element Visibility handling
//=======================================================================================================================
typedef struct hudelement_hidden_s
{
	char	*sElementName;
	int		iHiddenBits;	// Bits in which this hud element is hidden
} hudelement_hidden_t;

ConVar hidehud( "hidehud", "0", FCVAR_CHEAT | FCVAR_SS );

//=======================================================================================================================
//  CHudElement
//	All hud elements are derived from this class.
//=======================================================================================================================
//-----------------------------------------------------------------------------
// Purpose: Registers the hud element in a global list, in CHud
//-----------------------------------------------------------------------------
CHudElement::CHudElement( const char *pElementName )
{
	InitCHudElementAfterConstruction( pElementName );
}

void CHudElement::InitCHudElementAfterConstruction( const char* pElementName )
{
	m_pHud = NULL;
	m_bActive = false;
	m_iHiddenBits = 0;
	m_pElementName = pElementName;
	m_nSplitScreenPlayerSlot = -1;
	SetNeedsRemove( false );
	m_bIsParentedToClientDLLRootPanel = false;
	m_ignoreGlobalHudDisable = false;
    m_bWantLateUpdate = false;

	// Make this for all hud elements, but when its a bit safer
#if defined( TF_CLIENT_DLL )
	RegisterForRenderGroup( "global" );
#endif
}


//-----------------------------------------------------------------------------
// Purpose: Remove this hud element from the global list in CHUD
//-----------------------------------------------------------------------------
CHudElement::~CHudElement()
{
	if ( m_bNeedsRemove )
	{
		GetHud().RemoveHudElement( this );
	}
}

void CHudElement::SetHud( CHud *pHud )
{
	m_pHud = pHud;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudElement::SetActive( bool bActive )
{
	m_bActive = bActive;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : needsremove - 
//-----------------------------------------------------------------------------
void CHudElement::SetNeedsRemove( bool needsremove )
{
	m_bNeedsRemove = needsremove;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudElement::SetHiddenBits( int iBits )
{
	m_iHiddenBits = iBits;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudElement::SetIgnoreGlobalHudDisable( bool hide )
{
	m_ignoreGlobalHudDisable = hide;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CHudElement::GetIgnoreGlobalHudDisable( void )
{
	return m_ignoreGlobalHudDisable;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CHudElement::ShouldDraw( void )
{
	bool bShouldDraw = m_pHud && !m_pHud->IsHidden( m_iHiddenBits );
	if ( bShouldDraw )
	{
		// for each render group
		int iNumGroups = m_HudRenderGroups.Count();
		for ( int iGroupIndex = 0; iGroupIndex < iNumGroups; iGroupIndex++ )
		{
			if ( GetHud().IsRenderGroupLockedFor( this, m_HudRenderGroups.Element( iGroupIndex ) ) )
				return false;
		}
	}

	return bShouldDraw;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CHudElement::IsParentedToClientDLLRootPanel() const
{
	return m_bIsParentedToClientDLLRootPanel;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : parented - 
//-----------------------------------------------------------------------------
void CHudElement::SetParentedToClientDLLRootPanel( bool parented )
{
	m_bIsParentedToClientDLLRootPanel = parented;
}

//-----------------------------------------------------------------------------
// Purpose: We can register to be affected by multiple hud render groups
//-----------------------------------------------------------------------------
void CHudElement::RegisterForRenderGroup( const char *pszGroupName )
{
	int iGroupIndex = GetHud().RegisterForRenderGroup( pszGroupName );

	// add group index to our list of registered groups
	if ( m_HudRenderGroups.Find( iGroupIndex ) == m_HudRenderGroups.InvalidIndex() )
	{
		m_HudRenderGroups.AddToTail( iGroupIndex );
	}
}

void CHudElement::UnregisterForRenderGroup( const char *pszGroupName )
{
	int iGroupIndex = GetHud().RegisterForRenderGroup( pszGroupName );

	m_HudRenderGroups.FindAndRemove( iGroupIndex );
}

//-----------------------------------------------------------------------------
// Purpose: We want to obscure other elements in this group
//-----------------------------------------------------------------------------
void CHudElement::HideLowerPriorityHudElementsInGroup( const char *pszGroupName )
{
	// look up the render group
	int iGroupIndex = GetHud().LookupRenderGroupIndexByName( pszGroupName );

	// lock the group
	GetHud().LockRenderGroup( iGroupIndex, this );
}

//-----------------------------------------------------------------------------
// Purpose: Stop obscuring other elements in this group
//-----------------------------------------------------------------------------
void CHudElement::UnhideLowerPriorityHudElementsInGroup( const char *pszGroupName )
{	
	// look up the render group
	int iGroupIndex = GetHud().LookupRenderGroupIndexByName( pszGroupName );

	// unlock the group
	GetHud().UnlockRenderGroup( iGroupIndex, this );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int	CHudElement::GetRenderGroupPriority( void )
{
	return 0;
}

int CHudElement::GetSplitScreenPlayerSlot() const
{
	return m_nSplitScreenPlayerSlot;
}

void CHudElement::SetSplitScreenPlayerSlot( int nSlot )
{
	m_nSplitScreenPlayerSlot = nSlot;
}

CHud gHUD[ MAX_SPLITSCREEN_PLAYERS ];  // global HUD objects

CHud &GetHud( int nSlot /*= -1*/ )
{
	if ( nSlot == -1 )
	{
		ASSERT_LOCAL_PLAYER_RESOLVABLE();
		nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
	}
	return gHUD[ nSlot ];
}

bool MsgFunc_ResetHUD( const CCSUsrMsg_ResetHud& msg )
{
	ASSERT_LOCAL_PLAYER_RESOLVABLE();
	return gHUD[ GET_ACTIVE_SPLITSCREEN_SLOT() ].MsgFunc_ResetHUD( msg );
}

#ifdef CSTRIKE_DLL
bool MsgFunc_SendAudio( const CCSUsrMsg_SendAudio& msg )
{
	ASSERT_LOCAL_PLAYER_RESOLVABLE();
	return gHUD[ GET_ACTIVE_SPLITSCREEN_SLOT() ].MsgFunc_SendAudio( msg );
}
#endif


class CSplitHudHelper
{
public:
	CUserMessageBinder m_UMCMsgResetHud;
	CUserMessageBinder m_UMCMsgSendAudio;

	void Init()
	{
		m_UMCMsgResetHud.Bind< CS_UM_ResetHud, CCSUsrMsg_ResetHud >( UtlMakeDelegate( MsgFunc_ResetHUD ) );
#ifdef CSTRIKE_DLL
		m_UMCMsgSendAudio.Bind< CS_UM_SendAudio, CCSUsrMsg_SendAudio >( UtlMakeDelegate( MsgFunc_SendAudio ) );
#endif
	}


};

static CSplitHudHelper g_HudHelper;

CHud::CHud()
{
	SetDefLessFunc( m_RenderGroups );

	m_flScreenShotTime = -1;
	m_nSplitScreenSlot = -1;
	m_bEngineIsInGame = false;
}

CUtlVector< CHudElement * > &CHud::GetHudList()
{
	return m_HudList;
}

const CUtlVector< CHudElement * > &CHud::GetHudList() const
{
	return m_HudList;
}

CUtlVector< vgui::Panel * > &CHud::GetHudPanelList()
{
	return m_HudPanelList;
}

const CUtlVector< vgui::Panel * > &CHud::GetHudPanelList() const
{
	return m_HudPanelList;
}

//-----------------------------------------------------------------------------
// Purpose: This is called every time the DLL is loaded
//-----------------------------------------------------------------------------
void CHud::Init( void )
{
	ASSERT_LOCAL_PLAYER_RESOLVABLE();

	m_nSplitScreenSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
	g_HudHelper.Init();

	m_iDisabledCount = 0;

	InitFonts();

	// Create all the Hud elements
	CHudElementHelper::CreateAllElements();

	gLCD.Init();

	// Initialize all created elements
	for ( int i = 0; i < GetHudList().Count(); i++ )
	{
		GetHudList()[ i ]->Init();
	}

	KeyValues *kv = new KeyValues( "layout" );
	if ( kv )
	{
		if ( kv->LoadFromFile( filesystem, "scripts/HudLayout.res" ) )
		{
			int numelements = GetHudList().Count();

			for ( int i = 0; i < numelements; i++ )
			{
				CHudElement *element = GetHudList()[i];
				vgui::Panel *pPanel = GetHudPanelList()[i];
				if ( pPanel )
				{
					KeyValues *key = kv->FindKey( pPanel->GetName(), false );
					if ( !key )
					{
						Msg( "Hud element '%s' doesn't have an entry '%s' in scripts/HudLayout.res\n", element->GetName(), pPanel->GetName() );
					}

					// Note:  When a panel is parented to the module root, it's "parent" is returned as NULL.
					if ( !element->IsParentedToClientDLLRootPanel() && 
						 !pPanel->GetParent() )
					{
						DevMsg( "Hud element '%s'/'%s' doesn't have a parent\n", element->GetName(), pPanel->GetName() );
					}
				}
			}
		}

		kv->deleteThis();
	}

	if ( GET_ACTIVE_SPLITSCREEN_SLOT() == 0 )
	{
		HudIcons().Init();
	}
}


//-----------------------------------------------------------------------------
// Purpose: Init Hud global colors
// Input  : *scheme - 
//-----------------------------------------------------------------------------
void CHud::InitColors( vgui::IScheme *scheme )
{
	m_clrNormal = scheme->GetColor( "Normal", Color( 255, 208, 64 ,255 ) );
	m_clrCaution = scheme->GetColor( "Caution", Color( 255, 48, 0, 255 ) );
	m_clrYellowish = scheme->GetColor( "Yellowish", Color( 255, 160, 0, 255 ) );
}

//-----------------------------------------------------------------------------
// Initializes fonts
//-----------------------------------------------------------------------------
void CHud::InitFonts()
{
	vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
	vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( scheme );
	g_hFontTrebuchet24 = pScheme->GetFont( "DefaultLarge", true );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHud::Shutdown( void )
{
	gLCD.Shutdown();

	// Delete all the Hud elements
	int iMax = GetHudList().Count();
	for ( int i = iMax-1; i >= 0; i-- )
	{
		delete GetHudList()[i];
	}
	GetHudList().Purge();
	GetHudPanelList().Purge();

	if ( GET_ACTIVE_SPLITSCREEN_SLOT() == 0 )
	{
		HudIcons().Shutdown();
	}
}


//-----------------------------------------------------------------------------
// Purpose: LevelInit's called whenever a new level's starting
//-----------------------------------------------------------------------------
void CHud::LevelInit( void )
{
	// Tell all the registered hud elements to LevelInit
	for ( int i = 0; i < GetHudList().Count(); i++ )
	{
		GetHudList()[ i ]->LevelInit();
	}

	// Unhide all render groups
	int iCount = m_RenderGroups.Count();
	for ( int i = 0; i < iCount; i++ )
	{
		CHudRenderGroup *group = m_RenderGroups[ i ];
		group->bHidden = false;
		group->m_pLockingElements.Purge();
	}
}

//-----------------------------------------------------------------------------
// Purpose: LevelShutdown's called whenever a level's finishing
//-----------------------------------------------------------------------------
void CHud::LevelShutdown( void )
{
	// Tell all the registered hud elements to LevelInit
	for ( int i = 0; i < GetHudList().Count(); i++ )
	{
		GetHudList()[ i ]->LevelShutdown();
	}
}

//-----------------------------------------------------------------------------
// Purpose: cleans up memory allocated for m_rg* arrays
//-----------------------------------------------------------------------------
CHud::~CHud()
{
	int c = m_RenderGroups.Count();
	for ( int i = c - 1; i >= 0; i-- )
	{
		CHudRenderGroup *group = m_RenderGroups[ i ];
		m_RenderGroups.RemoveAt( i );
		delete group;
	}
}

void CHudTexture::Precache( void )
{
	// costly function, used selectively on specific hud elements to get font pages built out at load time
	if ( bRenderUsingFont && !bPrecached && hFont != vgui::INVALID_FONT )
	{
		wchar_t wideChars[2];
		wideChars[0] = ( wchar_t )cCharacterInFont;
		wideChars[1] = 0;
		vgui::surface()->PrecacheFontCharacters( hFont, wideChars );
		bPrecached = true;
	}
}

void CHudTexture::DrawSelf( int x, int y, const Color& clr, float flApparentZ ) const
{
	DrawSelf( x, y, Width(), Height(), clr, flApparentZ );
}

void CHudTexture::DrawSelf( int x, int y, int w, int h, const Color& clr, float flApparentZ ) const
{
	if ( bRenderUsingFont )
	{
		vgui::surface()->DrawSetApparentDepth( flApparentZ );
		vgui::surface()->DrawSetTextFont( hFont );
		vgui::surface()->DrawSetTextColor( clr );
		vgui::surface()->DrawSetTextPos( x, y );
		vgui::surface()->DrawUnicodeChar( cCharacterInFont );
		vgui::surface()->DrawClearApparentDepth();
	}
	else
	{
		if ( textureId == -1 )
			return;

		vgui::surface()->DrawSetApparentDepth( flApparentZ );
		vgui::surface()->DrawSetTexture( textureId );
		vgui::surface()->DrawSetColor( clr );
		vgui::surface()->DrawTexturedSubRect( x, y, x + w, y + h, 
			texCoords[ 0 ], texCoords[ 1 ], texCoords[ 2 ], texCoords[ 3 ] );
		vgui::surface()->DrawClearApparentDepth();
	}
}

void CHudTexture::DrawSelfCropped( int x, int y, int cropx, int cropy, int cropw, int croph, int finalWidth, int finalHeight, Color clr, float flApparentZ ) const
{
	if ( bRenderUsingFont )
	{
		// work out how much we've been cropped
		int height = vgui::surface()->GetFontTall( hFont );
		float frac = ( height - croph ) / ( float )height;
		y -= cropy;

		vgui::surface()->DrawSetApparentDepth( flApparentZ );
		vgui::surface()->DrawSetTextFont( hFont );
		vgui::surface()->DrawSetTextColor( clr );
		vgui::surface()->DrawSetTextPos( x, y );

		FontCharRenderInfo info;
		if ( vgui::surface()->DrawGetUnicodeCharRenderInfo( cCharacterInFont, info ) )
		{
			if ( cropy )
			{
				info.verts[0].m_Position.y = Lerp( frac, info.verts[0].m_Position.y, info.verts[1].m_Position.y );
				info.verts[0].m_TexCoord.y = Lerp( frac, info.verts[0].m_TexCoord.y, info.verts[1].m_TexCoord.y );
			}
			else if ( croph != height )
			{
				info.verts[1].m_Position.y = Lerp( 1.0f - frac, info.verts[0].m_Position.y, info.verts[1].m_Position.y );
				info.verts[1].m_TexCoord.y = Lerp( 1.0f - frac, info.verts[0].m_TexCoord.y, info.verts[1].m_TexCoord.y );
			}
			vgui::surface()->DrawRenderCharFromInfo( info );
		}
		vgui::surface()->DrawClearApparentDepth();
	}
	else
	{
		if ( textureId == -1 )
			return;

		float fw = ( float )Width();
		float fh = ( float )Height();

		float twidth	= texCoords[ 2 ] - texCoords[ 0 ];
		float theight	= texCoords[ 3 ] - texCoords[ 1 ];

		// Interpolate coords
		float tCoords[ 4 ];
		tCoords[ 0 ] = texCoords[ 0 ] + ( ( float )cropx / fw ) * twidth;
		tCoords[ 1 ] = texCoords[ 1 ] + ( ( float )cropy / fh ) * theight;
		tCoords[ 2 ] = texCoords[ 0 ] + ( ( float )( cropx + cropw ) / fw ) * twidth;
		tCoords[ 3 ] = texCoords[ 1 ] + ( ( float )( cropy + croph ) / fh ) * theight;

		vgui::surface()->DrawSetApparentDepth( flApparentZ );
		vgui::surface()->DrawSetTexture( textureId );
		vgui::surface()->DrawSetColor( clr );
		vgui::surface()->DrawTexturedSubRect( 
			x, y, 
			x + finalWidth, y + finalHeight, 
			tCoords[ 0 ], tCoords[ 1 ], 
			tCoords[ 2 ], tCoords[ 3 ] );
		vgui::surface()->DrawClearApparentDepth();
	}
}

void CHudTexture::DrawSelfCropped( int x, int y, int cropx, int cropy, int cropw, int croph, Color clr, float flApparentZ ) const
{
	DrawSelfCropped( x, y, cropx, cropy, cropw, croph, cropw, croph, clr, flApparentZ );
}


void CHudTexture::DrawSelfScalableCorners( int drawX, int drawY, int w, int h, int iSrcCornerW, int iSrcCornerH, int iDrawCornerW, int iDrawCornerH, Color clr, float flApparentZ ) const
{
	if ( bRenderUsingFont )
	{
		Assert( !"DrawSelfScalableCorners does not support drawing a font" );
		return;
	}

	if ( textureId == -1 )
		return;

	float fw = ( float )Width();
	float fh = ( float )Height();

	float flCornerWidthPercent = ( fw > 0 ) ? ( ( float )iSrcCornerW / fw ) : 0;
	float flCornerHeightPercent = ( fh > 0 ) ? ( ( float )iSrcCornerH / fh ) : 0;

	vgui::surface()->DrawSetColor( clr );
	vgui::surface()->DrawSetTexture( textureId );

	float uvx = 0;
	float uvy = 0;
	float uvw, uvh;

	float drawW, drawH;

	int x = drawX;
	int y = drawY;

	int row, col;
	for ( row=0;row<3;row++ )
	{
		x = drawX;
		uvx = 0;

		if ( row == 0 || row == 2 )
		{
			//uvh - row 0 or 2, is src_corner_height
			uvh = flCornerHeightPercent;
			drawH = iDrawCornerH;
		}
		else
		{
			//uvh - row 1, is tall - ( 2 * src_corner_height ) ( min 0 )
			uvh = MAX( 1.0 - 2 * flCornerHeightPercent, 0.0f );
			drawH = MAX( 0, ( h - 2 * iDrawCornerH ) );
		}

		for ( col=0;col<3;col++ )
		{
			if ( col == 0 || col == 2 )
			{
				//uvw - col 0 or 2, is src_corner_width
				uvw = flCornerWidthPercent;
				drawW = iDrawCornerW;
			}
			else
			{
				//uvw - col 1, is wide - ( 2 * src_corner_width ) ( min 0 )
				uvw = MAX( 1.0 - 2 * flCornerWidthPercent, 0.0f );
				drawW = MAX( 0, ( w - 2 * iDrawCornerW ) );
			}

			Vector2D uv11( uvx, uvy );
			Vector2D uv21( uvx+uvw, uvy );
			Vector2D uv22( uvx+uvw, uvy+uvh );
			Vector2D uv12( uvx, uvy+uvh );

			vgui::Vertex_t verts[4];
			verts[0].Init( Vector2D( x, y ), uv11 );
			verts[1].Init( Vector2D( x+drawW, y ), uv21 );
			verts[2].Init( Vector2D( x+drawW, y+drawH ), uv22 );
			verts[3].Init( Vector2D( x, y+drawH ), uv12  );

			vgui::surface()->DrawTexturedPolygon( 4, verts, false );	

			x += drawW;
			uvx += uvw;
		}

		y += drawH;
		uvy += uvh;
	}

	vgui::surface()->DrawSetTexture( 0 );
}


//-----------------------------------------------------------------------------
// Purpose: returns width of texture with scale factor applied.  ( If rendered
//			using font, scale factor is ignored. )
//-----------------------------------------------------------------------------
int CHudTexture::EffectiveWidth( float flScale ) const
{
	if ( !bRenderUsingFont )
	{
		return ( int ) ( Width() * flScale );
	}
	else
	{
		return vgui::surface()->GetCharacterWidth( hFont, cCharacterInFont );		
	}
}

//-----------------------------------------------------------------------------
// Purpose: returns height of texture with scale factor applied.  ( If rendered
//			using font, scale factor is ignored. )
//-----------------------------------------------------------------------------
int CHudTexture::EffectiveHeight( float flScale ) const
{
	if ( !bRenderUsingFont )
	{
		return ( int ) ( Height() * flScale );
	}
	else
	{
		return vgui::surface()->GetFontAscent( hFont, cCharacterInFont );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHud::OnRestore()
{
	ResetHUD();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHud::VidInit( void )
{
	for ( int i = 0; i < GetHudList().Count(); i++ )
	{
		GetHudList()[ i ]->VidInit();
	}

	ResetHUD();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CHudElement *CHud::FindElement( const char *pName )
{
	for ( int i = 0; i < GetHudList().Count(); i++ )
	{
		if ( stricmp( GetHudList()[ i ]->GetName(), pName ) == 0 )
			return GetHudList()[i];
	}

	DevWarning( 1, "[%d] Could not find Hud Element: %s\n", m_nSplitScreenSlot, pName );
	Assert( 0 );
	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Adds a member to the HUD
//-----------------------------------------------------------------------------
void CHud::AddHudElement( CHudElement *pHudElement ) 
{
	pHudElement->SetSplitScreenPlayerSlot( GET_ACTIVE_SPLITSCREEN_SLOT() );

	// Add the hud element to the end of the array
	GetHudList().AddToTail( pHudElement );

	vgui::Panel *pPanel = dynamic_cast< vgui::Panel * >( pHudElement );
	
	GetHudPanelList().AddToTail( pPanel );

	pHudElement->SetHud( this );
	pHudElement->SetNeedsRemove( true );
}

//-----------------------------------------------------------------------------
// Purpose: Remove an element from the HUD
//-----------------------------------------------------------------------------
void CHud::RemoveHudElement( CHudElement *pHudElement ) 
{
	int location = GetHudList().Find( pHudElement );
	GetHudList().Remove( location );
	GetHudPanelList().Remove( location );
}

//-----------------------------------------------------------------------------
// Purpose: Returns current mouse sensitivity setting
// Output : float - the return value
//-----------------------------------------------------------------------------
float CHud::GetSensitivity( void )
{
	return m_flMouseSensitivity;
}

float CHud::GetFOVSensitivityAdjust()
{
	return m_flFOVSensitivityAdjust;
}

void CHud::DisableHud( void )
{
	if ( m_iDisabledCount < 1 )
		m_iDisabledCount = 1;
	else
		m_iDisabledCount++;
}

void CHud::EnableHud( void )
{
	if ( m_iDisabledCount <= 0 )
		DebuggerBreakIfDebugging();

	m_iDisabledCount--;
}

bool CHud::HudDisabled( void )
{
	return m_iDisabledCount > 0;
}

//-----------------------------------------------------------------------------
// Purpose: Return true if the passed in sections of the HUD shouldn't be drawn
//-----------------------------------------------------------------------------
bool CHud::IsHidden( int iHudFlags )
{
	// Not in game?
	if ( !m_bEngineIsInGame )
		return true;

	// Grab the local or observed player
	C_BasePlayer *pPlayer = GetHudPlayer();

	// Grab the local player
	C_CSPlayer *localPlayer = C_CSPlayer::GetLocalCSPlayer();

	if ( !pPlayer )
		return true;

	int iHideHud = pPlayer->m_Local.m_iHideHUD;
	ConVarRef hidehudref( "hidehud" );
	if ( hidehudref.GetInt() )
	{
		iHideHud = hidehudref.GetInt();
	}

	// Everything hidden?
	if ( iHideHud & HIDEHUD_ALL )
		return true;

	// hide health if not chasing a target
	if ( localPlayer->GetObserverMode() == OBS_MODE_ROAMING || 
		localPlayer->GetObserverMode() == OBS_MODE_FIXED || 
		localPlayer->GetObserverMode() == OBS_MODE_FREEZECAM || 
		localPlayer->GetObserverMode() == OBS_MODE_DEATHCAM )
	{
		if ( (iHudFlags & HIDEHUD_HEALTH) || (iHudFlags & HIDEHUD_WEAPONSELECTION) )
			return true;
	}

	// Hide all hud elements if we're blurring the background, since they don't blur properly
	if ( GetClientMode()->GetBlurFade() )
		return true;

	// Don't show hud elements when we're at the mainmenu with a background map running
	if ( engine->IsLevelMainMenuBackground() )
		return true;

	// Local player dead?
	if ( ( iHudFlags & HIDEHUD_PLAYERDEAD ) && ( pPlayer->GetHealth() <= 0 ) )
		return true;

	// Need the HEV suit ( HL2 )
	if ( ( iHudFlags & HIDEHUD_NEEDSUIT ) && ( !pPlayer->IsSuitEquipped() ) )
		return true;

#if defined( CSTRIKE15 )
	if ( CSGameRules() && CSGameRules()->IsPlayingTraining() )
	{
		C_CSPlayer *pCSPlayer = static_cast<C_CSPlayer *>( pPlayer );
		// hide the mini scoreboard?
		if ( ( iHudFlags & HIDEHUD_MINISCOREBOARD ) && ( pCSPlayer && pCSPlayer->IsMiniScoreHidden() ) )
			return true;

		// hide the radar?
		if ( ( iHudFlags & HIDEHUD_RADAR ) && ( pCSPlayer && pCSPlayer->IsRadarHidden() ) )
			return true;
	}
#endif

	return ( ( iHudFlags & iHideHud ) != 0 );
}

//-----------------------------------------------------------------------------
// Purpose: Allows HUD to modify input data
//-----------------------------------------------------------------------------
void CHud::ProcessInput( bool bActive )
{
	if ( bActive )
	{
		m_iKeyBits = input->GetButtonBits( false );

		// Weaponbits need to be sent down as a UserMsg now.
		GetHud().Think();
	}
}

int CHud::LookupRenderGroupIndexByName( const char *pszGroupName )
{
	int iIndex = m_RenderGroupNames.Find( pszGroupName );

	Assert( m_RenderGroupNames.IsValidIndex( iIndex ) );

	return iIndex;
}

//-----------------------------------------------------------------------------
// Purpose: A hud element wants to lock this render group so other panels in the
// group do not draw
//-----------------------------------------------------------------------------
bool CHud::LockRenderGroup( int iGroupIndex, CHudElement *pLocker /* = NULL */ )
{
	// does this index exist?
	if ( !DoesRenderGroupExist( iGroupIndex ) )
		return false;

	int i = m_RenderGroups.Find( iGroupIndex );

	Assert( m_RenderGroups.IsValidIndex( i ) );

	CHudRenderGroup *group = m_RenderGroups.Element( i );

	Assert( group );

	if ( group )
	{
		// NULL pLocker means some higher power is globally hiding this group
		if ( pLocker == NULL )
		{
			group->bHidden = true;
		}
		else
		{
			bool bFound = false;
			// See if we have it locked already
			int iNumLockers = group->m_pLockingElements.Count();
			for ( int i=0;i<iNumLockers;i++ )
			{
				if ( pLocker == group->m_pLockingElements.Element( i ) )
				{
					bFound = true;
					break;
				}
			}

			// otherwise lock us
			if ( !bFound )
				group->m_pLockingElements.Insert( pLocker );
		}

		return true;
	}
	
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: A hud element wants to release the lock on this render group 
//-----------------------------------------------------------------------------
bool CHud::UnlockRenderGroup( int iGroupIndex, CHudElement *pLocker /* = NULL */ )
{
	// does this index exist?
	if ( !DoesRenderGroupExist( iGroupIndex ) )
		return false;

	int i = m_RenderGroups.Find( iGroupIndex );

	Assert( m_RenderGroups.IsValidIndex( i ) );

	CHudRenderGroup *group = m_RenderGroups.Element( i );

	if ( group )
	{
		// NULL pLocker means some higher power is globally hiding this group
		if ( group->bHidden && pLocker == NULL )
		{
			group->bHidden = false;
			return true;
		}

		int iNumLockers = group->m_pLockingElements.Count();
		for ( int i=0;i<iNumLockers;i++ )
		{
			if ( pLocker == group->m_pLockingElements.Element( i ) )
			{
				group->m_pLockingElements.RemoveAt( i );
				return true;
			}
		}
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: See if we should draw based on a hud render group
//			Return true if this group is locked, hud elem will be hidden
//-----------------------------------------------------------------------------
bool CHud::IsRenderGroupLockedFor( CHudElement *pHudElement, int iGroupIndex )
{
	// does this index exist?
	if ( !DoesRenderGroupExist( iGroupIndex ) )
		return false;

	int i = m_RenderGroups.Find( iGroupIndex );

	Assert( m_RenderGroups.IsValidIndex( i ) );

	CHudRenderGroup *group = m_RenderGroups.Element( i );

	if ( !group )
		return false;

	// hidden for everyone!
	if ( group->bHidden )
		return true;

	if ( group->m_pLockingElements.Count() == 0 )
		return false;

	if ( !pHudElement )
		return true;

	CHudElement *pLocker = group->m_pLockingElements.ElementAtHead();

	return ( pLocker != pHudElement && pLocker->GetRenderGroupPriority() > pHudElement->GetRenderGroupPriority() );
}

//-----------------------------------------------------------------------------
// Purpose: CHudElements can ask for the index of hud element render groups
//			returns a group index
//-----------------------------------------------------------------------------
int CHud::RegisterForRenderGroup( const char *pszGroupName )
{
	int iGroupNameIndex = m_RenderGroupNames.Find( pszGroupName );

	if ( iGroupNameIndex != m_RenderGroupNames.InvalidIndex() )
	{	
		return iGroupNameIndex;
	}

	// otherwise add the group
	return AddHudRenderGroup( pszGroupName );
}

//-----------------------------------------------------------------------------
// Purpose: Create a new hud render group
//			returns a group index
//-----------------------------------------------------------------------------
int CHud::AddHudRenderGroup( const char *pszGroupName )
{
	// we tried to register for a group but didn't find it, add a new one

	int iGroupNameIndex = m_RenderGroupNames.AddToTail( pszGroupName );

	CHudRenderGroup *group = new CHudRenderGroup();
	return m_RenderGroups.Insert( iGroupNameIndex, group );
}

//-----------------------------------------------------------------------------
// Purpose:  
//-----------------------------------------------------------------------------
bool CHud::DoesRenderGroupExist( int iGroupIndex )
{
	return ( m_RenderGroups.Find( iGroupIndex ) != m_RenderGroups.InvalidIndex() );
}

//-----------------------------------------------------------------------------
// Purpose: Allows HUD to Think and modify input data
// Input  : *cdata - 
//			time - 
// Output : int - 1 if there were changes, 0 otherwise
//-----------------------------------------------------------------------------
void CHud::UpdateHud( bool bActive )
{
	// clear the weapon bits.
	m_iKeyBits &= ( ~( IN_WEAPON1|IN_WEAPON2 ));

	GetClientMode()->Update();

	gLCD.Update();
}

void CHud::OnSplitScreenStateChanged()
{
	for ( int i = 0; i < GetHudList().Count(); ++i )
	{
		GetHudList()[ i ]->OnSplitScreenStateChanged();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Force a Hud UI anim to play
//-----------------------------------------------------------------------------
CON_COMMAND_F( testhudanim, "Test a hud element animation.\n\tArguments: <anim name>\n", FCVAR_CHEAT )
{
	if ( args.ArgC() != 2 )
	{
		Msg( "Usage:\n   testhudanim <anim name>\n" );
		return;
	}

	GetClientMode()->GetViewportAnimationController()->StartAnimationSequence( args[1] );
}

CHudIcons::CHudIcons() :
	m_bHudTexturesLoaded( false )
{
}

CHudIcons::~CHudIcons()
{
	int c = m_Icons.Count();
	for ( int i = c - 1; i >= 0; i-- )
	{
		CHudTexture *tex = m_Icons[ i ];
		g_HudTextureMemoryPool.Free( tex );
	}
	m_Icons.Purge();
}

void CHudIcons::Init()
{
	if ( m_bHudTexturesLoaded )
		return;

	m_bHudTexturesLoaded = true;
	CUtlDict< CHudTexture *, int >	textureList;

	// check to see if we have sprites for this res; if not, step down
	LoadHudTextures( textureList, "scripts/hud_textures", NULL );
	LoadHudTextures( textureList, "scripts/mod_textures", NULL );
	LoadHudTextures( textureList, "scripts/instructor_textures", NULL );
	LoadHudTextures( textureList, "scripts/instructor_modtextures", NULL );
#ifdef PORTAL2
	LoadHudTextures( textureList, "scripts/signifier_textures", NULL );
#endif
	// PORTAL2

	int c = textureList.Count();
	for ( int index = 0; index < c; index++ )
	{
		CHudTexture* tex = textureList[ index ];
		AddSearchableHudIconToList( *tex );
	}

	FreeHudTextureList( textureList );
}

void CHudIcons::Shutdown()
{
	m_bHudTexturesLoaded = false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CHudTexture *CHudIcons::AddUnsearchableHudIconToList( CHudTexture& texture )
{
	// These names are composed based on the texture file name
	char composedName[ 512 ];

	if ( texture.bRenderUsingFont )
	{
		Q_snprintf( composedName, sizeof( composedName ), "%s_c%i",
			texture.szTextureFile, texture.cCharacterInFont );
	}
	else
	{
		Q_snprintf( composedName, sizeof( composedName ), "%s_%i_%i_%i_%i",
			texture.szTextureFile, texture.rc.left, texture.rc.top, texture.rc.right, texture.rc.bottom );
	}

	CHudTexture *icon = GetIcon( composedName );
	if ( icon )
	{
		return icon;
	}

	CHudTexture *newTexture = ( CHudTexture * )g_HudTextureMemoryPool.Alloc();
	*newTexture = texture;

	SetupNewHudTexture( newTexture );

	int idx = m_Icons.Insert( composedName, newTexture );
	return m_Icons[ idx ];
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CHudTexture *CHudIcons::AddSearchableHudIconToList( CHudTexture& texture )
{
	CHudTexture *icon = GetIcon( texture.szShortName );
	if ( icon )
	{
		return icon;
	}

	CHudTexture *newTexture = ( CHudTexture * )g_HudTextureMemoryPool.Alloc();
	*newTexture = texture;

	SetupNewHudTexture( newTexture );

	int idx = m_Icons.Insert( texture.szShortName, newTexture );
	return m_Icons[ idx ];
}

//-----------------------------------------------------------------------------
// Purpose: returns a pointer to an icon in the list
//-----------------------------------------------------------------------------
CHudTexture *CHudIcons::GetIcon( const char *szIcon )
{
	int i = m_Icons.Find( szIcon );
	if ( i == m_Icons.InvalidIndex() )
		return NULL;

	return m_Icons[ i ];
}

//-----------------------------------------------------------------------------
// Purpose: Gets texture handles for the hud icon
//-----------------------------------------------------------------------------
void CHudIcons::SetupNewHudTexture( CHudTexture *t )
{
	if ( t->bRenderUsingFont )
	{
		vgui::HScheme scheme = vgui::scheme()->GetScheme( "basemodui_scheme" );
		t->hFont = vgui::scheme()->GetIScheme( scheme )->GetFont( t->szTextureFile, true );
		t->rc.top = 0;
		t->rc.left = 0;
		t->rc.right = vgui::surface()->GetCharacterWidth( t->hFont, t->cCharacterInFont );
		t->rc.bottom = vgui::surface()->GetFontTall( t->hFont );
	}
	else
	{
		// Set up texture id and texture coordinates
		t->textureId = vgui::surface()->CreateNewTextureID();
		vgui::surface()->DrawSetTextureFile( t->textureId, t->szTextureFile, false, false );

		int wide, tall;
		vgui::surface()->DrawGetTextureSize( t->textureId, wide, tall );

		t->texCoords[ 0 ] = ( float )( t->rc.left + 0.5f ) / ( float )wide;
		t->texCoords[ 1 ] = ( float )( t->rc.top + 0.5f ) / ( float )tall;
		t->texCoords[ 2 ] = ( float )( t->rc.right - 0.5f ) / ( float )wide;
		t->texCoords[ 3 ] = ( float )( t->rc.bottom - 0.5f ) / ( float )tall;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudIcons::RefreshHudTextures()
{
	if ( !m_bHudTexturesLoaded )
	{
		Assert( 0 );
		return;
	}

	CUtlDict< CHudTexture *, int >	textureList;

	// check to see if we have sprites for this res; if not, step down
	LoadHudTextures( textureList, "scripts/hud_textures", NULL );
	LoadHudTextures( textureList, "scripts/mod_textures", NULL );
	LoadHudTextures( textureList, "scripts/instructor_textures", NULL );

	// fix up all the texture icons first
	int c = textureList.Count();
	for ( int index = 0; index < c; index++ )
	{
		CHudTexture *tex = textureList[ index ];
		Assert( tex );

		CHudTexture *icon = GetIcon( tex->szShortName );
		if ( !icon )
			continue;

		// Update file
		Q_strncpy( icon->szTextureFile, tex->szTextureFile, sizeof( icon->szTextureFile ) );

		if ( !icon->bRenderUsingFont )
		{
			// Update subrect
			icon->rc = tex->rc;

			// Keep existing texture id, but now update texture file and texture coordinates
			vgui::surface()->DrawSetTextureFile( icon->textureId, icon->szTextureFile, false, false );

			// Get new texture dimensions in case it changed
			int wide, tall;
			vgui::surface()->DrawGetTextureSize( icon->textureId, wide, tall );

			// Assign coords
			icon->texCoords[ 0 ] = ( float )( icon->rc.left + 0.5f ) / ( float )wide;
			icon->texCoords[ 1 ] = ( float )( icon->rc.top + 0.5f ) / ( float )tall;
			icon->texCoords[ 2 ] = ( float )( icon->rc.right - 0.5f ) / ( float )wide;
			icon->texCoords[ 3 ] = ( float )( icon->rc.bottom - 0.5f ) / ( float )tall;
		}
	}

	FreeHudTextureList( textureList );

	// fixup all the font icons
	vgui::HScheme scheme = vgui::scheme()->GetScheme( "basemodui_scheme" );
	for ( int i = m_Icons.First(); m_Icons.IsValidIndex( i ); i = m_Icons.Next( i ))
	{
		CHudTexture *icon = m_Icons[i];
		if ( !icon )
			continue;

		// Update file
		if ( icon->bRenderUsingFont )
		{
			icon->hFont = vgui::scheme()->GetIScheme( scheme )->GetFont( icon->szTextureFile, true );
			icon->rc.top = 0;
			icon->rc.left = 0;
			icon->rc.right = vgui::surface()->GetCharacterWidth( icon->hFont, icon->cCharacterInFont );
			icon->rc.bottom = vgui::surface()->GetFontTall( icon->hFont );
		}
	}
}


static CHudIcons g_HudIcons;

CHudIcons &HudIcons()
{
	return g_HudIcons;
}