//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Predator_Sneaking.cpp
//
// Copyright (C) 2007 Rebellion. All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

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

// header
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Sneaking.h"

// asura
#ifndef ASURA_NO_DEBUG_OUTPUT
#include "Rendering/Asura_Font_System.h"
#endif

// axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Movement.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Predator.h"
#include "Core/AVP_Waypoint_Manager.h"
#include "Knowledge/AVP_Knowledge_System_Predator.h"
#include "Knowledge/Axon_Knowledge.h"

// avp
#include "Core/AVP_LOS.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ServerActor_Predator.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_PredatorSneakFunctor
// Class that calculate cost for A* algorithm trying to avoid moving visibly to all the threats in the knowledge system
//
//////////////////////////////////////////////////////////////////////////////

class AVP_PredatorSneakFunctor : public AVP_Navigation::AStarFunctorBase
{
public:
	AVP_PredatorSneakFunctor(const AVP_Knowledge_System_Predator* pxKnowledgeSystem , float fMinDistance, Asura_Guid uMainThreatGuid)
		: m_pxKnowledgeSystem(pxKnowledgeSystem)
		, m_fMinDistanceSquared(fMinDistance * fMinDistance)
	{}

	virtual float EstimateTraversalCost(const int iWaypointIndex, const int iLinkIndex, const u_int uPathfinderFlags);
	virtual bool LinkFlagsTest( const Asura_AI_NavConnection* pxLink, const u_int uPathfinderFlags );

private:

	float EstimateCostMultplierBecauseOfThreat( const Asura_AI_NavVertex* pxFromWP, u_int uToWPIndex, const Asura_AI_NavVertex* pxToWP, const Asura_Vector_3 xThreatPosition);
	const AVP_Knowledge_System_Predator* m_pxKnowledgeSystem;
	const float m_fMinDistanceSquared;
};

float AVP_PredatorSneakFunctor::EstimateCostMultplierBecauseOfThreat( const Asura_AI_NavVertex* pxFromWP, u_int uToWPIndex, const Asura_AI_NavVertex* pxToWP, const Asura_Vector_3 xThreatPosition)
{
	float fCostMultiPlier = 1.0f;

	// rather not be too close to threat;
	Asura_Vector_3 xNearestPoint;
	Asura_Maths::GetNearestPointOnLineSegment(pxFromWP->GetPosition(), pxToWP->GetPosition(), xThreatPosition, xNearestPoint);
	float fDistanceSquared = (xThreatPosition - xNearestPoint).MagnitudeSquared();
	if (fDistanceSquared < 1.0f * 1.0f)
	{
		fCostMultiPlier *= 100.0f;
	}
	else if(fDistanceSquared < m_fMinDistanceSquared)
	{
		fCostMultiPlier *=  (1.0f + 50.0f * (m_fMinDistanceSquared - fDistanceSquared)/ m_fMinDistanceSquared);
	}

	// rather not be seen by threat
	Asura_Vector_3 xWPToThreatDirection = xThreatPosition -pxToWP->GetPosition();
	xWPToThreatDirection.Normalise();
	float fMaxDistanceToSeeSq = AVP_Waypoint_Manager::GetMaxLOFValue(uToWPIndex, xWPToThreatDirection);

	// can see the target (so target can probably see me)
	// make it cost more the closer the target is
	if (fDistanceSquared < fMaxDistanceToSeeSq)
	{
		fCostMultiPlier *= 1.0 + 100.0f;
	}
	else // we can't be seen but if the distance between us and the cover is relativly short to the distance from the target its much better cover.
	{
		fCostMultiPlier *= 1.0f + 5.0f * fMaxDistanceToSeeSq/fDistanceSquared;
	}

	return fCostMultiPlier;
}

