CarryallPatrolPoints = {Carryall_patrol1, Carryall_patrol2, Carryall_patrol3, Spice_location4}
SpawnPoints = {SmugglerSpawnPoint1,SmugglerSpawnPoint2,SmugglerSpawnPoint3}
CreepsSquad = {"quad", "trike","combat_tank_cheap","quad", "trike","quad", "trike"}
StarportUnits = {"quad", "hunter","combat_tank_cheap", "trooper","trooper","trooper", "light_inf","light_inf","light_inf"}
CarryPatrolSquads = {
	{"quad", "trike"},
	{"trike", "trike"},
	{"combat_tank_cheap"},
	{"light_inf","light_inf","light_inf", "trooper"},
	{"light_inf","mortar_inf","mortar_inf", "trooper"}
}
PatrollingCarryalls = {Carryall1}
CrawlerSpawnPoints = {SpawnPoint_crawler1,SpawnPoint_crawler3,SpawnPoint_crawler2}
IdleCarryalls = {Carryall_idle1, Carryall_idle2, Carryall_idle}
Airstrike_waypoints = {Script_airstrike1, Script_airstrike2}
Airstrike_detectionRadius = {6, 8}
PatrolLoops = {{Squad1_patrolPoint1.Location,Squad1_patrolPoint2.Location, Squad1_patrolPoint3.Location}}
PatrolingSquads = {{Squad1_unit1,Squad1_unit2,Squad1_unit3,Squad1_unit4}}
WormsLocations = {waypoint_worm1, waypoint_worm2_west,waypoint_worm3_east,waypoint_worm4_north}
WormsActionRadius = {10,15,12,15}
SpawnInterval = 500 -- interval for triggering script events
CrawlerSquad = {"trooper", "light_inf","trooper","light_inf", "mortar_inf","trooper", "light_inf","trooper","light_inf", "mortar_inf"}
ItemList = {}
ItemList["Multi0"] = {}
ItemList["Multi1"] = {}
ItemList["Multi2"] = {}
ItemList["Multi3"] = {}

CalculateCellRange = function(xStart, xEnd, yStart, yEnd)
	xStart = xStart or 152
	xEnd = xEnd or 157
	yStart = yStart or 0
	yEnd = yEnd or 50
	local cellArray = {}
	for i = xStart, xEnd do
		for j = yStart, yEnd do
			table.insert(cellArray,CPos.New(i,j))
		end
	end
	return cellArray
end

InitEarlyGame = function()
	Sietch1.RallyPoint = CPos.New(21,60)
	InitWormLocations()
	InitRandomMoves()
	InitAirstrikes()
	InitPatrols()
	CarryallsMercenaryReinforcemnts()
end

InitMidGame = function()
	SpawnCrawler()
	SpawnGroundSquad(3, Creeps)
	GameStage[1] = true
	Trigger.AfterDelay(1000, function()
		FremenProduction()
		FremenProduction1()
		SpawnWormrider()
		FindSaboteur(Fremen)
		HarkonnenBarrackProduction()
		HarkonnenVehicleProduction()
		RandomAirStrike()
		StarportProduction(Smugglers_starport,5,1200)
	end)
end

InitLateGame = function()
	GameStage[3] = true
	TrikeSpawner()
end



InitAirstrikes = function()
	for i, airstrikePoint in pairs(Airstrike_waypoints) do
		local position = Map.CenterOfCell(airstrikePoint.Location)
		Trigger.OnEnteredProximityTrigger(position, WDist.FromCells(Airstrike_detectionRadius[i]), function(actor)
			if actor.Type == "harvester.d2k" or actor.Type == "harvester.ra" or actor.Type == "harvester.cnc" then
				Trigger.AfterDelay(Utils.RandomInteger(15, 50), function()
					if AirstrikeCounter[actor.Owner.InternalName] == nil then
						Media.Debug("calling airstrike against"..tostring(actor.Owner))
						CallAirstrike(actor, 1, Creeps)
						AirstrikeCounter[actor.Owner.InternalName] = true
					end
				end)
			end
		end)
	end
end

