local ScenarioUtils = import('/lua/sim/ScenarioUtilities.lua');
local ScenarioFramework = import('/lua/ScenarioFramework.lua');
local Utilities = import('/lua/utilities.lua');

##This works fairly simply and is easy to make work with any map.
##Create a series of markers on the map, named WAYPOINT_# from 0 to however many you want
##The defence object will spawn at WAYPOINT_0
##To make any waypoint the final waypoint, place a second marker on top of it named it GOAL

###For every waypoint, make a marker named GROUNDSPAWN.  The script keeps track of which
### waypoint the defense object is closest to and spawns ground units from this marker.
##For WAYPOINT_1 there must be a GROUNDSPAWN_1 and so forth for every waypoint.

##T1 units will spawn for Waypoints 0 to 5.  T2 for 5 to 9.  T3 for 10 and up.

local TotalWaypoints = 12;

local oldOnPopulate = OnPopulate

   function OnPopulate()
        #oldOnPopulate()
        ScenarioUtils.InitializeArmies()
	Survival_InitGame();

   end

   local oldOnStart = OnStart
   function OnStart(self)
      oldOnStart(self)
   end

## broadcast a text message to players
----------------------------------------------------------------------------
BroadcastMSG = function(MSG, Fade, TextColor)
	PrintText(MSG, 20, TextColor, Fade, 'center');	
end


-- initializes the game settings
--------------------------------------------------------------------------
Survival_InitGame = function()

	LOG("----- Survival MOD: Configuring match settings...");
	-- check game configuration
	
		#Checking difficulty options

		local SpawnMultiplier = ScenarioInfo.Options.opt_difficulty;
		local WaveDelay = ScenarioInfo.Options.opt_wavetime;


	ScenarioInfo.Options.Victory = 'sandbox'; -- force sandbox in order to implement our own rules

	local Armies = ListArmies();
	Survival_PlayerCount = table.getn(Armies) - 2; -- save player count, subtracting the 2 AI "players"

	-- loop through armies
	for i, Army in ListArmies() do
		-- check if it's a human army
		if (Army == "ARMY_1" or Army == "ARMY_2" or Army == "ARMY_3" or Army == "ARMY_4" or Army == "ARMY_5" or Army == "ARMY_6" or Army == "ARMY_7" or Army == "ARMY_8") then 

			#ScenarioFramework.AddRestriction(Army, categories.WALL); -- don't allow them to build walls
			#ScenarioFramework.AddRestriction(Army, categories.AIR); -- don't allow them to build air stuff

			-- loop through other armies to ally with other human armies
			for x, ArmyX in ListArmies() do
				-- if human army
				if (ArmyX == "ARMY_1" or ArmyX == "ARMY_2" or ArmyX == "ARMY_3" or ArmyX == "ARMY_4" or ArmyX == "ARMY_5" or ArmyX == "ARMY_6" or ArmyX == "ARMY_7" or ArmyX == "ARMY_8") then 
					SetAlliance(Army, ArmyX, 'Ally'); 
				end
			end			

			SetAlliance(Army, "ARMY_SURVIVAL_ALLY", 'Ally'); -- friendly AI team
			SetAlliance(Army, "ARMY_SURVIVAL_ENEMY", 'Enemy');  -- enemy AI team

			SetAlliedVictory(Army, true); -- can win together of course :)
		end
	end

	SetAlliance("ARMY_SURVIVAL_ALLY", "ARMY_SURVIVAL_ENEMY", 'Enemy'); -- the friendly and enemy AI teams should be enemies

	SetIgnoreArmyUnitCap('ARMY_SURVIVAL_ENEMY', true); -- remove unit cap from enemy AI team


		Survival_SpawnDef();
		ForkThread(Waypoint_Tracker);

		ForkThread(VictoryCheck);
		ForkThread(SpawnEnemies);
		#VictoryCheck(SpawnMultiplier, WaveDelay);
		#FSpawnEnemies(SpawnMultiplier, WaveDelay);

end





-- keeps the truck moving
--------------------------------------------------------------------------

Waypoint_Tracker = function(UnitID, ArmyID, POS, OrderID)
	BroadcastMSG("The defense object is on the move! Defend it!", 5);

	local WaypointNumber = 0;
	local TargetWaypoint = ScenarioUtils.MarkerToPosition("WAYPOINT_0");

	local PlatoonList = {};
	table.insert(PlatoonList, Survival_DefUnit); -- add unit to a platoon

	Truck_PlatoonOrder(ArmyID, PlatoonList, OrderID, WaypointNumber, TargetWaypoint); -- give the unit orders
	WaitSeconds(10)


end

