//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_AVP3Alien_AttackThreat.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\AVP3Alien\AVP_Behaviour_AVP3Alien_AttackThreat.h"

// asura
#include "AI/Asura_AI_Navigation.h"
#include "Blueprint/Asura_Blueprint_System.h"
#ifndef ASURA_NO_DEBUG_OUTPUT
#include "Debugging/Asura_DebugLineRender.h"
#include "Network/Asura_Message.h"
#include "Rendering/Asura_Font_System.h"
#endif

// axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Behaviour/BehaviourTypes/AVP3Alien/AVP_Behaviour_AVP3Alien_GetToMovingTarget.h"
#include "Core/BrainTypes/Axon_Project_Brain.h"

// avp
#include "Actor/Vertebrates/Alien/AVP_Alien_StateHandler.h"
#include "Entity/AVP_Damage_Messages.h"
#include "Entity/AVP_Messages.h"
#include "Entity/Controller/AVP_ServerEntity_AlienTeam.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_ServerActor_Vertebrate.h"
#include "Knowledge/AVP_Knowledge_System.h"
#include "Rendering/Asura_Project_Skin_Elements.h" // Limbs


//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_AVP3Alien_AttackThreat
//
//////////////////////////////////////////////////////////////////////////////

AVP_Behaviour_AVP3Alien_AttackThreat::AVP_Behaviour_AVP3Alien_AttackThreat( Axon_Project_Brain* pxBrain )
: PARENT( pxBrain, AXON_BEHAVIOUR_TYPE_AVP3ALIEN_ATTACK_THREAT )
, m_uCurrentAttackType( NUM_ATTACK_TYPE )
, m_fCurrentAttackTimer( 0.0f )
, m_bDamageDealt( false )
, m_bSecondAttackPossible( true )
, m_uTargetGuid( ASURA_GUID_UNREGISTERED )
{
}


