--[[
   Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
   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.
]]

HarkonnenBase = { HarkonnenConstructionYard, HarkonnenWindTrap1, HarkonnenWindTrap2, HarkonnenWindTrap3, HarkonnenWindTrap4, HarkonnenWindTrap5, HarkonnenWindTrap6, HarkonnenWindTrap7, HarkonnenWindTrap8, HarkonnenSilo1, HarkonnenSilo2, HarkonnenSilo3, HarkonnenSilo4, HarkonnenGunTurret1, HarkonnenGunTurret2, HarkonnenGunTurret3, HarkonnenGunTurret4, HarkonnenGunTurret5, HarkonnenGunTurret6, HarkonnenGunTurret7, HarkonnenHeavyFactory, HarkonnenRefinery, HarkonnenOutpost, HarkonnenLightFactory }
SmugglerBase = { SmugglerWindTrap1, SmugglerWindTrap2 }

HarkonnenReinforcements =
{
	easy =
	{
		{ "combat_tank_h", "trooper", "trooper", "trooper", "trooper" },
		{ "trooper", "trooper", "trooper", "trooper", "trooper" },
		{ "combat_tank_h", "light_inf", "light_inf", "trooper", "trooper" },
		{ "combat_tank_h", "light_inf", "light_inf", "light_inf", "trooper", "trooper" },
		{ "combat_tank_h", "trike", "light_inf", "light_inf", "trooper", "trooper" }
	},

	normal =
	{
		{ "combat_tank_h", "trooper", "trooper", "trooper", "trooper" },
		{ "trooper", "trooper", "trooper", "trooper", "trooper" },
		{ "combat_tank_h", "light_inf", "light_inf", "trooper", "trooper" },
		{ "combat_tank_h", "light_inf", "light_inf", "light_inf", "trooper", "trooper" },
		{ "combat_tank_h", "trike", "light_inf", "light_inf", "trooper", "trooper" },
		{ "combat_tank_h", "trike", "combat_tank_h", "light_inf", "trooper", "trooper", "quad" },
		{ "combat_tank_h", "trike", "light_inf", "light_inf", "trooper", "trooper", "quad", "quad" }
	},

	hard =
	{
		{ "combat_tank_h", "trooper", "trooper", "trooper", "trooper" },
		{ "trooper", "trooper", "trooper", "trooper", "trooper" },
		{ "combat_tank_h", "light_inf", "light_inf", "trooper", "trooper" },
		{ "combat_tank_h", "light_inf", "light_inf", "light_inf", "trooper", "trooper" },
		{ "combat_tank_h", "trike", "light_inf", "light_inf", "trooper", "trooper" },
		{ "combat_tank_h", "trike", "combat_tank_h", "light_inf", "trooper", "trooper", "quad" },
		{ "combat_tank_h", "trike", "light_inf", "light_inf", "trooper", "trooper", "quad", "quad" },
		{ "combat_tank_h", "combat_tank_h", "trike", "light_inf", "light_inf", "trooper", "trooper", "quad", "quad" },
		{ "combat_tank_h", "combat_tank_h", "combat_tank_h", "combat_tank_h", "combat_tank_h", "combat_tank_h" }
	}
}

