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

#include "cbase.h"
#include <vgui/ISurface.h>
#include "clientmode_csnormal.h"
#include "cs_gamerules.h"
#include "hud_numericdisplay.h"
#include "voice_status.h"
#include "c_plantedc4.h"
#include "weapon_c4.h"
#include "c_cs_hostage.h"
#include "c_cs_playerresource.h"
#include <coordsize.h>
#include "hud_macros.h"
#include "vgui/IVGui.h"
#include "vgui/ILocalize.h"
#include "mapoverview.h"
#include "cstrikespectatorgui.h"
#include "hud_radar.h"

#define RADAR_DOT_NORMAL		0
#define RADAR_DOT_BOMB			(1<<0)
#define RADAR_DOT_HOSTAGE		(1<<1)
#define RADAR_DOT_BOMBCARRIER	(1<<2)
#define RADAR_DOT_VIP			(1<<3)
#define RADAR_DOT_LARGE_FLASH	(1<<4)
#define RADAR_DOT_BOMB_PLANTED	(1<<5)
#define RADAR_IGNORE_Z			(1<<6)	//always draw this item as if it was at the same Z as the player

extern CUtlVector< CC4* > g_C4s;

ConVar cl_radartype( "cl_radartype", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
ConVar cl_radaralpha( "cl_radaralpha", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, NULL, true, 0, true, 255 );
ConVar cl_locationalpha( "cl_locationalpha", "150", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, NULL, true, 0, true, 255 );

DECLARE_HUDELEMENT( CHudRadar );
DECLARE_HUD_MESSAGE( CHudRadar, UpdateRadar );

static CHudRadar *s_Radar = NULL;
CUtlVector<CPlayerRadarFlash> g_RadarFlashes;


CHudRadar::CHudRadar( const char *pName ) :	vgui::Panel( NULL, "HudRadar" ), CHudElement( pName )
{
	SetParent( g_pClientMode->GetViewport() );

	m_pBackground = NULL;
	m_pBackgroundTrans = NULL;

	m_flNextBombFlashTime = 0.0;
	m_bBombFlash = true;

	m_flNextHostageFlashTime = 0.0;
	m_bHostageFlash = true;
	m_bHideRadar = false;

	s_Radar = this;

	SetHiddenBits( HIDEHUD_PLAYERDEAD );

}


CHudRadar::~CHudRadar()
{
	s_Radar = NULL;
}


void CHudRadar::Init()
{
	HOOK_HUD_MESSAGE( CHudRadar, UpdateRadar );
}

void CHudRadar::LevelInit()
{
	m_flNextBombFlashTime = 0.0;
	m_bBombFlash = true;

	m_flNextHostageFlashTime = 0.0;
	m_bHostageFlash = true;

	g_RadarFlashes.RemoveAll();

	// Map Overview handles radar duties now.
	if( g_pMapOverview )
		g_pMapOverview->SetMode(CCSMapOverview::MAP_MODE_RADAR);
}

void CHudRadar::Reset()
{
	CHudElement::Reset();

	if( g_pMapOverview )
		g_pMapOverview->SetMode(CCSMapOverview::MAP_MODE_RADAR);
}

void CHudRadar::MsgFunc_UpdateRadar(bf_read &msg )
{
	int iPlayerEntity = msg.ReadByte();

	//Draw objects on the radar
	//=============================
	C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();

	if ( !pLocalPlayer )
		return;

	C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources();

	if ( !pCSPR )
		return;

	//Players
	for(int i=1;i<=MAX_PLAYERS;i++)
	{
		if( i == pLocalPlayer->entindex() )
			continue;

		C_CSPlayer *pPlayer =	ToCSPlayer( UTIL_PlayerByIndex(i) );

		if ( pPlayer )
		{
			pPlayer->m_bDetected = false;
		}
	}


	while ( iPlayerEntity > 0 )
	{
		int x = msg.ReadSBitLong( COORD_INTEGER_BITS-1 ) * 4;
		int y = msg.ReadSBitLong( COORD_INTEGER_BITS-1 ) * 4;
		int z = msg.ReadSBitLong( COORD_INTEGER_BITS-1 ) * 4;
		int a = msg.ReadSBitLong( 9 );

		C_CSPlayer *pPlayer =	ToCSPlayer( UTIL_PlayerByIndex(iPlayerEntity) );

		Vector origin( x, y, z );
		QAngle angles( 0, a, 0 );

		if ( g_pMapOverview )
		{
			g_pMapOverview->SetPlayerPositions( iPlayerEntity-1, origin, angles );
		}

		iPlayerEntity = msg.ReadByte(); // read index for next player

		if ( !pPlayer )
			continue;

		bool bOppositeTeams = (pLocalPlayer->GetTeamNumber() != TEAM_UNASSIGNED && pCSPR->GetTeam( pPlayer->entindex() ) != pLocalPlayer->GetTeamNumber());		

		//=============================================================================
		// HPE_BEGIN:
		// [tj] This used to do slightly different logic that caused other players
		//		to twitch while you were observing.
		//=============================================================================
		// Don't update players if they are in PVS.
		if (!pPlayer->IsDormant())
		{
			continue;
		}

		//Don't update players if you are sill alive and they are an enemy.
		if (bOppositeTeams && !pLocalPlayer->IsObserver())
		{
			continue;
		}
		//=============================================================================
		// HPE_END
		//=============================================================================
		// update origin and angle for players out of my PVS
		origin = pPlayer->GetAbsOrigin();
		angles = pPlayer->GetAbsAngles();

		origin.x = x;
		origin.y = y;
		angles.y = a;

		pPlayer->SetAbsOrigin( origin );
		pPlayer->SetAbsAngles( angles );
		pPlayer->m_bDetected = true;
	}
}


bool CHudRadar::ShouldDraw()
{
	C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
	
	//=============================================================================
	// HPE_BEGIN:
	// [tj] Added base class call
	//=============================================================================
	return pPlayer && pPlayer->IsAlive() && !m_bHideRadar && CHudElement::ShouldDraw();
	//=============================================================================
	// HPE_END
	//=============================================================================
}

void CHudRadar::SetVisible(bool state)
{
	BaseClass::SetVisible(state);

	if( g_pMapOverview  &&  g_pMapOverview->GetMode() == CCSMapOverview::MAP_MODE_RADAR )
	{
		// We are the hud element still, but he is in charge of the new style now.
		g_pMapOverview->SetVisible( state );		
	}
}

void CHudRadar::Paint()
{
	// We are the hud element still, but Overview is in charge of the new style now.
	return;
}

void CHudRadar::WorldToRadar( const Vector location, const Vector origin, const QAngle angles, float &x, float &y, float &z_delta )
{
	float x_diff = location.x - origin.x;
	float y_diff = location.y - origin.y;
 
	int iRadarRadius = GetWide();		//width of the panel, it resizes now!
	float fRange = 16 * iRadarRadius;	// radar's range

	float flOffset = atan(y_diff/x_diff);
	flOffset *= 180;
	flOffset /= M_PI;

	if ((x_diff < 0) && (y_diff >= 0))
		flOffset = 180 + flOffset;
	else if ((x_diff < 0) && (y_diff < 0))
		flOffset = 180 + flOffset;
	else if ((x_diff >= 0) && (y_diff < 0))
		flOffset = 360 + flOffset;

	y_diff = -1*(sqrt((x_diff)*(x_diff) + (y_diff)*(y_diff)));
	x_diff = 0;

	flOffset = angles.y - flOffset;

	flOffset *= M_PI;
	flOffset /= 180;		// now theta is in radians

	float xnew_diff = x_diff * cos(flOffset) - y_diff * sin(flOffset);
	float ynew_diff = x_diff * sin(flOffset) + y_diff * cos(flOffset);

	// The dot is out of the radar's range.. Scale it back so that it appears on the border
	if ( (-1 * y_diff) > fRange )
	{
		float flScale;

		flScale = ( -1 * y_diff) / fRange;

		xnew_diff /= flScale;
		ynew_diff /= flScale;
	}
	xnew_diff /= 32;
	ynew_diff /= 32;

	//output
	x = (iRadarRadius/2) + (int)xnew_diff;
	y = (iRadarRadius/2) + (int)ynew_diff;
	z_delta = location.z - origin.z;
}

void CHudRadar::DrawPlayerOnRadar( int iPlayer, C_CSPlayer *pLocalPlayer )
{
	float x, y, z_delta;
	int iBaseDotSize = ScreenWidth() / 256;	
	int r, g, b, a = 235;

	C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources();

	if ( !pCSPR )
		return;

	C_CSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iPlayer ) );

	if ( !pPlayer )
		return;

	bool bOppositeTeams = (pLocalPlayer->GetTeamNumber() != TEAM_UNASSIGNED && pCSPR->GetTeam( iPlayer ) != pLocalPlayer->GetTeamNumber());

	if ( bOppositeTeams && pPlayer->m_bDetected == false )
		return;

	
	WorldToRadar( pPlayer->GetAbsOrigin(), pLocalPlayer->GetAbsOrigin(), pLocalPlayer->LocalEyeAngles(), x, y, z_delta );

	if( pCSPR->HasC4( iPlayer ) || pCSPR->IsVIP( iPlayer ) || bOppositeTeams )
	{
		r = 250; g = 0; b = 0;
	}
	else if ( 0 /*m_bTrackArray[i-1] == true */ )		// Tracked players (friends we want to keep track of on the radar)
	{
		iBaseDotSize *= 2;
		r = 185; g = 20; b = 20;
	}
	else
	{
		r = 75; g = 75; b = 250;
	}

	// Handle the radio flashes
	bool bRadarFlash = false;
	if ( g_RadarFlashes.Count() > iPlayer )
		bRadarFlash = g_RadarFlashes[iPlayer].m_bRadarFlash && g_RadarFlashes[iPlayer].m_iNumRadarFlashes > 0;

	if ( bRadarFlash || GetClientVoiceMgr()->IsPlayerSpeaking( iPlayer ) )
	{
		r = 230; g = 110; b = 25; a = 245; 

		DrawRadarDot( x, y, z_delta, iBaseDotSize, RADAR_DOT_LARGE_FLASH, r, g, b, a );
	}
	else
	{
		DrawRadarDot( x, y, z_delta, iBaseDotSize, RADAR_DOT_NORMAL, r, g, b, a );
	}
}


