importonly abstract class IReactionSystem extends IGameSystem
{
}

import enum AIReactionCountOutcome
{
	Failed,
	Succeded,
	NotFound,
}

import class ReactionSystem extends IReactionSystem
{
	public import function RegisterFearReaction( npc : GameObject, threat : GameObject );
	public import function UnregisterFearReaction( runner : GameObject );
	public import function AddTrafficRunnersDirectly( runner : GameObject, threat : GameObject ) : Bool;
	public import function RegisterReaction( reaction : CName ) : AIReactionCountOutcome;
	public import function UnregisterReaction( reaction : CName ) : AIReactionCountOutcome;
	public import function AddFearSource( sourcePos : Vector3, halfExtend : Float, optional debugInfo : CName );
	public import function MarkDespawnCandidate( id : EntityID );
	public import function UnmarkDespawnCandidate( id : EntityID );
	public import function MarkDeadBody( id : EntityID, isAggressive : Bool, isInScene : Bool );
	public import function TryRegisteringAggressiveNPC( civilianNPC : GameObject, checkPlayerLos : Bool ) : Bool;
	public import function TryUnregisteringAggressiveNPC( npcId : EntityID ) : Bool;
	public import function IsRegisteredAsAggressive( npcId : EntityID ) : Bool;
	public import function UsesLoreAnimationWorkspot( npcId : EntityID ) : Bool;
	public import function IsInImmovableWorkspot( npcId : EntityID ) : Bool;
	public import function TryAndJoinTraffic( npc : GameObject, threatPos : Vector3, shouldRunInTraffic : Bool ) : Bool;
}

class ScriptedReactionSystem extends ScriptableSystem
{
	private var m_fleeingNPCs : Int32;
	private var m_runners : array< weak< Entity > >;
	private var m_registeredTimeout : Float;
	private var m_callInAction : Bool;
	private var m_policeCaller : weak< Entity >;

	private function OnRegisterFleeingNPC( request : RegisterFleeingNPC )
	{
		if( !( IsRegistered( request.runner ) ) )
		{
			m_fleeingNPCs = m_fleeingNPCs + 1;
			m_runners.PushBack( request.runner );
			if( request.timestamp > m_registeredTimeout )
			{
				m_registeredTimeout = request.timestamp + 2.0;
			}
		}
	}

	private function OnUnregisterFleeingNPC( request : UnregisterFleeingNPC )
	{
		if( IsRegistered( request.runner ) )
		{
			m_fleeingNPCs = m_fleeingNPCs - 1;
			m_runners.Remove( request.runner );
		}
	}

	public const function GetFleeingNPCsCount() : Int32
	{
		return m_fleeingNPCs;
	}

	public const function GetFleeingNPCsCountInDistance( position : Vector4, distance : Float ) : Int32
	{
		var runners, count, i : Int32;
		var distanceSquaredCheck : Float;
		count = m_runners.Size();
		for( i = 0; i < count; i += 1 )
		{
			distanceSquaredCheck = Vector4.DistanceSquared( m_runners[ i ].GetWorldPosition(), position );
			if( distanceSquaredCheck < ( distance * distance ) )
			{
				runners = runners + 1;
			}
		}
		return runners;
	}

	public const function GetRegisterTimeout() : Float
	{
		return m_registeredTimeout;
	}

	private function IsRegistered( runner : Entity ) : Bool
	{
		if( m_runners.Contains( runner ) )
		{
			return true;
		}
		return false;
	}

	private function OnRegisterPoliceCaller( request : RegisterPoliceCaller )
	{
		if( !( m_callInAction ) || IsCallerCloser( request.caller, request.crimePosition ) )
		{
			m_callInAction = true;
			m_policeCaller = request.caller;
		}
	}

	private function OnUnregisterPoliceCaller( request : UnregisterPoliceCaller )
	{
		m_callInAction = false;
		m_policeCaller = NULL;
	}

	private function IsCallerCloser( newCaller : Entity, crimePosition : Vector4 ) : Bool
	{
		var callerDistance, newCallerDistance : Float;
		callerDistance = Vector4.DistanceSquared( m_policeCaller.GetWorldPosition(), crimePosition );
		newCallerDistance = Vector4.DistanceSquared( newCaller.GetWorldPosition(), crimePosition );
		if( ( newCallerDistance != 0.0 ) && ( newCallerDistance < callerDistance ) )
		{
			return true;
		}
		return false;
	}

	public const function IsCaller( entity : Entity ) : Bool
	{
		return entity == m_policeCaller;
	}

}

class RegisterFleeingNPC extends ScriptableSystemRequest
{
	var runner : weak< Entity >;
	var timestamp : Float;
}

class UnregisterFleeingNPC extends ScriptableSystemRequest
{
	var runner : weak< Entity >;
}

class RegisterPoliceCaller extends ScriptableSystemRequest
{
	var caller : weak< Entity >;
	var crimePosition : Vector4;
}

class UnregisterPoliceCaller extends ScriptableSystemRequest
{
}