InitWormLocations = function()
	for i, wormLocation in pairs(WormsLocations) do
		Actor.Create("sandworm", true, {Owner=Creeps, Location=wormLocation.Location})
		local center = Map.CenterOfCell(wormLocation.Location)
		Trigger.OnExitedProximityTrigger(center,WDist.FromCells(6), function(actor)
			if actor.Type == "sandworm" and not actor.IsDead then
				--Media.Debug("Come back you little worm !!!")
				actor.Stop()
				actor.Move(wormLocation.Location)
			end
		end)
	end
end

InitRandomMoves = function ()
	local waypoints = NeutralPlayer.GetActorsByType("waypoint")
	for i, carryall in pairs(IdleCarryalls) do
			RandomMoves(carryall, waypoints)
			if not carryall.IsDead then
				carryall.CallFunc(function()
					RandomMoves(carryall, waypoints)
				end)
			end
	end
end

InitPatrols = function()
	for i, squad in pairs(PatrolingSquads) do
		Utils.Do(squad, function(unit)
			unit.Patrol(PatrolLoops[1],true, 500)
		end)
	end
end

RandomMoves = function(actor, waypoints)
	if not actor.IsDead then
		local waypoints_shuffle = Utils.Shuffle(waypoints)
		for _, waypoint in pairs(waypoints_shuffle) do
			actor.Move(waypoint.Location)
		end
	end
end
SpawnGroundSquad = function(squadSize, faction, unitTypes, delayRange)
	--Media.Debug("spawn squad")
	unitTypes= unitTypes or CreepsSquad
	squadSize = squadSize or 3
	delayRange = delayRange or {300,450}
	Trigger.AfterDelay(Utils.RandomInteger(delayRange[1],delayRange[2]), function()
		local squad = Utils.Take(squadSize, Utils.Shuffle(unitTypes))
		local spawnPoint = Utils.Random(SpawnPoints)
		local path = {Map.ClosestEdgeCell(spawnPoint.Location),spawnPoint.Location}
		if path[1].X > 50 then
			path[1] = path[1] + CVec.New(-1,0)
		end
		local randomPoints= {SpawnPoints,CrawlerSpawnPoints, AllWaypoints,SpiceTriggers}
		Reinforcements.Reinforce(faction, squad,path, 25, function(actor)
			Patrol(actor, Utils.Shuffle(Utils.Random(randomPoints)))
		end)
		SpawnGroundSquad(squadSize, faction, unitTypes, delayRange)
	end)
end
SpawnCrawler = function()
	Trigger.AfterDelay(Utils.RandomInteger(250,450), function()
		local squad = Utils.Take(5, Utils.Shuffle(CrawlerSquad))
		local spawnPoint = Utils.Random(CrawlerSpawnPoints)
		local path = {Map.ClosestEdgeCell(spawnPoint.Location),spawnPoint.Location}
		--Media.Debug("spawning crawler")
		Reinforcements.ReinforceWithTransport(Mercenary, "crawler.light", squad,path, nil, function(transport, pass)
			Patrol(transport,Utils.Shuffle(AllWaypoints))
		end)
		SpawnCrawler()
	end)
end

Patrol = function(unit, waypoints)
	for i, waypoint in pairs(waypoints) do
		if unit.IsDead then
			return
		end
		unit.Wait(200)
		if unit.HasProperty("AttackMove") then
			unit.AttackMove(waypoint.Location,3)
		else
			unit.Move(waypoint.Location,3)
		end
	end
	Trigger.OnDamaged(unit, function(self, attaker)
		if	self.Type == "crawler.light" then
			self.Stop()
			self.UnloadPassengers()
			local passangers = self.Passengers
			for _, passanger in pairs(passangers) do
				Trigger.OnAddedToWorld(passanger, function()
					if attaker.HasProperty("Location") then
						passanger.AttackMove(attaker.Location)
						passanger.CallFunc(function()Patrol(unit,waypoints)end)
						IdleHunt(passanger)
					end
					unit.CallFunc(function()Patrol(unit,waypoints)end)
				end)
			end
		else
		if self.HasProperty("AttackMove") and attaker.HasProperty("Location") then
			self.Stop()
			unit.AttackMove(attaker.Location)
		end
		unit.CallFunc(function()Patrol(unit,waypoints)end)
		end
	end)
	Trigger.OnDiscovered(unit, function(discovered, discoverer)
		--Media.Debug("discovered "..tostring(discovered).." discoverer"..tostring(discoverer))
		if	discovered.Type == "crawler.light" then
			discovered.Stop()
			discovered.UnloadPassengers()
		else
			IdleHunt(discovered)
		end
	end)