##issues specific orders to the truck, will que orders until no more waypoints are left
----------------------------------------------------------------------------------
Truck_PlatoonOrder = function(ArmyID, UnitList, OrderID, WaypointNumber, TargetWaypoint)

	if (UnitList == nil) then
		return;
	end


	#BroadcastMSG("The defense object is on the move!");

	while WaypointNumber < TotalWaypoints do

		WaypointNumber = WaypointNumber + 1;

		#local CurrentPosition = Survival_DefUnit:GetPosition()

		TargetWaypoint = ScenarioUtils.MarkerToPosition("WAYPOINT_" .. WaypointNumber);
	
		local aiBrain = GetArmyBrain("ARMY_SURVIVAL_ALLY"); #GetArmyBrain(ArmyID)	--"ARMY_SURVIVAL_ENEMY");
		local aiPlatoon = aiBrain:MakePlatoon('','');
		aiBrain:AssignUnitsToPlatoon(aiPlatoon, UnitList, 'Attack', 'None'); -- platoon, unit list, "mission" and 

		#local POS = ScenarioUtils.MarkerToPosition("WAYPOINT_1"); 
		aiPlatoon:MoveToLocation(TargetWaypoint, false);

		LOG("Truck has qued a move order to waypoint number " .. WaypointNumber);

		WaitSeconds(1)	
		
	
	end


end






---------------------------------------------------------------------
function VictoryCheck()
	
	while not Survival_DefUnit:IsDead() do

		local Distance = VDist3(Survival_DefUnit:GetPosition(),ScenarioUtils.MarkerToPosition("GOAL"));	
	
			if (Distance <= 10) then
			BroadcastMSG("The defense object has reached it's destination!  You have won!", 4);
			Survival_GameState = 2;
				for i, army in ListArmies() do
					if (army == "ARMY_1" or army == "ARMY_2" or army == "ARMY_3" or army == "ARMY_4" or army == "ARMY_5" or army == "ARMY_6" or army == "ARMY_7" or army == "ARMY_8") then
					GetArmyBrain(army):OnVictory();
					end
				end
			GetArmyBrain("ARMY_SURVIVAL_ENEMY"):OnDefeat();
			end

	WaitSeconds(10)

	end
end

function GetMarker(MarkerName)
	return Scenario.MasterChain._MASTERCHAIN_.Markers[MarkerName];
end



---------------------------------------------------------------------
function SpawnEnemies()
	WaitSeconds(10)

	local SpawnMultiplier = ScenarioInfo.Options.SpawnMultiplier;
	local WaveDelay = ScenarioInfo.Options.WaveDelay;

	if (SpawnMultiplier == nil) then
		SpawnMultiplier = 1;
	end
	if (WaveDelay == nil) then
		WaveDelay = 10;
	end

	BroadcastMSG("Enemies are en route to attack the defense object!", 5)
	
	while not Survival_DefUnit:IsDead() do

		local Distance = VDist3(Survival_DefUnit:GetPosition(),ScenarioUtils.MarkerToPosition("GOAL"));	
		local ClosestWaypoint = GetClosestWaypoint();
		LOG("Closest waypoint is waypoint " .. ClosestWaypoint);
		SpawnGroundWave(ClosestWaypoint, SpawnMultiplier)
		SpawnAirWave(ClosestWaypoint, SpawnMultiplier)
		#SpawnAirDrop(ClosestWaypoint, SpawnMultiplier)
		#SpawnArtilleryWave(ClosestWaypoint, SpawnMultiplier)		

		WaitSeconds(WaveDelay)

	end


end



---------------------------------------------------------------------------------------------
SpawnGroundWave = function(ClosestWaypoint, SpawnMultiplier) -- This generates the units that will be in the wave then spawns them

	local UnitID = '0';
	local UnitID2 = 'o';

	if ClosestWaypoint < 5 then
	UnitID = 'xsl0201';
	Quantity = (((ClosestWaypoint + 1) * 2) * SpawnMultiplier);
	elseif ClosestWaypoint <10 then
	UnitID = 'xsl0202';
	Quantity = ((ClosestWaypoint + 1) * SpawnMultiplier);
	elseif ClosestWaypoint >= 10 then
	UnitID = 'xsl0303';
	Quantity = (((ClosestWaypoint + 1)/2) * SpawnMultiplier);
	end

	if ClosestWaypoint < 5 then
	UnitID2 = 'xsl0103';
	Quantity = (((ClosestWaypoint + 1) * 2) * SpawnMultiplier);
	elseif ClosestWaypoint <10 then
	UnitID2 = 'xsl0101';
	Quantity = ((ClosestWaypoint + 1) * SpawnMultiplier);
	elseif ClosestWaypoint >= 10 then
	UnitID2 = 'xsl0304';
	Quantity = (((ClosestWaypoint + 1)/2) * SpawnMultiplier);
	end

	local ArmyID = "ARMY_SURVIVAL_ENEMY";
	local POS = ScenarioUtils.MarkerToPosition("GROUNDSPAWN_" .. ClosestWaypoint);
	local OrderID = 2;

	Survival_SpawnUnit(UnitID, ArmyID, POS, OrderID, Quantity, UnitID2)


