//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Alien_AvoidLight.cpp
//
// Copyright (C) 2008 Rebellion. All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

// core
#include "Core/Asura.h"

// header
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_AvoidLight.h"

// asura
#include "Core/Asura_LOS.h"
#ifndef ASURA_NO_DEBUG_OUTPUT
#include "Debugging/Asura_DebugLineRender.h"
#include "Rendering/Asura_Font_System.h"
#endif

// axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_Movement.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Alien.h"

// avp
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/Marine/AVP_ServerActor_Marine.h"
#include "Entity/PhysicalObject/Projectile/AVP_ServerProjectile_HumanFlare.h"
#include "Entity/PlayerInfo/AVP_ServerEntity_PlayerInfo.h"
#include "Inventory/Items/AVP_InventoryItem_Torch.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Alien_AvoidLightTest
//
//////////////////////////////////////////////////////////////////////////////

AVP_Behaviour_Alien_AvoidLightTest::AVP_Behaviour_Alien_AvoidLightTest( AVP_Brain_Actor_Alien* pxBrain )
: PARENT( pxBrain, AVP_BEHAVIOUR_TYPE_ALIEN_AVOIDLIGHTTEST )
, m_fFlareCheckTimer( 0.0f )
, m_bLightNearby( false )
, m_xAvoidPosition( Asura_Maths::ZeroVector3 )
, m_xPrevAvoidPosition( Asura_Maths::ZeroVector3 )
, m_bMoving( false )
{
	m_bEvaluateChildren = false;
}

bool AVP_Behaviour_Alien_AvoidLightTest::Activate()
{
	const bool bParentOk = PARENT::Activate();

	// Reset flags
	SetInError( false );
	SetFinished( false );

	return bParentOk;
}

void AVP_Behaviour_Alien_AvoidLightTest::Deactivate()
{
	m_bLightNearby = false;
	m_bMoving = false;
	DestroyAllChildren( false );
	PARENT::Deactivate();
}

static Asura_Vector_3 s_xHitPos;

void AVP_Behaviour_Alien_AvoidLightTest::UpdateRunningConditions( const float fTimeslice )
{
	const Asura_Vector_3& xOurPosition = GetAlienBrain()->GetActor()->GetPosition();

	// Note that we only keep the closest avoid position.
	AVP_MinCalculator< Asura_Vector_3, float > xMinCalculator( Asura_Maths::ZeroVector3, Asura_Maths::LargeFloat );

	// Keep the marine players' torch calculations up to date constantly.
	for ( Asura_Hashed_List_ConstIt< AVP_ServerEntity_PlayerInfo > xPlayerInfoIter( AVP_ServerEntity_PlayerInfo::GetList() );
		!xPlayerInfoIter.Done();
		xPlayerInfoIter.Next() )
	{
		const AVP_ServerEntity_PlayerInfo* pxPlayerInfo = xPlayerInfoIter.GetCurrent();
		const AVP_ServerActor_Marine* pxMarinePlayer = AVP_ServerActor_Marine::FindEntity( pxPlayerInfo->GetPuppetGuid() );
		if ( !pxMarinePlayer )
		{
			continue;
		}

		// Check to see whether he has his torch switched on.
		const AVP_InventoryItem_Torch*	pxTorchItem	= pxMarinePlayer->GetTorchItem();

		if ( !pxTorchItem || !pxTorchItem->IsUsing( false ) )
		{
			continue;
		}

		// Fire a ray from the marine player's torch marker point and work out where it hits the environment.
		Asura_Vector_3   xTorchPosition;
		Asura_Matrix_3x3 xTorchOrient;
		if ( !pxMarinePlayer->GetWorldSpaceMarkerPoint( AVP_Human_Description::GetMarkerPointHashID( AVP_Human_Description::MPT_TORCH ),
														&xTorchPosition,
														&xTorchOrient ) )
		{
			continue;
		}
		const Asura_Vector_3 xTorchForward = Asura_Vector_3( 0.0f, 0.0f, 1.0f ) * xTorchOrient;

		Asura_LOS_InputData xInputData( xTorchPosition, xTorchForward, pxMarinePlayer->GetGuid(), 20.0f );
		const Asura_LOS_OutputData* pxOutputData = Asura_LOS::TestAgainstEnvironment( &xInputData );
		if ( pxOutputData->m_bHitEnvironment )
		{
			const Asura_Vector_3 xHitPosition = xTorchPosition + xTorchForward * pxOutputData->m_fDistanceToIntersection;
			s_xHitPos = xHitPosition;
			xMinCalculator.UpdateMin( xHitPosition, ( xHitPosition - xOurPosition ).MagnitudeSquared() );
		}
	}

	if ( xMinCalculator.GetMinComp() < 25.0f )
	{
		m_bLightNearby   = true;
		m_xAvoidPosition = xMinCalculator.GetMinEntry();
	}

	// Only update the flares periodically.
	m_fFlareCheckTimer -= fTimeslice;
	if ( m_fFlareCheckTimer > 0.0f )
	{
		return;
	}

	// Check if there are any flares near us.
	m_fFlareCheckTimer += 0.5f;

	const float fFlareRadiusSq = AVP_ServerProjectile_HumanFlare::GetFlareAILightRadius() * AVP_ServerProjectile_HumanFlare::GetFlareAILightRadius();
	for ( Asura_Hashed_List_ConstIt< AVP_ServerProjectile_HumanFlare > xFlareIter( AVP_ServerProjectile_HumanFlare::GetList() );
		!xFlareIter.Done();
		xFlareIter.Next() )
	{
		const AVP_ServerProjectile_HumanFlare* pxFlare = xFlareIter.GetCurrent();
		if ( pxFlare->GetPhysicsObject()->GetLinVelocity().MagnitudeSquared() > 1.0f )
		{
			// We only care about flares once they've slowed.
			continue;
		}

		const Asura_Vector_3 xToFlare = pxFlare->GetPosition() - xOurPosition;
		const float fToFlareSq = xToFlare.MagnitudeSquared();
		if ( fToFlareSq < fFlareRadiusSq )
		{
			xMinCalculator.UpdateMin( pxFlare->GetPosition(), fToFlareSq );
		}
	}

	if ( xMinCalculator.GetMinComp() < 25.0f )
	{
		m_bLightNearby   = true;
		m_xAvoidPosition = xMinCalculator.GetMinEntry();
	}
}

