abstract class AITicketCondition extends IScriptable
{

	public static function PerformChecking( entity : weak< Entity >, interface : weak< PuppetSquadInterface >, conditions : ref< array< weak< AITicketCondition_Record > > >, ticketRecord : weak< AITicket_Record >, squadRecord : weak< AISquadParams_Record > ) : Bool
	{
		if( AIScriptSquad.TicketDebugHelper( "", entity, ticketRecord ) )
		{
		}
		if( squadRecord && squadRecord.ProhibitedTicketsContains( ticketRecord.TicketType() ) )
		{
			return false;
		}
		if( !( CheckNumberOfTickets( entity, interface, ticketRecord ) ) )
		{
			return false;
		}
		return PerformChecking( entity, interface, conditions, ticketRecord );
	}

	public static function PerformChecking( entity : weak< Entity >, interface : weak< PuppetSquadInterface >, conditions : ref< array< weak< AITicketCondition_Record > > >, ticketRecord : weak< AITicket_Record > ) : Bool
	{
		var validSquadMembers : array< weak< Entity > >;
		var ticketName : CName;
		var i : Int32;
		var filter : AITicketFilter_Record;
		var check : AITicketCheck_Record;
		var willingSquadMembers : array< weak< Entity > >;
		if( conditions.Size() == 0 )
		{
			return true;
		}
		willingSquadMembers = interface.ListMembersWeak();
		ticketName = ticketRecord.TicketType().EnumName();
		for( i = willingSquadMembers.Size() - 1; i >= 0; i -= 1 )
		{
			if( ( willingSquadMembers[ i ] != entity ) && ( !( interface.IsSquadActionOpen( ticketName, willingSquadMembers[ i ] ) ) || interface.HasOrderBySquadAction( ticketName, willingSquadMembers[ i ] ) ) )
			{
				willingSquadMembers.Erase( i );
			}
		}
		validSquadMembers = willingSquadMembers;
		for( i = 0; i < conditions.Size(); i += 1 )
		{
			filter = ( ( AITicketFilter_Record )( conditions[ i ] ) );
			check = NULL;
			if( filter )
			{
				if( filter.ResetMembersIncludingUnwillings() )
				{
					validSquadMembers = interface.ListMembersWeak();
				}
				else if( filter.ResetMembers() )
				{
					validSquadMembers = willingSquadMembers;
				}
				if( filter.SkipSelfOnce() )
				{
					validSquadMembers.Remove( entity );
				}
				FilterOut( entity, interface, ( ( AITicketFilter_Record )( conditions[ i ] ) ), ticketRecord, validSquadMembers );
				if( filter.SkipSelfOnce() )
				{
					validSquadMembers.PushBack( entity );
				}
			}
			else
			{
				check = ( ( AITicketCheck_Record )( conditions[ i ] ) );
				if( Check( entity, interface, check, ticketRecord, validSquadMembers ) )
				{
					if( check.OptionalFastExit() )
					{
						return true;
					}
				}
				else
				{
					if( !( check.OptionalFastExit() ) )
					{
						return false;
					}
				}
			}
		}
		if( ( check == NULL ) || check.OptionalFastExit() )
		{
			return false;
		}
		return true;
	}