end


---------------------------------------------------------------------------------------------
SpawnAirWave = function(ClosestWaypoint, SpawnMultiplier) -- This generates the units that will be in the wave then spawns them

	local UnitID = '0';
	local UnitID2 = '0';
	local Quantity = 0;

	if ClosestWaypoint < 5 then
	UnitID = 'xsa0103';
	Quantity = ((ClosestWaypoint + 2) * SpawnMultiplier);
	elseif ClosestWaypoint <10 then
	UnitID = 'xsa0202';
	Quantity = (ClosestWaypoint * SpawnMultiplier);
	elseif ClosestWaypoint >= 10 then
	UnitID = 'xsl0304';
	Quantity = ((ClosestWaypoint)/2 * SpawnMultiplier);
	end


	if ClosestWaypoint < 5 then
	UnitID2 = 'xsa0102';
	Quantity = (((ClosestWaypoint + 1) * 2) * SpawnMultiplier);
	elseif ClosestWaypoint <10 then
	UnitID2 = 'xsa0203';
	Quantity = ((ClosestWaypoint + 1) * SpawnMultiplier);
	elseif ClosestWaypoint >= 10 then
	UnitID2 = 'xsa0304';
	Quantity = (((ClosestWaypoint + 1)/2) * SpawnMultiplier);
	end

	local ArmyID = "ARMY_SURVIVAL_ENEMY";
	local POS = ScenarioUtils.MarkerToPosition("AIRSPAWN_" .. ClosestWaypoint);
	local OrderID = 2;

	Survival_SpawnUnit(UnitID, ArmyID, POS, OrderID, Quantity, UnitID2)


end

---------------------------------------------------------------------------------------------
Survival_SpawnUnit = function(UnitID, ArmyID, POS, OrderID, Quantity, UnitID2) -- blueprint, army, position, order

--	LOG("----- Survival MOD: SPAWNUNIT: Start function...");
	local PlatoonList = {};
	local Counter = 0;

	while Counter < Quantity do
		local NewUnit = CreateUnitHPR(UnitID, ArmyID, POS[1], POS[2], POS[3], 0,0,0);
		local NewUnit2 = CreateUnitHPR(UnitID2, ArmyID, POS[1], POS[2], POS[3], 0,0,0);
		table.insert(PlatoonList, NewUnit); -- add unit to a platoon
		table.insert(PlatoonList, NewUnit2); -- add unit to a platoon
		Survival_PlatoonOrder(ArmyID, PlatoonList, OrderID); -- give the unit orders
		Counter = Counter + 1;
	end
end



--------------------------------------------------------------------------
Survival_PlatoonOrder = function(ArmyID, UnitList, OrderID)	

--	LOG("----- Survival MOD: PLATOON: Start function...");

	if (UnitList == nil) then
		return;
	end

	local aiBrain = GetArmyBrain(ArmyID); --"ARMY_SURVIVAL_ENEMY");
	local aiPlatoon = aiBrain:MakePlatoon('','');
	aiBrain:AssignUnitsToPlatoon(aiPlatoon, UnitList, 'Attack', 'None'); -- platoon, unit list, "mission" and formation

 	-- 1 center / 2 waypoint / 3 spawn
 

	if (OrderID == 2) then -- attack move

		-- attack move to random path
		local ClosestWaypoint = GetClosestWaypoint();
		POS = Survival_DefUnit:GetPosition();
		aiPlatoon:AggressiveMoveToLocation(POS);
	end

end




#Determine which waypoint the defense object is closest to so that way we can spawn enemies from corresponding spawn points
----------------------------
function GetClosestWaypoint()


	local WaypointCounter = 0;
	local ShortestDistance = 20000;
	local ShortestDistanceWaypoint = 100;
	local WaypointDistance = 100;

	while (WaypointCounter < (TotalWaypoints + 1)) do

		WaypointDistance = VDist3(Survival_DefUnit:GetPosition(),ScenarioUtils.MarkerToPosition("WAYPOINT_" .. WaypointCounter))
		if WaypointDistance < ShortestDistance then
			ShortestDistance = WaypointDistance;
			ShortestDistanceWaypoint = WaypointCounter;
		end

	WaypointCounter = WaypointCounter + 1;

	end

	return ShortestDistanceWaypoint;
end