void AVP_Behaviour_Alien_AvoidLightTest::EvaluateRunningConditions()
{
	m_bRunningConditionsMet = m_bLightNearby;
}

void AVP_Behaviour_Alien_AvoidLightTest::PerFrameUpdate( const float fTimeslice )
{
	PARENT::PerFrameUpdate( fTimeslice );

	if ( IsFinished() || IsInError() )
	{
		return;
	}

	if (!GetAlienBrain()->CanPlanMove())
	{
		return;
	}


	AVP_Alien_Movement& xMovement = GetAlienBrain()->GetMovement();
	if ( m_bMoving )
	{
		if ( xMovement.IsFinished() || xMovement.IsInError() )
		{
			SetInError( xMovement.IsInError() );
			SetFinished();
			return;
		}

		// The running conditions are constantly updated, even when we are running.
		// The avoid position is therefore updated. If it differs from our previous one by more than a certain amount update our route.
		if ( !m_xAvoidPosition.ApproximatelyEquals( m_xPrevAvoidPosition, 4.0f ) )
		{
			m_xPrevAvoidPosition = m_xAvoidPosition;
			AVP_Navigation::AlienAwayFromFunctor xFunctor(m_xAvoidPosition, 10.0f);
			xMovement.GetAwayFromPosition( xFunctor, AVP_Alien_Movement::EMM_MEDIUM );
		}
	}
	else
	{
		m_xPrevAvoidPosition = m_xAvoidPosition;
		AVP_Navigation::AlienAwayFromFunctor xFunctor(m_xAvoidPosition, 10.0f);
		if ( !xMovement.GetAwayFromPosition( xFunctor, AVP_Alien_Movement::EMM_MEDIUM ) )
		{
			SetInError( true );
			SetFinished();
			return;
		}

		m_bMoving = true;
	}
}

#ifndef ASURA_NO_DEBUG_OUTPUT
void AVP_Behaviour_Alien_AvoidLightTest::RenderDebugInfo()
{
	PARENT::RenderDebugInfo();

	Asura_Font_System::Print( GetProjectBrain()->GetDebugOutputPos().x, GetProjectBrain()->GetDebugOutputPos().y, 0.5f, 0xffffffff,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED, "Alien Avoid Light" );
	GetProjectBrain()->GetDebugOutputPos().y += 10.0f;
	Asura_DebugLineRender::AddSphere( s_xHitPos, 0.5f, 0xffff0000, 8, 4 );
}
#endif

AVP_Brain_Actor_Alien* AVP_Behaviour_Alien_AvoidLightTest::GetAlienBrain() const
{
	return static_cast< AVP_Brain_Actor_Alien* >( GetBrain() );
}

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Alien_AvoidLight
//
//////////////////////////////////////////////////////////////////////////////

//AVP_Behaviour_Alien_AvoidLight::AVP_Behaviour_Alien_AvoidLight( AVP_Brain_Actor_Alien* pxBrain )
//: PARENT( pxBrain, AVP_BEHAVIOUR_TYPE_ALIEN_AVOIDLIGHT )
//{
//}
//
//void AVP_Behaviour_Alien_AvoidLight::PerFrameUpdate( const float fTimeslice )
//{
//	PARENT::PerFrameUpdate( fTimeslice );
//}
//
//AVP_Brain_Actor_Alien* AVP_Behaviour_Alien_AvoidLight::GetAlienBrain() const
//{
//	return static_cast< AVP_Brain_Actor_Alien* >( GetBrain() );
//}

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