import enum moveCirclingDirection
{
	None,
	Left,
	Right,
}

import enum moveLineOfSight
{
	None,
	Keep,
	Avoid,
}

import enum moveLineOfSightPointPreference
{
	None,
	ClosestToOwner,
	ClosestToTarget,
	FurthestFromTarget,
}

import enum worldOffMeshConnectionType
{
	Door,
	Ladder,
	Floor,
	Jump,
	Elevator,
}

import enum moveExplorationType
{
	None,
	Ladder,
	Jump,
	Climb,
	Vault,
	ChargedJump,
	ThrusterJump,
}

import enum moveLocomotionAction
{
	Undefined,
	Exploration,
	Idle,
	IdleTurn,
	Reposition,
	Start,
	Move,
	Stop,
}

importonly class EnteredSplineEvent extends Event
{
	import var useDoors : Bool;
}

importonly class ExplorationEnteredEvent extends Event
{
	import var type : moveExplorationType;
}

importonly class ExplorationLeftEvent extends Event
{
	import var type : moveExplorationType;
}

importonly class ExitedSplineEvent extends Event
{
}

import class MovePolicies extends IScriptable
{
	public import function SetDestinationPosition( position : Vector4 );
	public import function SetInvalidDestinationPosition();
	public import function SetDestinationObject( object : GameObject );
	public import function SetDestinationCover( cover : Uint64 );
	public import function SetStrafingTarget( object : GameObject );
	public import function SetStrafingPosition( position : Vector4 );
	public import function SetDestinationOrientation( oreiantation : Quaternion );
	public import function SetDistancePolicy( distance : Float, tolerance : Float );
	public import function SetMaxZDiff( zDiff : Float );
	public import function SetMinDistancePolicy( minDistance : Float );
	public import function SetStrafingPredictionTime( strafingPredictionTime : Float, strafingPredictionVelocityMax : Float );
	public import function SetDynamicTargetUpdateTimer( updateTimer : Float, distance : Float );
	public import function SetStrafingRotationOffset( angle : Float );
	public import function SetCirclingPolicy( direction : moveCirclingDirection );
	public import function SetMovementType( movementType : moveMovementType );
	public import function SetStopOnObstacle( stopOnObstacle : Bool );
	public import function SetAvoidObstacleWithinTolerance( avoidWiyhinTolerance : Bool );
	public import function SetKeepLineOfSight( los : moveLineOfSight );
	public import function SetLineOfSightPointPreference( preference : moveLineOfSightPointPreference );
	public import function SetGetOutOfWay( getOutOfWay : Bool );
	public import function SetUseLineOfSitePrecheck( precheck : Bool );
	public import function SetUseFollowSlots( use : Bool );
	public import function SetUseSymmetricAnglesScores( use : Bool );
	public import function SetSquadInfo( squadIndex : Uint32, squadSize : Uint32 );
	public import function SetCollisionAvoidancePolicy( avoidance : Bool, reservation : Bool );
	public import function SetInRestrictedArea( inRestrictedArea : Bool );
	public import function SetAvoidPreventionFreeAreas( inRestrictedArea : Bool );
	public import function SetAvoidSafeArea( inRestrictedArea : Bool );
	public import function SetAvoidThreat( avoidThreat : Bool );
	public import function SetAvoidThreatIgnoringDirection( ignoreDirection : Bool );
	public import function SetUseStartStop( useStart : Bool, useStop : Bool );
	public import function SetIgnoreNavigation( ignore : Bool );
	public import function SetUseOffMeshAllowedTags( useOffMeshBlockedTags : Bool );
	public import function AddAllowedTag( tag : CName );
	public import function SetUseOffMeshBlockedTags( useOffMeshAllowedTags : Bool );
	public import function AddBlockedTag( tag : CName );
	public import function GetMovementType() : moveMovementType;
	public import function SetIdleTurnsDeadZoneAngle( angle : Float );
	public import function GetIdleTurnsDeadZoneAngle() : Float;
	public import function SetPositionProvider( provider : IPositionProvider );
	public import function SetStrafingPositionProvider( provider : IPositionProvider );
	public import function SetCalculateStartTangent( calculateTangent : Bool );
	public import function SetDebugName( debugName : CName );
	public import function SetTweakDBID( id : TweakDBID );
	public import function SetMaxPathLengthToDirectDistanceRatioCurve( curveName : CName );
	public import function GetMaxPathLengthToDirectDistanceRatioCurve() : CName;
	public import function SetMaxPathLength( length : Float );
	public import function GetMaxPathLength() : Float;
	public import function SetLocalTargetOffset( position : Vector4 );
	public import static function GetInvalidPos() : Vector4;
	public import function SetCostModCircle( costModCircle : NavigationCostModCircle );
}