-- spawns a defense object
--------------------------------------------------------------------------
Survival_SpawnDef = function()

	LOG("----- Survival MOD: Initializing defense object...");

	local POS = ScenarioUtils.MarkerToPosition("WAYPOINT_0");
	Survival_DefUnit = CreateUnitHPR('UEL0401', "ARMY_SURVIVAL_ALLY", POS[1], POS[2], POS[3], 0,0,0);  

	Survival_DefUnit:SetReclaimable(false);
	Survival_DefUnit:SetCapturable(false);
	#Survival_DefUnit:SetProductionPerSecondEnergy((Survival_PlayerCount * 100) + 0);
	#Survival_DefUnit:SetConsumptionPerSecondEnergy(0);

	#Survival_DefUnit:SetMaxHealth(9000 - (Survival_PlayerCount * 1000));
	#Survival_DefUnit:SetHealth(nil, 9000 - (Survival_PlayerCount * 1000));
	Survival_DefUnit:SetRegenRate(45 - (Survival_PlayerCount * 5));

	Survival_DefUnit:SetRegenRate(45 - (Survival_PlayerCount * 5));
	Survival_DefUnit:SetSpeedMult(0.5);

	Survival_DefUnit:HideBone('Turret_Right01', true)
	Survival_DefUnit:HideBone('Turret_Right01_Barrel01', true)
	Survival_DefUnit:HideBone('Turret_Right01_Barrel02', true)
	Survival_DefUnit:HideBone('Turret_Right01_Barrel03', true)
	Survival_DefUnit:HideBone('Turret_Right01_Muzzle01', true)
	Survival_DefUnit:HideBone('Turret_Right01_Muzzle02', true)
	Survival_DefUnit:HideBone('Turret_Right01_Muzzle03', true)

	Survival_DefUnit:HideBone('Turret_Right02', true)
	Survival_DefUnit:HideBone('Turret_Right02_Barrel01', true)
	Survival_DefUnit:HideBone('Turret_Right02_Barrel02', true)
	Survival_DefUnit:HideBone('Turret_Right02_Barrel03', true)
	Survival_DefUnit:HideBone('Turret_Right02_Muzzle01', true)
	Survival_DefUnit:HideBone('Turret_Right02_Muzzle02', true)
	Survival_DefUnit:HideBone('Turret_Right02_Muzzle03', true)

	Survival_DefUnit:HideBone('Turret_Left01', true)
	Survival_DefUnit:HideBone('Turret_Left01_Barrel01', true)
	Survival_DefUnit:HideBone('Turret_Left01_Barrel02', true)
	Survival_DefUnit:HideBone('Turret_Left01_Barrel03', true)
	Survival_DefUnit:HideBone('Turret_Left01_Muzzle01', true)
	Survival_DefUnit:HideBone('Turret_Left01_Muzzle02', true)
	Survival_DefUnit:HideBone('Turret_Left01_Muzzle03', true)

	Survival_DefUnit:HideBone('Turret_Left02', true)
	Survival_DefUnit:HideBone('Turret_Left02_Barrel01', true)
	Survival_DefUnit:HideBone('Turret_Left02_Barrel02', true)
	Survival_DefUnit:HideBone('Turret_Left02_Barrel03', true)
	Survival_DefUnit:HideBone('Turret_Left02_Muzzle01', true)
	Survival_DefUnit:HideBone('Turret_Left02_Muzzle02', true)
	Survival_DefUnit:HideBone('Turret_Left02_Muzzle03', true)

	Survival_DefUnit:SetWeaponEnabledByLabel('LeftTurret01', false)
	Survival_DefUnit:SetWeaponEnabledByLabel('LeftTurret02', false)
	Survival_DefUnit:SetWeaponEnabledByLabel('RightTurret01', false)
	Survival_DefUnit:SetWeaponEnabledByLabel('RightTurret02', false)

	Survival_DefUnit:DestroyShield()
	Survival_DefUnit:SetCustomName("Defend Me!");


	local Survival_DefUnitBP = Survival_DefUnit:GetBlueprint();

	Survival_DefUnit:SetIntelRadius('Vision', 50);

	-- when the def object dies
	Survival_DefUnit.OldOnKilled = Survival_DefUnit.OnKilled;

	Survival_DefUnit.OnKilled = function(self, instigator, type, overkillRatio)

		BroadcastMSG("The defense object has been destroyed! You have lost!", 8);
		self.OldOnKilled(self, instigator, type, overkillRatio);

		Survival_GameState = 3;

		for i, army in ListArmies() do

			if (army == "ARMY_1" or army == "ARMY_2" or army == "ARMY_3" or army == "ARMY_4" or army == "ARMY_5" or army == "ARMY_6" or army == "ARMY_7" or army == "ARMY_8") then
				--killAllCommanders(army);
				GetArmyBrain(army):OnDefeat();
			end
		end
	end

	#Survival_DefLastHP = Survival_DefUnit:GetHealth();


end