	public static function Check( entity : weak< Entity >, interface : weak< PuppetSquadInterface >, check : weak< AITicketCheck_Record >, ticketRecord : weak< AITicket_Record >, out squadMembers : array< weak< Entity > > ) : Bool
	{
		var result : Bool;
		result = false;
		if( check.IsA( 'gamedataAISquadORCondition_Record' ) )
		{
			result = CheckOR( entity, interface, ( ( AISquadORCondition_Record )( check ) ), ticketRecord, squadMembers );
		}
		else if( check.IsA( 'gamedataAISquadANDCondition_Record' ) )
		{
			result = CheckAND( entity, interface, ( ( AISquadANDCondition_Record )( check ) ), ticketRecord, squadMembers );
		}
		else if( check.IsA( 'gamedataAISquadMembersAmountCheck_Record' ) )
		{
			result = CheckMembersAmount( entity, interface, ( ( AISquadMembersAmountCheck_Record )( check ) ), squadMembers );
		}
		else if( check.IsA( 'gamedataAISquadContainsSelfCheck_Record' ) )
		{
			result = CheckContainsSelf( entity, interface, ( ( AISquadContainsSelfCheck_Record )( check ) ), squadMembers );
		}
		else if( check.IsA( 'gamedataAISquadDistanceRelationToTargetCheck_Record' ) )
		{
			return CheckDistanceRelationToTarget( entity, interface, ( ( AISquadDistanceRelationToTargetCheck_Record )( check ) ), check.IsA( 'gamedataAISquadClosestToTargetCheck_Record' ), squadMembers );
		}
		else if( check.IsA( 'gamedataAISquadDistanceRelationToSectorCheck_Record' ) )
		{
			return CheckDistanceRelationToSector( entity, interface, ( ( AISquadDistanceRelationToSectorCheck_Record )( check ) ), check.IsA( 'gamedataAISquadClosestToSectorCheck_Record' ), ticketRecord, squadMembers );
		}
		else
		{
			return false;
		}
		if( check.Invert() )
		{
			result = !( result );
		}
		return result;
	}

	public static function CheckOR( entity : weak< Entity >, interface : weak< PuppetSquadInterface >, check : weak< AISquadORCondition_Record >, ticketRecord : weak< AITicket_Record >, out squadMembers : array< weak< Entity > > ) : Bool
	{
		var i : Int32;
		var count : Int32;
		count = check.GetORCount();
		for( i = 0; i < count; i += 1 )
		{
			if( Check( entity, interface, check.GetORItem( i ), ticketRecord, squadMembers ) )
			{
				return true;
			}
		}
		return count == 0;
	}

	public static function CheckContainsSelf( entity : weak< Entity >, interface : weak< PuppetSquadInterface >, check : weak< AISquadContainsSelfCheck_Record >, const squadMembers : ref< array< weak< Entity > > > ) : Bool
	{
		return squadMembers.Contains( entity );
	}

	public static function CheckMembersAmount( entity : weak< Entity >, interface : weak< PuppetSquadInterface >, check : weak< AISquadMembersAmountCheck_Record >, const squadMembers : ref< array< weak< Entity > > > ) : Bool
	{
		var amount : Int32;
		amount = squadMembers.Size();
		if( !( check.CountSelf() ) )
		{
			amount -= 1;
		}
		if( ( check.MinAmount() > 0 ) && ( check.MinAmount() > amount ) )
		{
			return false;
		}
		if( ( check.MaxAmount() > 0 ) && ( amount > check.MaxAmount() ) )
		{
			return false;
		}
		return true;
	}

	public static function CheckAND( entity : weak< Entity >, interface : weak< PuppetSquadInterface >, check : weak< AISquadANDCondition_Record >, ticketRecord : weak< AITicket_Record >, out squadMembers : array< weak< Entity > > ) : Bool
	{
		var i : Int32;
		var count : Int32;
		count = check.GetANDCount();
		for( i = 0; i < count; i += 1 )
		{
			if( !( Check( entity, interface, check.GetANDItem( i ), ticketRecord, squadMembers ) ) )
			{
				return false;
			}
		}
		return true;
	}

	public static function FilterOut( entity : weak< Entity >, interface : weak< PuppetSquadInterface >, filter : weak< AITicketFilter_Record >, ticketRecord : weak< AITicket_Record >, squadMembers : ref< array< weak< Entity > > > )
	{
		if( filter.IsA( 'AISquadAvoidLastFilter_Record' ) )
		{
			FilterAgainstChoosingPreviouslySelected( entity, interface, ( ( AISquadAvoidLastFilter_Record )( filter ) ), squadMembers );
		}
		else if( filter.IsA( 'gamedataAISquadFilterByAICondition_Record' ) )
		{
			FilterAICondition( ( ( AISquadFilterByAICondition_Record )( filter ) ), squadMembers );
		}
		else if( filter.IsA( 'gamedataAISquadInSectorFilter_Record' ) )
		{
			FilterInSector( interface, ( ( AISquadInSectorFilter_Record )( filter ) ), ticketRecord, squadMembers );
		}
		else if( filter.IsA( 'gamedataAISquadJustSelfFilter_Record' ) )
		{
			FilterJustSelf( entity, ( ( AISquadJustSelfFilter_Record )( filter ) ), squadMembers );
		}
		else if( filter.IsA( 'gamedataAISquadSpatialForOwnTarget_Record' ) )
		{
			FilterSpatialForOwnTarget( entity, ( ( AISquadSpatialForOwnTarget_Record )( filter ) ), squadMembers );
		}
		else if( filter.IsA( 'gamedataAISquadFilterOwnTargetSpotted_Record' ) )
		{
			FilterTargetSpotted( entity, ( ( AISquadFilterOwnTargetSpotted_Record )( filter ) ), squadMembers );
		}
		else if( filter.IsA( 'gamedataAISquadItemPriorityFilter_Record' ) )
		{
			FilterItemPriority( ( ( AISquadItemPriorityFilter_Record )( filter ) ), squadMembers );
		}
	}

