-- Table of tables indexed by player names, each having three fields:
-- {
--     hp:  HP at time of left-click + regen_max,
--     pos: Position of player while initiating the healing process
--     hud: ID of "healing effect" HUD element
-- }
local players = {}

local regen_interval = 0.5 -- Time in seconds between each step
local regen_timer = 0      -- Timer to keep track of regen_interval
local regen_step = 1       -- Number of HP added every step

-- Boolean function for use by other mods to check if a player is healing
medkits = {}
function medkits.is_healing(name)
	return players[name] and true or false
end

-- Called when player uses a medkit
-- Aborts if player is already healing
local function start_healing(stack, player)
	if not player then
		return
	end
	local name = player:get_player_name()
	local hp = player:get_hp()

	local hp_max = player:get_properties().hp_max
	if players[name] or hp == hp_max then
		return
	end

	players[name] = {
		regen_max = hp_max,
		hp  = hp,
		pos = player:get_pos(),
		hud = player:hud_add({
			hud_elem_type = "image",
			position = {x = 0.5, y = 0.5},
			scale = {x = -100, y = -100},
			text = "medkit_hud.png"
		})
	}

	stack:take_item()
	return stack
end


-- Interrupt reasons:
-- attack: Attacked another player
-- move: Moved 1m away from initial healing pos
-- damage: Damaged by another player
local function reason_handler(reason)
	if reason == "attack" then
		return " because you attacked other player!"
	elseif reason == "move" then
		return " because you moved!"
	elseif reason == "damage" then
		return " because someone damaged you!"
	else
		return "!"
	end
end

-- Called after regen is complete. Remove additional effects
-- If interrupted == true, revert to original HP and give back one medkit.
local function stop_healing(player, interrupted)
	local name = player:get_player_name()
	local info = players[name]

	players[name] = nil
	if interrupted then
		hud_event.new(name, {
			name  = "medkits:interrupt",
			color = 0xFF4444,
			value = "Your healing was interrupted" .. reason_handler(interrupted),
		})
		player:set_hp(info.hp)
		player:get_inventory():add_item("main", ItemStack("medkits:medkit 1"))
	end

	player:hud_remove(info.hud)
end

ctf_match.register_on_new_match(function()
	-- Reset all player states at the end of the match
	for name, info in pairs(players) do
		players[name] = nil
		local player = minetest.get_player_by_name(name)
		if player then
			player:hud_remove(info.hud)
		end
	end
	return true
end)

-- Called after left-click every n seconds (determined by
-- regen_interval); heals player until player's hp_max
minetest.register_globalstep(function(dtime)
	regen_timer = regen_timer + dtime
	if regen_timer < regen_interval then
		return
	end

	for name, info in pairs(players) do
		local player = minetest.get_player_by_name(name)
		if not player then
			players[name] = nil
		else
			-- Abort if player moves more than 1m in any direction to
			-- allow players to manually interrupt healing if necessary
			local pos = player:get_pos()
			if vector.distance(pos, info.pos) >= 1 then
				stop_healing(player, "move")
			end

			-- Stop healing if target reached
			local pstat = players[name]
			if pstat then
				local hp = player:get_hp()
				if hp < pstat.regen_max then
					kill_assist.add_heal_assist(name, regen_step)
					player:set_hp(math.min(hp + regen_step, pstat.regen_max))
				else
					stop_healing(player)
				end
			end
		end
	end
	regen_timer = 0
end)

-- If player takes damage while healing,
-- stop regen and revert back to original state
minetest.register_on_player_hpchange(function(player, hp, reason)
	local name = player:get_player_name()
	if hp < 0 then
		if players[name] then
			player:hud_remove(players[name].hud)
			players[name] = nil -- Don't use stop_healing(), it uses set_hp() and won't allocate deaths or score properly
		end
		if reason and reason.type == "punch" then
			local hitter = reason.object
			if hitter and players[hitter:get_player_name()] then
				stop_healing(hitter, "attack")
			end
		end
	end
	return hp
end, true)

minetest.register_on_leaveplayer(function(player)
	players[player:get_player_name()] = nil
end)

minetest.register_craftitem("medkits:medkit", {
	description = "Medkit",
	inventory_image = "medkit_medkit.png",
	wield_image = "medkit_medkit.png",
	stack_max = 10,

	on_use = start_healing
})