//---------------------------------------------------------------------------
// File			: AVP_Behaviour_Marine_Patrol.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/Marine/AVP_Behaviour_Marine_Patrol.h"

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

// AVP
#include "Entity/Locations/GuardZone/AVP_ServerNode_GuardZone.h"
#include "Entity/Locations/OrientedNode/AVP_ServerNode_OrientedNode.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 "Knowledge/AVP_Knowledge_System_Actor.h"
#include "Tactical/AVP_Tactical_System.h"

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
AVP_Behaviour_Marine_Patrol::AVP_Behaviour_Marine_Patrol(AVP_Brain_Actor_Marine* pxBrain)
: PARENT(pxBrain, AVP_BEHAVIOUR_TYPE_MARINE_PATROL)
, m_eUpdateState(SELECT_GUARD_ZONE)
, m_pxMarineBrain(pxBrain)
, m_uGuardZoneGuid(ASURA_GUID_UNREGISTERED)
, m_fWaitTime(0.0f)
, m_iCurrentPatrolIndex(-1)
, m_uCurrentPatrolGuid(ASURA_GUID_UNREGISTERED)
, m_uPreviousPatrolGuid(ASURA_GUID_UNREGISTERED)
{
}

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

	return bParentOk;
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void AVP_Behaviour_Marine_Patrol::Deactivate(void)
{
	DestroyAllChildren( false );
	PARENT::Deactivate();

	if (m_uCurrentPatrolGuid != ASURA_GUID_UNREGISTERED)
	{
		AVP_ServerNode_OrientedNode* pxPatrolPoint = AVP_ServerNode_OrientedNode::FindEntity(m_uCurrentPatrolGuid);

		if (pxPatrolPoint)
		{
			pxPatrolPoint->SetIsOccupied(false);
		}
	}
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void AVP_Behaviour_Marine_Patrol::PerFrameUpdate(const float fTimeslice)
{
	AVP_ServerActor_Human*		pxOurActor	= GetMarineBrain()->GetHumanActor();
 	bool						bHaveThreat	= (GetMarineBrain()->GetKnowledgeSystem()->GetBestThreatKnowledge().GetGuid() != ASURA_GUID_UNREGISTERED) ? true : false;
 	bool						bHaveAlert	= (GetMarineBrain()->GetKnowledgeSystem()->GetBestAlertKnowledge().GetGuid() != ASURA_GUID_UNREGISTERED) ? true : false;
	AVP_Tactical_System*		pxTactical	= GetMarineBrain()->GetTacticalSystem();

	// if we have an alert or a threat set finished
	if (bHaveThreat) {SetFinished(); return;}


	if (bHaveAlert && GetUpdateState() != ALERT)
	{
		CreateChildWithType(AVP_BEHAVIOUR_TYPE_MARINE_ALERT);
		SetUpdateState(ALERT);
	}

	switch (GetUpdateState())
	{
		//--------------------------------------
		case ALERT:
		{
			Axon_Behaviour* pxAlertBehaviour = GetChildWithType(this, AVP_BEHAVIOUR_TYPE_MARINE_ALERT);

			if (pxAlertBehaviour && pxAlertBehaviour->IsFinished())
			{
				DestroyChildWithType(AVP_BEHAVIOUR_TYPE_MARINE_ALERT);
				SetUpdateState(SELECT_GUARD_ZONE);
			}
		}break;
		//--------------------------------------
		case SELECT_GUARD_ZONE:
		{
			if (SelectGuardZone())
			{
				SetUpdateState(SELECT_PATROL_POINT);
			}
			else
			{
				SetFinished();
				return;
			}
		}break;
		//--------------------------------------
		case SELECT_PATROL_POINT:
		{
			AVP_ServerNode_GuardZone* pxGuardZone = AVP_ServerNode_GuardZone::FindEntity(m_uGuardZoneGuid);

			if (pxGuardZone)
			{
				AVP_ServerNode_OrientedNode* pxPatrolPoint = NULL;

				switch (pxGuardZone->GetPatrolType())
				{
					//--------------------------------------------------------------
					case AVP_ServerNode_GuardZone::GUARD_ZONE_PATROL_TYPE_LOOPING:
					{
						if (m_uCurrentPatrolGuid == ASURA_GUID_UNREGISTERED)
						{
							pxPatrolPoint = pxGuardZone->GetNearestGuardPoint(pxOurActor->GetPosition());
						}
						else
						{
							pxPatrolPoint = pxGuardZone->GetNextLoopingGuardPoint(m_uCurrentPatrolGuid);
						}

					}break;
					//--------------------------------------------------------------
					case AVP_ServerNode_GuardZone::GUARD_ZONE_PATROL_TYPE_PING_PONG:
					{
						if (m_uCurrentPatrolGuid == ASURA_GUID_UNREGISTERED)
						{
							pxPatrolPoint = pxGuardZone->GetNearestGuardPoint(pxOurActor->GetPosition());
						}
						else
						{
							pxPatrolPoint = pxGuardZone->GetNextPingPongGuardPoint(m_uCurrentPatrolGuid);
						}
					}break;
					//--------------------------------------------------------------
					case AVP_ServerNode_GuardZone::GUARD_ZONE_PATROL_TYPE_RANDOM:
					default:
					{
						pxPatrolPoint = pxGuardZone->GetRandomGuardPoint();
					}break;
					//--------------------------------------------------------------
				}

				if (pxPatrolPoint)
				{
					// Set the old point as not occupied
					if ((pxPatrolPoint->GetGuid() != m_uCurrentPatrolGuid) && (m_uCurrentPatrolGuid != ASURA_GUID_UNREGISTERED))
					{
						AVP_ServerNode_OrientedNode* pxOldPoint = AVP_ServerNode_OrientedNode::FindEntity(m_uCurrentPatrolGuid);

						if (pxOldPoint)
						{
							pxOldPoint->SetIsOccupied(false);
						}
					}

					pxPatrolPoint->SetIsOccupied(true);
					m_uCurrentPatrolGuid = pxPatrolPoint->GetGuid();
					pxTactical->SetDestination(pxPatrolPoint->GetPosition());
					pxTactical->LetRouteSetLookAt(true);
					pxTactical->SetAtDestinationRange(1.0f);
					SetUpdateState(MOVE_TO_PATROL_POINT);
				}
			}
			else
			{
				SetFinished();
				return;
			}
		}break;
		//--------------------------------------
		case MOVE_TO_PATROL_POINT:
		{
			if (pxTactical->HasArrivedAtDestination())
			{
				AVP_ServerNode_OrientedNode* pxPatrolPoint = AVP_ServerNode_OrientedNode::FindEntity(m_uCurrentPatrolGuid);
				m_fWaitTime = pxPatrolPoint ? ( pxPatrolPoint->GetWaitTime() + pxPatrolPoint->GetWaitTimeRandomOffset() ) : 0.0f;

				SetUpdateState(WAIT_AT_PATROL_POINT);
			}
		}break;
		//--------------------------------------
		case WAIT_AT_PATROL_POINT:
		{
			pxTactical->LetRouteSetLookAt(false);
			SetLookAtFromNode();

			m_fWaitTime -= fTimeslice;

			if (m_fWaitTime < 0.0f)
			{
				SetUpdateState(SELECT_PATROL_POINT);
			}

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

		}break;
	}
}

//---------------------------------------------------------------------------
// AVP_Behaviour_Marine_Patrol :: SetLookAtFromNode
//---------------------------------------------------------------------------
void AVP_Behaviour_Marine_Patrol::SetLookAtFromNode(void)
{
	if (m_uCurrentPatrolGuid != ASURA_GUID_UNREGISTERED)
	{
		AVP_ServerNode_OrientedNode*	pxNode		= GetPatrolPoint();
		AVP_ServerActor_Human*			pxOurActor	= GetMarineBrain()->GetHumanActor();
		Asura_Matrix_3x3				xNodeOri;
		pxNode->GetOrientation( xNodeOri );
		Asura_Vector_3					xLookAt		= Asura_Vector_3(xNodeOri.e13, xNodeOri.e23, xNodeOri.e33);
		Asura_Vector_3					xMPPos		= Asura_Maths::ZeroVector3;

		pxOurActor->GetChestPosition(xMPPos);
		xLookAt = xMPPos + xLookAt;
		GetMarineBrain()->AimAtPosition(xLookAt);
	}
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
bool AVP_Behaviour_Marine_Patrol::SelectGuardZone(void)
{
	AVP_ServerActor_Human* pxOurActor = GetMarineBrain()->GetHumanActor();

	if (pxOurActor->GetGuardZoneGuid() != ASURA_GUID_UNREGISTERED)
	{
		m_uGuardZoneGuid = pxOurActor->GetGuardZoneGuid();
		return true;
	}
	else
	{
		return false;
	}
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
AVP_ServerNode_OrientedNode* AVP_Behaviour_Marine_Patrol::GetPatrolPoint(void)
{
	return AVP_ServerNode_OrientedNode::FindEntity(m_uCurrentPatrolGuid);
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
AVP_ServerNode_GuardZone* AVP_Behaviour_Marine_Patrol::GetGuardZone(void)
{
	return AVP_ServerNode_GuardZone::FindEntity(m_uGuardZoneGuid);
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#ifndef ASURA_NO_DEBUG_OUTPUT
static const char* s_aszStateStrings[AVP_Behaviour_Marine_Patrol::MAX_STATES] =
{
	"PATROL - UNSET",
	"PATROL - SELECT_GUARD_ZONE",
	"PATROL - SELECT_PATROL_POINT",
	"PATROL - MOVE_TO_PATROL_POINT",
	"PATROL - WAIT_AT_PATROL_POINT",
};

void AVP_Behaviour_Marine_Patrol::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