	public static function CheckNumberOfTickets( entity : weak< Entity >, interface : weak< PuppetSquadInterface >, ticketRecord : weak< AITicket_Record > ) : Bool
	{
		var squadMembers : array< weak< Entity > >;
		var workspotObject : weak< GameObject >;
		var ticketCount : Int32;
		var ticketCap : Int32;
		var i : Int32;
		squadMembers = interface.ListMembersWeak();
		for( i = 0; i < squadMembers.Size(); i += 1 )
		{
			if( interface.HasOrderBySquadAction( ticketRecord.TicketType().EnumName(), squadMembers[ i ] ) )
			{
				ticketCount += 1;
			}
		}
		if( ticketRecord.ScaleNumberOfTicketsFromWorkspots() )
		{
			workspotObject = ( ( weak< weak< GameObject > > )( ( ( ScriptedPuppet )( entity ) ).GetAIControllerComponent().GetBehaviorArgument( 'StimTarget' ) ) );
			ticketCap = workspotObject.GetNumberOfWorkpotsForAIAction( gamedataWorkspotActionType.DeviceInvestigation );
		}
		if( ticketCap > 0 )
		{
			if( ( ticketCount + 1 ) > ticketCap )
			{
				return false;
			}
			return true;
		}
		ticketCap = ticketRecord.MaxNumberOfTickets();
		if( ticketRecord.PercentageNumberOfTickets() >= 0.0 )
		{
			ticketCap = FloorF( ( ( Float )( squadMembers.Size() ) ) * ticketRecord.PercentageNumberOfTickets() );
			if( ( ticketCap == 0 ) && ( ticketRecord.MinNumberOfTickets() <= 0 ) )
			{
				ticketCap = 1;
			}
		}
		if( ( ticketRecord.MinNumberOfTickets() >= 0 ) && ( ticketCap < ticketRecord.MinNumberOfTickets() ) )
		{
			ticketCap = ticketRecord.MinNumberOfTickets();
		}
		if( ( ticketRecord.MaxNumberOfTickets() >= 0 ) && ( ticketCap > ticketRecord.MaxNumberOfTickets() ) )
		{
			ticketCap = ticketRecord.MaxNumberOfTickets();
		}
		if( ( ticketCap >= 0 ) && ( ( ticketCount + 1 ) > ticketCap ) )
		{
			return false;
		}
		return true;
	}

	public static function FilterAgainstChoosingPreviouslySelected( entity : weak< Entity >, interface : weak< PuppetSquadInterface >, condition : weak< AISquadAvoidLastFilter_Record >, squadMembers : ref< array< weak< Entity > > > )
	{
		var ticketName : CName;
		var canditate : weak< Entity >;
		var gameObject : weak< GameObject >;
		if( squadMembers.Size() <= 1 )
		{
			return;
		}
		gameObject = ( ( GameObject )( entity ) );
		canditate = GameInstance.FindEntityByID( gameObject.GetGame(), interface.GetLastTicketRecipient( ticketName ) );
		if( condition.Invert() )
		{
			squadMembers.Clear();
			squadMembers.PushBack( canditate );
		}
		else
		{
			squadMembers.Remove( canditate );
		}
	}