bool AVP_Behaviour_AVP3Alien_AttackThreat::Activate()
{
//	if( !PARENT::Activate() )
//	{
//		return false;
//	}

	// Wall or Floor? (Ideally there's no movement during attack, so this can be constant for the duration of the behaviour.)
	const Asura_AI_NavVertex* const pxCurrentWaypoint = GetProjectBrain() ? Asura_AI_Navigation_System::GetWaypoint( GetProjectBrain()->GetNavData().m_usCurrentWPID ) : NULL;
	if( pxCurrentWaypoint )
	{
		// Wall
		if( pxCurrentWaypoint->IsCeilingWaypoint() || pxCurrentWaypoint->IsWallWaypoint() )
		{
			// TODO: from blueprint
			m_axAttackAnims[ATTACK_TYPE_CLAW_1].eAnimType = AVP_Alien_Description::ALIEN_ANIM_CROUCH_CLAW_ATTACK_1;
			m_axAttackAnims[ATTACK_TYPE_CLAW_1].eReturnAnimType = AVP_Alien_Description::ALIEN_ANIM_CROUCH_CLAW_ATTACK_1_RETURN;
			m_axAttackAnims[ATTACK_TYPE_CLAW_1].fDoubleAttackProb = 0.0f;

			m_axAttackAnims[ATTACK_TYPE_CLAW_2].eAnimType = AVP_Alien_Description::ALIEN_ANIM_CROUCH_CLAW_ATTACK_2;
			m_axAttackAnims[ATTACK_TYPE_CLAW_2].eReturnAnimType = AVP_Alien_Description::ALIEN_ANIM_CROUCH_CLAW_ATTACK_2_RETURN;
			m_axAttackAnims[ATTACK_TYPE_CLAW_2].fDoubleAttackProb = 0.0f;

			m_axAttackAnims[ATTACK_TYPE_TAIL].eAnimType = AVP_Alien_Description::ALIEN_ANIM_TAIL_ATTACK;
			m_axAttackAnims[ATTACK_TYPE_TAIL].eReturnAnimType = AVP_Alien_Description::NUM_ANIMATION_IDS; // ALIEN_ANIM_TAIL_ATTACK_RETURN is broken (AN 10/10/07)
			m_axAttackAnims[ATTACK_TYPE_TAIL].fDoubleAttackProb = 0.0f;
		}
		else
		// Floor
		{
			// TODO: from blueprint
			m_axAttackAnims[ATTACK_TYPE_CLAW_1].eAnimType = AVP_Alien_Description::ALIEN_ANIM_STAND_CLAW_ATTACK_1;
			m_axAttackAnims[ATTACK_TYPE_CLAW_1].eReturnAnimType = AVP_Alien_Description::ALIEN_ANIM_STAND_CLAW_ATTACK_1_RETURN;
			m_axAttackAnims[ATTACK_TYPE_CLAW_1].fDoubleAttackProb = 1.0f;

			m_axAttackAnims[ATTACK_TYPE_CLAW_2].eAnimType = AVP_Alien_Description::ALIEN_ANIM_STAND_CLAW_ATTACK_2;
			m_axAttackAnims[ATTACK_TYPE_CLAW_2].eReturnAnimType = AVP_Alien_Description::ALIEN_ANIM_STAND_CLAW_ATTACK_2_RETURN;
			m_axAttackAnims[ATTACK_TYPE_CLAW_2].fDoubleAttackProb = 1.0f;

			m_axAttackAnims[ATTACK_TYPE_TAIL].eAnimType = AVP_Alien_Description::ALIEN_ANIM_TAIL_ATTACK;
			m_axAttackAnims[ATTACK_TYPE_TAIL].eReturnAnimType = AVP_Alien_Description::NUM_ANIMATION_IDS; // ALIEN_ANIM_TAIL_ATTACK_RETURN is broken (AN 10/10/07)
			m_axAttackAnims[ATTACK_TYPE_TAIL].fDoubleAttackProb = 0.0f;
		}

		// Common - TODO: from blueprint
		// Claw 1
		m_axAttackAnims[ATTACK_TYPE_CLAW_1].uDamageType = AVP_Damage::CLAW_SLASH_RIGHT;
		m_axAttackAnims[ATTACK_TYPE_CLAW_1].uDirectionalDamageType = AVP_Alien_StateHandler::DDT_CLAW_RIGHT_SWIPE;
		m_axAttackAnims[ATTACK_TYPE_CLAW_1].uMarkerHashID = 0xA27FF67B; // Asura_GetHashID("rightclaw");
		m_axAttackAnims[ATTACK_TYPE_CLAW_1].fDamageTime = 0.15f;
		m_axAttackAnims[ATTACK_TYPE_CLAW_1].fDamageAmount = 20.0f;
		m_axAttackAnims[ATTACK_TYPE_CLAW_1].fDamageImpulseStrength = 1.250f;
		m_axAttackAnims[ATTACK_TYPE_CLAW_1].fMinDistSq = 0.0f * 0.0f;
		m_axAttackAnims[ATTACK_TYPE_CLAW_1].fMaxDistSq = 2.75f * 2.75f;
		m_axAttackAnims[ATTACK_TYPE_CLAW_1].fProjectionDistance = 0.75f;
		m_axAttackAnims[ATTACK_TYPE_CLAW_1].bLimbAvailable = true;

		// Claw 2
		m_axAttackAnims[ATTACK_TYPE_CLAW_2].uDamageType = AVP_Damage::CLAW_SLASH_LEFT;
		m_axAttackAnims[ATTACK_TYPE_CLAW_2].uDirectionalDamageType = AVP_Alien_StateHandler::DDT_CLAW_LEFT_SWIPE;
		m_axAttackAnims[ATTACK_TYPE_CLAW_2].uMarkerHashID = 0x6673E6E6; // Asura_GetHashID("leftclaw");
		m_axAttackAnims[ATTACK_TYPE_CLAW_2].fDamageTime = 0.15f;
		m_axAttackAnims[ATTACK_TYPE_CLAW_2].fDamageAmount = 20.0f;
		m_axAttackAnims[ATTACK_TYPE_CLAW_2].fDamageImpulseStrength = 1.250f;
		m_axAttackAnims[ATTACK_TYPE_CLAW_2].fMinDistSq = 0.0f * 0.0f;
		m_axAttackAnims[ATTACK_TYPE_CLAW_2].fMaxDistSq = 2.75f * 2.75f;
		m_axAttackAnims[ATTACK_TYPE_CLAW_2].fProjectionDistance = 0.75f;
		m_axAttackAnims[ATTACK_TYPE_CLAW_2].bLimbAvailable = true;

		// Tail
		m_axAttackAnims[ATTACK_TYPE_TAIL].uDamageType = AVP_Damage::TAIL_SLASH;
		m_axAttackAnims[ATTACK_TYPE_TAIL].uDirectionalDamageType = AVP_Alien_StateHandler::DDT_TAIL_SWIPE;
		m_axAttackAnims[ATTACK_TYPE_TAIL].uMarkerHashID = 0x00363450; // Asura_GetHashID("tail");
		m_axAttackAnims[ATTACK_TYPE_TAIL].fDamageTime = 0.3f;
		m_axAttackAnims[ATTACK_TYPE_TAIL].fDamageAmount = 50.0f;
		m_axAttackAnims[ATTACK_TYPE_TAIL].fDamageImpulseStrength = 3.0f;
		m_axAttackAnims[ATTACK_TYPE_TAIL].fMinDistSq = 2.5f * 2.5f;
		m_axAttackAnims[ATTACK_TYPE_TAIL].fMaxDistSq = 4.5f * 4.5f;
		m_axAttackAnims[ATTACK_TYPE_TAIL].fProjectionDistance = 2.25f;
		m_axAttackAnims[ATTACK_TYPE_TAIL].bLimbAvailable = true;

		Asura_Blueprint* pxBlueprint = NULL;
		Asura_Blueprint_System::GetBlueprint( 0x48EA81D7, 0x65F17DA6, pxBlueprint ); // Asura_GetHashID( "Weapons" ), Asura_GetHashID( "AlienClaw" )
		if ( pxBlueprint )
		{
			Asura_Value xValue;

			if( pxBlueprint->GetParameterValueByHashID( 0xF3684C87, xValue ) ) // Asura_GetHashID( "DamageAmount" )
			{
				m_axAttackAnims[ATTACK_TYPE_CLAW_1].fDamageAmount = xValue.GetFloatValue();
				m_axAttackAnims[ATTACK_TYPE_CLAW_2].fDamageAmount = xValue.GetFloatValue();
			}

			if( pxBlueprint->GetParameterValueByHashID( 0x5B2F3036, xValue ) ) // Asura_GetHashID( "ImpulseStrength" )
			{
				m_axAttackAnims[ATTACK_TYPE_CLAW_1].fDamageImpulseStrength = xValue.GetFloatValue();
				m_axAttackAnims[ATTACK_TYPE_CLAW_2].fDamageImpulseStrength = xValue.GetFloatValue();
			}

			if( pxBlueprint->GetParameterValueByHashID( 0x887A9744, xValue ) ) // Asura_GetHashID( "ProjectionDistance" )
			{
				m_axAttackAnims[ATTACK_TYPE_CLAW_1].fProjectionDistance = xValue.GetFloatValue();
				m_axAttackAnims[ATTACK_TYPE_CLAW_2].fProjectionDistance = xValue.GetFloatValue();
			}
		}

		Asura_Blueprint_System::GetBlueprint( 0x48EA81D7, 0x65F90F97, pxBlueprint ); // Asura_GetHashID( "Weapons" ), Asura_GetHashID( "AlienTail" )
		if ( pxBlueprint )
		{
			Asura_Value xValue;

			if( pxBlueprint->GetParameterValueByHashID( 0xF3684C87, xValue ) ) // Asura_GetHashID( "DamageAmount" )
			{
				m_axAttackAnims[ATTACK_TYPE_TAIL].fDamageAmount = xValue.GetFloatValue();
			}

			if( pxBlueprint->GetParameterValueByHashID( 0x5B2F3036, xValue ) ) // Asura_GetHashID( "ImpulseStrength" )
			{
				m_axAttackAnims[ATTACK_TYPE_TAIL].fDamageImpulseStrength = xValue.GetFloatValue();
			}

			if( pxBlueprint->GetParameterValueByHashID( 0x887A9744, xValue ) ) // Asura_GetHashID( "ProjectionDistance" )
			{
				m_axAttackAnims[ATTACK_TYPE_TAIL].fProjectionDistance = xValue.GetFloatValue();
			}
		}
	}
	else
	{
		Asura_Assert( 0, "Couldn't find current waypoint (or no brain)" );
		return false;
	}

	m_uCurrentAttackType = NUM_ATTACK_TYPE;
	m_bSecondAttackPossible = true;

	return true;
}


