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

#include "cbase.h"
#include "hud.h"
#include "c_team.h"
#include "tf_shareddefs.h"
#include "tf_gamerules.h"
#include "iclientmode.h"
#include "c_playerresource.h"
#include "c_tf_playerresource.h"
#include "tf_hud_target_id.h"
#include "c_baseobject.h"
#include "tf_hud_spectator_extras.h"

#include <vgui/ILocalize.h>
#include <vgui/ISurface.h>

using namespace vgui;

ConVar tf_spec_xray_disable( "tf_spec_xray_disable", "0", FCVAR_ARCHIVE, "Disable the spectator xray mode." );
ConVar tf_enable_glows_after_respawn( "tf_enable_glows_after_respawn", "1", FCVAR_ARCHIVE, "Enable teammate glow effects after respawn." );

DECLARE_HUDELEMENT( CTFHudSpectatorExtras );

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CTFHudSpectatorExtras::CTFHudSpectatorExtras( const char *pszElementName ) : CHudElement( pszElementName ), EditablePanel( NULL, "HudSpectatorExtras" )
{
	vgui::Panel *pParent = g_pClientMode->GetViewport();
	SetParent( pParent );

	SetHiddenBits( 0 );

	vgui::ivgui()->AddTickSignal( GetVPanel(), 100 );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTFHudSpectatorExtras::ShouldDraw( void )
{
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFHudSpectatorExtras::Reset( void )
{
	if ( !g_PR )
		return;

	FOR_EACH_VEC( m_vecEntitiesToDraw, i )
	{
		int nEntIndex = m_vecEntitiesToDraw[i].m_nEntIndex;
		if ( IsPlayerIndex( nEntIndex ) && !g_PR->IsConnected( nEntIndex ) )
			continue;

		CBaseCombatCharacter *pEnt = dynamic_cast< CBaseCombatCharacter* >( cl_entitylist->GetEnt( nEntIndex ) );
		if ( !pEnt )
			continue;

		if ( pEnt->IsClientSideGlowEnabled() )
		{
			pEnt->SetClientSideGlowEnabled( false );
		}
	}

	m_vecEntitiesToDraw.Purge();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFHudSpectatorExtras::RemoveEntity( int nRemove )
{
	FOR_EACH_VEC( m_vecEntitiesToDraw, i )
	{
		if ( m_vecEntitiesToDraw[i].m_nEntIndex == nRemove )
		{
			m_vecEntitiesToDraw.Remove( i );
			return;
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFHudSpectatorExtras::OnTick()
{
	BaseClass::OnTick();

	if ( !g_PR )
		return;

	if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
	{
		Reset();
		return;
	}

	C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
	if ( !pLocalPlayer )
		return;

	int nLocalPlayerTeam = pLocalPlayer->GetTeamNumber();
	bool bIsHLTV = engine->IsHLTV();

	if ( tf_spec_xray_disable.GetBool() || ( !bIsHLTV && ( nLocalPlayerTeam < TEAM_SPECTATOR ) ) )
	{
		Reset();
		return;
	}

	if ( nLocalPlayerTeam >= FIRST_GAME_TEAM )
	{
		if ( pLocalPlayer->IsAlive() && !pLocalPlayer->m_Shared.InCond( TF_COND_TEAM_GLOWS ) )
		{
			if ( m_vecEntitiesToDraw.Count() > 0 )
			{
				Reset();
			}
			return;
		}
	}

	if ( bIsHLTV || 
		( tf_spec_xray.GetBool() && ( ( nLocalPlayerTeam == TEAM_SPECTATOR ) || ( pLocalPlayer->GetObserverMode() > OBS_MODE_FREEZECAM ) || ( pLocalPlayer->m_Shared.InCond( TF_COND_TEAM_GLOWS ) && tf_enable_glows_after_respawn.GetBool() ) ) ) )
	{
		bool bShowEveryone = ( bIsHLTV || 
							   ( ( nLocalPlayerTeam == TEAM_SPECTATOR ) && tf_spec_xray.GetBool() ) ||
							   ( ( nLocalPlayerTeam >= FIRST_GAME_TEAM ) && ( pLocalPlayer->GetObserverMode() > OBS_MODE_FREEZECAM ) && ( tf_spec_xray.GetInt() > 1 ) ) );

		// loop through the players
		for ( int i = 1; i <= gpGlobals->maxClients; i++ )
		{
			if ( !g_PR->IsConnected( i ) )
			{
				RemoveEntity( i );
				continue;
			}

			CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
			if ( !pPlayer || ( pPlayer == pLocalPlayer ) )
			{
				RemoveEntity( i );
				continue;
			}

			int nPlayerTeamNumber = pPlayer->GetTeamNumber();
			
			// remove the entities we don't want to draw anymore
			if ( pPlayer->IsDormant() ||
				( nPlayerTeamNumber < FIRST_GAME_TEAM ) ||
				( !pPlayer->IsAlive() ) ||
				( pPlayer->m_Shared.IsStealthed() && ( nLocalPlayerTeam >= FIRST_GAME_TEAM ) && ( nPlayerTeamNumber != nLocalPlayerTeam ) ) ||
				( !bShowEveryone && !pPlayer->IsPlayerClass( TF_CLASS_SPY ) && ( nPlayerTeamNumber != nLocalPlayerTeam ) ) ||
				( !bShowEveryone && pPlayer->IsPlayerClass( TF_CLASS_SPY ) && !pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && ( nPlayerTeamNumber != nLocalPlayerTeam ) ) ||
				( !bShowEveryone && pPlayer->IsPlayerClass( TF_CLASS_SPY ) && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && ( nPlayerTeamNumber != nLocalPlayerTeam ) && ( pPlayer->m_Shared.GetDisguiseTeam() != nLocalPlayerTeam ) ) )
			{
				if ( pPlayer->IsClientSideGlowEnabled() )
				{
					pPlayer->SetClientSideGlowEnabled( false );
				}
				RemoveEntity( i );
				continue;
			}

			// passed all of the tests, so make sure they're in the list
			int nVecIndex = -1;
			FOR_EACH_VEC( m_vecEntitiesToDraw, nTemp )
			{
				if ( m_vecEntitiesToDraw[nTemp].m_nEntIndex == i )
				{
					nVecIndex = nTemp;
					break;
				}
			}
			if ( nVecIndex == -1 )
			{
				nVecIndex = m_vecEntitiesToDraw.AddToTail();
			}

			// set the player index
			m_vecEntitiesToDraw[nVecIndex].m_nEntIndex = i;

			// don't draw their name if we're currently spectating them, but we still want them to glow
			m_vecEntitiesToDraw[nVecIndex].m_bDrawName = true;
			if ( !pLocalPlayer->IsAlive() )
			{
				CSpectatorTargetID *pSpecTargetID = (CSpectatorTargetID *)GET_HUDELEMENT( CSpectatorTargetID );
				if ( ( pLocalPlayer->GetObserverTarget() == pPlayer ) || ( pSpecTargetID && pSpecTargetID->GetTargetIndex() == i ) )
				{
					m_vecEntitiesToDraw[nVecIndex].m_bDrawName = false;

					// if we're in chase mode, just remove them entirely
					if ( pLocalPlayer->GetObserverMode() == OBS_MODE_CHASE )
					{
						if ( pPlayer->IsClientSideGlowEnabled() )
						{
							pPlayer->SetClientSideGlowEnabled( false );
						}
						RemoveEntity( i );
						continue;
					}
				}
			}

			// disguised Spy?
			C_TFPlayer *pDisguiseTarget = NULL;
			if ( !bIsHLTV && ( nLocalPlayerTeam >= FIRST_GAME_TEAM ) )
			{
				if ( pPlayer->IsPlayerClass( TF_CLASS_SPY ) && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && ( nPlayerTeamNumber != nLocalPlayerTeam ) )
				{
					pDisguiseTarget = ToTFPlayer( pPlayer->m_Shared.GetDisguiseTarget() );
				}
			}

			// use actual name or disguised name?
			int nNameIndex = pDisguiseTarget ? pDisguiseTarget->entindex() : i;
			g_pVGuiLocalize->ConvertANSIToUnicode( g_PR->GetPlayerName( nNameIndex ), m_vecEntitiesToDraw[nVecIndex].m_wszName, sizeof( m_vecEntitiesToDraw[nVecIndex].m_wszName ) );
			m_vecEntitiesToDraw[nVecIndex].m_nNameWidth = UTIL_ComputeStringWidth( m_hNameFont, m_vecEntitiesToDraw[nVecIndex].m_wszName );

			m_vecEntitiesToDraw[nVecIndex].m_nOffset = ( VEC_HULL_MAX_SCALED( pPlayer ).z );

			// use actual health or disguised health?
			float flHealth = 1.0f;

			if ( pDisguiseTarget )
			{
				flHealth = (float)( pPlayer->m_Shared.GetDisguiseHealth() ) / (float)( pPlayer->m_Shared.GetDisguiseMaxHealth() );
			}
			else
			{
				flHealth = (float)( pPlayer->GetHealth() ) / (float)( pPlayer->GetMaxHealth() );
			}
			// don't show buffed health for this simple bar
			if ( flHealth > 1.0f )
			{
				flHealth = 1.0f;
			}
			m_vecEntitiesToDraw[nVecIndex].m_flHealth = flHealth;

 			// what color should we use?
			float r, g, b;
			pPlayer->GetGlowEffectColor( &r, &g, &b );
			m_vecEntitiesToDraw[nVecIndex].m_clrGlowColor = Color( r * 255, g * 255, b * 255, 255 );

			if ( !pPlayer->IsClientSideGlowEnabled() )
			{
				pPlayer->SetClientSideGlowEnabled( true );
			}
		}

		// loop through the buildings
		for ( int nCount = 0; nCount < IBaseObjectAutoList::AutoList().Count(); nCount++ )
		{
			bool bDraw = false;
			C_BaseObject *pObject = static_cast<C_BaseObject*>( IBaseObjectAutoList::AutoList()[nCount] );
			if ( !pObject->IsDormant() && !pObject->IsMapPlaced() && !pObject->IsEffectActive( EF_NODRAW ) )
			{
				if ( bShowEveryone || ( ( nLocalPlayerTeam >= FIRST_GAME_TEAM ) && ( nLocalPlayerTeam == pObject->GetTeamNumber() ) ) )
				{
					bDraw = true;
				}
			}

			if ( bDraw )
			{
				int nVecIndex = -1;
				FOR_EACH_VEC( m_vecEntitiesToDraw, nTemp )
				{
					if ( m_vecEntitiesToDraw[nTemp].m_nEntIndex == pObject->entindex() )
					{
						nVecIndex = nTemp;
						break;
					}
				}
				if ( nVecIndex == -1 )
				{
					nVecIndex = m_vecEntitiesToDraw.AddToTail();
				}

				// set the player index
				m_vecEntitiesToDraw[nVecIndex].m_nEntIndex = pObject->entindex();

				// don't draw the name if we're currently spectating this building, but we still want it to glow
				m_vecEntitiesToDraw[nVecIndex].m_bDrawName = true;
				if ( pLocalPlayer->GetObserverTarget() == pObject )
				{
					m_vecEntitiesToDraw[nVecIndex].m_bDrawName = false;
				}

				if ( pObject->GetType() == OBJ_TELEPORTER )
				{
					m_vecEntitiesToDraw[nVecIndex].m_nOffset = 30;
				}
				else if ( pObject->GetType() == OBJ_DISPENSER )
				{
					m_vecEntitiesToDraw[nVecIndex].m_nOffset = 70;
				}
				else
				{
					switch ( pObject->GetUpgradeLevel() )
					{
					case 1:
						m_vecEntitiesToDraw[nVecIndex].m_nOffset = 50;
						break;
					case 2:
						m_vecEntitiesToDraw[nVecIndex].m_nOffset = 65;
						break;
					case 3:
					default:
						m_vecEntitiesToDraw[nVecIndex].m_nOffset = 80;
						break;
					}
				}

				pObject->GetTargetIDString( m_vecEntitiesToDraw[nVecIndex].m_wszName, sizeof( m_vecEntitiesToDraw[nVecIndex].m_wszName ), true );
				m_vecEntitiesToDraw[nVecIndex].m_nNameWidth = UTIL_ComputeStringWidth( m_hNameFont, m_vecEntitiesToDraw[nVecIndex].m_wszName );

				float flHealth = 1.0f;
				flHealth = (float)( pObject->GetHealth() ) / (float)( pObject->GetMaxHealth() );
				if ( flHealth > 1.0f )
				{
					flHealth = 1.0f;
				}
				m_vecEntitiesToDraw[nVecIndex].m_flHealth = flHealth;

				// what color should we use?
				float r, g, b;
				pObject->GetGlowEffectColor( &r, &g, &b );
				m_vecEntitiesToDraw[nVecIndex].m_clrGlowColor = Color( r * 255, g * 255, b * 255, 255 );

				if ( !pObject->IsClientSideGlowEnabled() )
				{
					pObject->SetClientSideGlowEnabled( true );
				}
			}
			else
			{
				if ( pObject->IsClientSideGlowEnabled() )
				{
					pObject->SetClientSideGlowEnabled( false );
				}
				RemoveEntity( pObject->entindex() );
			}
		}
	}
	else
	{
		Reset();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFHudSpectatorExtras::Paint()
{
	BaseClass::Paint();

	if ( !g_PR )
		return;

	if ( tf_spec_xray_disable.GetBool() )
		return;

	int nNameOffset = 35;
	int nHealthWidth = 70;
	int nHealthHeight = 6;

	FOR_EACH_VEC( m_vecEntitiesToDraw, i )
	{
		if ( !m_vecEntitiesToDraw[i].m_bDrawName )
			continue;

		int nEntIndex = m_vecEntitiesToDraw[i].m_nEntIndex;
		if ( IsPlayerIndex( nEntIndex ) && !g_PR->IsConnected( nEntIndex ) )
			continue;

		C_BaseEntity *pEnt = cl_entitylist->GetEnt( nEntIndex );
		if ( !pEnt )
			continue;

		Vector vecPos = pEnt->GetAbsOrigin();
		vecPos.z += m_vecEntitiesToDraw[i].m_nOffset;

		int iX, iY;
		Vector vecWorld( vecPos.x, vecPos.y, vecPos.z );
		if ( GetVectorInHudSpace( vecWorld, iX, iY ) )
		{
 			// draw the name
 			vgui::surface()->DrawSetTextFont( m_hNameFont );
			vgui::surface()->DrawSetTextPos( iX - ( m_vecEntitiesToDraw[i].m_nNameWidth / 2 ), iY - nNameOffset );
			vgui::surface()->DrawSetTextColor( m_vecEntitiesToDraw[i].m_clrGlowColor );
			vgui::surface()->DrawPrintText( m_vecEntitiesToDraw[i].m_wszName, wcslen( m_vecEntitiesToDraw[i].m_wszName ), vgui::FONT_DRAW_NONADDITIVE );

			int xHealthPos = iX - 35;
			int yHealthPos = iY - 10;

			// draw the health bar background
			vgui::surface()->DrawSetColor( Color( 127, 127, 127, 255 ) );
			vgui::surface()->DrawFilledRect( xHealthPos, yHealthPos, xHealthPos + nHealthWidth, yHealthPos + nHealthHeight );

			// draw the health bar
			vgui::surface()->DrawSetColor( m_vecEntitiesToDraw[i].m_clrGlowColor );
			vgui::surface()->DrawFilledRect( xHealthPos, yHealthPos, xHealthPos + ( nHealthWidth * m_vecEntitiesToDraw[i].m_flHealth ), yHealthPos + nHealthHeight );
		}
	}
}