	public static function CheckDistanceRelationToTarget( entity : weak< Entity >, interface : weak< PuppetSquadInterface >, condition : weak< AISquadDistanceRelationToTargetCheck_Record >, closest : Bool, squadMembers : ref< array< weak< Entity > > > ) : Bool
	{
		var context : ScriptExecutionContext;
		var target : weak< GameObject >;
		var targetPosition : Vector4;
		var candidate : weak< Entity >;
		var compareDistance : Float;
		var distance : Float;
		var i : Int32;
		var result : Bool;
		for( i = 0; i < squadMembers.Size(); i += 1 )
		{
			if( !( AIHumanComponent.GetScriptContext( ( ( ScriptedPuppet )( squadMembers[ i ] ) ), context ) ) )
			{
				continue;
			}
			if( !( AIActionTarget.Get( context, condition.Target(), false, target, targetPosition ) ) )
			{
				continue;
			}
			distance = AbsF( Vector4.Distance( squadMembers[ i ].GetWorldPosition(), targetPosition ) - condition.RingRadius() );
			if( ( compareDistance == 0.0 ) || closest == ( compareDistance > distance ) )
			{
				compareDistance = distance;
				candidate = squadMembers[ i ];
			}
		}
		if( candidate == NULL )
		{
			return false;
		}
		result = candidate == entity;
		return ( ( condition.Invert() ) ? ( !( result ) ) : ( result ) );
	}

	public static function FilterSpatialForOwnTarget( entity : weak< Entity >, condition : weak< AISquadSpatialForOwnTarget_Record >, squadMembers : ref< array< weak< Entity > > > )
	{
		var squadMemberContext : ScriptExecutionContext;
		var ownerContext : ScriptExecutionContext;
		var candidates : array< weak< Entity > >;
		var i : Int32;
		if( !( AIHumanComponent.GetScriptContext( ( ( ScriptedPuppet )( entity ) ), ownerContext ) ) )
		{
			return;
		}
		for( i = squadMembers.Size() - 1; i >= 0; i -= 1 )
		{
			if( !( AIHumanComponent.GetScriptContext( ( ( ScriptedPuppet )( squadMembers[ i ] ) ), squadMemberContext ) ) )
			{
				return;
			}
			if( !( AICondition.CheckSpatial( ownerContext, squadMemberContext, condition.Spatial() ) ) )
			{
				if( condition.Invert() )
				{
					candidates.PushBack( squadMembers[ i ] );
				}
				else
				{
					squadMembers.Erase( i );
				}
			}
		}
		if( condition.Invert() )
		{
			squadMembers = candidates;
		}
	}

	public static function FilterAICondition( condition : weak< AISquadFilterByAICondition_Record >, squadMembers : ref< array< weak< Entity > > > )
	{
		var context : ScriptExecutionContext;
		var candidates : array< weak< Entity > >;
		var i : Int32;
		if( !( condition.Condition() ) )
		{
			return;
		}
		for( i = squadMembers.Size() - 1; i >= 0; i -= 1 )
		{
			if( !( AIHumanComponent.GetScriptContext( ( ( ScriptedPuppet )( squadMembers[ i ] ) ), context ) ) )
			{
				return;
			}
			if( !( AICondition.CheckActionCondition( context, condition.Condition() ) ) )
			{
				if( condition.Invert() )
				{
					candidates.PushBack( squadMembers[ i ] );
				}
				else
				{
					squadMembers.Erase( i );
				}
			}
		}
		if( condition.Invert() )
		{
			squadMembers = candidates;
		}
	}

