type EntityType = (unitType, houseType);

const HITS_TO_BREAK_LEASH_COUNT = 5;
const TIME_WINDOW_TO_TRIGGER_LEASH_SECONDS = 2;
const TIME_LEASH_BREAK_DURATION_SECONDS_MOUNTED = 2;
const TIME_LEASH_BREAK_DURATION_SECONDS_UNMOUNTED = 3;

var aiPlayersRedZonesArray: array of Rectangle;

var aiDefenceGroupParametersArray: array of AiDefenceGroupParameters;
var aiHaltOrders: array of AiDefenceGroupParameters;

procedure GenerateAIGroupsDefendingOrders(entityAttacked: Integer; aAttacker: Integer; attackedEntityType: EntityType);
var attackedEntityOwnerId: integer;
var attackedEntityPosition: TKMPoint;
var defencePosition: TKMDefencePositionInfo;
var aiDefenceGroupPosition: TKMPoint;	
var aiDefenceGroupParameters: AiDefenceGroupParameters; 
var i, j, currentDefenceGroupIndex: integer;
begin
	if(attackedEntityType = unitType) then begin
		attackedEntityOwnerId := States.UnitOwner(entityAttacked);
		attackedEntityPosition := States.UnitPosition(entityAttacked);
	end;
	if(attackedEntityType = houseType) then begin
		attackedEntityOwnerId := States.HouseOwner(entityAttacked);
		attackedEntityPosition := States.HousePosition(entityAttacked);
	end;
	for i := 0 to States.StatPlayerCount - 1 do
		begin
			if (alliancesPerPlayerIdMap[attackedEntityOwnerId][i]) then
				begin
					for j := 0 to States.StatAIDefencePositionsCount(i) - 1 do
						begin
							defencePosition := States.AIDefencePositionGetByIndex(i,j);
							if ((defencePosition.GroupID <> -1) and (IsPointInRangeCircle(defencePosition.X,defencePosition.Y,defencePosition.Radius,attackedEntityPosition.X,attackedEntityPosition.Y))) then
								begin
									if(HasAiDefenceGroupParametersForGroup(aiDefenceGroupParametersArray, defencePosition.GroupID)) then 
										begin
											currentDefenceGroupIndex := GetAiDefenceGroupParametersForGroup(aiDefenceGroupParametersArray, defencePosition.GroupID);	
										end
									else 
										begin
											aiDefenceGroupParameters.aiDefenceGroupId := defencePosition.GroupID;
											aiDefenceGroupParametersArray := AddElementToAiDefenceGroupParametersArray(aiDefenceGroupParametersArray,aiDefenceGroupParameters);
											currentDefenceGroupIndex := Length(aiDefenceGroupParametersArray) - 1;
										end;
									aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefencePositionsAssignmentsX := defencePosition.X;
									aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefencePositionsAssignmentsY := defencePosition.Y;
									aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefencePositionsAssignmentsRadius := defencePosition.Radius;
									
									if (aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefenceLeashBreakCountResetTiming < States.GameTime ) then
										begin
											aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefenceLeashBreakCount := 0;
										end;
									
									if (aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefenceLeashBreakCount = 0) then
										begin
											aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefenceLeashBreakCountResetTiming := States.GameTime + Utils.TimeToTick(0,0,TIME_WINDOW_TO_TRIGGER_LEASH_SECONDS);
										end
									else if (aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefenceLeashBreakCount = HITS_TO_BREAK_LEASH_COUNT) then
										begin
											if (States.GroupTypeEx(aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefenceGroupId) = gtMounted) then
												begin
													aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefenceLeashBreakCountResetTiming := States.GameTime + Utils.TimeToTick(0,0,TIME_LEASH_BREAK_DURATION_SECONDS_MOUNTED);
												end
											else
												begin
													aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefenceLeashBreakCountResetTiming := States.GameTime + Utils.TimeToTick(0,0,TIME_LEASH_BREAK_DURATION_SECONDS_UNMOUNTED);
												end;
										end;
										
									aiDefenceGroupPosition := States.UnitPosition(States.GroupMember(defencePosition.GroupID,0));
									if (aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefenceLeashBreakCount < HITS_TO_BREAK_LEASH_COUNT) then
										begin
											if (not HasAiDefenceGroupParametersForGroup(aiHaltOrders, defencePosition.GroupID)) then
												begin
													aiHaltOrders := AddElementToAiDefenceGroupParametersArray(aiHaltOrders, aiDefenceGroupParametersArray[currentDefenceGroupIndex]);
												end;
											if (not IsPointInRangeCircle(aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefencePositionsAssignmentsX,aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefencePositionsAssignmentsY, aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefencePositionsAssignmentsRadius*0.1,aiDefenceGroupPosition.X,aiDefenceGroupPosition.Y)) then
												begin								
													Actions.GroupOrderHalt(aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefenceGroupId);
												end;
										end;
									aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefenceLeashBreakCount := aiDefenceGroupParametersArray[currentDefenceGroupIndex].aiDefenceLeashBreakCount + 1;
								end;		
						end;
				end;
		end;
