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

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

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

// header
#include "Core/BrainTypes/AVP_Brain_Actor_Predalien.h"


// asura
#include "Blueprint/Asura_Blueprint.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_Scheduler.h"
#include "Rendering/Asura_Font_System.h"
#include "Physics/Asura_Physics_MovementQuery.h"

// Axon headers
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Behaviour/BehaviourTypes/Predalien/AVP_Behaviour_Predalien.h"
#include "Behaviour/BehaviourTypes/Predalien/AVP_Behaviour_Predalien_Movement.h"
#include "Behaviour/BehaviourTypes/Predalien/AVP_Behaviour_Predalien_GS2Move.h"
#include "Behaviour/BehaviourTypes/Predalien/AVP_Behaviour_Predalien_Test.h"
#include "Core/Axon_Project_System.h"
#include "Core/Axon_System.h"

// AVP
#include "Actor/Vertebrates/Predalien/AVP_Predalien_AIAnimStateMachine.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Core/AVP_LOS.h"
#include "Entity/AVP_Messages.h"
#include "Entity/Locations/VantagePoint/AVP_ServerNode_VantagePoint_Predator.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predalien/AVP_ClientActor_Predalien.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predalien/AVP_ServerActor_Predalien.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predalien/AVP_Messages_Predalien.h"

#include "Debugging/Asura_DebugLineRender.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Brain_Actor_Predalien
//
//////////////////////////////////////////////////////////////////////////////

AVP_Brain_Actor_Predalien::AVP_Brain_Actor_Predalien( AVP_ServerActor_Predalien* pxPredalien )
: PARENT( pxPredalien )
, m_uFlags( 0 )
, m_xSpawnPoint(pxPredalien->GetPosition())
, m_bIsFalling(false)
, m_bIsJumping(false)
, m_bJustFinishedFalling(false)
, m_bIsMelee(false)
, m_fNotSupportedTime(0.0)
, m_xConfig()
{
	// defaults that should be overwritten by the blueprint
	m_xConfig.m_fMeleeConfig.m_fMinTimeBetweenAttacks = 0.0f;
	m_xConfig.m_fMeleeConfig.m_fMaxTimeBetweenAttacks = 10.0f;

	SetNormalTurnSpeed();
	SetNormalAimSpeed();
}

void AVP_Brain_Actor_Predalien::InitialiseFromBlueprint( const Asura_Blueprint& xBlueprint )
{
    PARENT::InitialiseFromBlueprint( xBlueprint );

	Asura_Value xValue;

	const Asura_Blueprint* xMeleeConfigBP = Asura_Blueprint_System::GetBlueprintByHashID(0x7ad7bd1b, 0xb1f06402 ); //"AI Settings", "MeleeConfiguration"
	if (xMeleeConfigBP)
	{
		if (xMeleeConfigBP->GetParameterValueByHashID(0x6ebece12, xValue)) //"MinttimeBetweenAttacks"
		{
			m_xConfig.m_fMeleeConfig.m_fMinTimeBetweenAttacks = xValue.GetFloatValue();
		}
		if (xMeleeConfigBP->GetParameterValueByHashID(0x164981c0, xValue)) //"MaxttimeBetweenAttacks"
		{
			m_xConfig.m_fMeleeConfig.m_fMaxTimeBetweenAttacks = xValue.GetFloatValue();
		}
	}

    DestroyAllBehaviours();
	if ( IsActive() )
	{
		CreateActivateAndPushBehaviour( AVP_BEHAVIOUR_TYPE_PREDALIEN );
		CreateGS2BehaviourIfNecessary();
	}

	EstablishNavData();
}

