local LS = {}

local status = false

--Stuff that can't be disabled
CreateConVar( "LS_AllowNukeEffect", "1" ) --Update to something changeable later on
--end 

--Local Functions

local SB_AIR_O2 = 0
local SB_AIR_CO2 = 1

local function CheckRegulators()
	for k, v in pairs(LS.generators.air) do
		if not v then 
			table.remove(LS.generators.air, k)
		end
	end
	for k, v in pairs(LS.generators.temperature) do
		if not v then 
			table.remove(LS.generators.temperature, k)
		end
	end
end

LS.generators = {}
LS.generators.air = {}
LS.generators.temperature = {}

local function LS_Reg_Veh(ply, ent)
	local RD = CAF.GetAddon("Resource Distribution")
	RD.RegisterNonStorageDevice(ent)
end

local function LSSpawnFunc( ply )
	if not ply:Ls_Init() then
		ErrorNoHalt("Error initializing player\n")
	end
end

local function LSResetSpawnFunc( ply )
	ply:LsResetSuit()
end


local function RemoveEntity( ent )
	if (ent:IsValid()) then
		ent:Remove()
	end
end

local function Explode1( ent )
	if ent:IsValid() then
		local Effect = EffectData()
			Effect:SetOrigin(ent:GetPos() + Vector( math.random(-60, 60), math.random(-60, 60), math.random(-60, 60) ))
			Effect:SetScale(1)
			Effect:SetMagnitude(25)
		util.Effect("Explosion", Effect, true, true)
	end
end

local function Explode2( ent )
	if ent:IsValid() then
		local Effect = EffectData()
			Effect:SetOrigin(ent:GetPos())
			Effect:SetScale(3)
			Effect:SetMagnitude(100)
		util.Effect("Explosion", Effect, true, true)
		RemoveEntity( ent )
	end
end

local function PlayerLSThink()
	for k, ply in pairs(player.GetAll( )) do
		ply:LsCheck()
	end
end

local function AddonDisabled(addon)
	if not addon then return false end
	if addon == "Resource Distribution" then 
		CAF.Destruct("Life Support")
	end
end

-- End Local Functions


--[[
	The Constructor for this Custom Addon Class
]]
function LS.__Construct()
	if status then return false , CAF.GetLangVar("This Addon is already Active!") end
	if not CAF.GetAddon("Resource Distribution") or not CAF.GetAddon("Resource Distribution").GetStatus() then return false, CAF.GetLangVar("Resource Distribution is Required and needs to be Active!") end
	util.PrecacheSound( "vehicles/v8/skid_lowfriction.wav" )
	util.PrecacheSound( "NPC_Stalker.BurnFlesh" )
	util.PrecacheModel("models/player/charple.mdl")
	util.PrecacheSound( "streetwar.slimegurgle04" )
	util.PrecacheSound( "Player.FallGib" )
	LS.generators = {}
	LS.generators.air = {}
	LS.generators.temperature = {}
	local SB = CAF.GetAddon("Spacebuild")
	if SB then
		--Msg("Adding Player override to SB\n")
		SB.AddPlayerOverride()
		SB.AddOverride_PlayerHeatDestroy()
	end
	--SB_PlayerOverride = true
	--SB_Override_PlayerHeatDestroy = true
	hook.Add( "PlayerSpawnedVehicle", "LS_vehicle_spawn", LS_Reg_Veh )
	if (SunAngle == nil) then SunAngle = Vector(0,0,-1) end
	for k, ply in pairs(player.GetAll( )) do
		LSSpawnFunc( ply );
	end
	local RD = CAF.GetAddon("Resource Distribution")
	RD.AddProperResourceName("energy", CAF.GetLangVar("Energy"))
	RD.AddProperResourceName("water", CAF.GetLangVar("Water"))
	RD.AddProperResourceName("nitrogen", CAF.GetLangVar("Nitrogen"))
	RD.AddProperResourceName("hydrogen", CAF.GetLangVar("Hydrogen"))
	RD.AddProperResourceName("oxygen", CAF.GetLangVar("Oxygen"))
	RD.AddProperResourceName("carbon dioxide", CAF.GetLangVar("Carbon Dioxide"))
	RD.AddProperResourceName("steam", CAF.GetLangVar("Steam"))
	RD.AddProperResourceName("heavy water", CAF.GetLangVar("Heavy Water"))
	RD.AddProperResourceName("liquid nitrogen", CAF.GetLangVar("Liquid Nitrogen"))
	hook.Add( "PlayerInitialSpawn", "LS_Core_SpawnFunc", LSSpawnFunc )
	hook.Add( "PlayerSpawn", "LS_Core_ResetSpawnFunc", LSResetSpawnFunc )
	CAF.AddHook("think3", PlayerLSThink)
	CAF.AddHook("OnAddonDestruct", AddonDisabled)
	CAF.AddServerTag("LSC")
	status = true
	return true
