//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Predator.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.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_Flee.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Melee.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Seeking.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Stalking.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_AtVentagePoint.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Predator.h"
#include "Core/AVP_Navigation_Types.h"

// avp
#include "Entity/AVP_Messages.h"
#include "Entity/Locations/VantagePoint/AVP_ServerNode_VantagePoint_Predator.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_Vertebrate_Messages.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ServerActor_Predator.h"
#include "Knowledge/AVP_Knowledge_System_Predator.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Predator
//
//////////////////////////////////////////////////////////////////////////////

AVP_Behaviour_Predator::AVP_Behaviour_Predator( AVP_Brain_Actor_Predator* pxBrain )
: PARENT( pxBrain, AVP_BEHAVIOUR_TYPE_PREDATOR )
, m_eCurrentState( UNSET )
, m_fTimeInState( 0.0f )
, m_iNextHealthPrecentageToChangeBehaviour(0)
, m_bNeedToCounterGrapple(false)
{
}

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

    PARENT::Deactivate();
}

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

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

    m_fTimeInState += fTimeslice;

	CounterGrapple();


	// wait for the fall or jump to finish...
	if (!GetPredatorBrain()->CanPlanMove())
	{
		return;
	}

	switch ( m_eCurrentState )
	{
	case UNSET:
		if ( GetPredatorBrain()->GetPredatorActor()->GetUseTestBehaviour() && m_eCurrentState != TEST_BEHAVIOUR)
		{
			SwitchToState( TEST_BEHAVIOUR );
		}
		else
		{
			SwitchToState(IDLE);
		}
		break;
	case STALKING:
		{
			AVP_Behaviour_Predator_Stalking* pxBehaviour = static_cast< AVP_Behaviour_Predator_Stalking* >( GetChildWithType( this, AVP_BEHAVIOUR_TYPE_PREDATOR_STALKING ) );
			if (GetPredatorBrain()->GetPredatorActor()->GetBehaviourData().m_iBehaviour != AVP_PredatorBehaviourData::STALK && pxBehaviour && !pxBehaviour->IsFinished())
			{
				pxBehaviour->Abort();
			}
			else if (HealthFallenTooMuch())
			{
				SwitchToState(FLEE);
			}
			else if ( !pxBehaviour || pxBehaviour->IsInError() || pxBehaviour->IsFinished())
			{
				SwitchToState(IDLE);
			}
			else if (GetPredatorBrain()->AreAllTargetsTooOld())
			{
				SwitchToState(SEEKING);
			}
		}
		break;
	case SEEKING:
		{
			AVP_Behaviour_Predator_Seeking* pxBehaviour = static_cast< AVP_Behaviour_Predator_Seeking* >( GetChildWithType( this, AVP_BEHAVIOUR_TYPE_PREDATOR_SEEKING ) );
			if ( !pxBehaviour || pxBehaviour->IsInError() || pxBehaviour->IsFinished())
			{
				SwitchToState(IDLE);
			}
			else if (GetPredatorBrain()->WasAnyTargetSeenRecently(GetPredatorBrain()->GetConfigInfo().m_xHighLevelBehaviour.m_fSeenRecentlyTime))
			{
				switch (GetPredatorBrain()->GetPredatorActor()->GetBehaviourData().m_iBehaviour)
				{
				case AVP_PredatorBehaviourData::MELEE:
					SwitchToState(MELEE);				
					break;
				case AVP_PredatorBehaviourData::STALK:
					SwitchToState(STALKING);				
					break;
				}
			}
		}
		break;
	case FLEE:
		{
			AVP_Behaviour_Predator_Flee* pxBehaviour = static_cast< AVP_Behaviour_Predator_Flee* >( GetChildWithType( this, AVP_BEHAVIOUR_TYPE_PREDATOR_FLEE ) );
			if ( !pxBehaviour || pxBehaviour->IsInError() || pxBehaviour->IsFinished())
			{
				SwitchToState(IDLE);
			}
			if (m_fTimeInState > GetPredatorBrain()->GetConfigInfo().m_xFleeBehaviour.m_fFleeTime)
			{
				SwitchToState(IDLE);
			}
		}
		break;
	case IDLE:
		SwitchToState(SEEKING);
		break;
	case ALERT:
		{
			if (GetPredatorBrain()->GetKnowledgeSystem()->GetBestThreatKnowledge().GetGuid() != ASURA_GUID_UNREGISTERED)
			{
				SwitchToState(ALERT);				
			}
			else
			{
				const Axon_Knowledge& xAlert = GetPredatorBrain()->GetKnowledgeSystem()->GetBestAlertKnowledge();
				if (xAlert.GetGuid() == ASURA_GUID_UNREGISTERED )
				{
					SwitchToState(IDLE);
				}
				else
				{
					Asura_Vector_3 xDir = xAlert.m_xDirection;
					GetPredatorBrain()->SetAimDirection(xDir);

				}
			}
		}
		break;
	case MELEE:
		{
			AVP_Behaviour_Predator_Melee* pxBehaviour = static_cast< AVP_Behaviour_Predator_Melee* >( GetChildWithType( this, AVP_BEHAVIOUR_TYPE_PREDATOR_MELEE ) );
			if (GetPredatorBrain()->GetPredatorActor()->GetBehaviourData().m_iBehaviour != AVP_PredatorBehaviourData::MELEE && pxBehaviour && !pxBehaviour->IsFinished())
			{
				pxBehaviour->Abort();
			}
			else if (pxBehaviour && pxBehaviour->ShouldFlee())
			{
				SwitchToState(FLEE);
			}
			else if ( !pxBehaviour || pxBehaviour->IsInError() || pxBehaviour->IsFinished())
			{
				SwitchToState(IDLE);
			}			
		}
		break;
	case TEST_BEHAVIOUR:
		break;
	default:
		Asura_Assert(false, "Unexpected state");
	}
}