end

IdleHunt = function(actor)
	if actor.HasProperty("Hunt") and not actor.IsDead then
		Trigger.OnIdle(actor, actor.Hunt)
	end
end
ReinforceCounter = {}


CarryallsMercenaryReinforcemnts = function()
	for _, locationPoint in pairs(CarryallPatrolPoints) do
		local position = Map.CenterOfCell(locationPoint.Location)
		Trigger.OnEnteredProximityTrigger(position, WDist.FromCells(6),function(actor)
			if actor.Type == "harvester.d2k" or actor.Type == "harvester.ra" or actor.Type == "harvester.cnc" then
				if ReinforceCounter[actor.Owner.InternalName] == nil then
					ReinforceCounter[actor.Owner.InternalName] = true
					local path = {Map.ClosestEdgeCell(actor.Location), actor.Location}
					Reinforcements.ReinforceWithTransport(Mercenary,"carryall.cargo",Utils.Random(CarryPatrolSquads),  path,{path[2],path[1]}, function(carry, pass)
						CarryPatrol(carry, pass,{locationPoint})
					end)
					Trigger.AfterDelay(50, function()
						Reinforcements.ReinforceWithTransport(Mercenary,"carryall.cargo",Utils.Random(CarryPatrolSquads),  path,{path[2],path[1]}, function(carry, pass)
							CarryPatrol(carry, pass,{locationPoint})
						end)
					end)
					
				end
			end
			if actor.Owner ~= Mercenary and SpawnInterval <= 0 and actor.HasProperty("Location") and not Map.IsNamedActor(actor) then
				SpawnInterval = 500
				local path = {Map.ClosestEdgeCell(actor.Location), actor.Location}
				Reinforcements.ReinforceWithTransport(Mercenary, "carryall.reinforce_with_sound",ReinforcementSquads[Utils.RandomInteger(1,4)], path,{path[2],path[1]})
			end
		end)
	end
end


CarryPatrol = function(carryall, cargo,  waypoints)
	if carryall.IsDead then
		return
	end
		local waypoint = Utils.Random(waypoints)
		carryall.Move(waypoint.Location)
	-- enemy check
	local harvesters = Map.ActorsInCircle(carryall.CenterPosition, WDist.FromCells(5),function(actor)
		return 
			actor.Type == "harvester.d2k" or
			actor.Type == "harvester.ra" or
			actor.Type == "harvester.cnc" or
			actor.Owner == Mercenary
	end)
	if harvesters[1] ~= nil then
		Media.Debug("Harvester found")
		carryall.Stop()
		local dummy = Actor.Create("waypoint", true, {Owner = Player.GetPlayer("Neutral"), Location = harvesters[1].Location + CVec.New(1,1)})
		carryall.Land(dummy)
		carryall.UnloadPassengers()
		carryall.Move(Map.ClosestEdgeCell(carryall.Location))
		dummy.Destroy()
		carryall.Destroy()
		Utils.Do(cargo, function(unit)
			Trigger.OnAddedToWorld(unit, function()
				if not harvesters[1].IsDead then
					unit.AttackMove(harvesters[1].Location,1)
				end
				IdleHunt(unit)
			end)
		end)
	else
		Trigger.AfterDelay(100, function()
			CarryPatrol(carryall,cargo, waypoints)
		end)
	end
end

RandomAirStrike = function()
	local cposStart = CPos.New(5,16)
	local cposEnds = CPos.New(46,165)
	local dummyOrni = Actor.Create("ornithopter_o", false,{Owner = Creeps})
	local allTargets = Map.ActorsInBox(Map.CenterOfCell(cposStart),Map.CenterOfCell(cposEnds), function(actor)
		return
			Map.IsNamedActor(actor) ~= true and
			actor.Type ~= "crate" and
			actor.Type ~= "waypoint" and
			actor.Type ~= "stealth_raider" and
			actor.Owner ~= "Corrino" and
			actor.Type ~= "dummy.generic" and
			dummyOrni.CanTarget(actor)
	end)
	if allTargets[1] ~= nil then
		local finalTarget = Utils.Random(allTargets)
		Media.Debug("random airstrike on "..tostring(finalTarget.Type).." position "..tostring(finalTarget.Location.X)..","..tostring(finalTarget.Location.Y))
		Actor.Create("waypoint.airtstrike_o",true,{Owner = Creeps, Location = finalTarget.Location})
		dummyOrni.Stop()
		dummyOrni.Kill()
	else
		dummyOrni.Kill()
	end
	Trigger.AfterDelay(1200, function()
		RandomAirStrike()
	end)
