--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| 	Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]


local PLUGIN = PLUGIN

local IsValid = IsValid
local ix = ix
local math = math

util.AddNetworkString("ixHealingData")

ix.log.AddType("medicalBleedoutKO", function(client)
	return string.format("%s a été assommé et saigne abondamment.", client:GetName())
end, FLAG_DANGER)
ix.log.AddType("medicalBleedoutKilled", function(client)
	return string.format("%s est mort alors qu'il saignait abondamment.", client:GetName())
end, FLAG_DANGER)
ix.log.AddType("medicalBleedoutStop", function(client)
	return string.format("%s n'est plus inconscient en raison de blessures critiques.", client:GetName())
end)
ix.log.AddType("medicalBleedout", function(client)
	return string.format("%s a saigné jusqu'à en mourir.", client:GetName())
end, FLAG_DANGER)
	ix.log.AddType("medicalFullyHealed", function(client)
	return string.format("%s est complètement guéri.", client:GetName())
end)
ix.log.AddType("medicalPainkillersNoHP", function(client)
	return string.format("%s n'a plus de points de vie.", client:GetName())
end, FLAG_DANGER)
ix.log.AddType("medicalPainkillersTooMany", function(client)
	return string.format("%s a pris trop d'analgésiques.", client:GetName())
end, FLAG_DANGER)
ix.log.AddType("medicalBleedoutStoppedBy", function(client, givenBy)
	return string.format("%s a arrêté l'hémorragie de %s.", givenBy:GetPlayer():Name(), client:Name())
end)
ix.log.AddType("medicalBandagedBy", function(client, givenBy, amount)
	return string.format("%s a été bandé par %s pour %d points de vie.", client:Name(), givenBy:GetPlayer():Name(), amount)
end)
ix.log.AddType("medicalDisinfectedBy", function(client, givenBy, amount)
	if (amount) then
		return string.format("%s a été désinfecté par %s pendant %d secondes.", client:Name(), givenBy:GetPlayer():Name(), amount)
	else
		return string.format("%s a essayé de désinfecter %s mais la plaie était déjà propre.", givenBy:GetPlayer():Name(), client:Name())
	end
end)