	public static function CheckDistanceRelationToSector( entity : weak< Entity >, interface : weak< PuppetSquadInterface >, condition : weak< AISquadDistanceRelationToSectorCheck_Record >, closest : Bool, ticketRecord : weak< AITicket_Record >, squadMembers : ref< array< weak< Entity > > > ) : Bool
	{
		var combatAlley : CombatAlley;
		var sectors : array< weak< AISectorType_Record > >;
		var sectorInt : Int32;
		var candidate : weak< Entity >;
		var compareDistance : Float;
		var distance : Float;
		var i, j : Int32;
		var result : Bool;
		combatAlley = interface.GetDefensiveCombatAlley();
		condition.Sectors( sectors );
		if( sectors.Size() == 0 )
		{
			( ( AITacticTicket_Record )( ticketRecord ) ).Sectors( sectors );
		}
		for( i = 0; i < sectors.Size(); i += 1 )
		{
			sectorInt = ( ( Int32 )( EnumValueFromName( 'AICombatSectorType', sectors[ i ].EnumName() ) ) );
			for( j = 0; j < squadMembers.Size(); j += 1 )
			{
				distance = combatAlley.GetDistanceFromSector( ( ( AICombatSectorType )( sectorInt ) ), squadMembers[ j ].GetWorldPosition() );
				if( ( compareDistance == 0.0 ) || closest == ( compareDistance > distance ) )
				{
					compareDistance = distance;
					candidate = squadMembers[ j ];
				}
			}
		}
		if( candidate == NULL )
		{
			return false;
		}
		result = candidate == entity;
		return ( ( condition.Invert() ) ? ( !( result ) ) : ( result ) );
	}

	public static function FilterJustSelf( entity : weak< Entity >, condition : weak< AISquadJustSelfFilter_Record >, squadMembers : ref< array< weak< Entity > > > )
	{
		if( condition.Invert() )
		{
			squadMembers.Remove( entity );
		}
		else
		{
			squadMembers.Clear();
			squadMembers.PushBack( entity );
		}
	}

	public static function FilterInSector( interface : weak< PuppetSquadInterface >, condition : weak< AISquadInSectorFilter_Record >, ticketRecord : weak< AITicket_Record >, squadMembers : ref< array< weak< Entity > > > )
	{
		var combatAlley : CombatAlley;
		var sectors : array< weak< AISectorType_Record > >;
		var sectorInt : Int32;
		var candidates : array< weak< Entity > >;
		var i, j : Int32;
		var isInAnySector : Bool;
		condition.Sectors( sectors );
		if( sectors.Size() == 0 )
		{
			( ( AITacticTicket_Record )( ticketRecord ) ).Sectors( sectors );
		}
		combatAlley = interface.GetDefensiveCombatAlley();
		for( j = squadMembers.Size() - 1; j >= 0; j -= 1 )
		{
			isInAnySector = false;
			for( i = 0; i < sectors.Size(); i += 1 )
			{
				sectorInt = ( ( Int32 )( EnumValueFromName( 'AICombatSectorType', sectors[ i ].EnumName() ) ) );
				isInAnySector = combatAlley.GetSector( squadMembers[ j ].GetWorldPosition() ) != ( ( AICombatSectorType )( sectorInt ) );
				if( isInAnySector )
				{
					break;
				}
			}
			if( !( isInAnySector ) )
			{
				if( condition.Invert() )
				{
					candidates.PushBack( squadMembers[ j ] );
				}
				else
				{
					squadMembers.Erase( j );
				}
			}
		}
		if( condition.Invert() )
		{
			squadMembers = candidates;
		}
	}

	public static function FilterTargetSpotted( entity : weak< Entity >, condition : weak< AISquadFilterOwnTargetSpotted_Record >, squadMembers : ref< array< weak< Entity > > > )
	{
		var squadMemberContext : ScriptExecutionContext;
		var ownerContext : ScriptExecutionContext;
		var candidates : array< weak< Entity > >;
		var target : weak< GameObject >;
		var i : Int32;
		if( !( AIHumanComponent.GetScriptContext( ( ( ScriptedPuppet )( entity ) ), ownerContext ) ) )
		{
			squadMembers.Clear();
			return;
		}
		if( !( AIActionTarget.GetObject( ownerContext, condition.Target(), target ) ) )
		{
			squadMembers.Clear();
			return;
		}
		for( i = squadMembers.Size() - 1; i >= 0; i -= 1 )
		{
			if( !( AIHumanComponent.GetScriptContext( ( ( ScriptedPuppet )( squadMembers[ i ] ) ), squadMemberContext ) ) )
			{
				return;
			}
			if( !( IsTargetSpotted( squadMemberContext, target ) ) )
			{
				if( condition.Invert() )
				{
					candidates.PushBack( squadMembers[ i ] );
				}
				else
				{
					squadMembers.Erase( i );
				}
			}
		}
		if( condition.Invert() )
		{
			squadMembers = candidates;
		}
	}