end

--- HArkonnen VS FremenProductionFremen
IdlingUnitsHarkonnen = {}
IdlingUnitsFremen = {}
IdlingUnitsFremen1 = {}

AttackThresholdSize = 5
AttackLocation = {Sietch_big.Location,Harkonnen_heavy.Location}

HarkonnenInfantry = {"trooper","trooper", "light_inf", "light_inf", "light_inf"}

FremenProduction = function()
	if Sietch_big.IsDead then
		return
	end
	local delay = Utils.RandomInteger(25, 50)
	if Harkonnen_barracks.IsDead then
		delay = 120
	end
	Fremen.Build({ "fremen_fedaykin" }, function(units)
		table.insert(IdlingUnitsFremen, units[1])
		if #IdlingUnitsFremen >= AttackThresholdSize then
			SendAttack(IdlingUnitsFremen, AttackLocation[2])
			IdlingUnitsFremen = {}
		end
		Trigger.AfterDelay(delay, FremenProduction)
	end)
end

FremenProduction1 = function()
	if Sietch1.IsDead then
		return
	end
	local delay = Utils.RandomInteger(70, 110)
	Fremen.Build({"fremen.vehicle"}, function(units)
		table.insert(IdlingUnitsFremen1, units[1])
		if #IdlingUnitsFremen1 >= AttackThresholdSize then
			SendAttack(IdlingUnitsFremen1, AttackLocation[2])
			IdlingUnitsFremen1 = {}
		end
		Trigger.AfterDelay(delay, FremenProduction1)
	end)
end

SpawnWormrider = function()
	Trigger.AfterDelay(4000, function()
		if Sietch_big.IsDead or Harkonnen_barracks.IsDead then
			return
		end
		local worm = Actor.Create("worm_rider", true,{Owner = Fremen, Location = Worm_rider_spawn_point.Location})
		worm.CallFunc(function()
			worm.Move(CPos.New(44,73),3)
		end)
		SpawnWormrider()
	end)
end

HarkonnenBarrackProduction = function()
	if Harkonnen_barracks.IsDead and Smugglers_starport.IsDead then
		return
	end
	local delay = Utils.RandomInteger(40, 60)
	if Sietch_big.IsDead then
		delay = Utils.RandomInteger(80, 150)
	end
	local unitToBuild = Utils.Random(HarkonnenInfantry)
	local test = Harkonnen.Build({ unitToBuild }, function(units)
		table.insert(IdlingUnitsHarkonnen, units[1])
		
		if #IdlingUnitsHarkonnen >= AttackThresholdSize then
			SendAttack(IdlingUnitsHarkonnen, AttackLocation[1])
			IdlingUnitsHarkonnen = {}
		end
		Trigger.AfterDelay(delay, HarkonnenBarrackProduction)
	end)
end



HarkonnenVehicleProduction = function()
	if Harkonnen_heavy.IsDead then
		return
	end
	local delay = Utils.RandomInteger(50, 145)
	if Sietch_big.IsDead then
		delay = Utils.RandomInteger(120, 250)
	end
	
	local test = Harkonnen.Build({ "combat_tank_h" }, function(units)
		table.insert(IdlingUnitsHarkonnen, units[1])
		if #IdlingUnitsHarkonnen >= AttackThresholdSize then
			SendAttack(IdlingUnitsHarkonnen, AttackLocation[1])
		end
		Trigger.AfterDelay(delay, HarkonnenVehicleProduction)
	end)
end
IdlingUnits ={}

SendAttack = function(units, location)
	location = location or nil
	--Media.Debug("sending attack")
	Utils.Do(units, function(unit)
		if not unit.IsDead then
			if location ~= nil then
				unit.AttackMove(location,3)
			end
			IdleHunt(unit)
		end
	end)
end