void AVP_Behaviour_AVP3Alien_AttackThreat::Deactivate()
{
	PARENT::Deactivate();

	if( AVP_ServerEntity_AlienTeam::GetInstance() && GetProjectBrain() && GetProjectBrain()->GetActor() )
	{
		AVP_ServerEntity_AlienTeam::GetInstance()->ReturnAttackToken( GetProjectBrain()->GetActor()->GetGuid() );
	}
}


// TODO: Remove if not needed
void AVP_Behaviour_AVP3Alien_AttackThreat::Update( const float fTimeslice )
{
//	PARENT::Update( fTimeslice );
}


void AVP_Behaviour_AVP3Alien_AttackThreat::PerFrameUpdate( const float fTimeslice )
{
	if ( IsFinished() || IsInError() ) { return; }

	if( ( !GetProjectBrain() ) || ( !GetProjectBrain()->GetKnowledgeSystem() ) )
	{
		SetInError();
		return;
	}

	UpdateStateTimer( fTimeslice );

	// Update limb status
	const AVP_ServerActor_Vertebrate* pxServerActor = static_cast<const AVP_ServerActor_Vertebrate*>( GetProjectBrain()->GetActor() );
	if( pxServerActor )
	{
		m_axAttackAnims[ATTACK_TYPE_CLAW_1].bLimbAvailable = pxServerActor->IsSkinElementAvailable( AVP_SET_RIGHT_ARM ) && pxServerActor->IsSkinElementAvailable( AVP_SET_RIGHT_ELBOW );
		m_axAttackAnims[ATTACK_TYPE_CLAW_2].bLimbAvailable = pxServerActor->IsSkinElementAvailable( AVP_SET_LEFT_ARM ) && pxServerActor->IsSkinElementAvailable( AVP_SET_LEFT_ELBOW );
		m_axAttackAnims[ATTACK_TYPE_TAIL].bLimbAvailable = pxServerActor->IsSkinElementAvailable( AVP_SET_ALIEN_TAIL ) && pxServerActor->IsSkinElementAvailable( AVP_SET_ALIEN_TAIL_TIP );
	}
	else
	{
		Asura_Assert( 0, "Couldn't find actor" );
		SetInError();
		return;
	}

	switch ( GetCurrentState() )
	{
		case STATE_INIT:
		{
			SwitchToState( STATE_ATTACK );
			break;
		}

		case STATE_ATTACK:
		{
			if( GetProjectBrain()->RequestedAnimationHasFinished() )
			{
				// Do return move after attack
				SwitchToState( STATE_RETURN );
			}
			else
			if( !m_bDamageDealt )
			{
				m_fCurrentAttackTimer += fTimeslice;

				if( m_fCurrentAttackTimer >= m_axAttackAnims[m_uCurrentAttackType].fDamageTime )
				{
					m_bDamageDealt = true;

					AVP_ServerEntity_Actor* pxAttackTarget = AVP_ServerEntity_Actor::FindEntity( m_uTargetGuid );
					if( !pxAttackTarget )
					{
						// No target, no damage to be dealt
						break;
					}

					const float fTargetDistSq = ( pxServerActor->GetPosition() - pxAttackTarget->GetPosition() ).MagnitudeSquared();

					if( ( m_axAttackAnims[m_uCurrentAttackType].bLimbAvailable ) && ( m_axAttackAnims[m_uCurrentAttackType].fMaxDistSq >= fTargetDistSq ) )
					{
						// Deal damage
						const AVP_Damage xDamage( static_cast<AVP_Damage::Type>( m_axAttackAnims[m_uCurrentAttackType].uDamageType ), m_axAttackAnims[m_uCurrentAttackType].fDamageAmount,
													GetProjectBrain()->GetActor()->GetGuid(), m_axAttackAnims[m_uCurrentAttackType].uMarkerHashID );

						pxAttackTarget->TakeDamage( xDamage );
					}
				}
/*
				// Calculate projection vector
				Asura_Vector_3 xProjectionDirection = Asura_Maths::ZeroVector3;
				if( m_axAttackAnims[m_uCurrentAttackType].fProjectionDistance > 0.0f )
				{
					const Asura_Matrix_3x3&	xOrientation = GetProjectBrain()->GetActor()->GetOrientation();
					xProjectionDirection = Asura_Vector_3( xOrientation.e13, xOrientation.e23, xOrientation.e33 );
					xProjectionDirection *= m_axAttackAnims[m_uCurrentAttackType].fProjectionDistance;
				}

				const AVP_Damage xDamage( static_cast<AVP_Damage::Type>( m_axAttackAnims[m_uCurrentAttackType].uDamageType ), m_axAttackAnims[m_uCurrentAttackType].fDamageAmount,
											GetProjectBrain()->GetActor()->GetGuid(), m_axAttackAnims[m_uCurrentAttackType].uMarkerHashID, m_axAttackAnims[m_uCurrentAttackType].fDamageImpulseStrength );
				Asura_Message* pxMessage = Asura_Network::CreateMessage( AVP_MSGID_CHECK_FOR_DIRECTIONAL_DAMAGE, sizeof( AVP_Message_CheckForDirectionalDamage ) );

				// Request directional damage check
				if( pxMessage )
				{
					AVP_Message_CheckForDirectionalDamage* pxData = static_cast<AVP_Message_CheckForDirectionalDamage*>( pxMessage->GetData() );

					pxData->SetDamage( xDamage );
					pxData->SetDirectionalDamageType( m_axAttackAnims[m_uCurrentAttackType].uDirectionalDamageType );
					pxData->SetProjectionDirection( xProjectionDirection );

					pxMessage->SetToGuid( GetProjectBrain()->GetActor()->GetGuid() );
					pxMessage->UseGuaranteedDelivery();

					Asura_Network::Send( pxMessage );
				}
*/			}
			break;
		}

		case STATE_RETURN:
		{
			if( GetProjectBrain()->RequestedAnimationHasFinished() )
			{
				const bool bAnotherAttack = m_bSecondAttackPossible ? Asura_Random::GetFloat() <= m_axAttackAnims[m_uCurrentAttackType].fDoubleAttackProb : false;
				if( bAnotherAttack )
				{
					SwitchToState( STATE_ATTACK );

					m_bSecondAttackPossible = false;
				}
				else
				{
					SetFinished();
				}
			}
			break;
		}

		default:
		{
			Asura_Assert( 0, "AVP_Behaviour_AVP3Alien_AttackThreat::SwitchToState - invalid prev state" );
			SetInError();
			return;
		}
	}
}


