--[[
   Copyright (c) The OpenRA Developers and Contributors
   This file is part of OpenRA, which is free software. It is made
   available to you under the terms of the GNU General Public License
   as published by the Free Software Foundation, either version 3 of
   the License, or (at your option) any later version. For more
   information, see COPYING.
]]

CapturableStructures = { "weap", "pyle", "hq", "nuke", "nuk2", "silo", "proc" , "fact" }

ClearPathCameras = { Cam1, Cam2, Cam3, Cam4, Cam5 }
NodSams = { NodSam1, NodSam2, NodSam3, NodSam4, NodSam5, NodSam6, NodSam7 }
NodGuns = { Actor68, Actor69, NodGun4, Actor66 }
OldGDIBase = { OldGDIProc, OldGDIWeap, OldGDIPyle, OldGDIGtwr1, OldGDIGtwr2, OldGDIGtwr3 }

HeliTransEngineers = { "e6", "e6", "e6", "e6", "e6" }

CenterNodBaseEntrance = { CPos.New(35,22), CPos.New(33,23), CPos.New(34,23), CPos.New(35,23), CPos.New(36,23), CPos.New(33,24), CPos.New(34,24), CPos.New(35,24), CPos.New(33,25), CPos.New(34,25), CPos.New(32,27), CPos.New(33,27), CPos.New(32,28), CPos.New(33,28), CPos.New(31,29), CPos.New(32,29), CPos.New(33,29), CPos.New(31,30), CPos.New(32,30), CPos.New(33,30) }

GoDemolitionMan = function(rmbo)
	if rmbo.IsDead then
		return
	end

	local structures = Utils.Where(Map.ActorsInCircle(rmbo.CenterPosition , WDist.FromCells(6)), function(u) return u.HasProperty("StartBuildingRepairs") and u.Owner == GDI end)
	if #structures > 0 then
		rmbo.Stop()
		rmbo.Demolish(Utils.Random(structures))
		rmbo.Hunt()
		Trigger.AfterDelay(DateTime.Seconds(10), function() GoDemolitionMan(rmbo) end)
	end
end

EngineersSent = false
SendEngTeam = function()
	if not EngineersSent then
		EngineersSent = true
		local cargo = Reinforcements.ReinforceWithTransport(Nod, "tran", HeliTransEngineers, { CPos.New(0,32), waypoint11.Location }, { CPos.New(0,32) })[2]
			Utils.Do(cargo, function(engs)
			engs.Move(CPos.New(39,52))
			Trigger.OnIdle(engs, CaptureStructures)
		end)
	end
end

CaptureStructures = function(actor)
	local structures = GDI.GetActorsByTypes(CapturableStructures)
	local distance = 500
	local captst = nil
	Utils.Do(structures, function(st)
		if not actor.IsDead and not st.IsDead and distance > (math.abs((actor.Location - st.Location).X) + math.abs((actor.Location - st.Location).Y)) then
			distance = math.abs((actor.Location - st.Location).X) + math.abs((actor.Location - st.Location).Y)
			captst = st
		end
	end)

	if captst then
		actor.Capture(captst)
	end
end

LaunchNuke = function(loop)
	if NukeDelay == 0 then
		return
	end

	local targets = GDI.GetActorsByTypes({ "nuk2", "atwr", "weap", "proc" })
	if #targets < 1 then
		targets = GDI.GetGroundAttackers()
	end

	if not NodTmpl.IsDead then
		Media.PlaySpeechNotification(GDI, "NuclearWarheadApproaching")
		NodTmpl.ActivateNukePower(Utils.Random(targets).Location)
	end

	if loop then
		Trigger.AfterDelay(DateTime.Seconds(NukeDelay), function() LaunchNuke(true) end)
	end
end

SendNodAirstrike = function(loop)
	if AstkDelay == 0 then
		return
	end

	local target = GetAirstrikeTarget(GDI)
	if target then
		NodAirSupport.TargetAirstrike(target, Angle.SouthEast)
		if loop then
			Trigger.AfterDelay(DateTime.Seconds(AstkDelay), function() SendNodAirstrike(true) end)
		end
	else
		Trigger.AfterDelay(DateTime.Seconds(20), function() SendNodAirstrike(loop) end)
	end
end

SendEasyReinforcements = function(i)
	local team = { }
	if i < 4 then
		team = ReinforceTeams[i]
	else
		team = ReinforceTeams[Utils.RandomInteger(1, 4)]
	end

	Media.PlaySpeechNotification(GDI, "Reinforce")
	Reinforcements.Reinforce(GDI, team, { CPos.New(56,2), waypoint0.Location }, 25, function(a)
		a.Move(waypoint1.Location, 2)
		a.Move(waypoint2.Location, 2)
		a.Move(CPos.New(49,44), 2)
	end)

	Trigger.AfterDelay(DateTime.Seconds(ReinforceDelay), function()
		SendEasyReinforcements(i + 1)
	end)