StarportProduction = function(starport, orderSize,delay, unittypes)
	if starport.IsDead then
		return
	end
	 if starport.Owner.Resources  > 500 then
		starport.Owner.Resources = starport.Owner.Resources - 500
	 end 
	unittypes = unittypes or StarportUnits
	local orderList = Utils.Take(orderSize, Utils.Shuffle(StarportUnits))
	local path = {Map.ClosestEdgeCell(starport.Location), starport.Location + CVec.New(1,1)}
	Trigger.AfterDelay(delay, function()
		local unit = Reinforcements.ReinforceWithTransport(starport.Owner, "frigate_choam",orderList,path, {path[2],path[1]})
		Trigger.OnPassengerExited(unit[1],function(transport, pass)
			Trigger.OnAddedToWorld(pass,function()
				SendAttack({pass}, nil)
			end)
		end)
		StarportProduction(starport, orderSize,delay, unittypes)
	end)
end

RepairBuilding = function(owner, actor)
	Trigger.OnDamaged(actor, function(building)
		if building.Owner == owner and building.Health < building.MaxHealth then
			building.StartBuildingRepairs()
		end
	end)
end


--------
---harvesterAI---
------
HarvesterPaths = {}
HarvesterPaths[1] = {SpawnPoint_crawler3.Location,Harvester_path1_1.Location, Harvester_path1_2.Location, waypoint_random3.Location}
HarvesterPaths[2] = {Squad1_patrolPoint1.Location,Squad1_patrolPoint3.Location,waypoint_worm3_east.Location}
HarvesterPaths[3] = {Harvester_path3_1.Location, Harvester_path3_2.Location, Harvester_path3_3.Location}
HarvesterPaths[4] = {waypoint_worm4_north.Location,Harvester_path4_1.Location, Harvester_path4_2.Location}
HarvesterPaths[5] = {Harvester_path5_1.Location}
InitHarvester_AI = function(player)
	if player.GetActorsByTypes({"harvester.d2k", "harvester.ra", "harvester.cnc"}) ~=nil then
		local harvesters = player.GetActorsByTypes({"harvester.d2k", "harvester.ra", "harvester.cnc"})
		if not player.IsObjectiveCompleted(Objective_GatherSpice[player.InternalName][1]) then
			Utils.Do(harvesters, function(harvester)
				Harvester_AI(nil, harvester, HarvesterPaths[1], true, Spice_location1.Location, 1)
			end)
		elseif not player.IsObjectiveCompleted(Objective_GatherSpice[player.InternalName][2]) then
			Utils.Do(harvesters, function(harvester)
				Harvester_AI(Spice_location1.Location, harvester, HarvesterPaths[2], true, Spice_location2.Location)
			end)
		elseif not player.IsObjectiveCompleted(Objective_GatherSpice[player.InternalName][3]) then
			Utils.Do(harvesters, function(harvester)
				Harvester_AI(Spice_location2.Location,harvester, HarvesterPaths[3], true, Spice_location3.Location)
			end)
		elseif not player.IsObjectiveCompleted(Objective_GatherSpice[player.InternalName][4]) then
			Utils.Do(harvesters, function(harvester)
				Harvester_AI(Spice_location3.Location,harvester, HarvesterPaths[4], true, Spice_location4.Location)
			end)
		else
			Utils.Do(harvesters, function(harvester)
				local number = Utils.RandomInteger(1, #FinishCells)
				Harvester_AI(nil, harvester,HarvesterPaths[5], true, FinishCells[number])
			end)
		end
		Utils.Do(harvesters, function(harvester)
			HideRepair(harvester)
		end)
	end
end

Harvester_AI = function(initialpath, harvester, paths, sideQuest,finalDestination, objective)
	if harvester.IsDead then
		return
	end
	harvester.Stop()
	paths = paths or {finalDestination}
	if initialpath ~= nil then
		harvester.Move(initialpath,1)
		Trigger.OnEnteredProximityTrigger(Map.CenterOfCell(initialpath),WDist.FromCells(4), function(actor, id)
			if harvester.IsDead then
				Trigger.RemoveProximityTrigger(id)
				return
			end
			if tostring(actor) == tostring(harvester) then
				Media.Debug("AI -".."harvester enter INIt point")
				Trigger.RemoveProximityTrigger(id)
				Harvester_AI(nil, harvester, paths, sideQuest,finalDestination, objective)
			end
		end)
	end
	if paths[1] == nil then
		return
	end
	Media.Debug("AI - calling new path")
	local randomPath = Utils.Random(paths)
	if sideQuest and initialpath == nil then
		if Utils.RandomInteger(1,4) == 1 then
			--Harvest additional resources
			Trigger.AfterDelay(300, function()
				if harvester.IsDead then
					return
				end
				Media.Debug("AI - looking for more spice")
				harvester.Stop()
				harvester.Owner.GrantCondition("findResources", 700)
			end)
			Trigger.AfterDelay(1200, function()
				--Media.Debug("AI - end of search")
				if harvester.IsDead then
					return
				end
				Harvester_AI(nil,harvester, nil, false ,finalDestination, objective)
			end)
		end
		if Utils.RandomInteger(1,3) == 2 then
			--search for crates
			Media.Debug("AI - crate search")
			CrateSearcher(harvester, {randomPath}, sideQuest,finalDestination, objective)
		end
	end
	harvester.Move(randomPath,1)
	harvester.Move(finalDestination,4)
	harvester.FindResources()
end


CrateSearcher = function(harvester, paths, sideQuest,finalDestination,objective, id)
	id = id or 5
	Trigger.AfterDelay(100,function ()
		if harvester.IsDead then
			return
		end
		id = id - 1
		--Media.Debug("id "..tostring(id))
		local position = Map.CenterOfCell(harvester.Location)
		local crates = Map.ActorsInCircle(position,WDist.FromCells(6), function(actor)
			return actor.Type == "crate" or 
					actor.Type == "crate.repair" or 
					actor.Type == "crate.jammer" or
					actor.Type == "crate.speedup"
		end)
		if crates ~=nil then
			harvester.Stop()
			Utils.Do(crates, function(crate)
				harvester.Move(crate.Location)
			end)
			harvester.CallFunc(function()
				if id == 0 then
					Harvester_AI(nil, harvester, paths, false,finalDestination, objective)
				else
					harvester.Move(paths[1])
					CrateSearcher(harvester, paths, sideQuest,finalDestination, objective, id)
				end
			end)
		end
	end)
end

HideRepair = function(harvester)
	if harvester.IsDead then
		return
	end
	if harvester.MaxHealth * 0.6 > harvester.Health then
		
	end
end

TrikeAttack= function(trike)
	if trike.IsDead then
		return
	end
	local harvesters = Map.ActorsInCircle(trike.CenterPosition,WDist.FromCells(4) ,function(actor)
		return 
				actor.Type == "harvester" or
				actor.Type == "harvester.ra" or
				actor.Type == "harvester.cnc"
	end)
	if harvesters[1] ~= nil then
		trike.AttackMove(harvesters[1].Location,1)
		IdleHunt(trike)
	end
	Trigger.AfterDelay(70, function()
		TrikeAttack(trike)
	end)
end
TrikeList = {}
TrikeSpawner= function()
	if TrikeList == nil or #TrikeList < 12 then
		--Media.Debug("trike spawnned")
		local spawnPoints = Player.GetPlayer("Neutral").GetActorsByType("cloaked_trike_spawner")
		local newTrike = Actor.Create("stealth_raider",true,{Owner = Creeps, Location = Utils.Random(spawnPoints).Location})
		table.insert(TrikeList, newTrike)
		Trigger.OnRemovedFromWorld(newTrike, function()
			for position, deadActor in pairs(TrikeList) do
				if tostring(deadActor) == tostring(newTrike) then
					table.remove(TrikeList, position)
				end
			end
		end)
		TrikeAttack(newTrike)
	end
	Trigger.AfterDelay(300, function()
		TrikeSpawner()
	end)

end

AI_ItemLogic= function(harvester)
	Trigger.OnDamaged(harvester, function(self, attaker)
		if self.IsDead then
			return
		end
		if self.MaxHealth * 0.6 > self.Health then
			local ownerName = self.Owner
			for _, item in pairs(ItemList[ownerName.InternalName]) do
				if item.HasTag("item_repair") then
					Media.Debug("REPAIRING")
					Actor.Create("dummy.repair", true, {Owner = ownerName,Location = harvester.Location})
					item.RemoveTag("item_repair")
				end
			end
		end
	end)
end