//========= Copyright © 1996-2005, Sander Corporation, All rights reserved. ============//
//
// Purpose: hud_rts_strategytext.cpp
//
// $NoKeywords: $
//=============================================================================//

#include "cbase.h"
#include "hud.h"
#include "hudelement.h"
#include <vgui_controls/RichText.h>
#include <vgui/IScheme.h>
#include <vgui/IVGui.h>
#include "iclientmode.h"
#include "hud_macros.h"
#include <vgui/ILocalize.h>
#include <vgui/ISurface.h>
#include "hl2wars/rts_shareddefs.h"

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

#define STRATEGYLINE_NUM_FLASHES 8.0f
#define STRATEGYLINE_FLASH_TIME 5.0f
#define STRATEGYLINE_FADE_TIME 1.0f

static Color m_textColors[S_TOTALTYPES];

//-----------------------------------------------------------------------------
// Purpose: An output/display line of the chat interface
//-----------------------------------------------------------------------------
class CBaseHudStrategyLine : public vgui::RichText
{
	typedef vgui::RichText BaseClass;

public:
	CBaseHudStrategyLine( vgui::Panel *parent, const char *panelName );

	void			SetExpireTime( void );

	bool			IsReadyToExpire( void );

	void			Expire( void );

	float			GetStartTime( void );

	int				GetCount( void );

	virtual void	ApplySchemeSettings(vgui::IScheme *pScheme);

	vgui::HFont		GetFont() { return m_hFont; }

	Color			GetTextColor( void ) { return m_clrText; }
	void			SetNameLength( int iLength ) { m_iNameLength = iLength;	}
	void			SetNameColor( Color cColor ){ m_clrNameColor = cColor; 	}
		
	virtual void	PerformFadeout( void );

	void			SetType( int type ) { m_iType = type; }

protected:
	int				m_iNameLength;
	vgui::HFont		m_hFont;

	Color			m_clrText;
	Color			m_clrNameColor;

	float			m_flExpireTime;
	
private:
	float			m_flStartTime;
	int				m_nCount;
	int				m_iType;

	vgui::HFont		m_hFontMarlett;


//private:
	//CBaseHudStrategyLine( const CBaseHudStrategyLine & ); // not defined, not accessible
};


class CHudStrategyText : public CHudElement, public vgui::Panel
{
public:
	DECLARE_CLASS_SIMPLE( CHudStrategyText, vgui::Panel );
	//DECLARE_MULTIPLY_INHERITED();

	enum
	{
		STRATEGY_INTERFACE_LINES = 6,
		MAX_CHARS_PER_LINE = 128
	};

	CHudStrategyText( const char *pElementName );

	void			Init();
	void			Reset();

	void			LevelInit( const char *newmap );
	void			LevelShutdown( void );

	virtual void	Paint( void );
	virtual void	OnTick( void );
	virtual void	CreateStrategyLines( void );
	virtual int		GetStrategyInputOffset( void );

	void			MsgFunc_StrategyText( bf_read &msg );

	static int		m_nLineCounter;

	virtual void	Printf( const char *fmt, ... );

protected:
	virtual void	ApplySchemeSettings( vgui::IScheme *pScheme );

protected:
	CBaseHudStrategyLine	*FindUnusedChatLine( void );
	void					ExpireOldest( void );

	CBaseHudStrategyLine	*m_StrategyLines[ STRATEGY_INTERFACE_LINES ];
	int						m_iFontHeight;

private:
	void			Clear( void );

	int				ComputeBreakChar( int width, const char *text, int textlen );

	int				m_nVisibleHeight;
	int				m_iType;		// temp save place
};

DECLARE_HUDELEMENT( CHudStrategyText );
DECLARE_HUD_MESSAGE( CHudStrategyText, StrategyText );

int CHudStrategyText::m_nLineCounter = 1;

#ifndef _XBOX
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *parent - 
//			*panelName - 
//-----------------------------------------------------------------------------
CBaseHudStrategyLine::CBaseHudStrategyLine( vgui::Panel *parent, const char *panelName ) : 
	vgui::RichText( parent, panelName )
{
	m_hFont = m_hFontMarlett = 0;
	m_flExpireTime = 0.0f;
	m_flStartTime = 0.0f;
	m_iNameLength	= 0;

	SetPaintBackgroundEnabled( true );
	
	SetVerticalScrollbar( false );

	SetScheme( vgui::scheme()->LoadSchemeFromFile("resource/HL2Wars.res", "HL2Wars") );
}
	