end

--[[
	The Destructor for this Custom Addon Class
]]
function LS.__Destruct()
	if not status then return false, CAF.GetLangVar("This addon wasn't on in the first place") end
	hook.Remove( "PlayerInitialSpawn", "LS_Core_SpawnFunc")
	hook.Remove( "PlayerSpawn", "LS_Core_ResetSpawnFunc")
	hook.Remove( "PlayerSpawnedVehicle", "LS_vehicle_spawn")
	CAF.RemoveHook("think3", PlayerLSThink)
	CAF.RemoveHook("OnAddonDestruct", AddonDisabled)
	local SB = CAF.GetAddon("Spacebuild")
	if SB then
		SB.RemovePlayerOverride()
		SB.RemoveOverride_PlayerHeatDestroy()
	end
	LS.generators = {}
	LS.generators.air = {}
	LS.generators.temperature = {}
	CAF.RemoveServerTag("LSC")
	status = false
	return true
end

--[[
	Get the required Addons for this Addon Class
]]
function LS.GetRequiredAddons()
	return {"Resource Distribution"}
end

--[[
	Get the Boolean Status from this Addon Class
]]
function LS.GetStatus()
	return status
end

--[[
	Get the Version of this Custom Addon Class
]]
function LS.GetVersion()
	return 3.08, CAF.GetLangVar("Beta")
end

--[[
	Get any custom options this Custom Addon Class might have
]]
function LS.GetExtraOptions()
	return {}
end

--[[
	Get the Custom String Status from this Addon Class
]]
function LS.GetCustomStatus()
	return CAF.GetLangVar("Not Implemented Yet")
end

function LS.AddResourcesToSend()
	--[[local list = file.Find("models/props_phx/life_support/*.mdl", "GAME")
	PrintTable(list)
	for k,v in pairs(list) do
		resource.AddFile("models/props_phx/life_support/"..v)
	end	
	list = file.Find("materials/props_phx/life_support/*.vtf", "GAME")
	PrintTable(list) 
	for k,v in pairs(list) do
		resource.AddFile("materials/props_phx/life_support/"..v)
	end
	resource.AddFile("info.txt")]]
end

CAF.RegisterAddon("Life Support", LS, "2")

--Extra Methodes
function LS.AddAirRegulator(ent)
	if ent.GetLSClass and ent:GetLSClass() == "air exchanger" then
		if table.insert(LS.generators.air, ent) then
		--table.insert(LS.generators.air, ent)
		--	Msg("Added Air Exchanger\n");
			return true
		end
		--Msg("Not Added Air Exchanger\n");
	end
	--Msg("Not Added Air Exchanger\n");
	return false
end

function LS.AddTemperatureRegulator(ent)
	if ent.GetLSClass and ent:GetLSClass() == "temperature exchanger" then
		if table.insert(LS.generators.temperature, ent) then
		--table.insert(LS.generators.temperature, ent)
			--Msg("Added Temp Exchanger\n");
			return true
		end
		--Msg("Not Added temp Exchanger\n");
	end
	--Msg("Not Added temp Exchanger\n");
	return false
end

function LS.RemoveAirRegulator(ent)
	for k, v in pairs(LS.generators.air) do
		if v == ent then
			table.remove(LS.generators.air, k)
		end
	end
