// C_NextBot.cpp
// Client-side implementation of Next generation bot system
// Author: Michael Booth, April 2005
//========= Copyright Valve Corporation, All rights reserved. ============//

#include "cbase.h"
#include "C_NextBot.h"
#include "debugoverlay_shared.h"
#include <bitbuf.h>
#include "viewrender.h"

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

#undef NextBot

ConVar NextBotShadowDist( "nb_shadow_dist", "400" );

//-----------------------------------------------------------------------------
IMPLEMENT_CLIENTCLASS_DT( C_NextBotCombatCharacter, DT_NextBot, NextBotCombatCharacter )
END_RECV_TABLE()


//-----------------------------------------------------------------------------
C_NextBotCombatCharacter::C_NextBotCombatCharacter()
{
	// Left4Dead have surfaces too steep for IK to work properly
	m_EntClientFlags |= ENTCLIENTFLAG_DONTUSEIK;

	m_shadowType = SHADOWS_SIMPLE;
	m_forcedShadowType = SHADOWS_NONE;
	m_bForceShadowType = false;

	TheClientNextBots().Register( this );
}


//-----------------------------------------------------------------------------
C_NextBotCombatCharacter::~C_NextBotCombatCharacter()
{
	TheClientNextBots().UnRegister( this );
}


//-----------------------------------------------------------------------------
void C_NextBotCombatCharacter::Spawn( void )
{
	BaseClass::Spawn();
}


//-----------------------------------------------------------------------------
void C_NextBotCombatCharacter::UpdateClientSideAnimation()
{
	if (IsDormant())
	{
		return;
	}

	BaseClass::UpdateClientSideAnimation();
}


//--------------------------------------------------------------------------------------------------------
void C_NextBotCombatCharacter::UpdateShadowLOD( void )
{
	ShadowType_t oldShadowType = m_shadowType;

	if ( m_bForceShadowType )
	{
		m_shadowType = m_forcedShadowType;
	}
	else
	{
#ifdef NEED_SPLITSCREEN_INTEGRATION
		FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
		{
			C_BasePlayer *pl = C_BasePlayer::GetLocalPlayer(hh);
			if ( pl )
			{
				Vector delta = GetAbsOrigin() - C_BasePlayer::GetLocalPlayer(hh)->GetAbsOrigin();
#else
		{
			if ( C_BasePlayer::GetLocalPlayer() )
			{
				Vector delta = GetAbsOrigin() - C_BasePlayer::GetLocalPlayer()->GetAbsOrigin();
#endif
				if ( delta.IsLengthLessThan( NextBotShadowDist.GetFloat() ) )
				{
					m_shadowType = SHADOWS_RENDER_TO_TEXTURE_DYNAMIC;
				}
				else
				{
					m_shadowType = SHADOWS_SIMPLE;
				}
			}
			else
			{
				m_shadowType = SHADOWS_SIMPLE;
			}
		}
	}

	if ( oldShadowType != m_shadowType )
	{
		DestroyShadow();
	}
}


//--------------------------------------------------------------------------------------------------------
ShadowType_t C_NextBotCombatCharacter::ShadowCastType( void ) 
{
	if ( !IsVisible() )
		return SHADOWS_NONE;

	if ( m_shadowTimer.IsElapsed() )
	{
		m_shadowTimer.Start( 0.15f );
		UpdateShadowLOD();
	}

	return m_shadowType;
}


//--------------------------------------------------------------------------------------------------------
bool C_NextBotCombatCharacter::GetForcedShadowCastType( ShadowType_t* pForcedShadowType ) const
{
	if ( pForcedShadowType )
	{
		*pForcedShadowType = m_forcedShadowType;
	}
	return m_bForceShadowType;
}

//--------------------------------------------------------------------------------------------------------
/**
 * Singleton accessor.
 * By returning a reference, we guarantee construction of the 
 * instance before its first use.
 */
C_NextBotManager &TheClientNextBots( void )
{
	static C_NextBotManager manager;
	return manager;
}


//--------------------------------------------------------------------------------------------------------
C_NextBotManager::C_NextBotManager( void )
{
}


//--------------------------------------------------------------------------------------------------------
C_NextBotManager::~C_NextBotManager()
{
}


//--------------------------------------------------------------------------------------------------------
void C_NextBotManager::Register( C_NextBotCombatCharacter *bot )
{
	m_botList.AddToTail( bot );
}


//--------------------------------------------------------------------------------------------------------
void C_NextBotManager::UnRegister( C_NextBotCombatCharacter *bot )
{
	m_botList.FindAndRemove( bot );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_NextBotManager::SetupInFrustumData( void )
{
#ifdef ENABLE_AFTER_INTEGRATION
	// Done already this frame.
	if ( IsInFrustumDataValid() )
		return true;

	// Can we use the view data yet?
	if ( !FrustumCache()->IsValid() )
		return false;

	// Get the number of active bots.
	int nBotCount = m_botList.Count();

	// Reset.
	for ( int iBot = 0; iBot < nBotCount; ++iBot )
	{
		// Get the current bot.
		C_NextBotCombatCharacter *pBot = m_botList[iBot];
		if ( !pBot )
			continue;

		pBot->InitFrustumData();
	}

	FOR_EACH_VALID_SPLITSCREEN_PLAYER( iSlot )
	{
		ACTIVE_SPLITSCREEN_PLAYER_GUARD( iSlot );
		// Get the active local player.
		C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
		if ( !pPlayer )
			continue;

		for ( int iBot = 0; iBot < nBotCount; ++iBot )
		{
			// Get the current bot.
			C_NextBotCombatCharacter *pBot = m_botList[iBot];
			if ( !pBot )
				continue;

			// Are we in the view frustum?
			Vector vecMin, vecMax;
			pBot->CollisionProp()->WorldSpaceAABB( &vecMin, &vecMax );
			bool bInFrustum = !FrustumCache()->m_Frustums[iSlot].CullBox( vecMin, vecMax );
		
			if ( bInFrustum )
			{
				Vector vecSegment;
				VectorSubtract( pBot->GetAbsOrigin(), pPlayer->GetAbsOrigin(), vecSegment );
				float flDistance = vecSegment.LengthSqr();
				if ( flDistance < pBot->GetInFrustumDistanceSqr() )
				{
					pBot->SetInFrustumDistanceSqr( flDistance );
				}	

				pBot->SetInFrustum( true );
			}
		}
	}

	// Mark as setup this frame.
	m_nInFrustumFrame = gpGlobals->framecount;
#endif

	return true;
}

//--------------------------------------------------------------------------------------------------------