float AVP_PredatorSneakFunctor::EstimateTraversalCost(const int iWaypointIndex, const int iLinkIndex, const u_int uPathfinderFlags)
{
	float fCost = Asura_AI_Navigation_System::DefaultTraversalCostFunction( iWaypointIndex, iLinkIndex, uPathfinderFlags, NULL );

	const Asura_AI_NavVertex* pxFromWP = Asura_AI_Navigation_System::GetWaypoint( iWaypointIndex );
	u_int uToWpIndex = pxFromWP->GetLink(iLinkIndex).GetIndex();
	const Asura_AI_NavVertex* pxToWP = Asura_AI_Navigation_System::GetWaypoint( uToWpIndex );

	const Axon_Knowledge xKnowledge = m_pxKnowledgeSystem->GetBestThreatKnowledge();
	// if we have a threat to avoid 
	if (xKnowledge.GetGuid() != ASURA_GUID_UNREGISTERED)
	{
		fCost *= EstimateCostMultplierBecauseOfThreat(pxFromWP, uToWpIndex, pxToWP, xKnowledge.m_xPosition);
	}
	
	for (u_int u =0; u < m_pxKnowledgeSystem->GetNumSecondaryThreats(); ++u)
	{
		const Axon_Knowledge xKnowledge = m_pxKnowledgeSystem->GetSecondaryThreatKnowledge(u);
		if (xKnowledge.GetGuid() != ASURA_GUID_UNREGISTERED)
		{
			fCost *= EstimateCostMultplierBecauseOfThreat(pxFromWP, uToWpIndex, pxToWP, xKnowledge.m_xPosition);
		}
	}

	// predator don't want to be in the light 
	fCost *= ( 1.0f + Asura_Maths::ClampToRange( ( AVP_Waypoint_Manager::GetWPLuminance( pxFromWP->GetLink( iLinkIndex ).GetIndex() ) - AVP_Waypoint_Manager::GetDefaultMinHideLuminance() ) / ( AVP_Waypoint_Manager::GetDefaultMaxHideLuminance() - AVP_Waypoint_Manager::GetDefaultMinHideLuminance() ), 0.0f, 1.0f ) );

	return fCost;
}

bool AVP_PredatorSneakFunctor::LinkFlagsTest( const Asura_AI_NavConnection* pxLink, const u_int uPathfinderFlags )
{
	return AVP_Navigation::PredatorLinkFlagsTest(pxLink, uPathfinderFlags);
}



//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Predator_Sneaking
//
//////////////////////////////////////////////////////////////////////////////

DEBUGVAR int g_iNumberOfRetries = 4;

AVP_Behaviour_Predator_Sneaking::AVP_Behaviour_Predator_Sneaking( AVP_Brain_Actor_Predator* pxBrain )
: PARENT( pxBrain, AVP_BEHAVIOUR_TYPE_PREDATOR_SNEAKING )
, m_eCurrentState( UNSET )
, m_fTimeInState( 0.0f )
, m_xDestination(Asura_Maths::ZeroVector3)
, m_iRetries(g_iNumberOfRetries)
, m_bWaitForCloak(true)
{
}

void AVP_Behaviour_Predator_Sneaking::SetDestination(Asura_Vector_3 xDestination, bool bWaitForCloak)
{
	m_bWaitForCloak = bWaitForCloak;
	m_xDestination = xDestination;
	SwitchToState(CLOAK);
}


void AVP_Behaviour_Predator_Sneaking::AimAt(const Asura_Vector_3& xAimTarget)
{
	AVP_Behaviour_Predator_Movement* pxMovementChild = static_cast<AVP_Behaviour_Predator_Movement*>(GetChildWithType( this, AVP_BEHAVIOUR_TYPE_PREDATOR_MOVEMENT ));
	if ( pxMovementChild)
	{
		pxMovementChild->AimAt(xAimTarget);
	}
}

void AVP_Behaviour_Predator_Sneaking::AimWhereYouMove()
{
	AVP_Behaviour_Predator_Movement* pxMovementChild = static_cast<AVP_Behaviour_Predator_Movement*>(GetChildWithType( this, AVP_BEHAVIOUR_TYPE_PREDATOR_MOVEMENT ));
	if ( pxMovementChild)
	{
		pxMovementChild->AimWhereYouMove();
	}
}

float AVP_Behaviour_Predator_Sneaking::GetLengthLeftOnPath() const
{
	AVP_Behaviour_Predator_Movement* pxMovementChild = static_cast<AVP_Behaviour_Predator_Movement*>(GetChildWithType( this, AVP_BEHAVIOUR_TYPE_PREDATOR_MOVEMENT ));
	if ( pxMovementChild)
	{
		return pxMovementChild->GetLengthLeftOnPath();
	}
	else 
	{
		return 0.0f;
	}
}


bool AVP_Behaviour_Predator_Sneaking::Activate()
{
#ifndef ASURA_NO_DEBUG_OUTPUT
	GetPredatorBrain()->SetRenderRoute( true );
#endif

	return PARENT::Activate();
}