function PLUGIN:MedicalPlayerTick(client, ticks)
	local character = client:GetCharacter()
	if (!character) then return end

	if (!client:Alive()) then
		return
	end

	local health = client:Health()
	local maxHealth = client:GetMaxHealth()
	local bMaxHealth = health >= maxHealth

	if (!bMaxHealth or character:GetHealing("fakeHealth") > 0) then
		local factor = 1
		if (character:GetHealing("disinfectant") > 0) then
			factor = ix.config.Get("HealingRegenBoostFactor") * 5
		end
		character:SetHealing("passive", ticks * factor * maxHealth / (ix.config.Get("HealthRegenTime") * 3600))
	end

	if (IsValid(client.ixRagdoll) and client:Alive() and client.ixRagdoll.bleedoutGetup and
		(health * 100 / maxHealth) >= ix.config.Get("WakeupTreshold")) then
		client:SetRagdolled(false)
		netstream.Start("BleedoutScreen", false)
		ix.log.Add(client, "medicalBleedoutStop")
	end

	-- Do bleedout stuff
	local bleedout = character:GetBleedout()
	if (bleedout > 0) then
		bleedout = bleedout - ticks
		if (bleedout <= 0) then
			if (IsValid(client.ixRagdoll)) then
				client.ixRagdoll.ixNoReset = true
			end

			character:SetBleedout(-1)
			client:SetNetVar("deathTime", nil)
			client:Kill()
			ix.log.Add(client, "medicalBleedout")
			return
		else
			self:BleedoutPainSound(client)
			character:SetBleedout(bleedout)
		end
	end

	local healingData = character:GetHealing()
	if (!healingData) then
		return
	end

	-- Apply healing from bandages
	if (healingData.bandage > 0) then
		local healingAmount = ticks * ix.config.Get("HealingRegenRate") / 60
		if (healingData.disinfectant > 0) then -- Boost if disinfected
			healingAmount = healingAmount * ix.config.Get("HealingRegenBoostFactor")
			healingData.disinfectant = math.max(healingData.disinfectant - ticks, 0)
		end

		-- Give healRate or whatever is left
		if (healingData.bandage > healingAmount) then
			healingData.healingAmount = math.Round(healingData.healingAmount + healingAmount, self.HEALING_PRECISION)
			healingData.bandage = math.Round(healingData.bandage - healingAmount, self.HEALING_PRECISION)
		else
			healingData.healingAmount = math.Round(healingData.healingAmount + healingData.bandage, self.HEALING_PRECISION)
			healingData.bandage = 0
		end
	elseif (healingData.disinfectant and healingData.disinfectant > 0) then
		healingData.disinfectant = math.max(healingData.disinfectant - ticks * 2, 0)
	end

	-- Apply fake healing from painkillers
	if (healingData.painkillers > 0) then
		local fakeAmount = ticks * ix.config.Get("HealingPainkillerRate") / 60

		if (healingData.painkillers > fakeAmount) then
			if (!bMaxHealth) then -- Only actually kill pain if not at max health
				healingData.fakeHealth = math.Round(healingData.fakeHealth + fakeAmount, self.HEALING_PRECISION)
				healingData.healingAmount = math.Round(healingData.healingAmount + fakeAmount, self.HEALING_PRECISION)
			end
			healingData.painkillers = math.Round(healingData.painkillers - fakeAmount, self.HEALING_PRECISION)
		else
			if (!bMaxHealth) then -- Only actually kill pain if not at max health
				healingData.healingAmount = math.Round(healingData.healingAmount + healingData.painkillers, self.HEALING_PRECISION)
				healingData.fakeHealth = math.Round(healingData.fakeHealth + healingData.painkillers, self.HEALING_PRECISION)
			end
			healingData.painkillers = 0
		end
	end

	-- Check if painkillers are still working
	if (healingData.painkillersDuration > 0) then
		-- Reduce duration
		healingData.painkillersDuration = math.max(healingData.painkillersDuration - ticks, 0)
	end

	-- At max health?
	if (bMaxHealth) then
		-- Check if there is any fake health we can heal
		if (healingData.fakeHealth > 0 and healingData.healingAmount > 0) then
			if (healingData.fakeHealth > healingData.healingAmount) then
				healingData.fakeHealth = healingData.fakeHealth - healingData.healingAmount
				healingData.healingAmount = 0
			else
				healingData.healingAmount = healingData.healingAmount - healingData.fakeHealth
				healingData.fakeHealth = 0
			end
		end

		-- At max health and no full health point of fake healing left
		if (healingData.fakeHealth - healingData.healingAmount < 1) then
			character:SetHealing("table", nil) -- Stop healing!
			ix.log.Add(client, "medicalFullyHealed")
			return
		end
	end

	-- Uh oh, the painkillers ran out... time to bring back the pain
	if (healingData.painkillersDuration == 0 and healingData.fakeHealth > 0) then
		local fakeAmount = ticks * ix.config.Get("HealingPainkillerDecayRate") / 60
		if (healingData.fakeHealth > fakeAmount) then
			healingData.healingAmount = math.Round(healingData.healingAmount - fakeAmount, self.HEALING_PRECISION)
			healingData.fakeHealth = math.Round(healingData.fakeHealth - fakeAmount, self.HEALING_PRECISION)
		else
			healingData.healingAmount = math.Round(healingData.healingAmount - healingData.fakeHealth, self.HEALING_PRECISION)
			healingData.fakeHealth = 0
		end
	end

	-- Check if we can heal/damage for at least one point of HP
	if ((!bMaxHealth and healingData.healingAmount >= 1) or healingData.healingAmount <= -1) then
		if (healingData.healingAmount >= 1 or healingData.healingAmount <= -1) then
			-- Source engine can only handle integer values for HP so...
			-- Heal as many integer points of HP as we can and save the fraction left for the future
			local totalHealing = math.min(math.Truncate(healingData.healingAmount), maxHealth - health)
			healingData.healingAmount = healingData.healingAmount - totalHealing
			client:SetHealth(math.min(health + totalHealing, maxHealth))

			if (client:Health() <= 0) then
				client:Kill()
				ix.log.Add(client, "medicalPainkillersNoHP")
				character:SetHealing("table", nil)
				return
			end
		end
	end

	if (healingData.fakeHealth - client:Health() > maxHealth * 1.1) then
		client:Kill()
		ix.log.Add(client, "medicalPainkillersTooMany")
		character:SetHealing("table", nil)
		return
	end

	character:SetHealing("table", healingData)
end

