//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//
#include "cbase.h"
#include "hud.h"
#include "hud_numeric.h"
#include "iclientmode.h"
#include <KeyValues.h>
#include <vgui/ISurface.h>
#include <vgui/IScheme.h>
#include <vgui_controls/AnimationController.h>
#include "ctype.h"

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

using namespace vgui;

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pElementName - 
//			*panelName - 
//-----------------------------------------------------------------------------
CHudNumeric::CHudNumeric( const char *pElementName, const char *panelName )
 : CHudElement( pElementName ), BaseClass( NULL, panelName )
{
	// Make sure we have the lookups built
	BuildPrintablesList();

	vgui::Panel *pParent = g_pClientMode->GetViewport();
	SetParent( pParent );

	SetActive( true );
	SetAutoDelete( false );

	m_nTextLen = 0;
	Q_memset( m_szPreviousValue, 0, sizeof( m_szPreviousValue ) );
	Q_memset( m_szLatchedValue, 0, sizeof( m_szLatchedValue ) );

	m_bDrawLabel = true;
	m_bSendPulses = true;
	m_bPulseForced = true;

	m_flRotaryTime = 0.0f;
	m_flRotaryStartTime = 0.0f;
	m_flActualCharactersPerSecond = 7.0f;
}

bool CHudNumeric::s_bPrintablesBuilt;
CUtlRBTree< int, int > CHudNumeric::m_Printables( 0, 0, DefLessFunc( int ) );