void CHudRadar::DrawEntityOnRadar( CBaseEntity *pEnt, C_CSPlayer *pLocalPlayer, int flags, int r, int g, int b, int a )
{
	float x, y, z_delta;
	int iBaseDotSize = 4;

	WorldToRadar( pEnt->GetAbsOrigin(), pLocalPlayer->GetAbsOrigin(), pLocalPlayer->LocalEyeAngles(), x, y, z_delta );

	if( flags & RADAR_IGNORE_Z )
		z_delta = 0;

	DrawRadarDot( x, y, z_delta, iBaseDotSize, flags, r, g, b, a );
}

void CHudRadar::FillRect( int x, int y, int w, int h )
{
	int panel_x, panel_y, panel_w, panel_h;
	GetBounds( panel_x, panel_y, panel_w, panel_h );
	vgui::surface()->DrawFilledRect( x, y, x+w, y+h );
}

void CHudRadar::DrawRadarDot( int x, int y, float z_diff, int iBaseDotSize, int flags, int r, int g, int b, int a )
{
	vgui::surface()->DrawSetColor( r, g, b, a );

	if ( flags & RADAR_DOT_LARGE_FLASH )
	{
		FillRect( x-1, y-1, iBaseDotSize+1, iBaseDotSize+1 );
	}
	else if ( z_diff < -128 ) // below the player
	{
		z_diff *= -1;

		if ( z_diff > 3096 )
		{
			z_diff = 3096;
		}

		int iBar = (int)( z_diff / 400 ) + 2;

		// Draw an upside-down T shape to symbolize the dot is below the player.

		iBaseDotSize /= 2;

		//horiz
		FillRect( x-(2*iBaseDotSize), y, 5*iBaseDotSize, iBaseDotSize );

		//vert
		FillRect( x, y - iBar*iBaseDotSize, iBaseDotSize, iBar*iBaseDotSize );
	}
	else if ( z_diff > 128 ) // above the player
	{
		if ( z_diff > 3096 )
		{
			z_diff = 3096;
		}

		int iBar = (int)( z_diff / 400 ) + 2;

		iBaseDotSize /= 2;
		
		// Draw a T shape to symbolize the dot is above the player.

		//horiz
		FillRect( x-(2*iBaseDotSize), y, 5*iBaseDotSize, iBaseDotSize );

		//vert
		FillRect( x, y, iBaseDotSize, iBar*iBaseDotSize );
	}
	else 
	{
		if ( flags & RADAR_DOT_HOSTAGE )
		{
			FillRect( x-1, y-1, iBaseDotSize+1, iBaseDotSize+1 );
		}
		else if ( flags & RADAR_DOT_BOMB )
		{
			if ( flags & RADAR_DOT_BOMB_PLANTED )
			{
				iBaseDotSize = 2;
				// draw an X for the planted bomb
				FillRect( x, y, iBaseDotSize, iBaseDotSize );
				FillRect( x-2, y-2, iBaseDotSize, iBaseDotSize );
				FillRect( x-2, y+2, iBaseDotSize, iBaseDotSize );	
				FillRect( x+2, y-2, iBaseDotSize, iBaseDotSize );
				FillRect( x+2, y+2, iBaseDotSize, iBaseDotSize );
			}
			else
			{
				FillRect( x-1, y-1, iBaseDotSize+1, iBaseDotSize+1 );
			}
		}
		else
		{
			FillRect( x, y, iBaseDotSize, iBaseDotSize );
		}
	}
}