import class MovePoliciesComponent extends IComponent
{
	public import function AddPolicies( object : MovePolicies );
	public import function PopPolicies( object : MovePolicies );
	public import function GetTopPolicies() : MovePolicies;
	public import function GetDestination() : Vector4;
	public import function GetDistanceToDestination() : Float;
	public import function GetDistanceToDestinationFrom( destination : Vector4 ) : Float;
	public import function GetClosestPointToPath( point : Vector4 ) : Vector4;
	public import function GetInclineAngle() : Float;
	public import function ChangeMovementType( movementType : moveMovementType ) : Bool;
	public import function IsPathfindingFailed() : Bool;
	public import function IsConstrainedByRestrictedArea() : Bool;
	public import function HasLineOfSightFailed() : Bool;
	public import function IsInIdle() : Bool;
	public import function IsTopPolicyEvaluated() : Bool;
	public import function IsPausedByDynamicCollision() : Bool;
	public import function IsDestinationCalculated() : Bool;
	public import function IsDestinationChanged() : Bool;
	public import function IsOnOffMeshLink() : Bool;
	public import function GetOffMeshLinkType( out type : worldOffMeshConnectionType ) : Bool;
	public import function GetExplorationOffMeshLinkType() : moveExplorationType;
	public import function GetCurrentLocomotionAction() : moveLocomotionAction;
	public import function IsOnStairs() : Bool;
}

abstract class AIActionMovePolicy extends IScriptable
{

	public static function GetTargetPositionProvider( owner : ScriptedPuppet, target : weak< GameObject >, trackingMode : gamedataTrackingMode ) : IPositionProvider
	{
		var tmpProvider : IPositionProvider;
		var targetTrackerComponent : TargetTrackerComponent;
		targetTrackerComponent = owner.GetTargetTrackerComponent();
		tmpProvider = IPositionProvider.CreateEntityPositionProvider( target );
		switch( trackingMode )
		{
			case gamedataTrackingMode.LastKnownPosition:
				tmpProvider = targetTrackerComponent.GetThreatBeliefPositionProvider( target, , false, , tmpProvider );
			return targetTrackerComponent.GetThreatLastKnownPositionProvider( target, , false, , tmpProvider );
			case gamedataTrackingMode.BeliefPosition:
				return targetTrackerComponent.GetThreatBeliefPositionProvider( target, , false, , tmpProvider );
			case gamedataTrackingMode.SharedLastKnownPosition:
				tmpProvider = targetTrackerComponent.GetThreatSharedBeliefPositionProvider( target, , false, , tmpProvider );
			return targetTrackerComponent.GetThreatSharedLastKnownPositionProvider( target, , false, , tmpProvider );
			case gamedataTrackingMode.SharedBeliefPosition:
				return targetTrackerComponent.GetThreatSharedBeliefPositionProvider( target, , false, , tmpProvider );
			default:
				return tmpProvider;
		}
	}