bool AVP_Behaviour_Predator::HandleMessage( const Asura_Message* pxMessage )
{
	switch (pxMessage->GetMsgID())
	{
	case AVP_MSGID_GRAPPLE_START:
		m_bNeedToCounterGrapple = true;
		return true;
	default:
		break;
	}

	return PARENT::HandleMessage( pxMessage );
}


#ifndef ASURA_NO_DEBUG_OUTPUT
static const char* s_aszStateStrings[] =
{
    "UNSET",
	"IDLE",
	"ALERT",
	"STALKING",
	"SEEKING",
	"FLEE",
	"MELEE",
	"TEST_BEHAVIOUR",	
	"NUM_STATES"

};

void AVP_Behaviour_Predator::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::GetPredatorBrain() const
{
    return static_cast< AVP_Brain_Actor_Predator* >( GetProjectBrain() );
}


Asura_Guid AVP_Behaviour_Predator::GetTarget() const
{
	const Axon_Knowledge& xKnowledge = GetPredatorBrain()->GetKnowledgeSystem()->GetBestThreatKnowledge();
	return xKnowledge.GetGuid();
}

void AVP_Behaviour_Predator::SwitchToState( const State eNextState )
{
	
	switch ( m_eCurrentState )
	{
	case UNSET:
		break;
	case STALKING:
		DestroyChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_STALKING );
		break;
	case SEEKING:
		DestroyChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_SEEKING );
		break;
	case IDLE:
	case ALERT:
		break;
	case FLEE:
		DestroyChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_FLEE );
		break;
	case MELEE:
		GetPredatorBrain()->SetNearbyActorGuidToIgnore(ASURA_GUID_UNREGISTERED);
		DestroyChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_MELEE );
		break;
	case TEST_BEHAVIOUR:
		DestroyChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_TESTBEHAVIOUR );
		break;
	default:
		Asura_Assert(false, "Unexpected state");
	}

	m_eCurrentState = eNextState;
	m_fTimeInState  = 0.0f;
	switch ( m_eCurrentState )
	{
	case UNSET:
	case IDLE:		
		break;
	case SEEKING:
		{
			AVP_Behaviour_Predator_Seeking* pxBehaviour = static_cast< AVP_Behaviour_Predator_Seeking* >( CreateChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_SEEKING ) );
			if ( !pxBehaviour || pxBehaviour->IsInError() || pxBehaviour->IsFinished())
			{
				SwitchToState( IDLE );
			}
		}
		break;
	case STALKING:
		{
			AVP_Behaviour_Predator_Stalking* pxBehaviour = static_cast< AVP_Behaviour_Predator_Stalking* >( CreateChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_STALKING ) );
			if ( !pxBehaviour || pxBehaviour->IsInError() || pxBehaviour->IsFinished())
			{
				SwitchToState( IDLE );
			}
		}
		break;
	case FLEE:
		{
			AVP_Behaviour_Predator_Flee* pxBehaviour = static_cast< AVP_Behaviour_Predator_Flee* >( CreateChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_FLEE ) );
			if ( !pxBehaviour || pxBehaviour->IsInError() || pxBehaviour->IsFinished())
			{
				SwitchToState( IDLE );
			}
		}
		break;
	case ALERT:		
		break;
	case MELEE:
		{
			const Axon_Knowledge& xTargetKnowledge = GetPredatorBrain()->GetKnowledgeSystem()->GetBestThreatKnowledge();
			if (xTargetKnowledge.GetGuid() == ASURA_GUID_UNREGISTERED)
			{
				SwitchToState(IDLE);
			}
			else
			{
				GetPredatorBrain()->SetNearbyActorGuidToIgnore(xTargetKnowledge.GetGuid());

				AVP_Behaviour_Predator_Melee* pxBehaviour = static_cast< AVP_Behaviour_Predator_Melee* >( CreateChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_MELEE ) );
				if ( !pxBehaviour || pxBehaviour->IsInError() || pxBehaviour->IsFinished())
				{
					SwitchToState( IDLE );
				}
			}
		}
		break;

	case TEST_BEHAVIOUR:
		{
			DestroyAllChildren();
			CreateChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_TESTBEHAVIOUR );
		}
		break;
	default:
		Asura_Assert(false, "Unexpected state");
	}
}