void Radar_FlashPlayer( int iPlayer )
{
	if ( g_RadarFlashes.Count() <= iPlayer )
	{
		g_RadarFlashes.AddMultipleToTail( iPlayer - g_RadarFlashes.Count() + 1 );
	}

	CPlayerRadarFlash *pFlash = &g_RadarFlashes[iPlayer];
	pFlash->m_flNextRadarFlashTime = gpGlobals->curtime;
	pFlash->m_iNumRadarFlashes = 16;
	pFlash->m_bRadarFlash = false;

	g_pMapOverview->FlashEntity(iPlayer);
}

CON_COMMAND( drawradar, "Draws HUD radar" )
{
	(GET_HUDELEMENT( CHudRadar ))->DrawRadar();
}

CON_COMMAND( hideradar, "Hides HUD radar" )
{
	(GET_HUDELEMENT( CHudRadar ))->HideRadar();
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

// Location text under radar

DECLARE_HUDELEMENT( CHudLocation );

CHudLocation::CHudLocation( const char *pName ) :	vgui::Label( NULL, "HudLocation", "" ), CHudElement( pName )
{
	SetParent( g_pClientMode->GetViewport() );
}

void CHudLocation::Init()
{
	// Make sure we get ticked...
	vgui::ivgui()->AddTickSignal( GetVPanel() );
}

void CHudLocation::LevelInit()
{
}

bool CHudLocation::ShouldDraw()
{
	CCSMapOverview *pCSMapOverview = (CCSMapOverview *)GET_HUDELEMENT( CCSMapOverview );

	if( g_pMapOverview && g_pMapOverview->GetMode() == CMapOverview::MAP_MODE_RADAR && pCSMapOverview && pCSMapOverview->ShouldDraw() == true )
		return true;
	else if( g_pMapOverview && g_pMapOverview->GetMode() == CMapOverview::MAP_MODE_INSET )	
		return true;

	return false;
}

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

	m_fgColor = Color( 64, 255, 64, 255 );
	SetFont( pScheme->GetFont( "ChatFont" ) );
	SetBorder( NULL );
	SetBgColor( Color( 0, 0, 0, 0 ) );
	SetFgColor( m_fgColor );
}

void CHudLocation::OnTick()
{
	m_fgColor[3] = cl_locationalpha.GetInt();
	SetFgColor( m_fgColor );

	const char *pszLocation = "";
	C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
	if ( pPlayer )
	{
		pszLocation = pPlayer->GetLastKnownPlaceName();
	}
	SetText( g_pVGuiLocalize->Find( pszLocation ) );

	// We have two different locations based on the Overview mode.
	// So we just position ourselves below, and center our text in their width.
	if( g_pMapOverview )
	{
		int x = 0, y = 0;
		int width = 0, height = 0;
		g_pMapOverview->GetAsPanel()->GetPos( x, y );
		g_pMapOverview->GetAsPanel()->GetSize( width, height );
		y += g_pMapOverview->GetAsPanel()->GetTall();
		SetPos( x, y );
		SetWide( width );
	}
}