#ifndef ASURA_NO_DEBUG_OUTPUT
void AVP_Behaviour_AVP3Alien_AttackThreat::RenderDebugInfo()
{
	PARENT::RenderDebugInfo();

	GetProjectBrain()->GetDebugOutputPos().y += 10.0f;

	Asura_Font_System::Print( GetProjectBrain()->GetDebugOutputPos().x, GetProjectBrain()->GetDebugOutputPos().y, 0.5f, 0xFFFFFF28,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, "%s%s%s", m_axAttackAnims[0].bLimbAvailable ? "|RIGHT_CLAW|" : "", m_axAttackAnims[1].bLimbAvailable ? "|LEFT_CLAW|" : "", m_axAttackAnims[2].bLimbAvailable ? "|TAIL|" : "" );

	GetProjectBrain()->GetDebugOutputPos().y += 10.0f;
}
#endif


//////////////////////////////////////////////////////////////////////////////
//
// Private
//
//////////////////////////////////////////////////////////////////////////////

void AVP_Behaviour_AVP3Alien_AttackThreat::SwitchToState( const State eNextState )
{
	if( GetCurrentState() == eNextState )
	{
		return;
	}

	if( ( !GetProjectBrain() ) || ( !GetProjectBrain()->GetKnowledgeSystem() ) )
	{
		Asura_Assert( 0, "No brain or knowledge system" );
		SetInError();
		return;
	}

	// Do any work we need to exit our current state.
	switch ( GetCurrentState() )
	{
		case STATE_INIT:
		{
			break;
		}

		case STATE_ATTACK:
		{
			break;
		}

		case STATE_RETURN:
		{
			break;
		}

		default:
		{
			Asura_Assert( 0, "AVP_Behaviour_AVP3Alien_AttackThreat::SwitchToState - invalid prev state" );
			SetInError();
			return;
		}
	}

	// Do any work we need to change into our next state.
	SetCurrentState( eNextState );

	switch ( GetCurrentState() )
	{
		case STATE_INIT:
		{
			break;
		}

		case STATE_ATTACK:
		{
			const AVP_ServerActor_Vertebrate* pxServerActor = static_cast<const AVP_ServerActor_Vertebrate*>( GetProjectBrain()->GetActor() );
			const AVP_ServerEntity_Actor* pxAttackTarget = AVP_ServerEntity_Actor::FindEntity( m_uTargetGuid );
			if( !pxServerActor || !pxAttackTarget )
			{
				Asura_Assert( 0, "Couldn't find actor" );
				SetInError();
				return;
			}

			// Calculate distance
			const float fDistSq = ( pxServerActor->GetPosition() - pxAttackTarget->GetPosition() ).MagnitudeSquared();

			// Find valid attacks
			u_int auValidAttackIdx[NUM_ATTACK_TYPE];
			u_int uValidAttackNum = 0;

			for( u_int uIdx = 0; uIdx < NUM_ATTACK_TYPE; uIdx++ )
			{
				// Attack is valid if neccesary limb is available & distance is inside limits & previous one was different (if this is the second attack)
				if( ( m_axAttackAnims[uIdx].bLimbAvailable ) && ( m_axAttackAnims[uIdx].fMinDistSq <= fDistSq ) && ( m_axAttackAnims[uIdx].fMaxDistSq >= fDistSq ) && ( m_uCurrentAttackType != uIdx ) )
				{
					auValidAttackIdx[uValidAttackNum++] = uIdx;
				}
			}

			if( uValidAttackNum > 0 )
			{
				m_uCurrentAttackType = auValidAttackIdx[Asura_Random::GetIntU( 0, uValidAttackNum - 1 )];
				m_fCurrentAttackTimer = 0.0f;
				m_bDamageDealt = false;

				if( m_axAttackAnims[m_uCurrentAttackType].eAnimType != AVP_Alien_Description::NUM_ANIMATION_IDS )
				{
					GetProjectBrain()->RequestAnimation( m_axAttackAnims[m_uCurrentAttackType].eAnimType );
				}

				// Orient to threat
				OrientToPosition( &pxAttackTarget->GetPosition() );
			}
			else
			{
				SetFinished();
			}
			break;
		}

		case STATE_RETURN:
		{
			if( m_axAttackAnims[m_uCurrentAttackType].eReturnAnimType != AVP_Alien_Description::NUM_ANIMATION_IDS )
			{
				GetProjectBrain()->RequestAnimation( m_axAttackAnims[m_uCurrentAttackType].eReturnAnimType );
			}
			break;
		}

		default:
		{
			Asura_Assert( 0, "AVP_Behaviour_AVP3Alien_AttackThreat::SwitchToState - invalid next state" );
			SetInError();
			return;
		}
	}
}

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