//---------------------------------------------------------------------------
// File			: AVP_Behaviour_Marine_Attack_FallBack.cpp
// Created		: 
// Created By	: Gareth Thomas
// Copyright (C) 2007 Rebellion. All Rights Reserved.
//---------------------------------------------------------------------------

// INCLUDES ///////////////////////////////////////////////////////////

// core
#include "Core/Asura.h"
// header
#include "Behaviour/BehaviourTypes/Human/Marine/AVP_Behaviour_Marine_Attack_FallBack.h"
// asura
#include "Entity/Locations/OrientedNode/CoverpointNode/Asura_ServerNode_Coverpoint.h"
#ifndef ASURA_NO_DEBUG_OUTPUT
#include "Rendering/Asura_Font_System.h"
#endif

// axon
// AVP
#include "Entity/GamePlayVolume/AVP_ServerEntity_GamePlayVolume.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/AVP_ServerActor_Human.h"
// AVP axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Marine.h"
#include "Tactical/AVP_Tactical_System.h"
#include "Core/AVP_PositionEvaluation.h"
#include "Knowledge/AVP_Knowledge_System_Actor.h"

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack_FallBack :: Constructor
//---------------------------------------------------------------------------
AVP_Behaviour_Marine_Attack_FallBack::AVP_Behaviour_Marine_Attack_FallBack( AVP_Brain_Actor_Marine* pxBrain )
: PARENT( pxBrain, AVP_BEHAVIOUR_TYPE_MARINE_ATTACK_FALLBACK )
, m_pxMarineBrain( pxBrain )
, m_eUpdateState( FALLBACK_CHOOSE )
, m_fTimer( 0.0f )
{

}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack_FallBack :: Deconstructor
//---------------------------------------------------------------------------
AVP_Behaviour_Marine_Attack_FallBack::~AVP_Behaviour_Marine_Attack_FallBack( void )
{

}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack_FallBack :: Activate
//---------------------------------------------------------------------------
bool AVP_Behaviour_Marine_Attack_FallBack::Activate( void )
{
	const bool bParentOk = PARENT::Activate();
	// Put Code Below This Comment

	AVP_ServerEntity_Actor* pxThreat = AVP_ServerEntity_Actor::FindEntity( GetKnowledgeSystem()->GetBestThreatKnowledge().GetGuid() );

	if ( pxThreat && pxThreat->IsCloaked( true ) )
	{
		float fDistanceSq = ( pxThreat->GetPosition() - GetMarineBrain()->GetHumanActor()->GetPosition() ).MagnitudeSquared();

		if (fDistanceSq < 100.0f)
		{
			GetMarineBrain()->GetHumanActor()->PlayDialogue( AVP_ServerActor_Human::AVP_DLG_HUMAN_SCARED_REACTION );
		}
	}
	else
	{
		GetMarineBrain()->GetHumanActor()->PlayDialogue( AVP_ServerActor_Human::AVP_DLG_HUMAN_FALLING_BACK );
	}

	// Put Code Above This Comment
	return bParentOk;
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack_FallBack :: Deactivate
//---------------------------------------------------------------------------
void AVP_Behaviour_Marine_Attack_FallBack::Deactivate( void )
{
	// Put Code Above This Comment
	DestroyAllChildren();
	PARENT::Deactivate();
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack_FallBack :: PerFrameUpdate
//---------------------------------------------------------------------------
void AVP_Behaviour_Marine_Attack_FallBack::PerFrameUpdate( const float fTimeslice )
{
	switch ( GetUpdateState() )
	{
		case FALLBACK_CHOOSE:
		{
			if ( m_fTimer <= 0.0f )
			{
				AVP_Navigation::NavData&	xNavData		= GetMarineBrain()->GetNavData();
				Asura_Guid					uCombatZoneGuid	= GetMarineBrain()->GetCombatZoneGuid();
				u_short						uNearestWPID	= ASURA_INVALID_WAYPOINT_ID;

				Asura_Vector_3 xMemberPos = Asura_Maths::ZeroVector3;
				
				if ( GetMemberPositionToPosEvalAround( xMemberPos ) )
				{
					AVP_Navigation::TestParams			xTestParams( AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID, xNavData.m_usCurrentZone );
					AVP_Navigation::GetNearestWaypointID( xMemberPos, uNearestWPID, xTestParams );
				}
				else
				{
					uNearestWPID = xNavData.m_usCurrentWPID;
				}

				AVP_Navigation::AVP_PosEvalParams	xPosEvalParams( GetMarineBrain()->GetOwnerGuid(), uNearestWPID, 30.0f, 25.0f, 30.0, uCombatZoneGuid );
				AVP_Navigation::PosEvalFunctor		pxPosEval( uNearestWPID, 30.0f, xPosEvalParams, EvaluationFunction );
				AVP_Navigation::SearchWaypoints( AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID, &pxPosEval );

				if ( FindBestWaypoint( &pxPosEval ) )
				{
					SwitchToState( FALLBACK_MOVE );
				}
				else
				{
					m_fTimer = 2.0f;
				}
			}

			m_fTimer -= fTimeslice;

		}break;

		case FALLBACK_MOVE:
		{
			if ( GetTacticalSystem()->HasArrivedAtDestination() )
			{
				m_fTimer -= fTimeslice;

				if ( m_fTimer <= 0.0f )
				{
					SwitchToState( FALLBACK_CHOOSE );
				}
			}

		}break;

		case UNSET:
		default:
		{
		}break;
	}
	

	GetMarineBrain()->AimAtThreat();
	GetMarineBrain()->LookAtThreat();
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack_FallBack :: FindBestWaypoint
//---------------------------------------------------------------------------
bool AVP_Behaviour_Marine_Attack_FallBack::GetMemberPositionToPosEvalAround( Asura_Vector_3 &xPosition )
{
	AVP_ServerEntity_Squad_Marine* pxSquad = GetMarineBrain()->GetMarineSquad(); // verified by brain every frame

	for ( u_int u = 0; u < pxSquad->GetNumSquadMembers(); ++u )
	{
		AVP_ServerActor_Human* pxHuman = AVP_ServerActor_Human::FindEntity( pxSquad->GetMemberGuid( u ) );

		if ( pxHuman && pxHuman->IsEntityActive() && !pxHuman->IsDead() )
		{
			if (pxHuman->GetFormationRole() == AVP_ServerActor_Human::FORMATION_ROLE_SMARTGUN ||
				pxHuman->GetFormationRole() == AVP_ServerActor_Human::FORMATION_ROLE_SUPPORT )
			{
				xPosition = pxHuman->GetPosition();
				return true;
			}
		}
	}

	return false;
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack_FallBack :: FindBestWaypoint
//---------------------------------------------------------------------------
bool AVP_Behaviour_Marine_Attack_FallBack::FindBestWaypoint( AVP_Navigation::PosEvalFunctor* pxPosEval )
{
	u_int uBestWaypointID = pxPosEval->GetBestWaypointID();

	if ( uBestWaypointID != ASURA_INVALID_WAYPOINT_ID )
	{
		Asura_AI_NavVertex*	pxWaypoint	= Asura_AI_Navigation_System::GetWaypoint( uBestWaypointID );
		AVP_Tactical_System* pxTactical	= GetMarineBrain()->GetTacticalSystem();

		if ( pxWaypoint )
		{
			float fDistanceSq = ( GetOurActor()->GetPosition() - pxWaypoint->m_xPosition ).MagnitudeSquared();

			if ( fDistanceSq > 9.0f )
			{
				pxTactical->SetDestination( pxWaypoint->m_xPosition );
				pxTactical->SetAtDestinationRange( ONE_METER );
				pxTactical->SetMovementSpeed( AVP_Navigation::MM_MEDIUM );
				pxTactical->OverrideStartMovementDelay();
				return true;
			}
		}
	}

	return false;
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack_FallBack :: GetAttackingScoreForWaypoint
//---------------------------------------------------------------------------
u_int AVP_Behaviour_Marine_Attack_FallBack::EvaluationFunction( AVP_Navigation::AVP_PosEvalParams xPosEvalParams )
{
	AVP_ServerActor_Human* pxHuman = AVP_ServerActor_Human::FindEntity( xPosEvalParams.m_uActorGuid );

	u_int uScore = 0;

	// this should always be valid as he has just requested a position evaluation
	if ( !pxHuman ) { return uScore; }

	// We need a fall back position

	// Waypoint Proximity
	//uScore += AVP_PositionEvaluation::GetProximityScore( pxHuman, xPosEvalParams.m_uTestWPID, xPosEvalParams.m_fEvaluationRange );

	AVP_Brain_Actor_Marine* pxBrain = (AVP_Brain_Actor_Marine*)pxHuman->GetActorBrain();

	if ( pxBrain )
	{
		if ( !pxBrain->GetKnowledgeSystem()->ShootAtScriptedTarget() || ( pxBrain->GetKnowledgeSystem()->ShootAtScriptedTarget() && !pxBrain->GetKnowledgeSystem()->ScriptedTargetIsShootAtPoint() ) )
		{
			// Line Of Fire To The Primary Threat
			uScore += AVP_PositionEvaluation::GetWaypointNoLOFToPrimaryThreatScore( pxHuman, xPosEvalParams.m_uTestWPID, 40 );

			// Don't favour locations beyond the enemy
			uScore += AVP_PositionEvaluation::GetWaypointIsBeyondTargetScore( pxHuman, xPosEvalParams.m_uTestWPID, 40 );
		}
		else
		{
			uScore += AVP_PositionEvaluation::GetWaypointLOFToScriptedTarget( pxHuman, xPosEvalParams.m_uTestWPID, 40 );
		}
	}

	// Preferred Fighting Range
	uScore += AVP_PositionEvaluation::GetPreferredFightingRangeScore( pxHuman, xPosEvalParams.m_uTestWPID, xPosEvalParams.m_fMinFightingRange, xPosEvalParams.m_fMaxFightingRange, 40 );

	// Waypoint Occupied by ally
	uScore += AVP_PositionEvaluation::GetHumanoidPositionOccupiedByAllyScore( pxHuman, xPosEvalParams.m_uTestWPID, 30 );

	// Waypoint Gives no LOF from Secondary Threats
	uScore += AVP_PositionEvaluation::GetHumanoidPositionGivesCoverFromSecondaryThreatsScore( pxHuman, xPosEvalParams.m_uTestWPID, 30 );

	// Waypoint Has a cover point that provides cover from primary threat
	uScore += AVP_PositionEvaluation::GetPositionProvidesCoverFromPrimaryThreat( pxHuman, xPosEvalParams.m_uTestWPID, 60 );

	return uScore;

}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack_FallBack :: SwitchToState
//---------------------------------------------------------------------------
void AVP_Behaviour_Marine_Attack_FallBack::SwitchToState( UpdateState eState )
{
	switch (GetUpdateState())
	{
		case UNSET:
		default:
		{

		}
	}

	SetUpdateState( eState );

	switch (GetUpdateState())
	{
		case FALLBACK_CHOOSE:
		{
			m_fTimer = 0.0f;
		}break;

		case FALLBACK_MOVE:
		{
			m_fTimer = 2.0f;
		}break;


		case UNSET:
		default:
		{

		}
	}
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Attack_FallBack :: RenderDebugInfo
//---------------------------------------------------------------------------
#ifndef ASURA_NO_DEBUG_OUTPUT
static const char* s_aszStateStrings[ AVP_Behaviour_Marine_Attack_FallBack::MAX_STATES ] =
{
	"Attack_FallBack - UNSET",
};

void AVP_Behaviour_Marine_Attack_FallBack::RenderDebugInfo( void )
{
	PARENT::RenderDebugInfo();

	Asura_Font_System::Print
	(
		GetProjectBrain()->GetDebugOutputPos().x,
		GetProjectBrain()->GetDebugOutputPos().y,
		0.5f, 
		0XFF00FFFF,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED, 
		s_aszStateStrings[GetUpdateState()]
	);

	// Offset the debug output position for other behaviours to render there own text
	GetProjectBrain()->GetDebugOutputPos().y += 10.0f;
}
#endif

/// End Of File 'AVP_behaviour_Marine_Attack_FallBack.cpp'
