//---------------------------------------------------------------------------
// File			: AVP_Behaviour_Civilian_Panic.cpp
// Created		: 04 / 03 / 2008
// Created By	: Gareth Thomas
// Copyright (C) 2007 Rebellion. All Rights Reserved.
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Includes
//---------------------------------------------------------------------------
// Core
#include "Core/Asura.h"

// This Files Header
#include "Behaviour/BehaviourTypes/Human/Civilian/AVP_Behaviour_Civilian_Panic.h"

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

// AVP
#include "Actor/Vertebrates/Human/AVP_Human_AIAnimStateMachine.h"
#include "Actor/Vertebrates/Human/AVP_Human_AIAnimStateData.h"
#include "Entity/GamePlayVolume/AVP_ServerEntity_GamePlayVolume.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/Civilian/AVP_ServerActor_Civilian.h"
#include "Entity/Locations/FleePoint/AVP_ServerNode_FleePoint.h"
#include "Entity/Locations/OrientedNode/GamesceneNode/AVP_ServerNode_GS2Node.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/AVP_Messages_Human.h"
#include "Inventory/AVP_Inventory_Group.h"
#include "Inventory/AVP_Inventory_HashIDs.h"
#include "Inventory/AVP_Inventory_Item.h"
#include "Inventory/AVP_Inventory_System.h"
#include "Moves/Vertebrates/Human/AVP_Human_MovesGamescene.h"