//-----------------------------------------------------------------------------
// Purpose: Builds a list of printable characters
//-----------------------------------------------------------------------------
void CHudNumeric::BuildPrintablesList( void )
{
	if ( s_bPrintablesBuilt )
		return;

	s_bPrintablesBuilt = true;
	int i;
	for ( i = 0; i < 256; i++ )
	{
		if ( isalnum( i ) )  // isprint or isgraph?
		{
			m_Printables.Insert( i );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Find the index of a printable character
// Input  : ch - 
// Output : int
//-----------------------------------------------------------------------------
int CHudNumeric::FindPrintableIndex( int ch )
{
	int idx = m_Printables.Find( ch );
	return idx;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *scheme - 
//-----------------------------------------------------------------------------
void CHudNumeric::ApplySchemeSettings(IScheme *scheme)
{
	BaseClass::ApplySchemeSettings(scheme);

	m_flActualCharactersPerSecond = (float)m_flDesiredCharactersPerSecond;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CHudNumeric::IsRotating( void ) const
{
	if ( gpGlobals->curtime >= m_flRotaryStartTime &&
		 gpGlobals->curtime <= ( m_flRotaryStartTime + m_flRotaryTime ) )
	{
		 return true;
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : frac - 
//			startchar - 
//			endchar - 
//			prevchar - 
//			nextchar - 
//			subfrac - 
//-----------------------------------------------------------------------------
void CHudNumeric::GetRotatedChar( float frac, char startchar, char endchar, char& prevchar, char& nextchar, float& subfrac )
{
	// Recast into database of printable characters
	int startidx	= FindPrintableIndex( startchar );
	int endidx		= FindPrintableIndex( endchar );

	float diff		= ( float )endidx - ( float )startidx;

	// No delta
	if ( diff == 0.0f || 
		startidx == m_Printables.InvalidIndex() ||
		endidx == m_Printables.InvalidIndex() )
	{
		prevchar	= startchar;
		nextchar	= startchar;
		subfrac		= 0.0f;

		return;
	}

	bool reverse = false;
	// Going backwards is same as forward except frac is reversed
	if ( diff < 0.0f )
	{
		reverse = true;
		frac = 1.0f - frac;
		int temp = startidx;
		startidx = endidx;
		endidx = temp;
		diff = -diff;
	}

	float	foutindex = (float)startidx;
	int		outindex;
	float   fnextindex;
	int		nextindex;

	int		maxdelta = (int)diff;

	if ( m_nRotaryMaxDelta != 0 )
	{
		maxdelta = MIN( m_nRotaryMaxDelta, maxdelta );
	}

	// Quantize steps
	// Map frac into maxdelta discrete intervals
	float indicesperstep = diff / (float)maxdelta;
	float fnumstepstaken = clamp( frac * (float)maxdelta, 0.0f, (float)( maxdelta  -0.001f ) );
	int numstepstaken = (int)(fnumstepstaken);

	foutindex = (float)startidx + (float)numstepstaken * indicesperstep;
	foutindex = clamp( foutindex, (float)startidx, (float)endidx );

	outindex	= ( int )foutindex;

	fnextindex = (float)startidx + (float)( numstepstaken + 1 ) * indicesperstep;
	fnextindex = clamp( fnextindex, (float)startidx, (float)endidx );

	nextindex = (int)fnextindex;

	subfrac = fnumstepstaken - (float)numstepstaken;

	if ( !m_Printables.IsValidIndex( outindex ) ||
		 !m_Printables.IsValidIndex( nextindex ) )
	{
		prevchar	= startchar;
		nextchar	= startchar;
		subfrac		= 0.0f;
		return;
	}

	if ( reverse )
	{
		subfrac = 1.0f - subfrac;
		prevchar	= m_Printables[ nextindex ];
		nextchar	= m_Printables[ outindex ];
	}
	else
	{
		prevchar	= m_Printables[ outindex ];
		nextchar	= m_Printables[ nextindex ];
	}
}	

void CHudNumeric::PaintRotatedCharacterHoriz( int x, int y, vgui::HFont& font, int prevchar, int nextchar, float frac )
{
	frac = 3 * frac * frac - 2 * frac * frac * frac;

	int abcA, abcB, abcC;
	surface()->GetCharABCwide( font, prevchar, abcA, abcB, abcC );
	// int fontTall = surface()->GetFontTall( font );

	CharRenderInfo info;
	if ( frac < 0.5f )
	{
		surface()->DrawSetTextPos( x, y );

		// Paint the right half of the prevchar still
		if ( surface()->DrawGetUnicodeCharRenderInfo( prevchar, info ) )
		{
			// Shift tex coord and y position half way down glyph
			info.verts[0].m_Position.x = Lerp( 0.5f, info.verts[0].m_Position.x, info.verts[1].m_Position.x );
			info.verts[0].m_TexCoord.x = Lerp( 0.5f, info.verts[0].m_TexCoord.x, info.verts[1].m_TexCoord.x );
			surface()->DrawRenderCharFromInfo( info );
		}

		surface()->DrawSetTextPos( x, y );

		// Paint up to frac from left of next char
		if ( surface()->DrawGetUnicodeCharRenderInfo( nextchar, info ) )
		{
			// Shift tex coord and y position part way up glyph
			info.verts[1].m_Position.x = Lerp( frac, info.verts[0].m_Position.x, info.verts[1].m_Position.x );
			info.verts[1].m_TexCoord.x = Lerp( frac, info.verts[0].m_TexCoord.x, info.verts[1].m_TexCoord.x );
			surface()->DrawRenderCharFromInfo( info );
		}

		// Paint divider

		surface()->DrawSetTextPos( x, y );

		// Paint from frac to 0.5 of prevchar on the left
		if ( surface()->DrawGetUnicodeCharRenderInfo( prevchar, info )  )
		{
			// Shift tex coord and y position half way up glyph
			vgui::Vertex_t save[2];
			
			save[0] = info.verts[0];
			save[1] = info.verts[1];

			info.verts[0].m_Position.x = Lerp( frac, save[0].m_Position.x, save[1].m_Position.x );
			//info.verts[0].m_TexCoord.x = Lerp( frac, save[0].m_TexCoord.x, save[1].m_TexCoord.x );

			info.verts[1].m_Position.x = Lerp( 0.5f, save[0].m_Position.x, save[1].m_Position.x );
			info.verts[1].m_TexCoord.x = Lerp( 0.5f, save[0].m_TexCoord.x, save[1].m_TexCoord.x );
			
			surface()->DrawRenderCharFromInfo( info );
		}
	}
	else if ( frac > 0.5f )
	{
		surface()->DrawSetTextPos( x, y );

		// Paint entire left half of next char
		if ( surface()->DrawGetUnicodeCharRenderInfo( nextchar, info ) )
		{
			// Shift tex coord and y position half way down glyph
			info.verts[1].m_Position.x = Lerp( 0.5f, info.verts[0].m_Position.x, info.verts[1].m_Position.x );
			info.verts[1].m_TexCoord.x = Lerp( 0.5f, info.verts[0].m_TexCoord.x, info.verts[1].m_TexCoord.x );
			surface()->DrawRenderCharFromInfo( info );
		}

		surface()->DrawSetTextPos( x, y );

		// paint a bit of the previous char at far right
		if ( surface()->DrawGetUnicodeCharRenderInfo( prevchar, info ) )
		{
			// Shift tex coord and y position part way up glyph
			info.verts[0].m_Position.x = Lerp( frac, info.verts[0].m_Position.x, info.verts[1].m_Position.x );
			info.verts[0].m_TexCoord.x = Lerp( frac, info.verts[0].m_TexCoord.x, info.verts[1].m_TexCoord.x );
			surface()->DrawRenderCharFromInfo( info );
		}

		// Paint divider

		surface()->DrawSetTextPos( x, y );

		// Paint from 0.5 to frac of next char
		if ( surface()->DrawGetUnicodeCharRenderInfo( nextchar, info ) )
		{
			vgui::Vertex_t save[2];
			
			save[0] = info.verts[0];
			save[1] = info.verts[1];

			info.verts[0].m_Position.x = Lerp( 0.5f, save[0].m_Position.x, save[1].m_Position.x );
			info.verts[0].m_TexCoord.x = Lerp( 0.5f, save[0].m_TexCoord.x, save[1].m_TexCoord.x );

			info.verts[1].m_Position.x = Lerp( frac, save[0].m_Position.x, save[1].m_Position.x );
			//info.verts[1].m_TexCoord.x = Lerp( frac, save[0].m_TexCoord.x, save[1].m_TexCoord.x );
			
			surface()->DrawRenderCharFromInfo( info );
		}
	}

	x += ( abcA + abcB + abcC );
	surface()->DrawSetTextPos( x, y );
}

void CHudNumeric::PaintRotatedCharacterSpeedomter( int x, int y, vgui::HFont& font, int prevchar, int nextchar, float frac )
{
	frac = 3 * frac * frac - 2 * frac * frac * frac;

	int abcA, abcB, abcC;
	surface()->GetCharABCwide( font, prevchar, abcA, abcB, abcC );
	// int fontTall = surface()->GetFontTall( font );

	CharRenderInfo info;
	if ( frac <= 0.0f )
	{
		surface()->DrawSetTextPos( x, y );

		// Paint the whole previous char
		if ( surface()->DrawGetUnicodeCharRenderInfo( prevchar, info ) )
		{
			surface()->DrawRenderCharFromInfo( info );
		}
	}
	else if ( frac >= 1.0f )
	{
		surface()->DrawSetTextPos( x, y );

		// Paint the whole previous char
		if ( surface()->DrawGetUnicodeCharRenderInfo( nextchar, info ) )
		{
			surface()->DrawRenderCharFromInfo( info );
		}
	}
	else
	{
		// Draw part of previous and part of next
		surface()->DrawSetTextPos( x, y );

		if ( surface()->DrawGetUnicodeCharRenderInfo( prevchar, info ) )
		{
			// Shift tex coord and y position part way up glyph
			info.verts[1].m_Position.y = Lerp( frac, info.verts[0].m_Position.y, info.verts[1].m_Position.y );
			info.verts[1].m_TexCoord.y = Lerp( frac, info.verts[0].m_TexCoord.y, info.verts[1].m_TexCoord.y );
			surface()->DrawRenderCharFromInfo( info );
		}

		// Paint divider

		surface()->DrawSetTextPos( x, y );

		if ( surface()->DrawGetUnicodeCharRenderInfo( nextchar, info ) )
		{
			// Shift tex coord and y position part way up glyph
			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 );
			surface()->DrawRenderCharFromInfo( info );
		}
	}

	x += ( abcA + abcB + abcC );
	surface()->DrawSetTextPos( x, y );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : x - 
//			y - 
//			font - 
//			prevchar - 
//			nextchar - 
//			frac - 
//-----------------------------------------------------------------------------
void CHudNumeric::PaintRotatedCharacter( int x, int y, HFont& font, int prevchar, int nextchar, float frac )
{
	frac = SimpleSpline( frac );

	int abcA[2], abcB[2], abcC[2];
	surface()->GetCharABCwide( font, prevchar, abcA[0], abcB[0], abcC[0] );
	surface()->GetCharABCwide( font, nextchar, abcA[1], abcB[1], abcC[1] );

	int w0 = abcA[0] + abcB[0] + abcC[0];
	int w1 = abcA[1] + abcB[1] + abcC[1];

	int cellwidth = MAX( w0, w1 );

	int fontTall = surface()->GetFontTall( font );

	int dividery = y + clamp( Lerp( frac, 0, fontTall ), 2, fontTall - 2 );

	int xprev = x;
	int xnext = x;

	int diff = abs( abcB[0] - abcB[1] ) * 0.5f;

	if ( diff != 0 )
	{
		// Prev is wider than next, push next right a bit
		if ( w0 > w1 )
		{
			xnext += diff;
		}
		else
		{
			xprev += diff;
		}
	}

	CharRenderInfo info;
	if ( frac < 0.5f )
	{
		surface()->DrawSetTextPos( xprev, y );

		// Paint the bottom half of the prevchar still
		if ( surface()->DrawGetUnicodeCharRenderInfo( prevchar, info ) )
		{
			// Shift tex coord and y position half way down glyph
			info.verts[0].m_Position.y = Lerp( 0.5f, info.verts[0].m_Position.y, info.verts[1].m_Position.y );
			info.verts[0].m_TexCoord.y = Lerp( 0.5f, info.verts[0].m_TexCoord.y, info.verts[1].m_TexCoord.y );
			surface()->DrawRenderCharFromInfo( info );
		}

		surface()->DrawSetTextPos( xnext, y );

		// Paint up to frac from top of prev char
		if ( surface()->DrawGetUnicodeCharRenderInfo( nextchar, info ) )
		{
			// Shift tex coord and y position part way up glyph
			info.verts[1].m_Position.y = Lerp( frac, info.verts[0].m_Position.y, info.verts[1].m_Position.y );
			info.verts[1].m_TexCoord.y = Lerp( frac, info.verts[0].m_TexCoord.y, info.verts[1].m_TexCoord.y );
			surface()->DrawRenderCharFromInfo( info );
		}

		// Paint divider
		if ( frac > 0.0f )
		{
			surface()->DrawSetColor( m_CharBgBorder );
			surface()->DrawLine( x - abcA[0] + 1, dividery, x + abcB[0] + abcC[0] - 1, dividery );
		}

		surface()->DrawSetTextPos( xprev, y );

		// Paint from frac to 0.5 of nextchar on the top
		if ( surface()->DrawGetUnicodeCharRenderInfo( prevchar, info )  )
		{
			// Shift tex coord and y position half way up glyph
			vgui::Vertex_t save[2];
			
			save[0] = info.verts[0];
			save[1] = info.verts[1];

			info.verts[0].m_Position.y = Lerp( frac, save[0].m_Position.y, save[1].m_Position.y );
			//info.verts[0].m_TexCoord.y = Lerp( frac, save[0].m_TexCoord.y, save[1].m_TexCoord.y );

			info.verts[1].m_Position.y = Lerp( 0.5f, save[0].m_Position.y, save[1].m_Position.y );
			info.verts[1].m_TexCoord.y = Lerp( 0.5f, save[0].m_TexCoord.y, save[1].m_TexCoord.y );
			
			surface()->DrawRenderCharFromInfo( info );
		}
	}
	else if ( frac >= 0.5f )
	{
		surface()->DrawSetTextPos( xnext, y );

		// Paint entire top half of next char
		if ( surface()->DrawGetUnicodeCharRenderInfo( nextchar, info ) )
		{
			// Shift tex coord and y position half way down glyph
			info.verts[1].m_Position.y = Lerp( 0.5f, info.verts[0].m_Position.y, info.verts[1].m_Position.y );
			info.verts[1].m_TexCoord.y = Lerp( 0.5f, info.verts[0].m_TexCoord.y, info.verts[1].m_TexCoord.y );
			surface()->DrawRenderCharFromInfo( info );
		}

		surface()->DrawSetTextPos( xprev, y );

		// paint a bit of the previous char at the bottom
		if ( surface()->DrawGetUnicodeCharRenderInfo( prevchar, info ) )
		{
			// Shift tex coord and y position part way up glyph
			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 );
			surface()->DrawRenderCharFromInfo( info );
		}

		// Paint divider
		if ( frac < 1.0f )
		{
			surface()->DrawSetColor( m_CharBgBorder );
			surface()->DrawLine( x - abcA[0] + 1, dividery, x + abcB[0] + abcC[0] - 1, dividery );
		}

		surface()->DrawSetTextPos( xnext, y );

		// Paint from 0.5 to frac of next char
		if ( surface()->DrawGetUnicodeCharRenderInfo( nextchar, info ) )
		{
			// Shift tex coord and y position half way up glyph
			vgui::Vertex_t save[2];
			
			save[0] = info.verts[0];
			save[1] = info.verts[1];

			info.verts[0].m_Position.y = Lerp( 0.5f, save[0].m_Position.y, save[1].m_Position.y );
			info.verts[0].m_TexCoord.y = Lerp( 0.5f, save[0].m_TexCoord.y, save[1].m_TexCoord.y );

			info.verts[1].m_Position.y = Lerp( frac, save[0].m_Position.y, save[1].m_Position.y );
			//info.verts[1].m_TexCoord.y = Lerp( frac, save[0].m_TexCoord.y, save[1].m_TexCoord.y );
			
			surface()->DrawRenderCharFromInfo( info );
		}
	}

	x += cellwidth;
	surface()->DrawSetTextPos( x, y );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *prev - 
//			*next - 
// Output : float
//-----------------------------------------------------------------------------
float CHudNumeric::MaxCharacterDiff( const char *prev, const char *next )
{
	float maxdiff = 0.0f;

	int textlen = Q_strlen( next );
	int prevlen = Q_strlen( prev );
	int ch;
	for ( ch = 0; ch < textlen; ch++ )
	{
		int charfromend = textlen - ch;
		char c = next[ ch ];
		char prevc = prev[ MAX( 0, prevlen - charfromend ) ];
		if ( prevc == 0 )
		{
			int tempindex = FindPrintableIndex( c );
			if ( tempindex != m_Printables.InvalidIndex() )
			{
				tempindex = MAX( 0, tempindex - 3 );
				prevc = m_Printables[ tempindex ];
			}
			else
			{
				prevc = c;
			}
		}

		float diff = (float)fabs( FindPrintableIndex( c ) - FindPrintableIndex( prevc ) );

		if ( diff > maxdiff )
		{
			maxdiff = diff;
		}
	}

	float maxdelta = maxdiff;

	if ( m_nRotaryMaxDelta != 0 )
	{
		maxdelta = MIN( (float)m_nRotaryMaxDelta, maxdelta );
	}

	return maxdelta;
}

int CHudNumeric::ComputePixelsRequired( vgui::HFont& font, const char *text, int textlen )
{
	int pixels = 0;
	for ( int ch = 0; ch < textlen; ch++ )
	{
		char c = text[ ch ];
		pixels += surface()->GetCharacterWidth( font, c );
	}
	return pixels;
}

void CHudNumeric::DrawCharacterBackground( const char *text, int textlen, HFont& font, int x, int y )
{
	int abcA, abcB, abcC;
	int fontTall = surface()->GetFontTall( font );

	int ch;
	int curx = x - ComputePixelsRequired( font, text, textlen );
	for ( ch = 0; ch < textlen; ch++ )
	{
		char c = text[ ch ];

		surface()->GetCharABCwide( font, c, abcA, abcB, abcC );

		curx += abcA;

		surface()->DrawSetColor( m_CharBg );
		surface()->DrawFilledRect( curx - abcA, y + 2, curx + abcB + abcC, y + fontTall - 2 );

		if ( m_bDrawCharacterBackgroundBorder )
		{
			surface()->DrawSetColor( m_CharBgBorder );
			surface()->DrawOutlinedRect( curx - abcA, y + 2, curx + abcB + abcC, y + fontTall - 2  );
		}

		curx += ( abcB + abcC );
	}
}

void CHudNumeric::DrawCharacterForeground( const char *text, int textlen, HFont& font, int x, int y )
{
	if ( m_nRotaryEffect == ROTARY_EFFECT_NONE ||
		m_nRotaryEffect == ROTARY_EFFECT_SPEEDOMETER )	
		return;

	int abcA, abcB, abcC;
	int fontTall = surface()->GetFontTall( font );

	int ch;
	int curx = x - ComputePixelsRequired( font, text, textlen );
	for ( ch = 0; ch < textlen; ch++ )
	{
		char c = text[ ch ];

		surface()->GetCharABCwide( font, c, abcA, abcB, abcC );

		curx += abcA;

		switch ( m_nRotaryEffect )
		{
		default:
		case ROTARY_EFFECT_VERTICAL_ALARM:
			{
				surface()->DrawSetColor( m_CharFg );
				surface()->DrawLine( curx - abcA + 1, y + fontTall / 2, curx + abcB + abcC - 1, y + fontTall / 2 );
			}
			break;
		case ROTARY_EFFECT_HORIZONTAL_ALARM:
			{
				surface()->DrawSetColor( m_CharFg );
				int avex = curx + ( - abcA + abcB + abcC ) / 2;
				surface()->DrawLine( avex, y + 2, avex, y + fontTall - 2 );
			}
			break;
		}

		curx += ( abcB + abcC );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *text - 
//			textlen - 
//			font - 
//			x - 
//			y - 
//-----------------------------------------------------------------------------
void CHudNumeric::PaintStringRotary( float t, const char *text, int textlen, HFont& font, int x, int y )
{
	surface()->DrawSetTextFont( font );
	int ch;

	int prevlen = Q_strlen( m_szLatchedValue );

	// Compute pixels actually required
	int pixels = 0;

	for ( ch = 0; ch < textlen; ch++ )
	{
		int charfromend = textlen - ch;
		char c = text[ ch ];
		char prevc = m_szLatchedValue[ MAX( 0, prevlen - charfromend ) ];
		if ( prevc == 0 )
		{
			int tempindex = FindPrintableIndex( c );
			if ( tempindex != m_Printables.InvalidIndex() )
			{
				tempindex = MAX( 0, tempindex - 3 );
				prevc = m_Printables[ tempindex ];
			}
			else
			{
				prevc = c;
			}
		}

		float diff = (float)fabs( FindPrintableIndex( c ) - FindPrintableIndex( prevc ) );

		if ( m_nRotaryMaxDelta != 0 )
		{
			diff = MIN( (float)m_nRotaryMaxDelta, diff );
		}

		float dt = diff / m_flActualCharactersPerSecond;

		float frac = 1.0f;
		if ( dt > 0.0f )
		{
			frac = t / MIN( dt, m_flRotaryTime );
		}
		frac = clamp( frac, 0.0f, 1.0f );

		float s;
		char chstart, chend;
		GetRotatedChar( frac, prevc, c, chstart, chend, s );

		int w0 = surface()->GetCharacterWidth( font, chstart );
		int w1 = surface()->GetCharacterWidth( font, chend );

		pixels += MAX( w0, w1 );
	}

	surface()->DrawSetTextPos( x - pixels, y );

	// Now render
	for ( ch = 0; ch < textlen; ch++ )
	{
		int charfromend = textlen - ch;
		char c = text[ ch ];
		char prevc = m_szLatchedValue[ MAX( 0, prevlen - charfromend ) ];
		if ( prevc == 0 )
		{
			int tempindex = FindPrintableIndex( c );
			if ( tempindex != m_Printables.InvalidIndex() )
			{
				tempindex = MAX( 0, tempindex - 3 );
				prevc = m_Printables[ tempindex ];
			}
			else
			{
				prevc = c;
			}
		}

		float diff = (float)fabs( FindPrintableIndex( c ) - FindPrintableIndex( prevc ) );

		if ( m_nRotaryMaxDelta != 0 )
		{
			diff = MIN( (float)m_nRotaryMaxDelta, diff );
		}

		float dt = diff / m_flActualCharactersPerSecond;

		float frac = 1.0f;
		if ( dt > 0.0f )
		{
			frac = t / MIN( dt, m_flRotaryTime );
		}
		frac = clamp( frac, 0.0f, 1.0f );

		float s;
		char chstart, chend;
		GetRotatedChar( frac, prevc, c, chstart, chend, s );
		int outx, outy;
		surface()->DrawGetTextPos( outx, outy );
		switch ( m_nRotaryEffect )
		{
		default:
		case ROTARY_EFFECT_VERTICAL_ALARM:
			{
				PaintRotatedCharacter( outx, outy, font, chstart, chend, s );
			}
			break;
		case ROTARY_EFFECT_HORIZONTAL_ALARM:
			{
				PaintRotatedCharacterHoriz( outx, outy, font, chstart, chend, s );
			}
			break;
		case ROTARY_EFFECT_SPEEDOMETER:
			{
				PaintRotatedCharacterSpeedomter( outx, outy, font, chstart, chend, s );
			}
			break;
		}

	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *text - 
//			textlen - 
//			font - 
//			x - 
//			y - 
//-----------------------------------------------------------------------------
void CHudNumeric::PaintString( const char *text, int textlen, HFont& font, int x, int y )
{
	if ( m_nRotaryEffect != ROTARY_EFFECT_NONE && 
		IsRotating() && 
		m_flRotaryTime > 0.0f )
	{
		float rotation_time = gpGlobals->curtime - m_flRotaryStartTime;

		PaintStringRotary( rotation_time, text, textlen, font, x, y );
		
		return;
	}
	PaintStringNormal( text, textlen, font, x, y );
}

void CHudNumeric::PaintStringNormal( const char *text, int textlen, vgui::HFont& font, int x, int y )
{
	surface()->DrawSetTextFont( font );
	int ch;
	surface()->DrawSetTextPos( x - ComputePixelsRequired( font, text, textlen ), y );

	for ( ch = 0; ch < textlen; ch++ )
	{
		char c = text[ ch ];
		surface()->DrawUnicodeChar( c );
	}
}

void CHudNumeric::PaintBackground( void )
{
	char value[ MAX_VALUE_LENGTH ];

	if ( !GetValue( value, sizeof( value ) ) )
		return;

	float alpha = m_flAlphaOverride / 255.0f;

	Color boxColor = GetBoxColor();
	boxColor[3] *= alpha;

	SetBgColor( boxColor );

	BaseClass::PaintBackground();

	if ( !m_bUseIcon )
		return;

	CHudTexture *tex = m_hIcon;
	if ( !tex )
		return;

	Color iconColor = m_IconColor;
	iconColor[3] *= alpha;

	tex->DrawSelf( m_flIconXPos, m_flIconYPos, m_flIconWidth, m_flIconHeight, iconColor );
}

void CHudNumeric::PaintValue( const char *value, int textlen, int wide, int tall, Color& clr )
{
	float alpha = m_flAlphaOverride / 255.0f;

	Color boxColor = GetBoxColor();
	boxColor[3] *= alpha;

	SetBgColor( boxColor );

	Color useColor = clr;
	useColor[3] *= alpha;

	surface()->DrawSetTextColor( useColor );

	int x = wide - label_xpos_right;
	int y = label_ypos;

	if ( m_bDrawLabel ) 
	{
		// Label
		PaintStringNormal( GetLabelText(), Q_strlen( GetLabelText() ), m_hLabelFont, x, y );
	}

	x = wide - value_xpos_right;
	y = value_ypos;

	if ( m_nRotaryEffect != ROTARY_EFFECT_NONE && 
		(bool)m_bDrawCharacterBackground )
	{
		DrawCharacterBackground( value, textlen, m_hTextFont, x, y );
	}

	PaintString( value, textlen, m_hTextFont, x, y );

	if ( m_nRotaryEffect != ROTARY_EFFECT_NONE && 
		(bool)m_bDrawCharacterForeground )
	{
		DrawCharacterForeground( value, textlen, m_hTextFont, x, y );
	}

	// draw the overbright blur
	for (float fl = m_flBlur; fl > 0.0f; fl -= 1.0f)
	{
		if (fl >= 1.0f)
		{
			PaintString( value, textlen, m_hTextFontPulsing, x, y );
		}
		else
		{
			// draw a percentage of the last one
			Color col = useColor;
			col[3] *= fl;
			surface()->DrawSetTextColor(col);
			PaintString( value, textlen, m_hTextFontPulsing, x, y );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : vgui::Color
//-----------------------------------------------------------------------------
Color CHudNumeric::GetColor()
{
	return m_TextColor;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : vgui::Color
//-----------------------------------------------------------------------------
Color CHudNumeric::GetBoxColor()
{
	return m_BoxColor;
}

//-----------------------------------------------------------------------------
// Purpose: Redraw
//-----------------------------------------------------------------------------
void CHudNumeric::Paint( void )
{
	char value[ MAX_VALUE_LENGTH ];

	if ( !GetValue( value, sizeof( value ) ) )
		return;

	int w, h;
	GetSize( w, h );

	bool dopulse = ( Q_strcmp( value, m_szPreviousValue ) != 0 ) || ( m_bPulseForced );
	bool increment = false;
	if ( dopulse )
	{
		if ( atof( m_szPreviousValue ) <= atof( value ) )
		{
			increment = true;
		}

		Q_strncpy( m_szLatchedValue, m_szPreviousValue, sizeof( m_szLatchedValue ) );
		m_nTextLen = Q_strlen( value );
		
		m_flRotaryStartTime = gpGlobals->curtime;
		float maxdiff = MaxCharacterDiff( m_szLatchedValue, value );
		float timerequired = maxdiff / m_flDesiredCharactersPerSecond;
		m_flActualCharactersPerSecond = (float)m_flDesiredCharactersPerSecond;
		m_flRotaryTime = MIN( m_flRotaryTimeMax, timerequired );
		if ( m_flRotaryTime < timerequired )
		{
			// Speed up rotation since we're moving so far
			m_flActualCharactersPerSecond =  ( timerequired / m_flRotaryTime ) * m_flDesiredCharactersPerSecond;
		}
	}

	Q_strncpy( m_szPreviousValue, value, sizeof( m_szPreviousValue ) );

	Color clr = GetColor();
	PaintValue( value, m_nTextLen, w, h, clr );

	if ( dopulse && m_bSendPulses )
	{
		// Start with a short pulse
		g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( GetPulseEvent( increment ) );

		m_bPulseForced = false;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Derived class has forced a pulse
//-----------------------------------------------------------------------------
void CHudNumeric::ForcePulse( void )
{
	m_bPulseForced = true;
}