/*
	Stargate Auto-Spawner for GarrysMod10
	Copyright (C) 2007  aVoN

	This program is free software: you can redistribute it and/or modify
	it 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.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

if not SERVER then return end; -- Just to be sure

--##################################
--#### Spawning
--##################################

StarGate.GateSpawner = {};
StarGate.GateSpawner.Gates = {}; -- Gates
StarGate.GateSpawner.DHDs = {}; -- DHDs
StarGate.GateSpawner.Props = {}; -- Any props, attached to a stargate (like ramps etc)
StarGate.GateSpawner.Rings = {};
StarGate.GateSpawner.RingPanel = {};
StarGate.GateSpawner.RingProps = {};
StarGate.GateSpawner.Spawned = false;
StarGate.GateSpawner.Ents = {};

-- ############### Load config @aVoN
function StarGate.GateSpawner.LoadConfig()
	local map = game.GetMap();
	local ini = INIParser:new("stargate/gatespawner_maps/"..map..".ini");
	-- FIXME: Add config for Enabled/Disabled again
	if(ini) then
		StarGate.GateSpawner.Version = ((ini.gatespawner or {})[1] or {}).version; -- To determine the spawnheight
		StarGate.GateSpawner.Gates = ini.stargate or {};
		StarGate.GateSpawner.DHDs = ini.dhd or {};
		StarGate.GateSpawner.Props = ini.prop_physics or {};
		StarGate.GateSpawner.Rings = ini.ring or {};
		StarGate.GateSpawner.RingPanel = ini.ring_panel or {};
		StarGate.GateSpawner.RingProps = ini.prop_physics_ring or {}; -- I have to recode this - seriously!
	end
end

-- ############### Spawning function @aVoN
function StarGate.GateSpawner.Spawn(v,protect,k)
	if(v.position and v.classname) then
		local e = ents.Create(v.classname);
		e.GateSpawnerSpawned = true;
		e.GateSpawnerProtected = protect;
		local pos = Vector(unpack(v.position:TrimExplode(" ")));
		local IsGate = v.classname:find("stargate_");
		if(not StarGate.GateSpawner.Version and IsGate) then
			pos = pos + Vector(0,0,87); -- Or gate would stuck in the ground
		end
		e.CDSIgnore = true; -- Fixes Combat Damage System destroying Ramps - http://mantis.39051.vs.webtropia.com/view.php?id=45
		e:SetPos(pos);
		-- Set model (if not a gate and valid key exists)
		if(not IsGate and v.model) then
			e:SetModel(v.model);
		end
		-- Spawn the gate a bit later. And we need to spawn it before anyone sets the angles, or it will look weird
		timer.Simple(0.1*k,
			function()
				if(not ValidEntity(e)) then return end; -- WHY DID THIS HAPPEN? SHOULD NEVER!
				e:Spawn();
				-- Set angles only AFTER we spawned the prop to avoid chevrons being added incorrectly
				if(v.angles) then
					e:SetAngles(Angle(unpack(v.angles:TrimExplode(" "))));
				end
				-- Set the address of a gate
				if(IsGate) then
					if(v.address and v.address ~= "") then
						e:SetGateAddress(v.address:upper());
					end
					if(v.name and v.name ~= "") then
						e:SetGateName(v.name);
					end
					if(v.private ~= nil and v.private ~= "") then
						e:SetPrivate(util.tobool(v.private));
					end
				else
					-- For e.g. Rings
					if(v.address and v.address ~= "") then
						e.Address = v.address;
						e:SetNetworkedString("address",v.address);
					end
					if(v.__id) then
						if(v.classname == "ring_panel") then
							-- Catdaemon needs to code an function directly to his rings
							for _,vv in pairs(ents.FindByClass("ring_base")) do
								if(vv.__id == v.__id) then
									e.Pair = vv;
									constraint.Weld(e,vv,0,0,0,true); -- Makes the panel unteleportable with the rings
									break;
								end
							end
						elseif(v.classname == "prop_physics") then
							-- Catdaemon needs to code an function directly to his rings
							for _,vv in pairs(ents.FindByClass("ring_base")) do
								if(vv.__id == v.__id) then
									constraint.Weld(e,vv,0,0,0,true); -- Makes the panel unteleportable with the rings
									e:SetCollisionGroup(COLLISION_GROUP_WORLD);
									break;
								end
							end
						else
							e.__id = v.__id;
						end
					end
				end
				local phys = e:GetPhysicsObject();
				if(phys:IsValid()) then
					phys:EnableMotion(false);
				end
			end
		);
		return e;
	end
	return nil;
end

-- ############### Initial spawn handling @aVoN
function StarGate.GateSpawner.InitialSpawn()
	-- First, remove all previous gate_spawner gates.
	local remove = {
		ents.FindByClass("stargate_*"),
		ents.FindByClass("dhd_*"),
		ents.FindByClass("prop_physics"),
		ents.FindByClass("ring_base"),
		ents.FindByClass("ring_panel"),
	};
	for _,v in pairs(remove) do
		for _,e in pairs(v) do
			if(e.GateSpawnerSpawned) then
				e:Remove();
			end
		end
	end
	if(not StarGate.GateSpawner.Spawned) then
		StarGate.GateSpawner.LoadConfig();
		-- FIXME: Add config for enabled/disabled again
	--	if(StarGate.spawner_enabled) then
			local protect = true; -- FIXME: Add protected/unprotected config again
			local i = 0; -- For delayed spawning
			for _,v in pairs(StarGate.GateSpawner.Props) do
				table.insert(StarGate.GateSpawner.Ents,{Entity=StarGate.GateSpawner.Spawn(v,protect,i),SpawnData=v});
				i = i + 1;
			end
			for _,v in pairs(StarGate.GateSpawner.Gates) do
				table.insert(StarGate.GateSpawner.Ents,{Entity=StarGate.GateSpawner.Spawn(v,protect,i),SpawnData=v});
				i = i + 1;
			end
			for _,v in pairs(StarGate.GateSpawner.DHDs) do
				table.insert(StarGate.GateSpawner.Ents,{Entity=StarGate.GateSpawner.Spawn(v,protect,i),SpawnData=v});
				i = i + 1;
			end
			-- Rings
			for _,v in pairs(StarGate.GateSpawner.Rings) do
				table.insert(StarGate.GateSpawner.Ents,{Entity=StarGate.GateSpawner.Spawn(v,protect,i),SpawnData=v});
				i = i + 1;
			end
			for _,v in pairs(StarGate.GateSpawner.RingPanel) do
				table.insert(StarGate.GateSpawner.Ents,{Entity=StarGate.GateSpawner.Spawn(v,protect,i),SpawnData=v});
				i = i + 1;
			end
			for _,v in pairs(StarGate.GateSpawner.RingProps) do
				table.insert(StarGate.GateSpawner.Ents,{Entity=StarGate.GateSpawner.Spawn(v,protect,i),SpawnData=v});
				i = i + 1;
			end
			StarGate.GateSpawner.Spawned = true;
	--	end
	end
end

-- ############### Auto Respawner @aVoN
function StarGate.GateSpawner.AutoRespawn()
	-- FIXME: Add config for enabled/disabled again
	if(DEBUG) then return end;
	--if(StarGate.spawner_enabled and StarGate.spawner_autorespawn) then
		local add = {};
		local i = 0; -- For delayed spawning
		for k,v in pairs(StarGate.GateSpawner.Ents) do
			local protect = true; -- FIXME: Add protected/unprotected config again
			if(not v.Entity or not v.Entity:IsValid()) then
				table.insert(add,{Entity=StarGate.GateSpawner.Spawn(v.SpawnData,protect,i),SpawnData=v.SpawnData});
				i = i + 1;
				StarGate.GateSpawner.Ents[k] = nil;
			end
		end
		for _,v in pairs(add) do
			table.insert(StarGate.GateSpawner.Ents,v);
		end
	--end
end

-- ############### Init @aVoN
timer.Simple(2,StarGate.GateSpawner.InitialSpawn); -- Spawn them, 2 seconds after the map start
timer.Create("StarGate.GateSpawner.AutoRespawn",3,0,StarGate.GateSpawner.AutoRespawn); -- Check for existance every 3 seconds

--##################################
--#### Creating of spawnfils
--##################################


-- This script works the following way: Spawn your gates and DHDs, set the addrese and run this script by console with the following command: stargate_creategatespawner_file
-- Now, a new file called <the name of the map>.ini.txt has been created in garrysmod/data/
-- Copy this file to garrysmod/addons/stargate/data/gatespawner_maps and remove the .txt extension so it only looks up like <the name of the map>.ini
-- Now your gates will autospawn on that specific map
-- YOU NEED TO BE ADMIN TO PERFORM THIS COMMAND!

-- ############### Gatespawner creation command @aVoN
concommand.Add("stargate_creategatespawner_file",
	function(p)
		if(ValidEntity(p) and p:IsAdmin()) then
			local f = "[gatespawner]\nversion = 2\n\n\n";
			-- Gates and Attachments
			local already_added = {};
			for _,v in pairs(ents.FindByClass("stargate_*")) do
				if(v.IsStargate) then
					f = f .. "[stargate]\nclassname="..v:GetClass().."\nposition="..tostring(v:GetPos()).."\nangles="..tostring(v:GetAngles()).."\naddress="..v:GetGateAddress().."\nname="..v:GetGateName().."\nprivate="..tostring(v:GetPrivate()).."\n";
					-- Now add props which are attached to this stargate (like ramps) - ATTENTION - Do not use this as a "AdvancedDuplicator" - It's really only done for the ramps and may get REALLY slow on much things attached to this gate
					local gate = v;
					for _,v in pairs(StarGate.GetConstrainedEnts(v,2) or {}) do
						if(v ~= gate and v:GetClass() == "prop_physics" and ValidEntity(v)) then
							already_added[v] = true;
							f = f.."[prop_physics]\nclassname=prop_physics\nposition="..tostring(v:GetPos()).."\nangles="..tostring(v:GetAngles()).."\nmodel="..v:GetModel().."\n";
						end
					end
				end
			end
			-- DHDs!
			for _,v in pairs(ents.FindByClass("dhd_*")) do
				f = f .. "[dhd]\nclassname="..v:GetClass().."\nposition="..tostring(v:GetPos()).."\nangles="..tostring(v:GetAngles()).."\n";
			end
			-- Old gatespawner protected props!
			for _,v in pairs(ents.FindByClass("prop_physics")) do
				if(ValidEntity(v) and not already_added[v] and v.GateSpawnerSpawned) then
					f = f.."[prop_physics]\nclassname=prop_physics\nposition="..tostring(v:GetPos()).."\nangles="..tostring(v:GetAngles()).."\nmodel="..v:GetModel().."\n";
				end
			end
			-- Rings
			for _,v in pairs(ents.FindByClass("ring_base")) do
				f = f.."[ring]\nclassname=ring_base\nposition="..tostring(v:GetPos()).."\nangles="..tostring(v:GetAngles()).."\naddress="..(v.Address or "").."\n__id="..v:EntIndex().."\n";
				local ring = v;
				for _,v in pairs(StarGate.GetConstrainedEnts(v,2) or {}) do
					if(v ~= ring and v:GetClass() == "prop_physics" and ValidEntity(v) and not already_added[v]) then
						already_added[v] = true;
						f = f.."[prop_physics_ring]\nclassname=prop_physics\nposition="..tostring(v:GetPos()).."\nangles="..tostring(v:GetAngles()).."\nmodel="..v:GetModel().."\n__id="..ring:EntIndex().."\n";
					end
				end
			end
			-- Ring panels
			for _,v in pairs(ents.FindByClass("ring_panel")) do
				if(ValidEntity(v.Pair) and v.Pair ~= v) then
					f = f.."[ring_panel]\nclassname=ring_panel\nposition="..tostring(v:GetPos()).."\nangles="..tostring(v:GetAngles()).."\n__id="..v.Pair:EntIndex().."\n";
				end
			end
			file.Write(game.GetMap():lower()..".ini.txt",f);
		end
	end
);