end

CheckObjectives = function()
	if Nod.HasNoRequiredUnits() then GDI.MarkCompletedObjective(EliminateAllNod) end
	if GDI.HasNoRequiredUnits() then GDI.MarkFailedObjective(EliminateAllNod) end
	Trigger.AfterDelay(50, CheckObjectives)
end

CompleteCaptureCommCenterObjective = function()
	GDI.MarkCompletedObjective(CaptureCommCenter)
	if not NodCYard.IsDead and NodCYard.Owner == Nod then
		Trigger.Clear(NodCYard, "OnDamaged")
		Trigger.AfterDelay(1, function()
			RepairBuilding(Nod, NodCYard, 0.75)
			NodCYard.StartBuildingRepairs()
		end)
	end

	Media.DisplayMessage(UserInterface.GetFluentMessage("communications-center-captured-sams-located"))
	local activeSams = Nod.GetActorsByType("sam")
	local miniCams = { }
	if #activeSams > 0 then
		Utils.Do(activeSams, function(s)
			table.insert(miniCams, Actor.Create("camera.mini", true, { Owner = GDI, Location = s.Location }))
			table.insert(miniCams, Actor.Create("camera.mini", true, { Owner = GDI, Location = CPos.New(s.Location.X + 1, s.Location.Y) }))
		end)

		Trigger.AfterDelay(1, function()
			Utils.Do(miniCams, function(c)
				c.Destroy()
			end)
		end)
	end
end