void CBaseHudStrategyLine::ApplySchemeSettings(vgui::IScheme *pScheme)
{
	BaseClass::ApplySchemeSettings(pScheme);

	m_hFont = pScheme->GetFont("InterfaceInfoFont", IsProportional() );
	SetBorder( NULL );
	SetBgColor( Color( 0, 0, 0, 0 ) );
	SetFgColor( Color( 0, 0, 0, 0 ) );

	SetFont( m_hFont );

	m_hFontMarlett = pScheme->GetFont( "Marlett" );

	m_clrText = pScheme->GetColor( "FgColor", GetFgColor() );
	SetFont( m_hFont );
}


void CBaseHudStrategyLine::PerformFadeout( void )
{
	// Flash + Extra bright when new
	float curtime = gpGlobals->curtime;

	int lr, lg, lb;
	if( m_iType < 0 || m_iType >= S_TOTALTYPES )
	{
		lr = m_clrText[0];
		lg = m_clrText[1];
		lb = m_clrText[2];
	}
	else
	{
		lr = m_textColors[m_iType].r();
		lg = m_textColors[m_iType].g();
		lb = m_textColors[m_iType].b();
	}
	
	//CSPort chat only fades out, no blinking.
	if ( curtime <= m_flExpireTime && curtime > m_flExpireTime - STRATEGYLINE_FADE_TIME )
	{
		float frac = ( m_flExpireTime - curtime ) / STRATEGYLINE_FADE_TIME;

		int alpha = frac * 255;
		alpha = clamp( alpha, 0, 255 );

		wchar_t wbuf[4096];
		
		GetText(0, wbuf, sizeof(wbuf));

		SetText( "" );

		InsertColorChange( Color( lr, lg, lb, alpha ) );
		InsertString( wbuf );
	}
	
	OnThink();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : time - 
//-----------------------------------------------------------------------------
void CBaseHudStrategyLine::SetExpireTime( void )
{
	m_flStartTime = gpGlobals->curtime;
	m_flExpireTime = m_flStartTime + 12.0f;
	m_nCount = CHudStrategyText::m_nLineCounter++;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CBaseHudStrategyLine::GetCount( void )
{
	return m_nCount;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseHudStrategyLine::IsReadyToExpire( void )
{
	// Engine disconnected, expire right away
	if ( !engine->IsInGame() && !engine->IsConnected() )
		return true;

	if ( gpGlobals->curtime >= m_flExpireTime )
		return true;
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : float
//-----------------------------------------------------------------------------
float CBaseHudStrategyLine::GetStartTime( void )
{
	return m_flStartTime;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBaseHudStrategyLine::Expire( void )
{
	SetVisible( false );

	// Spit out label text now
//	char text[ 256 ];
//	GetText( text, 256 );

//	Msg( "%s\n", text );
}
#endif _XBOX

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CHudStrategyText::CHudStrategyText( const char *pElementName ) : BaseClass(NULL, "HudStrategyText"), CHudElement( pElementName )
{
	vgui::Panel *pParent = g_pClientMode->GetViewport();
	SetParent( pParent );
	
	SetScheme( vgui::scheme()->LoadSchemeFromFile("resource/HL2Wars.res", "HL2Wars") );

	if ( IsPC() )
	{
		vgui::ivgui()->AddTickSignal( GetVPanel() );
	}

	SetHiddenBits( HIDEHUD_RTS | HIDEHUD_PLAYERDEAD );

	m_textColors[S_GREEN] = Color(246,248,108,100);
	m_textColors[S_RED] = Color(236,50,63,100);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudStrategyText::Paint( void )
{
#ifndef _XBOX
	if ( m_nVisibleHeight == 0 )
		return;

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

	vgui::surface()->DrawSetColor( GetBgColor() );
	vgui::surface()->DrawFilledRect( 0, h - m_nVisibleHeight, w, h );

	vgui::surface()->DrawSetColor( GetFgColor() );
	vgui::surface()->DrawOutlinedRect( 0, h - m_nVisibleHeight, w, h );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudStrategyText::Init()
{
	if ( IsXbox() )
		return;

	CreateStrategyLines();

	HOOK_HUD_MESSAGE( CHudStrategyText, StrategyText );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudStrategyText::Reset()
{
#ifndef HL1_CLIENT_DLL
	m_nVisibleHeight = 0;
	Clear();
#endif
}

void CHudStrategyText::CreateStrategyLines( void )
{
#ifndef _XBOX
	for ( int i = 0; i < STRATEGY_INTERFACE_LINES; i++ )
	{
		char sz[ 32 ];
		Q_snprintf( sz, sizeof( sz ), "StrategyLine%02i", i );
		m_StrategyLines[ i ] = new CBaseHudStrategyLine( this, sz );
		m_StrategyLines[ i ]->SetVisible( false );		
	}
#endif
}

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

	SetBgColor( Color( 0, 0, 0, 0 ) );
	SetFgColor( Color( 0, 0, 0, 0 ) );
}

//-----------------------------------------------------------------------------
// Purpose: Save CPU cycles by letting the HUD system early cull
// costly traversal.  Called per frame, return true if thinking and 
// painting need to occur.
//-----------------------------------------------------------------------------
/*bool CHudStrategyText::ShouldDraw( void )
{
	return ( ( GetAlpha() > 0 ) && CHudElement::ShouldDraw() );
}*/

#ifndef _XBOX
static int __cdecl SortLines( void const *line1, void const *line2 )
{
	CBaseHudStrategyLine *l1 = *( CBaseHudStrategyLine ** )line1;
	CBaseHudStrategyLine *l2 = *( CBaseHudStrategyLine ** )line2;

	// Invisible at bottom
	if ( l1->IsVisible() && !l2->IsVisible() )
		return -1;
	else if ( !l1->IsVisible() && l2->IsVisible() )
		return 1;

	// Oldest start time at top
	if ( l1->GetStartTime() < l2->GetStartTime() )
		return -1;
	else if ( l1->GetStartTime() > l2->GetStartTime() )
		return 1;

	// Otherwise, compare counter
	if ( l1->GetCount() < l2->GetCount() )
		return -1;
	else if ( l1->GetCount() > l2->GetCount() )
		return 1;

	return 0;
}
#endif

//-----------------------------------------------------------------------------
// Purpose: Allow inheriting classes to change this spacing behavior
//-----------------------------------------------------------------------------
int CHudStrategyText::GetStrategyInputOffset( void )
{
	return m_iFontHeight;
}

//-----------------------------------------------------------------------------
// Purpose: Do respositioning here to avoid latency due to repositioning of vgui
//  voice manager icon panel
//-----------------------------------------------------------------------------
void CHudStrategyText::OnTick( void )
{
#ifndef _XBOX
	// if we are not ingame or not connected, we don't need to tick
	if ( !engine->IsInGame() || !engine->IsConnected() )
	{
		return;
	}

	int i;
	for ( i = 0; i < STRATEGY_INTERFACE_LINES; i++ )
	{
		CBaseHudStrategyLine *line = m_StrategyLines[ i ];
		if ( !line )
			continue;

		if ( !line->IsVisible() )
			continue;

		if ( !line->IsReadyToExpire() )
			continue;

		line->Expire();
	}

	int w, h;

	GetSize( w, h );

	CBaseHudStrategyLine *line = m_StrategyLines[ 0 ];

	if ( line )
	{
		vgui::HFont font = line->GetFont();

		if ( font )
		{
			m_iFontHeight = vgui::surface()->GetFontTall( font ) + 2;
		}
	}

	// Sort chat lines 
	qsort( m_StrategyLines, STRATEGY_INTERFACE_LINES, sizeof( CBaseHudStrategyLine * ), SortLines );

	// Step backward from bottom
	int currentY = h - m_iFontHeight - 1;
	int startY = currentY;
	int ystep = m_iFontHeight;

	currentY -= GetStrategyInputOffset();

	// Walk backward
	for ( i = STRATEGY_INTERFACE_LINES - 1; i >= 0 ; i-- )
	{
		CBaseHudStrategyLine *line = m_StrategyLines[ i ];
		if ( !line )
			continue;

		if ( !line->IsVisible() )
		{
			line->SetSize( w, m_iFontHeight );
			continue;
		}

		line->PerformFadeout();
		line->SetSize( w, m_iFontHeight * line->GetNumLines() );
		line->SetPos( 0, ( currentY+m_iFontHeight) - m_iFontHeight * line->GetNumLines() );
		
		currentY -= ystep * line->GetNumLines();
	}

	if ( currentY != startY )
	{
		m_nVisibleHeight = startY - currentY + 2;
	}
	else
	{
		m_nVisibleHeight = 0;
	}

	vgui::surface()->MovePopupToBack( GetVPanel() );
#endif
}

// Release build is crashing on long strings...sigh
#pragma optimize( "", off )

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : width - 
//			*text - 
//			textlen - 
// Output : int
//-----------------------------------------------------------------------------
int CHudStrategyText::ComputeBreakChar( int width, const char *text, int textlen )
{
#ifndef _XBOX
	CBaseHudStrategyLine *line = m_StrategyLines[ 0 ];
	vgui::HFont font = line->GetFont();

	int currentlen = 0;
	int lastbreak = textlen;
	for (int i = 0; i < textlen ; i++)
	{
		char ch = text[i];

		if ( ch <= 32 )
		{
			lastbreak = i;
		}

		wchar_t wch[2];

		g_pVGuiLocalize->ConvertANSIToUnicode( &ch, wch, sizeof( wch ) );

		int a,b,c;

		vgui::surface()->GetCharABCwide(font, wch[0], a, b, c);
		currentlen += a + b + c;

		if ( currentlen >= width )
		{
			// If we haven't found a whitespace char to break on before getting
			//  to the end, but it's still too long, break on the character just before
			//  this one
			if ( lastbreak == textlen )
			{
				lastbreak = max( 0, i - 1 );
			}
			break;
		}
	}

	if ( currentlen >= width )
	{
		return lastbreak;
	}
	return textlen;
#else
	return 0;
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudStrategyText::MsgFunc_StrategyText( bf_read &msg )
{
	char szString[2048];
	m_iType = msg.ReadByte();
	msg.ReadString( szString, sizeof(szString) );
	Printf( szString );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : CBaseHudStrategyLine
//-----------------------------------------------------------------------------
CBaseHudStrategyLine *CHudStrategyText::FindUnusedChatLine( void )
{
#ifndef _XBOX
	for ( int i = 0; i < STRATEGY_INTERFACE_LINES; i++ )
	{
		CBaseHudStrategyLine *line = m_StrategyLines[ i ];
		if ( !line )
			continue;

		if ( line->IsVisible() )
			continue;

		return line;
	}
	return NULL;
#else
	return NULL;
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudStrategyText::ExpireOldest( void )
{
#ifndef _XBOX
	float oldestTime = 100000000.0f;
	CBaseHudStrategyLine *oldest = NULL;

	for ( int i = 0; i < STRATEGY_INTERFACE_LINES; i++ )
	{
		CBaseHudStrategyLine *line = m_StrategyLines[ i ];
		if ( !line )
			continue;

		if ( !line->IsVisible() )
			continue;

		if ( !oldest )
		{
			oldest = line;
			oldestTime = line->GetStartTime();
			continue;
		}

		if ( line->GetStartTime() < oldestTime )
		{
			oldest = line;
			oldestTime = line->GetStartTime();
		}
	}

	if ( !oldest )
	{
		oldest = m_StrategyLines[ 0  ];
	}

	oldest->Expire(); 
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudStrategyText::Clear( void )
{
#ifndef _XBOX
	// Expire all messages
	for ( int i = 0; i < STRATEGY_INTERFACE_LINES; i++ )
	{
		CBaseHudStrategyLine *line = m_StrategyLines[ i ];
		if ( !line )
			continue;

		if ( !line->IsVisible() )
			continue;

		line->Expire();
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *fmt - 
//			... - 
//-----------------------------------------------------------------------------
void CHudStrategyText::Printf( const char *fmt, ... )
{
#ifndef _XBOX
	va_list marker;
	char msg[4096];

	/*const char *pTranslatedFmt = hudtextmessage->LookupString(fmt);

	va_start(marker, fmt);
	Q_vsnprintf( msg, sizeof( msg ), pTranslatedFmt, marker );
	va_end(marker);*/

	va_start(marker, fmt);
	Q_vsnprintf( msg, sizeof( msg ), fmt, marker );
	va_end(marker);

	

	// Strip any trailing '\n'
	/*if ( strlen( msg ) > 0 && msg[ strlen( msg )-1 ] == '\n' )
	{
		msg[ strlen( msg ) - 1 ] = 0;
	}

	// Strip leading \n characters ( or notify/color signifiers )
	char *pmsg = msg;
	while ( *pmsg && ( *pmsg == '\n' || *pmsg == 1 || *pmsg == 2 ) )
	{
		pmsg++;
	}
	
	if ( !*pmsg )
		return;

	// Search for return characters
	char *pos = strstr( pmsg, "\n" );
	if ( pos )
	{
		char msgcopy[ 8192 ];
		Q_strncpy( msgcopy, pmsg, sizeof( msgcopy ) );

		int offset = pos - pmsg;
		
		// Terminate first part
		msgcopy[ offset ] = 0;
		
		// Print first part
#if defined( CSTRIKE_DLL ) || defined( DOD_DLL ) // reltodo
		Printf( "%s", msgcopy );

		// Print remainder
		Printf( "%s", &msgcopy[ offset ] + 1 );
#else
		Printf( msgcopy );

		// Print remainder
		Printf( &msgcopy[ offset ] + 1 );
#endif
		return;
	}*/

	// localize
	const wchar_t *pText;
	wchar_t textBuf[ 1024 ];
	char textBuf2[ 1024 ];
	char *pmsg;
	pText = g_pVGuiLocalize->Find( msg );
	if ( !pText )
	{
		g_pVGuiLocalize->ConvertANSIToUnicode(msg, textBuf, sizeof( textBuf ) );
		pText = textBuf;
	}
	g_pVGuiLocalize->ConvertUnicodeToANSI(pText, textBuf2, sizeof( textBuf2 ) );
	pmsg = textBuf2;

	CBaseHudStrategyLine *firstline = m_StrategyLines[ 0 ];

	int len = strlen( pmsg );

	// Check for string too long and split into multiple lines 
	// 
	int breakpos = ComputeBreakChar( firstline->GetWide() - 10, pmsg, len );
	if ( breakpos > 0 && breakpos < len )
	{
		char msgcopy[ 8192 ];
		Q_strncpy( msgcopy, pmsg, sizeof( msgcopy ) );

		int offset = breakpos;
		
		char savechar;

		savechar = msgcopy[ offset ];

		// Terminate first part
		msgcopy[ offset ] = 0;
		
		// Print first part
#if defined( CSTRIKE_DLL ) || defined( DOD_DLL ) // reltodo
		Printf( "%s", msgcopy );
#else
		Printf( msgcopy );
#endif

		// Was breakpos a printable char?
		if ( savechar > 32 )
		{
			msgcopy[ offset ] = savechar;

			// Print remainder
#if defined( CSTRIKE_DLL ) || defined( DOD_DLL ) // reltodo
			Printf( "%s", &msgcopy[ offset ] );
#else
			Printf( &msgcopy[ offset ] );
#endif
		}
		else
		{
#if defined( CSTRIKE_DLL ) || defined( DOD_DLL ) // reltodo
			Printf( "%s", &msgcopy[ offset ] + 1 );
#else
			Printf( &msgcopy[ offset ] + 1 );
#endif
		}
		return;
	}

	CBaseHudStrategyLine *line = FindUnusedChatLine();
	if ( !line )
	{
		ExpireOldest();
		line = FindUnusedChatLine();
	}

	if ( !line )
	{
		return;
	}

	line->SetType(m_iType);
	line->SetText( "" );
	line->InsertColorChange( m_textColors[m_iType] );
	line->SetExpireTime();
	line->InsertString( pmsg );
	line->SetVisible( true );
	line->SetNameLength( 0 );
	
	//CLocalPlayerFilter filter;
	//C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "HudChat.Message" );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *newmap - 
//-----------------------------------------------------------------------------
void CHudStrategyText::LevelInit( const char *newmap )
{
	Clear();
}

void CHudStrategyText::LevelShutdown( void )
{
	Clear();
}