void AVP_Brain_Actor_Predalien::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
    int iVersion;
    xStream >> iVersion;

    PARENT::ReadFromChunkStream( xStream );

    if ( iVersion == CURRENT_CHUNK_VERSION )
    {
        Asura_ReadFromStream_Bitfield_Wrapper< u_int > xBitfieldWrapper( m_uFlags );
        xStream >> xBitfieldWrapper;
    }
    else if ( iVersion == 0 )
    {
    }
    else
    {
        Asura_Assertf( false, ( "AVP_Brain_Actor_Predalien::ReadFromChunkStream - unhandled chunk version (%d)", iVersion ) );
    }
}

void AVP_Brain_Actor_Predalien::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
    xStream << CURRENT_CHUNK_VERSION;

    PARENT::WriteToChunkStream( xStream );

    Asura_WriteToStream_Bitfield_Wrapper< u_int > xBitfieldWrapper( m_uFlags );
    xBitfieldWrapper.Add( false, 1 );
    xStream << xBitfieldWrapper;
}


void AVP_Brain_Actor_Predalien::DoPerFrameUpdate( const float fTimeslice )
{
	if (!m_bActive)
	{
		return;
	}

	UpdateFalling(fTimeslice);

	PARENT::DoPerFrameUpdate( fTimeslice );
}


#include "Core/Asura_MemoryManagement_DebugOff.h"
Axon_Behaviour* AVP_Brain_Actor_Predalien::CreateBehaviour( const u_int uBehaviourType, const Asura_Message* pxMessage /*= NULL*/ )
{
    void* pxMemory = s_pxBehaviourMempool->Allocate();
    if ( !pxMemory )
    {
        Asura_Assert( false, "Axon_Project_Brain::CreateBehaviour - could not allocate behaviour memory" );
        return NULL;
    }

    Axon_Behaviour* pxBehaviour = NULL;
    switch ( uBehaviourType )
    {
        case AVP_BEHAVIOUR_TYPE_PREDALIEN:
        {
            pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predalien( this );
            break;
        }
		case AVP_BEHAVIOUR_TYPE_PREDALIEN_MOVEMENT:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predalien_Movement( this );
			break;
		}
		case AVP_BEHAVIOUR_TYPE_PREDALIEN_TEST:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predalien_Test( this );
			break;
		}
		case AVP_BEHAVIOUR_TYPE_PREDALIEN_GS2MOVE:
		{
			pxBehaviour = new ( pxMemory ) AVP_Behaviour_Predalien_GS2Move( this );
			break;
		}

		default:
        {
            break;
        }
    }

    if ( !pxBehaviour )
    {
        // Couldn't create the behaviour - try the parent.
        s_pxBehaviourMempool->Deallocate( pxMemory );
        pxBehaviour = PARENT::CreateBehaviour( uBehaviourType, pxMessage );
    }
    else if ( pxMessage )
    {
        pxBehaviour->DoHandleMessage( pxMessage, true );
    }

    return pxBehaviour;
}
#include "Core/Asura_MemoryManagement_DebugOn.h"

void AVP_Brain_Actor_Predalien::KnockDown()
{
	if (m_pxFirstBehaviour->GetType() == AVP_BEHAVIOUR_TYPE_PREDALIEN)
	{
		static_cast<AVP_Behaviour_Predalien*>(m_pxFirstBehaviour)->KnockDown();
	}
	else
	{
		AVP_Behaviour_Predalien* pxBehaviour = static_cast<AVP_Behaviour_Predalien*>(m_pxFirstBehaviour->GetChildWithType(m_pxFirstBehaviour, AVP_BEHAVIOUR_TYPE_PREDALIEN));
		if (pxBehaviour)
		{
			pxBehaviour->KnockDown();
		}
	}
}


Asura_Guid AVP_Brain_Actor_Predalien::GetPrimaryThreatGuid()
{
	return ASURA_GUID_UNREGISTERED;
}

Asura_Guid AVP_Brain_Actor_Predalien::GetAlertGuid( void )
{
	return ASURA_GUID_UNREGISTERED;
}