void AVP_Behaviour_Predator_Sneaking::Deactivate()
{
	Axon_Behaviour* pxChild = GetChild();
	while ( pxChild )
	{
		UnlinkChild( pxChild );
		GetProjectBrain()->DestroyBehaviour( pxChild );
		pxChild = GetChild();
	}

	PARENT::Deactivate();
}

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

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

	m_fTimeInState += fTimeslice;

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

	switch ( m_eCurrentState )
	{
	case UNSET:
		Asura_Assert(false, "unexpected UNSET state");
		SetInError(true);
		SwitchToState(FINISHED);
	case CLOAK:
		if (GetPredatorBrain()->IsCloaked() && !GetPredatorBrain()->IsHoldingPlasmaCaster())
		{
			SwitchToState(MOVE_TO_POSITION);
		}
		break;
	case MOVE_TO_POSITION:
		{
			Axon_Behaviour* pxMovementChild = GetChildWithType( this, AVP_BEHAVIOUR_TYPE_PREDATOR_MOVEMENT );
			if ( !pxMovementChild || pxMovementChild->IsInError() )
			{
				if (m_iRetries-- == 0)
				{
					SetInError(true);
					SwitchToState(FINISHED);
				}
				else
				{
					SwitchToState(MOVE_TO_POSITION);
				}
			}
			else if (pxMovementChild->IsFinished())
			{
				SwitchToState(FINISHED);
			}
		}
		break;
	case FINISHED:
		break;
	default:
		Asura_Assert(false, "unexpected state");
	}
}

#ifndef ASURA_NO_DEBUG_OUTPUT
static const char* s_aszStateStrings[] =
{
	"UNSET",
	"CLOAKING", 
	"MOVE TO POSITION",
	"FINISHED"
};

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

	Asura_Font_System::Print( GetProjectBrain()->GetDebugOutputPos().x, GetProjectBrain()->GetDebugOutputPos().y, 0.5f, 0xffffffff,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, "%s", s_aszStateStrings[ m_eCurrentState ] );
	GetProjectBrain()->GetDebugOutputPos().y += 10.0f;
}
#endif

AVP_Brain_Actor_Predator* AVP_Behaviour_Predator_Sneaking::GetPredatorBrain() const
{
	return static_cast< AVP_Brain_Actor_Predator* >( GetProjectBrain() );
}

void AVP_Behaviour_Predator_Sneaking::SwitchToState( const State eNextState )
{

	switch ( m_eCurrentState )
	{
	case UNSET:
	case CLOAK:
		break;
	case MOVE_TO_POSITION:
		DestroyChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_MOVEMENT );
		break;
	case FINISHED:
	default:
		Asura_Assert(false, "unexpected state");
	}

	m_eCurrentState = eNextState;
	m_fTimeInState  = 0.0f;
	switch ( m_eCurrentState )
	{
	case CLOAK:
		GetPredatorBrain()->Cloak();
		if (!m_bWaitForCloak)
		{
			SwitchToState(MOVE_TO_POSITION);
		}
		break;
	case MOVE_TO_POSITION:
		if (PlanPath())
		{
			AVP_Behaviour_Predator_Movement* pxMovementBehaviour =
				static_cast< AVP_Behaviour_Predator_Movement* >( CreateChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_MOVEMENT ) );
			if ( !pxMovementBehaviour )
			{
				SetInError(true);
				SwitchToState( FINISHED );
			}
			else
			{
				pxMovementBehaviour->SetMovementMode(AVP_Behaviour_Predator_Movement::EMM_MEDIUM);
			}
		}
		else
		{
			SetInError(true);
			SwitchToState( FINISHED );
		}
		break;
	case FINISHED:
		SetFinished(true);
		break;
	case UNSET:
	default:
		Asura_Assert(false, "unexpected state");
		break;
	}
}

bool AVP_Behaviour_Predator_Sneaking::PlanPath()
{
	AVP_Navigation::NavData& xNavData = GetPredatorBrain()->GetNavData();

	xNavData.m_xStartPosition = GetPredatorBrain()->GetActor()->GetPosition();
	xNavData.m_xDestinationPosition = m_xDestination;
	xNavData.m_eMovementMode = AVP_Navigation::MM_MEDIUM;
	// Our current waypoint ID should already be up to date, as we keep it up to date when moving.
	// Update our destination waypoint ID.
	const AVP_Navigation::TestParams xTestParams( xNavData.m_uPathfinderFlags, ASURA_NAVVERTEX_MAX_ZONE_ID );
	if ( !AVP_Navigation::GetNearestWaypointID( xNavData.m_xDestinationPosition, xNavData.m_usDestinationWPID, xTestParams ) )
	{
		return false;
	}

	AVP_PredatorSneakFunctor xFunctor(GetPredatorBrain()->GetKnowledgeSystem(), GetPredatorBrain()->GetConfigInfo().m_xHighLevelBehaviour.m_fAvoidDistance, GetPredatorBrain()->GetActor()->GetGuid());

	// Clear out the old route and find the new one.
	xNavData.ResetRoute();
	if ( !AVP_Navigation::FindRoute( xNavData, &xFunctor, false ) )
	{
		return false;
	}

	return true;
}

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