function PLUGIN.OnSetHealing(character, healType, value, givenBy)
	if (!healType) then
		ErrorNoHalt("Tentative de définir la guérison mais aucun type n'a été fourni !")
		return
	end

	if (healType == "table") then
		character.vars.healing = value

		net.Start("ixHealingData")
			net.WriteUInt(character:GetID(), 32)
			if (character.vars.healing) then
				net.WriteUInt(0, 2)
				net.WriteUInt(value.bandage, 16)
				net.WriteUInt(value.disinfectant, 16)
				net.WriteUInt(value.painkillers, 16)
				net.WriteUInt(value.painkillersDuration, 16)
				net.WriteFloat(value.healingAmount)
				net.WriteFloat(value.fakeHealth)
			end
		net.Broadcast()
	else
		if (!value or value <= 0) then
			ErrorNoHalt("Tentative de soignement '"..healType.."' sans valeurs ("..(value or "nil")..")!")
			return
		end

		local healingData = character:GetHealing()
		if (!healingData or !healingData.bandage) then
			healingData = {
				bandage = 0,
				disinfectant = 0,
				painkillers = 0,
				painkillersDuration = 0,
				healingAmount = 0,
				fakeHealth = 0
			}
		end

		if (healType == "bandage") then
			value = value * (givenBy and (1 + givenBy:GetSkillScale("bandage_skill")) or 1)
			healingData.bandage = healingData.bandage + value
			net.Start("ixHealingData")
				net.WriteUInt(character:GetID(), 32)
				net.WriteUInt(1, 2)
				net.WriteUInt(healingData.bandage, 16)
			net.Broadcast()

			if (character:GetBleedout() > 0) then
				character:SetBleedout(-1)

				ix.log.Add(character:GetPlayer(), "medicalBleedoutStoppedBy", givenBy)
			end

			if (givenBy) then
				ix.log.Add(character:GetPlayer(), "medicalBandagedBy", givenBy, value)
			end
		elseif (healType == "bandage_damage") then
				healingData.bandage = math.max(healingData.bandage - value, 0)
				healingData.painkillers = 0
				net.Start("ixHealingData")
					net.WriteUInt(character:GetID(), 32)
					net.WriteUInt(1, 2)
					net.WriteUInt(healingData.bandage, 16)
				net.Broadcast()
				net.Start("ixHealingData")
					net.WriteUInt(character:GetID(), 32)
					net.WriteUInt(3, 2)
					net.WriteUInt(healingData.painkillers, 16)
				net.Broadcast()
		elseif (healType == "disinfectant") then
			value = value * (1 + givenBy:GetSkillScale("disinfectant_skill")) * 60 /
				(ix.config.Get("HealingRegenRate") * ix.config.Get("HealingRegenBoostFactor"))
			healingData.disinfectant = math.max(healingData.disinfectant, value)
			net.Start("ixHealingData")
				net.WriteUInt(character:GetID(), 32)
				net.WriteUInt(2, 2)
				net.WriteUInt(healingData.disinfectant, 16)
			net.Broadcast()

			if (givenBy) then
				ix.log.Add(character:GetPlayer(), "medicalDisinfectedBy", givenBy, healingData.disinfectant == value and value)
			end
		elseif (healType == "painkillers") then
			healingData.painkillers = healingData.painkillers + value
			healingData.painkillersDuration = ix.config.Get("HealingPainkillerDuration") * 60
			net.Start("ixHealingData")
				net.WriteUInt(character:GetID(), 32)
				net.WriteUInt(3, 2)
				net.WriteUInt(healingData.painkillers, 16)
			net.Broadcast()
		elseif (healType == "passive") then
			healingData.healingAmount = math.Round(healingData.healingAmount + value, PLUGIN.HEALING_PRECISION)
		end
		character.vars.healing = healingData
	end
end

local painSounds = {
	Sound("vo/npc/male01/pain01.wav"),
	Sound("vo/npc/male01/pain02.wav"),
	Sound("vo/npc/male01/pain03.wav"),
	Sound("vo/npc/male01/pain04.wav"),
	Sound("vo/npc/male01/pain05.wav"),
	Sound("vo/npc/male01/pain06.wav")
}

function PLUGIN:BleedoutPainSound(client)
	if ((client.ixNextPain or 0) < CurTime() and math.random() < 0.1) then
		local painSound = hook.Run("GetPlayerPainSound", client) or painSounds[math.random(1, #painSounds)]

		if (client:IsFemale() and !painSound:find("female")) then
			painSound = painSound:gsub("male", "female")
		end

		client:EmitSound(painSound)
		client.ixNextPain = CurTime() + 5
	end
end


netstream.Hook("ixConfirmRespawn", function(client)
	client.confirmRespawn = true
end)

netstream.Hook("ixAcceptDeath", function(client)
	if (IsValid(client.ixRagdoll)) then
		client.ixRagdoll.ixNoReset = true
	end

	if (!client:GetCharacter()) then return end

	client:GetCharacter():SetBleedout(-1)
	client:SetNetVar("deathTime", nil)
	client:Kill()

	ix.log.Add(client, "qolDeathLog", "accepting their death")
end)