void AVP_Brain_Actor_Predalien::SetFastTurnSpeed()
{
	SetMaxMoveDirAngularVelocity( Asura_Maths::Pi * 4.0f );
}

void AVP_Brain_Actor_Predalien::SetNormalTurnSpeed()
{
	SetMaxMoveDirAngularVelocity( Asura_Maths::Pi * 2.0f );

}

void AVP_Brain_Actor_Predalien::SetNormalAimSpeed()
{
	SetMaxAimDirAngularVelocity( Asura_Maths::Pi * 2.0f );
	SetMaxLookDirAngularVelocity( Asura_Maths::Pi * 2.0f );
}

AVP_ServerActor_Predalien* AVP_Brain_Actor_Predalien::GetPredalienActor() const
{
    return static_cast< AVP_ServerActor_Predalien* >( GetActor() );
}

AVP_ClientActor_Predalien* AVP_Brain_Actor_Predalien::GetPredalienClientActor() const
{
		return static_cast< AVP_ClientActor_Predalien* >( GetClientActor() );
}



void AVP_Brain_Actor_Predalien::SetJumping( bool bIsJumping )
{
	Asura_Assert(m_bIsJumping != bIsJumping, "Setting Jump to existing value - mismatch");
	m_bIsJumping = bIsJumping;
}

void AVP_Brain_Actor_Predalien::SetMelee( bool bIsMelee )
{
	Asura_Assert(m_bIsMelee != bIsMelee, "Setting Melee to existing value - mismatch");
	m_bIsMelee = bIsMelee;
}


u_int AVP_Brain_Actor_Predalien::GetLargestBehaviourSize()
{
    u_int uLargestBehaviourSize = 0;

	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predalien );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predalien_Movement );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predalien_Test );
	AXON_CALC_MAX_BEHAVIOUR_SIZE( uLargestBehaviourSize, AVP_Behaviour_Predalien_GS2Move );
	
    return uLargestBehaviourSize;
}

bool AVP_Brain_Actor_Predalien::HandleMessage( const Asura_Message* pxMessage )
{
	return PARENT::HandleMessage( pxMessage );
}

DEBUGVAR float dbg_fSlowRange = 4.0f;
DEBUGVAR float dbg_fMediumRange = 8.0f;
DEBUGVAR float dbg_fFastRange = 10.0f;

float AVP_Brain_Actor_Predalien::GetNearbyRange() const
{
	switch (GetActor()->GetAIAnimStateMachine()->GetTargetState())
	{

	case AVP_Vertebrate_AIAnimStateMachine::ACTOR_STATE_IDLE:
		return 0.0f;
	case AVP_Vertebrate_AIAnimStateMachine::VERTEBRATE_STATE_MOVE_SLOW:
		return dbg_fSlowRange;
	default:
	case AVP_Vertebrate_AIAnimStateMachine::VERTEBRATE_STATE_MOVE_MEDIUM:
		return dbg_fMediumRange;
	case AVP_Vertebrate_AIAnimStateMachine::VERTEBRATE_STATE_MOVE_FAST:
		return dbg_fFastRange;
	}
}

#ifndef ASURA_NO_DEBUG_OUTPUT
bool AVP_Brain_Actor_Predalien::Verify()
{
	return ( PARENT::Verify() );
}
#endif

void AVP_Brain_Actor_Predalien::EstablishNavData()
{
	AVP_Navigation::NavData& xNavData = GetNavData();

	xNavData.m_uPathfinderFlags = AVP_Navigation::AVP_AI_PATHFINDER_FLAG_PREDATOR | ASURA_AI_PATHFINDER_FLAGS_IGNOREZONES;

	// Establish our current waypoint ID.
	AVP_Navigation::TestParams xTestParams;
	xTestParams.m_uPathfinderFlags		= xNavData.m_uPathfinderFlags;
	xTestParams.m_bRejectNonalignedWPs	= true;
	xTestParams.m_xNormal				= -GetPredalienActor()->GetCurrentGravityDirection();
	AVP_Navigation::GetNearestWaypointID( GetPredalienActor()->GetPosition(), xNavData.m_usCurrentWPID, xTestParams );

	// As a predator we don't use zones.
	xNavData.m_usCurrentZone = ASURA_NAVVERTEX_MAX_ZONE_ID;

	xNavData.m_fCharacterHeight = 2.0f;
	xNavData.m_fCharacterWidth  = 1.0f;

	xNavData.m_eMovementMode = AVP_Navigation::MM_MEDIUM;
}

