//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Item pickup history displayed onscreen when items are picked up.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "history_resource.h"
#include "hud_macros.h"
#include <vgui_controls/Controls.h>
#include <vgui/ILocalize.h>
#include <vgui/ISurface.h>
#include "iclientmode.h"
#include "vgui_controls/AnimationController.h"

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

using namespace vgui;

extern ConVar hud_drawhistory_time;

DECLARE_HUDELEMENT( CHudHistoryResource );
DECLARE_HUD_MESSAGE( CHudHistoryResource, ItemPickup );
DECLARE_HUD_MESSAGE( CHudHistoryResource, AmmoDenied );

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CHudHistoryResource::CHudHistoryResource( const char *pElementName ) :
	CHudElement( pElementName ), BaseClass( NULL, "HudHistoryResource" )
{	
	vgui::Panel *pParent = g_pClientMode->GetViewport();
	SetParent( pParent );
	m_bDoNotDraw = true;
	m_wcsAmmoFullMsg[0] = 0;
	m_bNeedsDraw = false;
	SetHiddenBits( HIDEHUD_MISCSTATUS );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudHistoryResource::ApplySchemeSettings( IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );
	SetPaintBackgroundEnabled( false );

	// lookup text to display for ammo full message
	wchar_t *wcs = g_pVGuiLocalize->Find("#hl2_AmmoFull");
	if (wcs)
	{
		wcsncpy(m_wcsAmmoFullMsg, wcs, sizeof(m_wcsAmmoFullMsg) / sizeof(wchar_t));
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudHistoryResource::Init( void )
{
	HOOK_HUD_MESSAGE( CHudHistoryResource, ItemPickup );
	HOOK_HUD_MESSAGE( CHudHistoryResource, AmmoDenied );

	Reset();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudHistoryResource::Reset( void )
{
	m_PickupHistory.RemoveAll();
	m_iCurrentHistorySlot = 0;
	m_bDoNotDraw = true;
}

//-----------------------------------------------------------------------------
// Purpose: these kept only for hl1-port compatibility
//-----------------------------------------------------------------------------
void CHudHistoryResource::SetHistoryGap( int iNewHistoryGap )
{
}

//-----------------------------------------------------------------------------
// Purpose: adds an element to the history
//-----------------------------------------------------------------------------
void CHudHistoryResource::AddToHistory( C_BaseCombatWeapon *weapon )
{
	// don't draw exhaustable weapons (grenades) since they'll have an ammo pickup icon as well
 	if ( weapon->GetWpnData().iFlags & ITEM_FLAG_EXHAUSTIBLE )
 		return;

	int iId = weapon->entindex();

	// don't show the same weapon twice
	for ( int i = 0; i < m_PickupHistory.Count(); i++ )
	{
		if ( m_PickupHistory[i].iId == iId )
		{
			// it's already in list
			return;
		}
	}
	
	AddIconToHistory( HISTSLOT_WEAP, iId, weapon, 0, NULL );
}

//-----------------------------------------------------------------------------
// Purpose: Add a new entry to the pickup history
//-----------------------------------------------------------------------------
void CHudHistoryResource::AddToHistory( int iType, int iId, int iCount )
{
	// Ignore adds with no count
	if ( iType == HISTSLOT_AMMO )
	{
		if ( !iCount )
			return;

#if defined( CSTRIKE_DLL )
		// don't leave blank gaps for ammo we're not going to display
		const FileWeaponInfo_t *pWpnInfo = gWR.GetWeaponFromAmmo( iId );
		if ( pWpnInfo && ( pWpnInfo->iMaxClip1 >= 0 || pWpnInfo->iMaxClip2 >= 0 ) )
		{
			if ( !pWpnInfo->iconSmall )
				return;
		}
#endif

		// clear out any ammo pickup denied icons, since we can obviously pickup again
		for ( int i = 0; i < m_PickupHistory.Count(); i++ )
		{
			if ( m_PickupHistory[i].type == HISTSLOT_AMMODENIED && m_PickupHistory[i].iId == iId )
			{
				// kill the old entry
				m_PickupHistory[i].DisplayTime = 0.0f;
				// change the pickup to be in this entry
				m_iCurrentHistorySlot = i;
				break;
			}
		}
	}

	AddIconToHistory( iType, iId, NULL, iCount, NULL );
}

//-----------------------------------------------------------------------------
// Purpose: Add a new entry to the pickup history
//-----------------------------------------------------------------------------
void CHudHistoryResource::AddToHistory( int iType, const char *szName, int iCount )
{
	if ( iType != HISTSLOT_ITEM )
		return;

	// Get the item's icon
	CHudTexture *i = gHUD.GetIcon( szName );
	if ( i == NULL )
		return;  

	AddIconToHistory( iType, 1, NULL, iCount, i );
}

//-----------------------------------------------------------------------------
// Purpose: adds a history icon
//-----------------------------------------------------------------------------
void CHudHistoryResource::AddIconToHistory( int iType, int iId, C_BaseCombatWeapon *weapon, int iCount, CHudTexture *icon )
{
	m_bNeedsDraw = true;

	// Check to see if the pic would have to be drawn too high. If so, start again from the bottom
	if ( (m_flHistoryGap * (m_iCurrentHistorySlot+1)) > GetTall() )
	{
		m_iCurrentHistorySlot = 0;
	}

	// If the history resource is appearing, slide the hint message element down
	if ( m_iCurrentHistorySlot == 0 )
	{
		g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "HintMessageLower" ); 
	}

	// ensure the size 
	m_PickupHistory.EnsureCount(m_iCurrentHistorySlot + 1);

	// default to just writing to the first slot
	HIST_ITEM *freeslot = &m_PickupHistory[m_iCurrentHistorySlot];

	if ( iType == HISTSLOT_AMMODENIED && freeslot->DisplayTime )
	{
		// don't override existing pickup icons with denied icons
		return;
	}

	freeslot->iId = iId;
	freeslot->icon = icon;
	freeslot->type = iType;
	freeslot->m_hWeapon  = weapon;
	freeslot->iCount = iCount;

	if (iType == HISTSLOT_AMMODENIED)
	{
		freeslot->DisplayTime = gpGlobals->curtime + (hud_drawhistory_time.GetFloat() / 2.0f);
	}
	else
	{
		freeslot->DisplayTime = gpGlobals->curtime + hud_drawhistory_time.GetFloat();
	}

	++m_iCurrentHistorySlot;
}


//-----------------------------------------------------------------------------
// Purpose: Handle an item pickup event from the server
//-----------------------------------------------------------------------------
void CHudHistoryResource::MsgFunc_ItemPickup( bf_read &msg )
{
	char szName[1024];
	
	msg.ReadString( szName, sizeof(szName) );

	// Add the item to the history
	AddToHistory( HISTSLOT_ITEM, szName );
}

//-----------------------------------------------------------------------------
// Purpose: ammo denied message
//-----------------------------------------------------------------------------
void CHudHistoryResource::MsgFunc_AmmoDenied( bf_read &msg )
{
	int iAmmo = msg.ReadShort();

	// see if there are any existing ammo items of that type
	for ( int i = 0; i < m_PickupHistory.Count(); i++ )
	{
		if ( m_PickupHistory[i].type == HISTSLOT_AMMO && m_PickupHistory[i].iId == iAmmo )
		{
			// it's already in the list as a pickup, ignore
			return;
		}
	}

	// see if there are any denied ammo icons, if so refresh their timer
	for ( int i = 0; i < m_PickupHistory.Count(); i++ )
	{
		if ( m_PickupHistory[i].type == HISTSLOT_AMMODENIED && m_PickupHistory[i].iId == iAmmo )
		{
			// it's already in the list, refresh
			m_PickupHistory[i].DisplayTime = gpGlobals->curtime + (hud_drawhistory_time.GetFloat() / 2.0f);
			m_bNeedsDraw = true;
			return;
		}
	}

	// add into the list
	AddToHistory( HISTSLOT_AMMODENIED, iAmmo, 0 );
}

//-----------------------------------------------------------------------------
// Purpose: If there aren't any items in the history, clear it out.
//-----------------------------------------------------------------------------
void CHudHistoryResource::CheckClearHistory( void )
{
	for ( int i = 0; i < m_PickupHistory.Count(); i++ )
	{
		if ( m_PickupHistory[i].type )
			return;
	}

	m_iCurrentHistorySlot = 0;

	// Slide the hint message element back up
	g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "HintMessageRaise" ); 
}

//-----------------------------------------------------------------------------
// 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 CHudHistoryResource::ShouldDraw( void )
{
#ifdef TF_CLIENT_DLL
	return false;
#else
	return ( ( m_iCurrentHistorySlot > 0 || m_bNeedsDraw ) && CHudElement::ShouldDraw() );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Draw the pickup history
//-----------------------------------------------------------------------------
void CHudHistoryResource::Paint( void )
{
	if ( m_bDoNotDraw )
	{
		// this is to not draw things until the first rendered
		m_bDoNotDraw = false;
		return;
	}

	// set when drawing should occur
	// will be set if valid drawing does occur
	m_bNeedsDraw = false;

	int wide, tall;
	GetSize( wide, tall );

	for ( int i = 0; i < m_PickupHistory.Count(); i++ )
	{
		if ( m_PickupHistory[i].type )
		{
			m_PickupHistory[i].DisplayTime = MIN( m_PickupHistory[i].DisplayTime, gpGlobals->curtime + hud_drawhistory_time.GetFloat() );
			if ( m_PickupHistory[i].DisplayTime <= gpGlobals->curtime )
			{  
				// pic drawing time has expired
				memset( &m_PickupHistory[i], 0, sizeof(HIST_ITEM) );
				CheckClearHistory();
				continue;
			}

			float elapsed = m_PickupHistory[i].DisplayTime - gpGlobals->curtime;
			float scale = elapsed * 80;
			Color clr = gHUD.m_clrNormal;
			clr[3] = MIN( scale, 255 );

			bool bUseAmmoFullMsg = false;

			// get the icon and number to draw
			const CHudTexture *itemIcon = NULL;
			const CHudTexture *itemAmmoIcon = NULL;
			int iAmount = 0;
			bool bHalfHeight = true;

			switch ( m_PickupHistory[i].type )
			{
			case HISTSLOT_AMMO:
				{
					// Get the weapon we belong to
#ifndef HL2MP
					const FileWeaponInfo_t *pWpnInfo = gWR.GetWeaponFromAmmo( m_PickupHistory[i].iId );
					if ( pWpnInfo && ( pWpnInfo->iMaxClip1 >= 0 || pWpnInfo->iMaxClip2 >= 0 ) )
					{
						// The weapon will be the main icon, and the ammo the smaller
						itemIcon = pWpnInfo->iconSmall;
						itemAmmoIcon = gWR.GetAmmoIconFromWeapon( m_PickupHistory[i].iId );
					}
					else
#endif // HL2MP
					{
						itemIcon = gWR.GetAmmoIconFromWeapon( m_PickupHistory[i].iId );
						itemAmmoIcon = NULL;
					}

#ifdef CSTRIKE_DLL
					// show grenades as the weapon icon
					if ( pWpnInfo && pWpnInfo->iFlags & ITEM_FLAG_EXHAUSTIBLE )	
					{
						itemIcon = pWpnInfo->iconActive;
						itemAmmoIcon = NULL;
						bHalfHeight = false;
					}
#endif

					iAmount = m_PickupHistory[i].iCount;
				}
				break;
			case HISTSLOT_AMMODENIED:
				{
					itemIcon = gWR.GetAmmoIconFromWeapon( m_PickupHistory[i].iId );
					iAmount = 0;
					bUseAmmoFullMsg = true;
					// display as red
					clr = gHUD.m_clrCaution;	
					clr[3] = MIN( scale, 255 );
				}
				break;

			case HISTSLOT_WEAP:
				{
					C_BaseCombatWeapon *pWeapon = m_PickupHistory[i].m_hWeapon;
					if ( !pWeapon )
						return;

					if ( !pWeapon->HasAmmo() )
					{
						// if the weapon doesn't have ammo, display it as red
						clr = gHUD.m_clrCaution;	
						clr[3] = MIN( scale, 255 );
					}

					itemIcon = pWeapon->GetSpriteInactive();
					bHalfHeight = false;
				}
				break;
			case HISTSLOT_ITEM:
				{
					if ( !m_PickupHistory[i].iId )
						continue;

					itemIcon = m_PickupHistory[i].icon;
					bHalfHeight = false;
				}
				break;
			default:
				// unknown history type
				Assert( 0 );
				break;
			}

			if ( !itemIcon )
				continue;

			if ( clr[3] )
			{
				// valid drawing will occur
				m_bNeedsDraw = true;
			}

			int ypos = tall - (m_flHistoryGap * (i + 1));
			int xpos = wide - itemIcon->Width() - m_flIconInset;

#ifndef HL2MP
			// Adjust for a half-height icon
			if ( bHalfHeight )
			{
				ypos += itemIcon->Height() / 2;
			}
#endif // HL2MP

			itemIcon->DrawSelf( xpos, ypos, clr );

			if ( itemAmmoIcon )
			{
				itemAmmoIcon->DrawSelf( xpos - ( itemAmmoIcon->Width() * 1.25f ), ypos, clr );
			}

			if ( iAmount )
			{
				wchar_t text[16];
				_snwprintf( text, sizeof( text ) / sizeof(wchar_t), L"%i", m_PickupHistory[i].iCount );

				// offset the number to sit properly next to the icon
				ypos -= ( surface()->GetFontTall( m_hNumberFont ) - itemIcon->Height() ) / 2;

				vgui::surface()->DrawSetTextFont( m_hNumberFont );
				vgui::surface()->DrawSetTextColor( clr );
				vgui::surface()->DrawSetTextPos( wide - m_flTextInset, ypos );
				vgui::surface()->DrawUnicodeString( text );
			}
			else if ( bUseAmmoFullMsg )
			{
				// offset the number to sit properly next to the icon
				ypos -= ( surface()->GetFontTall( m_hTextFont ) - itemIcon->Height() ) / 2;

				vgui::surface()->DrawSetTextFont( m_hTextFont );
				vgui::surface()->DrawSetTextColor( clr );
				vgui::surface()->DrawSetTextPos( wide - m_flTextInset, ypos );
				vgui::surface()->DrawUnicodeString( m_wcsAmmoFullMsg );
			}
		}
	}
}