	public static function IsTargetSpotted( const context : ScriptExecutionContext, target : weak< GameObject > ) : Bool
	{
		var threats : array< TrackedLocation >;
		var ttc : TargetTrackerComponent;
		var ownerAsPuppet : ScriptedPuppet;
		var i : Int32;
		ownerAsPuppet = ( ( ScriptedPuppet )( ScriptExecutionContext.GetOwner( context ) ) );
		ttc = ownerAsPuppet.GetTargetTrackerComponent();
		if( ttc )
		{
			threats = ttc.GetHostileThreats( false );
			for( i = threats.Size() - 1; i >= 0; i -= 1 )
			{
				if( threats[ i ].entity == target )
				{
					return true;
				}
			}
		}
		return false;
	}

	private static function GetItem( entity : weak< Entity > ) : weak< ItemObject >
	{
		var item : weak< ItemObject >;
		var go : weak< GameObject >;
		go = ( ( GameObject )( entity ) );
		item = GameInstance.GetTransactionSystem( go.GetGame() ).GetItemInSlot( go, T"AttachmentSlots.WeaponRight" );
		if( !( item ) )
		{
			item = GameInstance.GetTransactionSystem( go.GetGame() ).GetItemInSlot( go, T"AttachmentSlots.WeaponLeft" );
		}
		return item;
	}

	private static function FilterItemTypePredicate( itemRecord : weak< Item_Record >, condition : weak< AISquadItemTypePriorityFilter_Record >, index : Int32 ) : Bool
	{
		return itemRecord.ItemType() == condition.GetTypesItem( index );
	}

	private static function FilterItemTypePredicate( itemRecord : weak< Item_Record >, condition : weak< AISquadItemCategoryPriorityFilter_Record >, index : Int32 ) : Bool
	{
		return itemRecord.ItemCategory() == condition.GetCategoriesItem( index );
	}

	public static function FilterItemPriority( condition : weak< AISquadItemPriorityFilter_Record >, squadMembers : ref< array< weak< Entity > > > )
	{
		var itemRecord : weak< Item_Record >;
		var i, j : Int32;
		var prioritiesAmount : Int32;
		var foundMatch : Bool;
		var candidates : array< weak< Entity > >;
		var backupMembers : array< weak< Entity > >;
		var itemTypeFilter : weak< AISquadItemTypePriorityFilter_Record >;
		var itemCategoryFilter : weak< AISquadItemCategoryPriorityFilter_Record >;
		itemTypeFilter = ( ( AISquadItemTypePriorityFilter_Record )( condition ) );
		itemCategoryFilter = ( ( AISquadItemCategoryPriorityFilter_Record )( condition ) );
		if( !( itemTypeFilter ) && !( itemCategoryFilter ) )
		{
			squadMembers.Clear();
			return;
		}
		backupMembers = squadMembers;
		prioritiesAmount = ( ( itemTypeFilter ) ? ( itemTypeFilter.GetTypesCount() ) : ( itemCategoryFilter.GetCategoriesCount() ) );
		foundMatch = false;
		for( j = 0; ( j < prioritiesAmount ) && !( foundMatch ); j += 1 )
		{
			for( i = 0; i < squadMembers.Size(); i += 1 )
			{
				itemRecord = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( GetItem( squadMembers[ i ] ).GetItemID() ) );
				if( ( ( itemTypeFilter ) ? ( FilterItemTypePredicate( itemRecord, itemTypeFilter, j ) ) : ( FilterItemTypePredicate( itemRecord, itemCategoryFilter, j ) ) ) )
				{
					if( condition.Invert() )
					{
						squadMembers.Erase( i );
					}
					else
					{
						candidates.PushBack( squadMembers[ i ] );
					}
					foundMatch = true;
				}
			}
		}
		if( !( foundMatch ) && condition.RestoreOnFail() )
		{
			squadMembers = backupMembers;
			return;
		}
		if( !( condition.Invert() ) )
		{
			squadMembers = candidates;
		}
	}

}