DEBUGVAR float dbg_fPreFallTime = 0.2f;

void AVP_Brain_Actor_Predalien::UpdateFalling(float fTimeslice )
{
	m_bJustFinishedFalling = false;

	if (IsJumping())
	{
		m_bIsFalling = false;
		m_bJustFinishedFalling = false;	
		return;
	}

	bool bIsSupported = GetActor()->GetPhysicsObject()->IsSupported();

	if (!bIsSupported)
	{
		m_fNotSupportedTime += fTimeslice;
		if (m_fNotSupportedTime > dbg_fPreFallTime)
		{
			m_bIsFalling = true;
		}
	}
	else
	{
		m_fNotSupportedTime = 0.0f;
		if (m_bIsFalling)
		{
			m_bIsFalling = false;
			m_bJustFinishedFalling = true;
		}
	}
}


DEBUGVAR float dbg_fVerySmallDeltaSquared = 0.5f*0.5f;
DEBUGVAR float dbg_fSmallDeltaSquared = 2.0f*2.0f;
DEBUGVAR float dbg_fMediumDeltaSquared = 5.0f*5.0f;
DEBUGVAR float dbg_fLargeDeltaSquared = 15.0f*15.0f;
DEBUGVAR float dbg_fVeryLargeDeltaSquared = 25.0f*25.0f;

DEBUGVAR float dbg_fVerySmallPathDistance = 7.0f;
DEBUGVAR float dbg_fSmallPathDistance = 20.0f;
DEBUGVAR float dbg_fMediumPathDistance = 40.0f;
DEBUGVAR float dbg_fLargePathDistance = 80.0f;

DEBUGVAR float dbg_fVerySmallTurnCosDelta = Asura_Maths::Cosine(Asura_Maths::DegToRad(15.0f));
DEBUGVAR float dbg_fSmallTurnCosDelta = Asura_Maths::Cosine(Asura_Maths::DegToRad(30.0f));
DEBUGVAR float dbg_fMediumTurnCosDelta = Asura_Maths::Cosine(Asura_Maths::DegToRad(45.0f));


bool AVP_Brain_Actor_Predalien::ShouldRefreshPath(float fPathLength, float fDeltaDestinationSquared, float fDeltaTargetForwardCos) const
{

	const AVP_Navigation::NavData& xNavData = GetNavData();
	if (xNavData.m_usRouteLength == 0)
	{
		return true;
	}

	if (fPathLength < dbg_fVerySmallPathDistance)
	{
		if (fDeltaDestinationSquared > dbg_fVerySmallDeltaSquared || fDeltaTargetForwardCos < dbg_fVerySmallTurnCosDelta)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else if (fPathLength < dbg_fSmallPathDistance)
	{
		if (fDeltaDestinationSquared > dbg_fSmallDeltaSquared || fDeltaTargetForwardCos < dbg_fSmallTurnCosDelta)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else if (fPathLength < dbg_fMediumPathDistance || fDeltaTargetForwardCos < dbg_fMediumTurnCosDelta)
	{
		if (fDeltaDestinationSquared > dbg_fMediumDeltaSquared)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else if (fPathLength < dbg_fLargePathDistance)
	{
		if (fDeltaDestinationSquared > dbg_fLargeDeltaSquared)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else
	{
		if (fDeltaDestinationSquared > dbg_fVeryLargeDeltaSquared)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

}



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