end

function LS.RemoveTemperatureRegulator(ent)
	for k, v in pairs(LS.generators.temperature) do
		if v == ent then
			table.remove(LS.generators.temperature, k)
		end
	end
end

function LS.GetAirRegulators()
	return LS.generators.air or {}
end

function LS.GetTemperatureRegulators()
	return LS.generators.temperature or {}
end

function LS.ZapMe(pos, magnitude)
	if not (pos and magnitude) then return end
	zap = ents.Create("point_tesla")
	zap:SetKeyValue("targetname", "teslab")
	zap:SetKeyValue("m_SoundName" ,"DoSpark")
	zap:SetKeyValue("texture" ,"sprites/physbeam.spr")
	zap:SetKeyValue("m_Color" ,"200 200 255")
	zap:SetKeyValue("m_flRadius" ,tostring(magnitude*80))
	zap:SetKeyValue("beamcount_min" ,tostring(math.ceil(magnitude)+4))
	zap:SetKeyValue("beamcount_max", tostring(math.ceil(magnitude)+12))
	zap:SetKeyValue("thick_min", tostring(magnitude))
	zap:SetKeyValue("thick_max", tostring(magnitude*8))
	zap:SetKeyValue("lifetime_min" ,"0.1")
	zap:SetKeyValue("lifetime_max", "0.2")
	zap:SetKeyValue("interval_min", "0.05")
	zap:SetKeyValue("interval_max" ,"0.08")
	zap:SetPos(pos)
	zap:Spawn()
	zap:Fire("DoSpark","",0)
	zap:Fire("kill","", 1)
end


function LS.Burn_Quiet(ent)
	if not ent then return end
	ent:StopSound( "NPC_Stalker.BurnFlesh" )
end


function LS.LS_Immolate(ent)
	if not ent then return end
	ent:EmitSound( "NPC_Stalker.BurnFlesh" )
	ent:SetModel("models/player/charple.mdl")
	timer.Simple(3, function() LS.Burn_Quiet(ent) end)
end


function LS.LS_Frosty(ent)
	if not ent then return end
	ent:EmitSound( "vehicles/v8/skid_lowfriction.wav" )
end

function LS.LS_Crush(ent)
	if not ent then return end
	ent:EmitSound( "Player.FallGib" )
end

function LS.ColorDamage(ent, HP, Col)
	if not ent or not HP or not Col or not IsValid(ent) then return end
	if (ent:Health() <= (ent:GetMaxHealth( ) / HP)) then
		ent:SetColor(Color(Col, Col, Col, 255))
	end
end

function LS.DamageLS(ent, dam)
	if not (ent and ent:IsValid() and dam) then return end
	if ent:GetMaxHealth( ) == 0 then return end
	dam = math.floor(dam / 2)
	if (ent:Health( ) > 0) then
		local HP = ent:Health( ) - dam
		ent:SetHealth( HP )
		if (ent:Health( ) <= (ent:GetMaxHealth( ) / 2)) then
			if ent.Damage then
				ent:Damage()
			end
		end
		LS.ColorDamage(ent, 2, 200)
		LS.ColorDamage(ent, 3, 175)
		LS.ColorDamage(ent, 4, 150)
		LS.ColorDamage(ent, 5, 125)
		LS.ColorDamage(ent, 6, 100)
		LS.ColorDamage(ent, 7, 75)
		if (ent:Health( ) <= 0) then
			ent:SetColor(Color(50, 50, 50, 255))
			if ent.Destruct then
				ent:Destruct()
			else
				LS.Destruct( ent, true )
			end
		end
	end
end

function LS.Destruct( ent, Simple )
	if (Simple) then
		Explode2( ent )
	else
		timer.Simple(1, function() Explode1(ent) end)
		timer.Simple(1.2, function() Explode1(ent) end)
		timer.Simple(2, function() Explode1(ent) end)
		timer.Simple(2, function() Explode2(ent) end)
	end
end