end;

function IsEntityAttackedInRedZone(entityAttacked: Integer; attackedEntityType: EntityType): boolean;
var attackedEntityOwnerId: integer;
var attackedEntityPosition: TKMPoint;
var attackedAiPlayerRedZone: Rectangle;
begin
	if(attackedEntityType = unitType) then begin
		attackedEntityOwnerId := States.UnitOwner(entityAttacked);
		attackedEntityPosition := States.UnitPosition(entityAttacked);
	end;
	if(attackedEntityType = houseType) then begin
		attackedEntityOwnerId := States.HouseOwner(entityAttacked);
		attackedEntityPosition := States.HousePosition(entityAttacked);
	end;
	if(not States.PlayerIsAI(attackedEntityOwnerId)) then begin
		Result := true;
	end
	else begin
		attackedAiPlayerRedZone := aiPlayersRedZonesArray[attackedEntityOwnerId -1];
		Result := IsPointInRangeRectangle(attackedAiPlayerRedZone, attackedEntityPosition);
	end;
end;	

procedure OnHouseDamaged(aHouse: integer; aAttacker: integer);
begin;	
	if(not IsEntityAttackedInRedZone(aHouse, houseType)) then begin
		GenerateAIGroupsDefendingOrders(aHouse, aAttacker, houseType);
	end;
end;

procedure OnUnitAttacked(aUnit: integer; aAttacker: integer);
begin;	
	if(not IsEntityAttackedInRedZone(aUnit, unitType)) then begin
		GenerateAIGroupsDefendingOrders(aUnit, aAttacker, unitType);
	end;
end;
	
procedure OnTick;
var i: integer;
var aiDefenceGroupParameters: AiDefenceGroupParameters;
var aiDefencePostionX: integer;
var aiDefencePostionY: integer;
var aiDefencePostionRadius: integer;
var aiDefenceGroupPosition: TKMPoint;	
var aiDefenceGroupParametersIndexesToDelete: array of integer;
var aiHaltOrdersIndexesToDelete: array of integer;
begin
	if States.GameTime mod 10 = 0 then
	begin
		for i := 0 to High(aiDefenceGroupParametersArray) do
			begin
				if(States.GroupDead(aiDefenceGroupParametersArray[i].aiDefenceGroupId)) then
					begin
						aiDefenceGroupParametersIndexesToDelete := AddElementToIntegerArray(aiDefenceGroupParametersIndexesToDelete,i);
					end;
			end;
			aiDefenceGroupParametersArray := DeleteIndexesFromAiDefenceGroupParametersArray(aiDefenceGroupParametersArray, aiDefenceGroupParametersIndexesToDelete);
			SetLength(aiDefenceGroupParametersIndexesToDelete, 0);	
			
		for i := 0 to High(aiHaltOrders) do
			begin
				if(States.GroupDead(aiHaltOrders[i].aiDefenceGroupId)) then
					begin
						aiHaltOrdersIndexesToDelete := AddElementToIntegerArray(aiHaltOrdersIndexesToDelete,i);
						Continue;
					end;
				aiDefenceGroupParameters := aiHaltOrders[i];
				aiDefencePostionX := aiDefenceGroupParameters.aiDefencePositionsAssignmentsX;
				aiDefencePostionY := aiDefenceGroupParameters.aiDefencePositionsAssignmentsY;
				aiDefencePostionRadius := aiDefenceGroupParameters.aiDefencePositionsAssignmentsRadius;
				aiDefenceGroupPosition := States.UnitPosition(States.GroupMember(aiDefenceGroupParameters.aiDefenceGroupId,0));
				if(
				(aiDefenceGroupParameters.aiDefenceLeashBreakCount < HITS_TO_BREAK_LEASH_COUNT)
				and
				(not IsPointInRangeCircle(aiDefencePostionX,aiDefencePostionY,aiDefencePostionRadius*0.1,aiDefenceGroupPosition.X,aiDefenceGroupPosition.Y))
				) then
					begin
						Actions.GroupOrderHalt(aiDefenceGroupParameters.aiDefenceGroupId);
						aiHaltOrdersIndexesToDelete := AddElementToIntegerArray(aiHaltOrdersIndexesToDelete,i);
					end;
			end;
			aiHaltOrders := DeleteIndexesFromAiDefenceGroupParametersArray(aiHaltOrders, aiHaltOrdersIndexesToDelete);
			SetLength(aiHaltOrdersIndexesToDelete, 0);				
	end;
end;