HarkonnenInfantryReinforcements =
{
	normal =
	{
		{ "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper", "trooper" }
	},

	hard =
	{
		{ "light_inf", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper", "trooper", "trooper", "trooper", "trooper" },
		{ "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper", "trooper" }
	}
}
InfantryPath = { HarkonnenEntry3.Location }

HarkonnenAttackDelay =
{
	easy = DateTime.Minutes(3),
	normal = DateTime.Minutes(2) + DateTime.Seconds(20),
	hard = DateTime.Minutes(1)
}

HarkonnenAttackWaves =
{
	easy = 5,
	normal = 7,
	hard = 9
}

MercenaryReinforcements =
{
	easy =
	{
		{ "combat_tank_o", "combat_tank_o", "quad", "quad", "light_inf", "light_inf", "light_inf", "light_inf" },
		{ "trike", "trike", "quad", "quad", "quad", "trike", "trike", "trike" },
		{ "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper" }
	},

	normal =
	{
		{ "trike", "trike", "quad", "quad", "quad", "trike", "trike", "trike" },
		{ "combat_tank_o", "combat_tank_o", "quad", "quad", "light_inf", "light_inf", "light_inf", "light_inf" },
		{ "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper" },
		{ "combat_tank_o", "combat_tank_o", "quad", "quad", "light_inf", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper" },
		{ "trike", "trike", "quad", "quad", "quad", "trike", "trike", "trike", "trike", "trike", "trike" },
		{ "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper" }
	},

	hard =
	{
		{ "combat_tank_o", "combat_tank_o", "quad", "quad", "light_inf", "light_inf", "light_inf", "light_inf" },
		{ "trike", "trike", "quad", "quad", "quad", "trike", "trike", "trike" },
		{ "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper" },
		{ "trike", "trike", "quad", "quad", "quad", "trike", "trike", "trike", "trike", "trike", "trike" },
		{ "combat_tank_o", "combat_tank_o", "quad", "quad", "light_inf", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper" },
		{ "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper" },
		{ "combat_tank_o", "combat_tank_o", "quad", "quad", "trike", "trike", "light_inf", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper" },
		{ "trike", "trike", "quad", "quad", "quad", "trike", "trike", "trike", "trike", "trike", "trike", "quad", "quad" },
		{ "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "trike", "trike", "quad", "quad", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper" }
	}
}

MercenaryAttackDelay =
{
	easy = DateTime.Minutes(2) + DateTime.Seconds(40),
	normal = DateTime.Minutes(1) + DateTime.Seconds(50),
	hard = DateTime.Minutes(1) + DateTime.Seconds(10)
}

MercenaryAttackWaves =
{
	easy = 3,
	normal = 6,
	hard = 9
}

MercenarySpawn = { HarkonnenRally4.Location + CVec.New(2, -2) }

-- Ordos tanks because those were intended for the Smugglers not the Atreides
ContrabandReinforcements = { "mcv", "quad", "quad", "combat_tank_o", "combat_tank_o", "combat_tank_o" }
SmugglerReinforcements = { "quad", "quad", "trike", "trike" }
InitialHarkonnenReinforcements = { "trooper", "trooper", "quad", "quad", "trike", "trike", "trike", "light_inf" }

HarkonnenPaths =
{
	{ HarkonnenEntry1.Location, HarkonnenRally1.Location },
	{ HarkonnenEntry1.Location, HarkonnenRally3.Location },
	{ HarkonnenEntry1.Location, HarkonnenRally2.Location },
	{ HarkonnenEntry1.Location, HarkonnenRally4.Location },
	{ HarkonnenEntry2.Location }
}

AtreidesReinforcements = { "trike", "combat_tank_a" }
AtreidesPath = { AtreidesEntry.Location, AtreidesRally.Location }

ContrabandTimes =
{
	easy = DateTime.Minutes(10),
	normal = DateTime.Minutes(5),
	hard = DateTime.Minutes(2) + DateTime.Seconds(30)
}

wave = 0
SendHarkonnen = function()
	Trigger.AfterDelay(HarkonnenAttackDelay[Difficulty], function()
		if player.IsObjectiveCompleted(KillHarkonnen) then
			return
		end

		wave = wave + 1

		if InfantryReinforcements and wave % 4 == 0 then
			local inf = Reinforcements.Reinforce(harkonnen, HarkonnenInfantryReinforcements[Difficulty][wave/4], InfantryPath)
			Utils.Do(inf, function(unit)
				unit.AttackMove(HarkonnenAttackLocation)
				IdleHunt(unit)
			end)
		end

		local entryPath = Utils.Random(HarkonnenPaths)
		local units = Reinforcements.ReinforceWithTransport(harkonnen, "carryall.reinforce", HarkonnenReinforcements[Difficulty][wave], entryPath, { entryPath[1] })[2]
		Utils.Do(units, function(unit)
			unit.AttackMove(HarkonnenAttackLocation)
			IdleHunt(unit)
		end)

		if wave < HarkonnenAttackWaves[Difficulty] then
			SendHarkonnen()
			return
		end

		Trigger.AfterDelay(DateTime.Seconds(3), function() LastHarkonnenArrived = true end)
	end)
end

mercWave = 0
SendMercenaries = function()
	Trigger.AfterDelay(MercenaryAttackDelay[Difficulty], function()
		mercWave = mercWave + 1

		Media.DisplayMessage("Incoming hostile Mercenary force detected.", "Mentat")

		local units = Reinforcements.Reinforce(mercenary, MercenaryReinforcements[Difficulty][mercWave], MercenarySpawn)
		Utils.Do(units, function(unit)
			unit.AttackMove(MercenaryAttackLocation1)
			unit.AttackMove(MercenaryAttackLocation2)
			IdleHunt(unit)
		end)

		if mercWave < MercenaryAttackWaves[Difficulty] then
			SendMercenaries()
			return
		end

		Trigger.AfterDelay(DateTime.Seconds(3), function() LastMercenariesArrived = true end)
	end)
end

SendContraband = function(owner)
	ContrabandArrived = true
	UserInterface.SetMissionText("The Contraband has arrived!", player.Color)

	local units = SmugglerReinforcements
	if owner == player then
		units = ContrabandReinforcements
	end

	Reinforcements.ReinforceWithTransport(owner, "frigate", units, { ContrabandEntry.Location, Starport.Location + CVec.New(1, 1) }, { ContrabandExit.Location })

	Trigger.AfterDelay(DateTime.Seconds(3), function()
		if owner == player then
			player.MarkCompletedObjective(CaptureStarport)
			Media.DisplayMessage("Contraband has arrived and been confiscated.", "Mentat")
		else
			player.MarkFailedObjective(CaptureStarport)
			Media.DisplayMessage("Smuggler contraband has arrived. It is too late to confiscate.", "Mentat")
		end
	end)

	Trigger.AfterDelay(DateTime.Seconds(5), function()
		UserInterface.SetMissionText("")
	end)
end

SmugglersAttack = function()
	Utils.Do(SmugglerBase, function(building)
		if not building.IsDead and building.Owner == smuggler then
			building.Sell()
		end
	end)

	Trigger.AfterDelay(DateTime.Seconds(1), function()
		Utils.Do(smuggler.GetGroundAttackers(), function(unit)
			IdleHunt(unit)
		end)
	end)
end

AttackNotifier = 0
Tick = function()
	if player.HasNoRequiredUnits() then
		harkonnen.MarkCompletedObjective(KillAtreides)
	end

	if LastHarkonnenArrived and not player.IsObjectiveCompleted(KillHarkonnen) and harkonnen.HasNoRequiredUnits() then
		Media.DisplayMessage("The Harkonnen have been annihilated!", "Mentat")
		player.MarkCompletedObjective(KillHarkonnen)
	end

	if LastMercenariesArrived and not player.IsObjectiveCompleted(KillSmuggler) and smuggler.HasNoRequiredUnits() and mercenary.HasNoRequiredUnits() then
		Media.DisplayMessage("The Smugglers have been annihilated!", "Mentat")
		player.MarkCompletedObjective(KillSmuggler)
	end

	if LastHarvesterEaten[harkonnen] and DateTime.GameTime % DateTime.Seconds(10) == 0 then
		local units = harkonnen.GetActorsByType("harvester")

		if #units > 0 then
			LastHarvesterEaten[harkonnen] = false
			ProtectHarvester(units[1], harkonnen, AttackGroupSize[Difficulty])
		end
	end

	AttackNotifier = AttackNotifier - 1

	if TimerTicks and not ContrabandArrived then
		TimerTicks = TimerTicks - 1
		UserInterface.SetMissionText("The contraband will arrive in " .. Utils.FormatTime(TimerTicks), player.Color)

		if TimerTicks <= 0 then
			SendContraband(smuggler)
		end
	end
end

WorldLoaded = function()
	harkonnen = Player.GetPlayer("Harkonnen")
	smuggler = Player.GetPlayer("Smugglers")
	mercenary = Player.GetPlayer("Mercenaries")
	player = Player.GetPlayer("Atreides")

	InfantryReinforcements = Difficulty ~= "easy"

	InitObjectives(player)
	KillAtreides = harkonnen.AddPrimaryObjective("Kill all Atreides units.")
	CaptureBarracks = player.AddPrimaryObjective("Capture the Barracks at Sietch Tabr.")
	KillHarkonnen = player.AddSecondaryObjective("Annihilate all other Harkonnen units\nand reinforcements.")
	CaptureStarport = player.AddSecondaryObjective("Capture the Smuggler Starport and\nconfiscate the contraband.")

	Camera.Position = ARefinery.CenterPosition
	HarkonnenAttackLocation = AtreidesRally.Location
	MercenaryAttackLocation1 = Starport.Location + CVec.New(-16, 0)
	MercenaryAttackLocation2 = Starport.Location

	Trigger.AfterDelay(DateTime.Seconds(2), function()
		TimerTicks = ContrabandTimes[Difficulty]
		Media.DisplayMessage("The contraband is approaching the Starport to the north in " .. Utils.FormatTime(TimerTicks) .. ".", "Mentat")
	end)

	Trigger.OnAllKilledOrCaptured(HarkonnenBase, function()
		Utils.Do(harkonnen.GetGroundAttackers(), IdleHunt)
	end)

	Trigger.OnKilled(Starport, function()
		if not player.IsObjectiveCompleted(CaptureStarport) then
			ContrabandArrived = true
			UserInterface.SetMissionText("Starport destroyed! Contraband can't land.", player.Color)
			player.MarkFailedObjective(CaptureStarport)
			SmugglersAttack()

			Trigger.AfterDelay(DateTime.Seconds(15), function()
				UserInterface.SetMissionText("")
			end)
		end

		if DefendStarport then
			player.MarkFailedObjective(DefendStarport)
		end
	end)
	Trigger.OnDamaged(Starport, function()
		if Starport.Owner ~= smuggler then
			return
		end

		if AttackNotifier <= 0 then
			AttackNotifier = DateTime.Seconds(10)
			Media.DisplayMessage("Don't destroy the Starport!", "Mentat")

			local defenders = smuggler.GetGroundAttackers()
			if #defenders > 0 then
				Utils.Do(defenders, function(unit)
					unit.Guard(Starport)
				end)
			end
		end
	end)
	Trigger.OnCapture(Starport, function()
		DefendStarport = player.AddSecondaryObjective("Defend the captured Starport.")

		Trigger.ClearAll(Starport)
		Trigger.AfterDelay(0, function()
			Trigger.OnRemovedFromWorld(Starport, function()
				player.MarkFailedObjective(DefendStarport)
			end)
		end)

		if not ContrabandArrived then
			SendContraband(player)
		end
		SmugglersAttack()
	end)

	Trigger.OnKilled(HarkonnenBarracks, function()
		player.MarkFailedObjective(CaptureBarracks)
	end)
	Trigger.OnDamaged(HarkonnenBarracks, function()
		if AttackNotifier <= 0 and HarkonnenBarracks.Health < HarkonnenBarracks.MaxHealth * 3/4 then
			AttackNotifier = DateTime.Seconds(10)
			Media.DisplayMessage("Don't destroy the Barracks!", "Mentat")
		end
	end)
	Trigger.OnCapture(HarkonnenBarracks, function()
		Media.DisplayMessage("Hostages Released!", "Mentat")

		if DefendStarport then
			player.MarkCompletedObjective(DefendStarport)
		end

		Trigger.AfterDelay(DateTime.Seconds(3), function()
			player.MarkCompletedObjective(CaptureBarracks)
		end)
	end)

	SendHarkonnen()
	Actor.Create("upgrade.barracks", true, { Owner = harkonnen })
	Actor.Create("upgrade.light", true, { Owner = harkonnen })
	Actor.Create("upgrade.heavy", true, { Owner = harkonnen })
	Trigger.AfterDelay(0, ActivateAI)

	Trigger.AfterDelay(DateTime.Minutes(1), function()
		Media.PlaySpeechNotification(player, "Reinforce")
		Reinforcements.ReinforceWithTransport(player, "carryall.reinforce", AtreidesReinforcements, AtreidesPath, { AtreidesPath[1] })
	end)

	local smugglerWaypoint = SmugglerWaypoint1.Location
	Trigger.OnEnteredFootprint({ smugglerWaypoint + CVec.New(-2, 0), smugglerWaypoint + CVec.New(-1, 0), smugglerWaypoint, smugglerWaypoint + CVec.New(1, -1), smugglerWaypoint + CVec.New(2, -1), SmugglerWaypoint3.Location }, function(a, id)
		if not warned and a.Owner == player and a.Type ~= "carryall" then
			warned = true
			Trigger.RemoveFootprintTrigger(id)
			Media.DisplayMessage("Stay away from our Starport.", "Smuggler Leader")
		end
	end)

	Trigger.OnEnteredFootprint({ SmugglerWaypoint2.Location }, function(a, id)
		if not paid and a.Owner == player and a.Type ~= "carryall" then
			paid = true
			Trigger.RemoveFootprintTrigger(id)
			Media.DisplayMessage("You were warned. Now you will pay.", "Smuggler Leader")
			Utils.Do(smuggler.GetGroundAttackers(), function(unit)
				unit.AttackMove(SmugglerWaypoint2.Location)
			end)

			Trigger.AfterDelay(DateTime.Seconds(3), function()
				KillSmuggler = player.AddSecondaryObjective("Destroy the Smugglers and their Mercenaries.")
				SendMercenaries()
			end)
		end
	end)

	Trigger.OnEnteredProximityTrigger(HarkonnenBarracks.CenterPosition, WDist.New(5 * 1024), function(a, id)
		if a.Owner == player and a.Type ~= "carryall" then
			Trigger.RemoveProximityTrigger(id)
			Media.DisplayMessage("Capture the Harkonnen barracks to release the hostages.", "Mentat")
			StopInfantryProduction = true
		end
	end)
end