bool AVP_Behaviour_Predator::HealthFallenTooMuch()
{
	const AVP_Brain_Actor_Predator_ConfigurationData& xConfig = GetPredatorBrain()->GetConfigInfo();

	if (m_iNextHealthPrecentageToChangeBehaviour == xConfig.m_xHighLevelBehaviour.m_iNumberOfDamagePercentPoint)
	{
		return false;
	}

	AVP_ServerActor_Predator* pxActor = GetPredatorBrain()->GetPredatorActor();

	float fMaxHealth = pxActor->GetMaxHealth();
	float fCurrentHealth = pxActor->GetHealth();

	if (fMaxHealth - fCurrentHealth > 
		fMaxHealth * xConfig.m_xHighLevelBehaviour.m_fDamagePercentPoint[m_iNextHealthPrecentageToChangeBehaviour])
	{
		++m_iNextHealthPrecentageToChangeBehaviour;
	return true;
	}

	return false;
}

void AVP_Behaviour_Predator::CounterGrapple()
{
	if (m_bNeedToCounterGrapple )
	{
		m_bNeedToCounterGrapple = false;
		Asura_Message* pxMessage = Asura_Network::CreateMessage(AVP_MSGID_ACTIVEGRAPPLE_VALUE, sizeof(AVP_Message_Vertebrate_ActiveGrappleValue));

		if (pxMessage)
		{
			AVP_Message_Vertebrate_ActiveGrappleValue* pxData = static_cast <AVP_Message_Vertebrate_ActiveGrappleValue*>(pxMessage->GetData());
			pxData->SetValue(1.0f);
		}
		pxMessage->SetFromGuid( GetPredatorBrain()->GetOwnerGuid() );
		pxMessage->SetToGuid( GetPredatorBrain()->GetOwnerGuid() );
		Asura_Network::Send( pxMessage );
	}
}

bool AVP_Behaviour_Predator::GetToPosition( const Asura_Vector_3& xDestination )
{
	AVP_Navigation::NavData& xNavData = GetPredatorBrain()->GetNavData();
	xNavData.m_xStartPosition       = GetPredatorBrain()->GetActor()->GetPosition();
	xNavData.m_xDestinationPosition = xDestination;
	// 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( xDestination, xNavData.m_usDestinationWPID, xTestParams ) )
	{
		return false;
	}

	xNavData.ResetRoute();
	if ( !AVP_Navigation::FindRoute( xNavData) )
	{
		return false;
	}
	return true;
}

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