WorldLoaded = function()
	Camera.Position = DefaultCameraPosition.CenterPosition
	GDI = Player.GetPlayer("GDI")
	Nod = Player.GetPlayer("Nod")

	if Difficulty == "hard" then
		ProduceBuildingsDelay = 50
		ProduceUnitsDelay = 10
		ProductionCooldownSeconds = 30
		MinHarvs = 3
		AstkDelay = 180
		NukeDelay = 900
		ProcUpgrade = "AIHProcUpgrade"
		Nod.Resources = 3000
		GDI.Cash = 3000
		MCVReinf = { "mcv" }
	end

	if Difficulty == "normal" then
		ProduceBuildingsDelay = 70
		ProduceUnitsDelay = 15
		ProductionCooldownSeconds = 55
		MinHarvs = 3
		AstkDelay = 220
		NukeDelay = 1200
		Nod.Resources = 3000
		GDI.Cash = 4000
		ProcUpgrade = "AINProcUpgrade"
		MCVReinf = { "mtnk", "mcv" }
	end

	if Difficulty == "easy" then
		ProduceBuildingsDelay = 100
		ProduceUnitsDelay = 35
		ProductionCooldownSeconds = 85
		MinHarvs = 2
		AstkDelay = 250
		NukeDelay = 1500
		Nod.Resources = 2000
		GDI.Cash = 5000
		MCVReinf = { "mtnk", "mtnk", "mcv", "mtnk" }
		RT1 = { "jeep", "jeep", "apc" }
		RT2 = { "mtnk", "msam" }
		RT3 = { "htnk" }
		ReinforceTeams = { RT1, RT2, RT3 }
		ReinforceDelay = 240
		Actor137.Teleport(CPos.New(57,6))
		Actor203.Destroy()
	end

	GDI.PlayLowPowerNotification = false

	InitObjectives(GDI)
	ClearPath = AddPrimaryObjective(GDI, "clear-path")
	Trigger.AfterDelay(DateTime.Seconds(5), function()
		EliminateAllNod = AddPrimaryObjective(GDI, "eliminate-nod")
	end)

	InitAI()
	CheckObjectives()

	Flare = Actor.Create("flare", true, { Owner = GDI, Location = DefaultFlareLocation.Location })

	Trigger.AfterDelay(1, function()
		AmbushTeam = Map.ActorsInBox(Map.CenterOfCell(CPos.New(46,5)), Map.CenterOfCell(CPos.New(60,53)), function(a) return a.Owner == Nod and a.Type ~= "stnk" end)
		Trigger.OnAllKilled(AmbushTeam, function()
			GDI.MarkCompletedObjective(ClearPath)

			Trigger.AfterDelay(DateTime.Seconds(2), function()
				Trigger.AfterDelay(DateTime.Seconds(30), function()
					Flare.Destroy()
				end)

				Media.PlaySpeechNotification(GDI, "Reinforce")
				Reinforcements.Reinforce(GDI, MCVReinf, { CPos.New(56,2), waypoint0.Location }, 25, function(a)
					a.Move(waypoint1.Location, 2)
					a.Move(waypoint2.Location, 2)
					a.Move(CPos.New(49,44), 2)
					if a.HasProperty("Deploy") then
						a.Move(CPos.New(48,51))
						a.Deploy()

						Trigger.OnRemovedFromWorld(a, function()
							if not a.IsDead then
								GDI.PlayLowPowerNotification = true
							end
						end)
					end
				end)

				Utils.Do(ClearPathCameras, function(c)
					c.Destroy()
				end)

				if Difficulty == "easy" then
					Trigger.AfterDelay(DateTime.Seconds(ReinforceDelay), function()
						SendEasyReinforcements(1)
					end)
				end
			end)
			Trigger.AfterDelay(DateTime.Seconds(8), function()
				RecoverOldBase = AddSecondaryObjective(GDI, "recover-old-base")
			end)
		end)
	end)

	NodAirSupport = Actor.Create("Astk.proxy", true, { Owner = Nod })
	Trigger.AfterDelay(DateTime.Seconds(AstkDelay), function()
		if AstkDelay > 0 then
			SendNodAirstrike(true)
			Trigger.AfterDelay(DateTime.Seconds(15), function()
				Media.DisplayMessage(UserInterface.GetFluentMessage("air-strikes-intel-report"))
				Trigger.AfterDelay(DateTime.Seconds(8), function()
					CaptureCommCenter = AddSecondaryObjective(GDI, "capture-nod-communications-center")
					if NodAstkHq.IsDead then
						GDI.MarkFailedObjective(CaptureCommCenter)
						return
					end

					if NodAstkHq.Owner == GDI then
						Trigger.AfterDelay(DateTime.Seconds(4), CompleteCaptureCommCenterObjective)
					end
				end)
			end)
		end
	end)

	Trigger.OnKilled(NodOutPostCYard, function()
		SendNodAirstrike(false)
		if not RecoverOldBase then
			RecoverOldBase = AddSecondaryObjective(GDI, "recover-old-base")
		end
		GDI.MarkFailedObjective(RecoverOldBase)
	end)

	Trigger.OnCapture(NodOutPostCYard, function()
		Trigger.ClearAll(NodOutPostCYard)
		Utils.Do(OldGDIBase, function(building)
			if not building.IsDead then
				building.Owner = GDI
			end
		end)

		GDI.MarkCompletedObjective(RecoverOldBase)
		table.insert(SneakPaths, SP2)
		table.insert(SneakPaths, SP1)
		table.insert(SneakPaths, SP2)
	end)

	Utils.Do(NodGuns, function(g)
		Trigger.OnKilled(g, function()
			SendEngTeam()
			Utils.Do(NodGuns, function(gun) if not gun.IsDead then Trigger.Clear(gun, "OnKilled") end end)
		end)
	end)

	RamboSent = false
	Trigger.OnEnteredFootprint(CenterNodBaseEntrance, function(a, id)
		if a.Owner == GDI and not RamboSent then
			RamboSent = true
			local cargo = Reinforcements.ReinforceWithTransport(Nod, "tran", { "rmbo" }, { CPos.New(0,32), waypoint12.Location }, { CPos.New(0,32) })[2]
			Trigger.OnIdle(cargo[1], function()
				Trigger.Clear(cargo[1], "OnIdle")
				Media.PlaySpeechNotification(GDI, "BaseAttack")
				Trigger.AfterDelay(5, function() GoDemolitionMan(cargo[1]) end)
			end)
			Trigger.RemoveFootprintTrigger(id)
		end
	end)

	Trigger.OnDamaged(NodCYard, function(_, atk, _)
		if atk.Owner == GDI and not NukeLaunched then
			NukeLaunched = true
			LaunchNuke(false)
			Trigger.Clear(NodCYard, "OnDamaged")
			Trigger.AfterDelay(1, function()
				RepairBuilding(Nod, NodCYard, 0.75)
				NodCYard.StartBuildingRepairs()
			end)
		end
	end)

	Trigger.OnKilledOrCaptured(NodTmpl, function()
		NukeDelay = 0
	end)

	Trigger.OnCapture(NodAstkHq, function()
		AstkDelay = 0
		Trigger.ClearAll(NodAstkHq)
		if CaptureCommCenter then
			CompleteCaptureCommCenterObjective()
		end
	end)

	Trigger.OnKilled(NodAstkHq, function()
		if CaptureCommCenter then
			GDI.MarkFailedObjective(CaptureCommCenter)
		end
		AstkDelay = 0
	end)
end