function LS.RemoveEnt( ent )
	constraint.RemoveAll( ent )
	timer.Simple( 1, function() if IsValid(ent) then SafeRemoveEntity(ent) end end)
	ent:SetNotSolid( true )
	ent:SetMoveType( MOVETYPE_NONE )
	ent:SetNoDraw( true )
end

--End Extra Methodes

--Extensions on MetaTable

local Ply = FindMetaTable( "Player" )

function Ply:Ls_Init()
	if LS.GetVersion and LS.GetVersion() >= 0.1 then
		self.suit = self.suit or {}
		self.caf = self.caf or {}
		self.caf.custom = self.caf.custom or {}
		self.caf.custom.ls = {}
		self:LsResetSuit()
		return true
	end
	return false
end

function Ply:LsResetSuit()
	local hash = self.suit
	hash.env = {}
	hash.air = 200 --100
	hash.energy = 200 --100
	hash.coolant = 200 --100
	hash.recover = 0
	self.suit = hash
	hash = {}
	hash.temperature = 288
	hash.air = true
	hash.inspace = false
	self.airused = false
	self.highpressure = false
	self.caf.custom.ls = hash
end

function Ply:LsCheck()
	if self:IsValid() and self:Alive() and LS.GetStatus() then
		local pod = self:GetParent()
		local RD = CAF.GetAddon("Resource Distribution")
		local SB = CAF.GetAddon("Spacebuild")
		if SB and SB.GetStatus() then
			local space = SB.GetSpace()
			local environment = space --restore to default before doing the Environment checks
			local oldenvironment = self.environment
        for k, v in pairs(SB.GetPlanets()) do
                if v and v:IsValid() then
					--Msg("Checking planet\n")
					environment = v:OnEnvironment(self, environment, space) or environment
				end
			end
			if environment == space then
				for k, v in pairs(SB.GetStars()) do
					if v and v:IsValid() then
						environment = v:OnEnvironment(self, environment, space) or environment
					end
				end
			end
			for k, v in pairs(SB.GetEnvironments()) do
				if v and v:IsValid() then
					environment = v:OnEnvironment(self, environment, space) or environment
				end
			end
			if oldenvironment ~= environment then
				self.environment = environment
				SB.OnEnvironmentChanged(self)
			elseif oldenvironment ~= self.environment then
				self.environment = oldenvironment
			end
			self.environment:UpdateGravity(self)
			
			if self.environment:GetPressure() > 1.5 and not pod:IsValid() then
				local pressure = self.environment:GetPressure() - 1.5
				for k, v in pairs(LS.GetAirRegulators()) do
					if v and IsValid(v) and v:IsActive() and self:GetPos():Distance(v:GetPos()) < v:GetRange() then
						pressure = v:UsePersonPressure(pressure)
					end
				end
				if pressure > 0 then
					if self.suit.air <= 0 then
						self:TakeDamage( (pressure) * 50 , 0 )
						LS.LS_Crush( self )
						if self:Health() <= 0 then
							self:LsResetSuit()
							return
						end
					elseif self.suit.air < math.ceil(pressure/5) then
						self:TakeDamage(math.Round((self.suit.air/(pressure/5))* ((pressure +1) * 50)) , 0 )
						self.suit.air = 0
						if self:Health() <= 0 then
							self:LsResetSuit()
							return
						end
					else
						self.suit.air = math.Round(self.suit.air - (pressure/5))
					end
				end
			end
			self.caf.custom.ls.temperature = self.environment:GetTemperature(self)
			if self.caf.custom.ls.temperature < 283 or self.caf.custom.ls.temperature > 308 then
				if pod and pod:IsValid() then
					if self.caf.custom.ls.temperature < 283 then
						local needed = math.ceil((283 - self.caf.custom.ls.temperature)/8)
						if (RD.GetResourceAmount(pod, "energy") > needed) then
							RD.ConsumeResource(pod, "energy", needed)
							self.caf.custom.ls.temperature = 283
						else
							needed = RD.GetResourceAmount(pod, "energy")
							RD.ConsumeResource(pod, "energy", needed)
							self.caf.custom.ls.temperature = self.caf.custom.ls.temperature + math.ceil(needed * 8)
						end
					elseif self.caf.custom.ls.temperature > 308 then
						local needed = math.ceil((self.caf.custom.ls.temperature - 308)/16)
						if (RD.GetResourceAmount(pod, "nitrogen") > needed) then
							RD.ConsumeResource(pod, "nitorgen", needed)
							self.caf.custom.ls.temperature = 308
						else
							needed = RD.GetResourceAmount(pod, "nitrogen")
							RD.ConsumeResource(pod, "nitrogen", needed)
							self.caf.custom.ls.temperature = self.caf.custom.ls.temperature - math.ceil(needed * 16)
						end
						if self.caf.custom.ls.temperature > 308 then
							needed = math.ceil((self.caf.custom.ls.temperature - 308)/8)
							if (RD.GetResourceAmount(pod, "water") > needed) then
								RD.ConsumeResource(pod, "water", needed)
								self.caf.custom.ls.temperature = 308
							else
								needed = RD.GetResourceAmount(pod, "water")
								RD.ConsumeResource(pod, "water", needed)
								self.caf.custom.ls.temperature = self.caf.custom.ls.temperature - math.ceil(needed * 8)
							end
						end
					end
				end
				for k, v in pairs(LS.GetTemperatureRegulators()) do
					if v and IsValid(v) and v:IsActive() and self:GetPos():Distance(v:GetPos()) < v:GetRange() then
						self.caf.custom.ls.temperature = self.caf.custom.ls.temperature + v:CoolDown(self.caf.custom.ls.temperature)
					end
				end
				if not LS_Core_Override_Heat then
					local dec = 0
					if self.caf.custom.ls.temperature < 283 then
						dam = (283 - self.caf.custom.ls.temperature) / 5
						if (self.environment:GetPressure() > 0) then
							dec = math.ceil(5 * (4 - (self.caf.custom.ls.temperature / 72)))
						else
							dec = 5
						end
						if (self.suit.energy > dec) then
							self.suit.energy = self.suit.energy - dec
						else
							self.suit.energy = 0
							if (self.environment:GetPressure() > 0) then
								if (self:Health() <= dam) then
									if self:Health() > 0 then
										self:TakeDamage( dam, 0 )
										LS.LS_Frosty(self)
										self:LsResetSuit()
										return
									end
								else
									self:TakeDamage( dam, 0 )
									self.suit.recover = self.suit.recover + dam
								end
							else
								if (self:Health() <= 7) and (self:Health() > 0) then
									self:TakeDamage( 7, 0 )
									LS.LS_Frosty(self)
									self:LsResetSuit()
									return
								else
									self:TakeDamage( 7, 0 )
									self.suit.recover = self.suit.recover + 7
								end
							end
						end
					elseif self.caf.custom.ls.temperature > 308 then
						dam = (self.caf.custom.ls.temperature - 308) / 5
						dec = math.ceil(5 * ((self.caf.custom.ls.temperature - 308) / 72))
						if (self.suit.coolant > dec) then
							self.suit.coolant = self.suit.coolant - dec
						else
							self.suit.coolant = 0
							if (self:Health() <= dam) and (self:Health() > 0) then
								LS.LS_Immolate(self)
								self:TakeDamage( dam, 0 )
								self:LsResetSuit()
								return
							else
								self:TakeDamage( dam, 0 )
								self.suit.recover = self.suit.recover + dam
							end
						end
					end
				end
			end
			if self.environment:GetO2Percentage() * self.environment:GetAtmosphere() < 5 then
				self.caf.custom.ls.air = false
				self.caf.custom.ls.airused = false
				if pod and pod:IsValid() then
					local air = RD.GetResourceAmount(pod, "oxygen")
					if (air >= 5) then
						RD.ConsumeResource(pod, "oxygen", 5)
						self.caf.custom.ls.airused = true
					end
				end
				if not self.caf.custom.ls.airused then
					for k, v in pairs(LS.GetAirRegulators()) do
						if v and IsValid(v) and v:IsActive() and self:GetPos():Distance(v:GetPos()) < v:GetRange() then
							self.suit.air = self.suit.air + v:UsePerson()
							self.caf.custom.ls.airused = true
							break
						end
					end
				end
				if not self.caf.custom.ls.airused then
					if (self.suit.air <= 0 ) then
						if (self:Health() > 10) then
							self:TakeDamage( 10, 0 )
							self.suit.recover = self.suit.recover + 10
							self:EmitSound( "Player.DrownStart" )
						else
							if (self:Health() > 0) then
								self:TakeDamage( 10, 0 )
								self:LsResetSuit()
								self:EmitSound( "streetwar.slimegurgle04" )
								self:EmitSound( "streetwar.slimegurgle04" )
								self:EmitSound( "streetwar.slimegurgle04" )
								return
							end
						end
					else
						self.suit.air = self.suit.air - 5
						if self.suit.air < 0 then
							self.suit.air = 0
						end
					end
				end
			else
				self.caf.custom.ls.air = true
				if self:WaterLevel() <= 2 then
					if self.suit.air < 100  then
						self.suit.air = self.suit.air + self.environment:Convert(SB_AIR_O2, SB_AIR_CO2, 100-self.suit.air)
					end
					self.environment:Convert(SB_AIR_O2, SB_AIR_CO2, 5)
				end
			end
		end
		if not self.caf.custom.ls.airused then
			if self:WaterLevel() > 2 then
				if self.caf.custom.ls.air then
					self.caf.custom.ls.air = false
					self.caf.custom.ls.airused = false
					if pod and pod:IsValid() then
						local air = RD.GetResourceAmount(pod, "oxygen")
						if (air >= 5) then
							RD.ConsumeResource(pod, "oxygen", 5)
							self.caf.custom.ls.airused = true
						end
					end
					if not self.caf.custom.ls.airused then
						for k, v in pairs(LS.GetAirRegulators()) do
							if v and IsValid(v) and v:IsActive() and self:GetPos():Distance(v:GetPos()) < v:GetRange() then
								self.suit.air = self.suit.air + v:UsePerson()
								self.caf.custom.ls.airused = true
								break
							end
						end
					end
					if not self.caf.custom.ls.airused then
						if (self.suit.air <= 0 ) then
							if (self:Health() > 10) then
								self:TakeDamage( 10, 0 )
								self.suit.recover = self.suit.recover + 10
								self:EmitSound( "Player.DrownStart" )
							else
								if (self:Health() > 0) then
									self:TakeDamage( 10, 0 )
									self:LsResetSuit()
									self:EmitSound( "streetwar.slimegurgle04" )
									self:EmitSound( "streetwar.slimegurgle04" )
									self:EmitSound( "streetwar.slimegurgle04" )
									return
								end
							end
						else
							self.suit.air = self.suit.air - 5
							if self.suit.air < 0 then
								self.suit.air = 0
							end
						end
					end
				end
			elseif self.caf.custom.ls.air then
				if self.suit.air < 100 then
					self.suit.air = 100
				end
			end
		end
		if self.caf.custom.ls.temperature >= 283 and self.caf.custom.ls.temperature <= 308 and self.caf.custom.ls.air and self.suit.recover > 0 then
			if ((self:Health() + 5 )>= 100) then
				self:SetHealth(100)
				self.suit.recover = 0
			else
				self:SetHealth(self:Health() + 5)
				self.suit.recover = self.suit.recover - 5
			end
		end
		self.caf.custom.ls.airused = false
		self.caf.custom.ls.air = true
		self:UpdateLSClient()
	end
end

function Ply:UpdateLSClient()
	local SB = CAF.GetAddon("Spacebuild");
	if SB and SB.GetStatus() then
		umsg.Start("LS_umsg1", self)
			umsg.Float( self.environment:GetO2Percentage() or -1)
			umsg.Short( self.suit.air or -1 )
			umsg.Short( self.environment:GetTemperature(self) or -1)
			umsg.Short( self.suit.coolant or -1)
			umsg.Short( self.suit.energy  or -1)
		umsg.End() 
	else
		umsg.Start("LS_umsg2", self)
			umsg.Short( self.suit.air or -1 )
		umsg.End() 
	end
end