// AVP Axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Behaviour/BehaviourTypes/Human/AVP_Behaviour_Human_Movement.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Civilian.h"
#include "Knowledge/AVP_Knowledge_System_Actor.h"

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
AVP_Behaviour_Civilian_Panic::AVP_Behaviour_Civilian_Panic(AVP_Brain_Actor_Civilian* pxBrain)
: PARENT(pxBrain, AVP_BEHAVIOUR_TYPE_CIVILIAN_PANIC)
, m_eUpdateState(PANIC)
, m_pxCivilianBrain(pxBrain)
, m_xFleeDirection( Asura_Maths::ZeroVector3 )
, m_uFleePoint( ASURA_GUID_UNREGISTERED )
, m_uGS2NodeGuid( ASURA_GUID_UNREGISTERED )
{

}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
bool AVP_Behaviour_Civilian_Panic::Activate(void)
{
	const bool bParentOk = PARENT::Activate();

	return bParentOk;
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void AVP_Behaviour_Civilian_Panic::Deactivate(void)
{
	DestroyAllChildren();

	PARENT::Deactivate();

	if (GetCivilianBrain()->IsActive())
	{
		AVP_ServerActor_Civilian*		pxOurActor		= GetCivilianBrain()->GetCivilianActor();
		AVP_Human_AIAnimStateMachine*	pxStateMachine	= static_cast<AVP_Human_AIAnimStateMachine*>(pxOurActor->GetAIAnimStateMachine());

		pxStateMachine->RequestIdle();
	}
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void AVP_Behaviour_Civilian_Panic::ChooseAction( void )
{
	AVP_ServerEntity_Actor*		pxThreatActor	= AVP_ServerEntity_Actor::FindEntity(GetCivilianBrain()->GetKnowledgeSystem()->GetBestThreatKnowledge().GetGuid());
	AVP_ServerActor_Civilian*	pxOurActor		= GetCivilianBrain()->GetCivilianActor();

	AVP_Navigation::NavData&			xNavData		= GetCivilianBrain()->GetNavData();
	u_int								uCurrentWPID	= ASURA_INVALID_WAYPOINT_ID;
	AVP_Navigation::TestParams			xTestParams;

	xTestParams.m_usZone				= xNavData.m_usCurrentZone;
	xTestParams.m_uPathfinderFlags		=  AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID;
	xTestParams.m_xNormal				= -GetCivilianBrain()->GetCivilianActor()->GetCurrentGravityDirection();
	xTestParams.m_bRejectNonalignedWPs	= true;
	AVP_Navigation::GetNearestWaypointID( pxOurActor->GetPosition(), xNavData.m_usCurrentWPID, xTestParams );
	uCurrentWPID = xNavData.m_usCurrentWPID;

	Asura_Vector_3 xDirToThreat( pxThreatActor->GetPosition() - pxOurActor->GetPosition() );
	xDirToThreat.Normalise();

	float			fBestDot = 1.0f;
	Asura_Vector_3	xBestDirection( Asura_Maths::ZeroVector3 );

	if ( uCurrentWPID != ASURA_INVALID_WAYPOINT_ID )
	{
		Asura_AI_NavVertex* pxCurrWP = Asura_AI_Navigation_System::GetWaypoint( uCurrentWPID );

		if ( pxCurrWP )
		{
			u_int uNumLinks = pxCurrWP->GetNumLinks();

			for ( u_int u = 0; u < uNumLinks; ++u )
			{
				Asura_AI_NavVertex* pxLinkedWP = Asura_AI_Navigation_System::GetWaypoint( pxCurrWP->GetLink( u ).GetIndex() );

				if ( pxLinkedWP )
				{
					Asura_Vector_3 xDirToLinkedWP( pxLinkedWP->GetPosition() - pxOurActor->GetPosition() );
					xDirToLinkedWP.Normalise();

					float fDot = xDirToThreat * xDirToLinkedWP;

					if ( fDot < fBestDot )
					{
						fBestDot = fDot;
						xBestDirection = xDirToLinkedWP;
					}
				}
			}
		}
	}

	if ( fBestDot <= 0.707f )
	{
		m_xFleeDirection = xBestDirection;
		SwitchToState( MOVE_AWAY );
	}
	else
	{
		SetUpdateState( ROOTED_TO_SPOT );
	}
}

DEBUGVAR float fDistSq = 25.0f;

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void AVP_Behaviour_Civilian_Panic::PerFrameUpdate(const float fTimeslice)
{
	bool bHaveThreat = (GetCivilianBrain()->GetKnowledgeSystem()->GetBestThreatKnowledge().GetGuid() != ASURA_GUID_UNREGISTERED) ? true : false;

	if (!bHaveThreat)
	{
		SetFinished();
		return;
	}

	AVP_ServerEntity_Actor*		pxThreatActor	= AVP_ServerEntity_Actor::FindEntity(GetCivilianBrain()->GetKnowledgeSystem()->GetBestThreatKnowledge().GetGuid());
	AVP_ServerActor_Civilian*	pxOurActor		= GetCivilianBrain()->GetCivilianActor();

	if (!pxThreatActor)
	{
		SetFinished();
		return;
	}

	switch (GetUpdateState())
	{
		//--------------------------------------
		
		case MOVE_AWAY:
		{
			const AVP_Navigation::NavData& xNavData = GetCivilianBrain()->GetNavData();
			
			float fDistanceSq = ( xNavData.m_xDestinationPosition - pxOurActor->GetPosition() ).MagnitudeSquared();

			if ( fDistanceSq < 25.0f )
			{
				SwitchToState( FIND_FLEE_POINT );
			}

		}break;

		//--------------------------------------
		case PANIC:
		{
			ChooseAction();

		}break;
		//--------------------------------------
		case ROOTED_TO_SPOT:
		{
			AVP_Human_AIAnimStateMachine* pxStateMachine = static_cast<AVP_Human_AIAnimStateMachine*>(pxOurActor->GetAIAnimStateMachine());

			if ( !pxStateMachine->IsPanicIdle() )
			{
				pxStateMachine->RequestPanicIdle();
			}
			Asura_Vector_3 xPos;
			pxThreatActor->GetHeadPosition( xPos );
			GetCivilianBrain()->SetLookAtPosition( xPos );
			pxThreatActor->GetChestPosition( xPos );
			GetCivilianBrain()->AimAtPosition( xPos );

			float fDistanceToThreatSq = ( pxOurActor->GetPosition() - pxThreatActor->GetPosition() ).MagnitudeSquared();
			if ( fDistanceToThreatSq < fDistSq )
			{
				ChooseAction();
			}

		}break;
		//--------------------------------------
		case FIND_FLEE_POINT:
		{
			// find flee point
			AVP_ServerEntity_GamePlayVolume* pxGPVolume = AVP_ServerEntity_GamePlayVolume::GetVolumeThatContainsPosition(pxOurActor->GetPosition());

			int		iBestIndex	= -1;
			float	fBestDot	= 0.0f;

			if (pxGPVolume)
			{
				for (int i = 0; i < (int)pxGPVolume->GetNumFleePointGuids(); ++i)
				{
					AVP_ServerNode_FleePoint* pxFleePoint = AVP_ServerNode_FleePoint::FindEntity(pxGPVolume->GetFleePointGuid(i));

					if (pxFleePoint)
					{
						Asura_Vector_3 xDirectionFromThreat = pxOurActor->GetPosition() - pxThreatActor->GetPosition();
						xDirectionFromThreat.Normalise();

						Asura_Vector_3 xDirectionToFleePoint = pxFleePoint->GetPosition() - pxOurActor->GetPosition();
						xDirectionToFleePoint.Normalise();

						float fDot = xDirectionToFleePoint * xDirectionFromThreat;

						if (fDot > fBestDot)
						{
							fBestDot = fDot;
							iBestIndex = i;
						}
					}
				}

				if (iBestIndex != -1)
				{
					AVP_ServerNode_FleePoint* pxFleePoint = AVP_ServerNode_FleePoint::FindEntity(pxGPVolume->GetFleePointGuid(iBestIndex));

					if (pxFleePoint)
					{
						m_uFleePoint = pxFleePoint->GetGuid();

						if ( pxFleePoint->GetNumFleeActions() > 0 )
						{
							AVP_ServerNode_FleePoint*	pxFleePoint = AVP_ServerNode_FleePoint::FindEntity( m_uFleePoint );
							AVP_ServerNode_GS2Node*		pxGS2NodeToUse = NULL;

							if ( pxFleePoint )
							{
								for (u_int u = 0; u < pxFleePoint->GetNumFleeActions(); ++u )
								{
									AVP_ServerNode_GS2Node* pxGS2Node = AVP_ServerNode_GS2Node::FindEntity( pxFleePoint->GetFleeActionGuid( u ) );

									if ( pxGS2Node && !pxGS2Node->IsOccupied() )
									{
										pxGS2NodeToUse = pxGS2Node;
									}
								}
							}

							if ( pxGS2NodeToUse )
							{
								m_uGS2NodeGuid = pxGS2NodeToUse->GetGuid();
								CreateMovementBehaviour( pxGS2NodeToUse->GetPosition() );
								SetUpdateState(FLEEING);
								return;
							}
						}

						m_uGS2NodeGuid = ASURA_GUID_UNREGISTERED;
						CreateMovementBehaviour( pxFleePoint->GetPosition() );
						SetUpdateState(FLEEING);
						return;
					}
				}
			}

			SetUpdateState(ROOTED_TO_SPOT);
		}break;
		//--------------------------------------
		case FLEEING:
		{
			AVP_Behaviour_Human_Movement* pxHumanBehaviour = (AVP_Behaviour_Human_Movement*)GetChildWithType(this, AVP_BEHAVIOUR_TYPE_HUMAN_MOVEMENT);

			if (pxHumanBehaviour)
			{
				if (pxHumanBehaviour->IsFinished())
				{
					DestroyChildWithType(AVP_BEHAVIOUR_TYPE_HUMAN_MOVEMENT);

					if ( m_uGS2NodeGuid != ASURA_GUID_UNREGISTERED )
					{
						SwitchToState( USE_FLEE_ACTION );
					}
					else
					{
						SetUpdateState(ROOTED_TO_SPOT);
					}
				}
			}
			else
			{
				SetUpdateState(ROOTED_TO_SPOT);
			}
		}break;
		//--------------------------------------
		case USE_FLEE_ACTION:
		{

		}
		//--------------------------------------
		case UNSET:
		default:
		{

		}break;
	}
}


//---------------------------------------------------------------------------
// AVP_Behaviour_Civilian_Panic :: SetRouteToActionGameScene
//---------------------------------------------------------------------------
void AVP_Behaviour_Civilian_Panic::SendMountGamesceneMessage()
{
	Asura_Message* pxMessage = Asura_Network::CreateMessage( ASURA_MSG_MOVE_START, sizeof( AVP_Message_Human_MountGamescene ) );

	if ( pxMessage )
	{
		AVP_Message_Human_MountGamescene*	pxData		= static_cast< AVP_Message_Human_MountGamescene* >( pxMessage->GetData() );
		AVP_ServerNode_GS2Node*				pxGS2Node	= AVP_ServerNode_GS2Node::FindEntity( m_uGS2NodeGuid );

		if ( pxGS2Node )
		{
			Asura_Matrix_3x3 xNodeOri;
			pxGS2Node->GetOrientation(xNodeOri);

			pxMessage->SetFromGuid( GetCivilianBrain()->GetOwnerGuid() );
			pxMessage->SetToGuid( GetCivilianBrain()->GetOwnerGuid() );
			pxData->ClearFlags();
			pxData->SetMoveID( AVP_Move_Human_MountGamescene::GetStaticHash() );
			pxData->SetDestinationPosition( pxGS2Node->GetPosition() );
			pxData->SetDestinationOrientation( xNodeOri );
			pxData->SetAnimBlendTemplateBlueprintHash( GetMountGamesceneAnimBlendTemplateBlueprintHash() );

			AVP_ServerActor_Human* xOurActor = GetCivilianBrain()->GetCivilianActor();

			if ( xOurActor->GetAIAnimStateMachine() )
			{
				xOurActor->GetAIAnimStateMachine()->RequestInMove( pxMessage, AVP_Human_AIAnimStateMachine::ACTOR_STATE_IDLE );
			}
			else
			{
				Asura_Network::DestroyMessage(pxMessage);
			}
		}
		else
		{
			Asura_Network::DestroyMessage(pxMessage);
		}
	}
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine :: GetMountGamesceneAnimation
//---------------------------------------------------------------------------
Asura_Hash_ID AVP_Behaviour_Civilian_Panic::GetMountGamesceneAnimBlendTemplateBlueprintHash(void)
{
	const AVP_ServerActor_Human* pxHuman = GetCivilianBrain()->GetCivilianActor();

	const AVP_Inventory_System* pxInvSys = pxHuman->GetInventorySystem();
	const AVP_Inventory_Group* pxWeaponGroup = pxInvSys != NULL ? pxInvSys->GetGroup( uAVP_INVENTORY_GROUPHASH_WEAPON ) : NULL;
	Asura_Assert( pxWeaponGroup != NULL, "Unable to get wepaon group" );
	if ( pxWeaponGroup != NULL )
	{
		const AVP_Inventory_Item* pxItem = pxWeaponGroup->GetCurrentInventoryItem();
		if ( pxItem )
		{
			const AVP_Actor_AnimStateData* pxStateData = AVP_Human_AIAnimStateData::GetAnimStateData( pxHuman->GetAnimStateBlueprintType(), pxItem->GetBlueprintHash(), AVP_Human_AIAnimStateMachine::VERTEBRATE_STATE_MOVE_MEDIUM_TO_IDLE, pxHuman->IsAiming(), pxHuman->GetCoverType() );

			if ( pxStateData )
			{
				return pxStateData->GetAnimBlendTemplateBlueprintHash();
			}
		}
	}

	// else
	return ASURA_HASH_ID_UNSET;
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_HandleObjective :: HandleMessage
//---------------------------------------------------------------------------
bool AVP_Behaviour_Civilian_Panic::HandleMessage( const Asura_Message* pxMessage )
{
	switch ( pxMessage->GetMsgID() )
	{
		case ASURA_MSG_MOVECHAINCOMPLETE:
		{
			const Asura_Msg_MoveChainComplete* pxData = static_cast< const Asura_Msg_MoveChainComplete* >( pxMessage->GetData() );

			if ( pxData && pxData->GetInitialMoveID() == AVP_Move_Human_MountGamescene::GetStaticHash() )
			{
				GetCivilianBrain()->GetCivilianActor()->SetInitialGamesceneGuid( m_uGS2NodeGuid );
			}

			return true;

		}break;

		default:
		{
			return PARENT::HandleMessage( pxMessage );

		}break;
	}
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void AVP_Behaviour_Civilian_Panic::SwitchToState( PanicUpdateState eState )
{
	switch ( GetUpdateState() )
	{
		case UNSET:
		case PANIC:
		case ROOTED_TO_SPOT:
		case FIND_FLEE_POINT:
		case FLEEING:
		case MOVE_AWAY:
		{
			if ( GetChildWithType(this, AVP_BEHAVIOUR_TYPE_HUMAN_MOVEMENT ) )
			{
				AVP_Behaviour_Human_Movement* pxHumanMovement = static_cast<AVP_Behaviour_Human_Movement*>( GetChildWithType(this, AVP_BEHAVIOUR_TYPE_HUMAN_MOVEMENT ) );
				pxHumanMovement->SetStopAtEnd( false );
				DestroyChildWithType( AVP_BEHAVIOUR_TYPE_HUMAN_MOVEMENT );
			}
		}break;
		default:
		break;
	}

	SetUpdateState( eState );

	switch ( GetUpdateState() )
	{
		case UNSET:
		case PANIC:
		case ROOTED_TO_SPOT:
		case FIND_FLEE_POINT:
		case FLEEING:
		break;
		
		case MOVE_AWAY:
		{
			AVP_Navigation::NavData&						xNavData	= GetCivilianBrain()->GetNavData();
			AVP_Navigation::HumanoidInDirectionSearch		xFunctor( xNavData.m_usCurrentWPID, 225.0f, 100.0f, m_xFleeDirection );
			AVP_Navigation::SearchWaypoints( AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID, &xFunctor );

			int iRouteLength = 0;

			Asura_AI_Navigation_System::FindRouteCustom
			( 
				AVP_Navigation::GetWorkingRouteBuffer(), 
				iRouteLength, 
				AVP_Navigation::GetWorkingRouteBufferLength(),
				xNavData.m_usCurrentWPID, 
				xNavData.m_uPathfinderFlags,
				&xFunctor,
				AVP_Navigation::FunctorGoalCostFunction,
				AVP_Navigation::FunctorTraversalCostFunction,
				AVP_Navigation::FunctorGoalAchievedFunction,
				AVP_Navigation::FunctorLinkFlagsTestFunction 

			);

			if ( iRouteLength )
			{
				const Asura_AI_NavVertex* pxEndWP = Asura_AI_Navigation_System::GetWaypoint( AVP_Navigation::GetWorkingRouteBuffer()[ iRouteLength - 1 ] );

				m_uGS2NodeGuid = ASURA_GUID_UNREGISTERED;
				CreateMovementBehaviour( pxEndWP->GetPosition() );
			}
			else
			{
				SwitchToState( ROOTED_TO_SPOT );
			}
		}break;

		case USE_FLEE_ACTION:
		{
			SendMountGamesceneMessage();
		}break;

		default:
		break;
	}
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void AVP_Behaviour_Civilian_Panic::CreateMovementBehaviour(const Asura_Vector_3& xGoToPoint)
{
	AVP_Behaviour_Human_Movement* pxHumanBehaviour = (AVP_Behaviour_Human_Movement*)CreateChildWithType(AVP_BEHAVIOUR_TYPE_HUMAN_MOVEMENT);

	if (pxHumanBehaviour)
	{
		if ( m_uGS2NodeGuid != ASURA_GUID_UNREGISTERED )
		{
			pxHumanBehaviour->SetDestinationGS2NodeGuid( m_uGS2NodeGuid );
		}

		pxHumanBehaviour->GetToPosition(xGoToPoint, AVP_Navigation::MM_FAST, true);
	}
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#ifndef ASURA_NO_DEBUG_OUTPUT
static const char* s_aszStateStrings[AVP_Behaviour_Civilian_Panic::MAX_STATES] =
{
	"PANIC - UNSET",
	"PANIC - PANIC",
	"PANIC - ROOTED_TO_SPOT",
	"PANIC - FIND_FLEE_POINT",
	"PANIC - FLEEING",
	"PANIC - MOVE_AWAY"
};

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

	Asura_Font_System::Print
	(
		GetProjectBrain()->GetDebugOutputPos().x,
		GetProjectBrain()->GetDebugOutputPos().y,
		0.5f, 
		0xffffffff,
		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