	public static function Add( const context : ScriptExecutionContext, record : weak< MovementPolicy_Record >, out policy : MovePolicies )
	{
		var target : weak< GameObject >;
		var strafingTarget : weak< GameObject >;
		var targetPosition : Vector4;
		var strafingPosition : Vector4;
		var puppet : weak< ScriptedPuppet >;
		var coverID : Uint64;
		var count : Int32;
		var i : Int32;
		var tagList : MovementPolicyTagList_Record;
		var tagCount : Int32;
		var tagIndex : Int32;
		var tagCondition : AIActionCondition_Record;
		var targetTrackerComponent : TargetTrackerComponent;
		var currentTopThreat : TrackedLocation;
		var threatPos : Vector4;
		var movingExactlyToTarget : Bool;
		var costModCircle : NavigationCostModCircle;
		var avoidThreatRange : Float;
		var distance : Float;
		var tolerance : Float;
		var ringRecord : weak< AIRingType_Record >;
		var destinationOrientationTarget : weak< GameObject >;
		var destinationOrientationPosition : Vector4;
		var directionOrientationVector : Vector4;
		var destinationOrientation : Quaternion;
		var stimTarget : weak< GameObject >;
		var squadMembers : array< weak< Entity > >;
		var hintDistanceMult : Float;
		movingExactlyToTarget = false;
		hintDistanceMult = 1.0;
		puppet = ( ( ScriptedPuppet )( ScriptExecutionContext.GetOwner( context ) ) );
		if( !( puppet ) || ( !( record.Target() ) && !( record.StrafingTarget() ) ) )
		{
			return;
		}
		if( record.Target() && !( AIActionTarget.Get( context, record.Target(), false, target, targetPosition, coverID ) ) )
		{
			return;
		}
		if( record.StrafingTarget() && !( AIActionTarget.Get( context, record.StrafingTarget(), false, strafingTarget, strafingPosition ) ) )
		{
			return;
		}
		hintDistanceMult = CombatSpaceHelper.GetDistanceMultiplier( puppet, record.SpatialHintMults() );
		policy = new MovePolicies;
		policy.SetTweakDBID( record.GetID() );
		policy.SetMaxPathLengthToDirectDistanceRatioCurve( record.MaxPathLengthToDirectDistanceRatioCurve() );
		policy.SetMaxPathLength( record.MaxPathLength() * hintDistanceMult );
		if( record.UseFollowSlots() )
		{
			policy.SetUseFollowSlots( true );
			policy.SetUseSymmetricAnglesScores( record.SymmetricAnglesScores() );
			policy.SetSquadInfo( 0, 1 );
			if( AISquadHelper.GetSquadmates( target, squadMembers, false ) )
			{
				for( i = 0; i < squadMembers.Size(); i += 1 )
				{
					if( ScriptExecutionContext.GetOwner( context ) == squadMembers[ i ] )
					{
						policy.SetSquadInfo( ( ( Uint32 )( i ) ), ( ( Uint32 )( squadMembers.Size() ) ) );
					}
				}
			}
		}
		if( target )
		{
			policy.SetDestinationObject( target );
			if( record.Ring().Type() == gamedataAIRingType.LatestActive )
			{
				ringRecord = AIActionHelper.GetLatestActiveRingTypeRecord( puppet );
				distance = ringRecord.Distance();
				tolerance = ringRecord.Tolerance();
			}
			else if( record.Ring().Type() == gamedataAIRingType.Undefined || !( AIActionHelper.GetDistanceAndToleranceFromRingType( record, distance, tolerance ) ) )
			{
				distance = record.Distance();
				tolerance = record.Tolerance();
			}
			if( ( distance == 0.0 ) && ( tolerance == 0.0 ) )
			{
				movingExactlyToTarget = true;
			}
		}
		else if( coverID )
		{
			policy.SetDestinationCover( coverID );
		}
		else if( !( Vector4.IsZero( targetPosition ) ) )
		{
			policy.SetDestinationPosition( targetPosition );
		}
		targetTrackerComponent = puppet.GetTargetTrackerComponent();
		avoidThreatRange = record.AvoidThreatRange();
		if( avoidThreatRange > 0.0 )
		{
			if( targetTrackerComponent && targetTrackerComponent.GetTopHostileThreat( false, currentTopThreat ) )
			{
				if( currentTopThreat.entity )
				{
					threatPos = currentTopThreat.entity.GetWorldPosition();
					if( !( movingExactlyToTarget ) || ( Vector4.Distance( threatPos, target.GetWorldPosition() ) > 0.1 ) )
					{
						costModCircle = new NavigationCostModCircle;
						costModCircle.pos = threatPos;
						costModCircle.range = avoidThreatRange;
						costModCircle.cost = record.AvoidThreatCost();
						policy.SetCostModCircle( costModCircle );
					}
				}
			}
			else if( puppet.GetHighLevelStateFromBlackboard() == gamedataNPCHighLevelState.Fear )
			{
				stimTarget = ScriptExecutionContext.GetArgumentObject( context, 'StimTarget' );
				if( stimTarget )
				{
					costModCircle = new NavigationCostModCircle;
					costModCircle.pos = stimTarget.GetWorldPosition();
					costModCircle.range = avoidThreatRange;
					costModCircle.cost = record.AvoidThreatCost();
					policy.SetCostModCircle( costModCircle );
				}
			}
		}
		if( strafingTarget )
		{
			policy.SetStrafingTarget( strafingTarget );
		}
		else if( !( Vector4.IsZero( strafingPosition ) ) )
		{
			policy.SetStrafingPosition( strafingPosition );
		}
		if( puppet.GetNPCType() == gamedataNPCType.Drone )
		{
			policy.SetMovementType( moveMovementType.Walk );
			DroneComponent.SetLocomotionWrappers( puppet, record.MovementType() );
		}
		else
		{
			policy.SetMovementType( ( ( moveMovementType )( ( ( Int32 )( EnumValueFromName( 'moveMovementType', record.MovementType() ) ) ) ) ) );
		}
		if( record.DestinationOrientationPosition() )
		{
			if( AIActionTarget.Get( context, record.DestinationOrientationPosition(), false, destinationOrientationTarget, destinationOrientationPosition ) )
			{
				directionOrientationVector = Vector4( destinationOrientationPosition.X, destinationOrientationPosition.Y, 0.0, 0.0 ) - Vector4( targetPosition.X, targetPosition.Y, 0.0, 0.0 );
				if( Vector4.IsXYZZero( directionOrientationVector ) )
				{
					Quaternion.SetIdentity( destinationOrientation );
				}
				else
				{
					destinationOrientation = Quaternion.BuildFromDirectionVector( directionOrientationVector );
				}
				policy.SetDestinationOrientation( destinationOrientation );
			}
		}
		if( hintDistanceMult != 1.0 )
		{
			if( !( record.UseFollowSlots() ) )
			{
				distance *= hintDistanceMult;
			}
			tolerance *= hintDistanceMult;
		}
		policy.SetDistancePolicy( distance, tolerance );
		policy.SetMinDistancePolicy( record.MinDistance() );
		policy.SetStrafingPredictionTime( record.StrafingPredictionTime(), record.StrafingPredictionVelocityMax() );
		policy.SetDynamicTargetUpdateTimer( record.DynamicTargetUpdateTimer(), record.DynamicTargetUpdateDistance() );
		policy.SetCirclingPolicy( ( ( moveCirclingDirection )( ( ( Int32 )( EnumValueFromName( 'moveCirclingDirection', record.CirclingDirection() ) ) ) ) ) );
		policy.SetKeepLineOfSight( ( ( moveLineOfSight )( ( ( Int32 )( EnumValueFromName( 'moveLineOfSight', record.KeepLineOfSight() ) ) ) ) ) );
		policy.SetLineOfSightPointPreference( ( ( moveLineOfSightPointPreference )( ( ( Int32 )( EnumValueFromName( 'moveLineOfSightPointPreference', record.LineOfSightPointPreference() ) ) ) ) ) );
		policy.SetGetOutOfWay( record.GetOutOfWay() );
		policy.SetCollisionAvoidancePolicy( !( record.IgnoreCollisionAvoidance() ), !( record.IgnoreSpotReservation() ) );
		policy.SetInRestrictedArea( !( record.IgnoreRestrictedMovementArea() ) );
		policy.SetAvoidPreventionFreeAreas( puppet.IsPrevention() );
		policy.SetAvoidSafeArea( record.AvoidSafeArea() );
		policy.SetUseStartStop( !( record.DontUseStart() ), !( record.DontUseStop() ) );
		policy.SetAvoidThreat( record.AvoidThreat() );
		policy.SetAvoidThreatIgnoringDirection( record.AvoidThreatIgnoringDirection() );
		policy.SetIgnoreNavigation( record.IgnoreNavigation() );
		policy.SetStrafingRotationOffset( record.StrafingRotationOffset() );
		policy.SetUseLineOfSitePrecheck( !( record.IgnoreLoSPrecheck() ) );
		policy.SetStopOnObstacle( record.StopOnObstacle() );
		policy.SetAvoidObstacleWithinTolerance( record.AvoidObstacleWithinTolerance() );
		policy.SetIdleTurnsDeadZoneAngle( record.DeadAngle() );
		policy.SetCalculateStartTangent( record.CalculateStartTangent() );
		if( record.ZDiff() >= 0.0 )
		{
			policy.SetMaxZDiff( record.ZDiff() );
		}
		if( record.DebugName() != 'Script' )
		{
			policy.SetDebugName( record.DebugName() );
		}
		else
		{
			policy.SetDebugName( StringToName( TDBID.ToStringDEBUG( record.GetID() ) ) );
		}
		if( ( target && record.Target() ) && record.Target().TrackingMode().Type() != gamedataTrackingMode.RealPosition )
		{
			policy.SetPositionProvider( GetTargetPositionProvider( puppet, target, record.Target().TrackingMode().Type() ) );
		}
		if( ( strafingTarget && record.StrafingTarget() ) && record.StrafingTarget().TrackingMode().Type() != gamedataTrackingMode.RealPosition )
		{
			policy.SetStrafingPositionProvider( GetTargetPositionProvider( puppet, strafingTarget, record.StrafingTarget().TrackingMode().Type() ) );
		}
		if( record.UseOffMeshAllowedTags() )
		{
			policy.SetUseOffMeshAllowedTags( true );
			count = record.GetAllowedTagsCount();
			for( i = 0; i < count; i += 1 )
			{
				tagList = record.GetAllowedTagsItem( i );
				tagCondition = tagList.Condition();
				if( CheckCondition( context, tagCondition ) )
				{
					tagCount = tagList.GetTagsCount();
					for( tagIndex = 0; tagIndex < tagCount; tagIndex += 1 )
					{
						policy.AddAllowedTag( tagList.GetTagsItem( tagIndex ) );
					}
				}
			}
		}
		if( record.UseOffMeshBlockedTags() )
		{
			policy.SetUseOffMeshBlockedTags( true );
			count = record.GetBlockedTagsCount();
			for( i = 0; i < count; i += 1 )
			{
				tagList = record.GetBlockedTagsItem( i );
				tagCondition = tagList.Condition();
				if( CheckCondition( context, tagCondition ) )
				{
					tagCount = tagList.GetTagsCount();
					for( tagIndex = 0; tagIndex < tagCount; tagIndex += 1 )
					{
						policy.AddBlockedTag( tagList.GetTagsItem( tagIndex ) );
					}
				}
			}
		}
		puppet.GetMovePolicesComponent().AddPolicies( policy );
	}

	private static function CheckCondition( const context : ScriptExecutionContext, condition : AIActionCondition_Record ) : Bool
	{
		if( !( condition ) )
		{
			return true;
		}
		return AICondition.CheckActionCondition( context, condition );
	}

	public static function Pop( const context : ScriptExecutionContext, out policy : MovePolicies )
	{
		var puppet : ScriptedPuppet;
		puppet = ( ( ScriptedPuppet )( ScriptExecutionContext.GetOwner( context ) ) );
		if( !( puppet ) )
		{
			return;
		}
		puppet.GetMovePolicesComponent().PopPolicies( policy );
		policy = NULL;
	}

}

