local S = minetest.get_translator("vg_hunger")

vg_hunger = {}
local player_is_active = {}
local player_step = {}
local player_bar = {}
local player_heal_time = {}
local base_interval = 5
local heal_interval = 20
local hunger_max = 20

local enable_damage = minetest.settings:get_bool("enable_damage")

local hunger_texture, hunger_texture_empty
if enable_damage then
	hunger_texture = "hunger.png"
	hunger_texture_empty = "hunger_empty.png"
end -- without enable_damage, hunger is invisible

-- Hunger bar
function vg_hunger.new_bar(player, full)
	local name = player:get_player_name()
	player_bar[name] = player:hud_add({
		type = "statbar",
		position = {x=0.5,y=1.0},
		text = hunger_texture,
		text2 = hunger_texture_empty,
		number = full,
		item = hunger_max,
		direction = 1,
		offset = {x=(9*24)-6,y=-(3*24+8)},
		size = {x=16, y=16},
	})
end

-- Change display
function vg_hunger.update_bar(player, full, init)
	if not player then return end
	if not full then return end
	local name = player:get_player_name()
	if minetest.get_player_privs(name)["no_hunger"] then
		if player_bar[name] then
			player:hud_remove(player_bar[name])
			player_bar[name] = nil
		end
		return
	end
	if not init and player_bar[name] then
		player:hud_change(player_bar[name], "number", full)
	else
		vg_hunger.new_bar(player, full)
	end
end

-- Allow mods to tell us that the player is active
function vg_hunger.active(player)
	if not player then return end
	local name = player:get_player_name()
	player_is_active[name] = true
end

function vg_hunger.get(player)
	if not player then return end
	local meta = player:get_meta()
	local full = meta:get_int("hunger")
	return full
end

function vg_hunger.set(player, full)
	if not player then return end
	local meta = player:get_meta()
	vg_hunger.update_bar(player, full)
	meta:set_int("hunger", full)
end

-- no_hunger privilege
minetest.register_privilege("no_hunger", {
	description = S("Player will feel no hunger."),
	give_to_singleplayer = false,
	on_grant = function(name, revoker_name)
		local player = minetest.get_player_by_name(name)
		local meta = player:get_meta()
		local full = hunger_max
		vg_hunger.update_bar(player, full)
		meta:set_int("hunger", full)
	end,
	on_revoke = function(name, revoker_name)
		local player = minetest.get_player_by_name(name)
		local meta = player:get_meta()
		local full = meta:get_int("hunger")
		vg_hunger.update_bar(player, full)
	end
})

minetest.register_on_joinplayer(function(player)
	local meta = player:get_meta()
	local full = meta:get_int("hunger")
	if not full then
		full = hunger_max
		meta:set_int("hunger", full)
	end
	vg_hunger.update_bar(player, full, true)
end)

minetest.register_on_respawnplayer(function(player)
	local meta = player:get_meta()
	local full = hunger_max
	vg_hunger.update_bar(player, full)
	meta:set_int("hunger", full)
end)

minetest.register_on_newplayer(function(player)
	local meta = player:get_meta()
	meta:set_int("hunger", hunger_max)
end)

if enable_damage then -- don't starve or gain satiation if enable_damage is off

-- Prevent players from starving while afk (<--joke)
minetest.register_on_dignode(function(pos, oldnode, player)
	if not player then return end
	local name = player:get_player_name()
	player_is_active[name] = true
end)

minetest.register_on_placenode(function(pos, node, player)
	if not player then return end
	local name = player:get_player_name()
	player_is_active[name] = true
end)

minetest.register_on_item_eat(function(hp_change, replace_with_item, itemstack, player, pointed_thing)
	if not player then return end
	if not hp_change then return end
	if itemstack:take_item() == nil then return end
	-- Restore default behaviour when player can feel no hunger
	local name = player:get_player_name()
	local meta = player:get_meta()
	if minetest.get_player_privs(name)["no_hunger"] then
		player:set_hp(player:get_hp() + hp_change)
	else
		local full = meta:get_int("hunger")
		if full + hp_change > hunger_max then
			full = hunger_max
		else
			full = full + hp_change
		end
		vg_hunger.update_bar(player, full)
		meta:set_int("hunger", full)
	end

	local headpos  = player:get_pos()
	headpos.y = headpos.y + 1
	local sound = "hunger_eating"
	--[[ what was this supposed to mean?
	if def and def.sound and def.sound.eat then
		sound = def.sound.eat
	end
	]]
	minetest.sound_play(sound, {
		pos = player:get_pos(),
		max_hear_distance = 16
	}, true)

	if replace_with_item then
		if itemstack:is_empty() then
			itemstack:add_item(replace_with_item)
		else
			local inv = player:get_inventory()
			-- Check if inv is null, since non-players don't have one
			if inv and inv:room_for_item("main", {name=replace_with_item}) then
				inv:add_item("main", replace_with_item)
			else
				local pos = player:get_pos()
				pos.y = math.floor(pos.y + 0.5)
				core.add_item(pos, replace_with_item)
			end
		end
	end

	return itemstack
end)

-- Main function
function vg_hunger.timer(player)
	local name = player:get_player_name()
	if minetest.get_player_privs(name)["no_hunger"] then return end
	local hp = player:get_hp()
	local meta = player:get_meta()
	if hp <= 0 then return end
	local full = tonumber(meta:get_int("hunger"))
	if not full then full = hunger_max end
	-- heal when hunger there is hunger
	if player_heal_time[name] then
		player_heal_time[name] = player_heal_time[name] + 1
	else
		player_heal_time[name] = 0
	end
	if player_heal_time[name] >= heal_interval / full then
		player:set_hp(hp + 1)
		player_heal_time[name] = 0
	end
	-- hurt when no saturation is left
	if full <= 0 then
		player:set_hp(hp - 1)
		minetest.chat_send_player(name, S("You are hungry."))
	end
	-- reduce saturation when player is active
	if not player_is_active[name] then return end
	-- The hunger interval for each player depends on the health
	if not player_step[name] or player_step[name] >= 20 then
		player_step[name] = 0
	end
	player_step[name] = player_step[name] + 1
	if player_step[name] < hp then return end
	player_step[name] = 0
	full = full - 1
	player_is_active[name] = false
	vg_hunger.update_bar(player, full)
	meta:set_int("hunger", full)
end

local timer = 0
minetest.register_globalstep(function(dtime)
	timer = timer + dtime;
	if timer < base_interval then return end
	timer = 0
	for _,player in ipairs(minetest.get_connected_players()) do
		vg_hunger.timer(player)
	end
end)

end

-- Alias hunger for vg_hunger
hunger = vg_hunger
