give_initial_stuff = {}

local tools = {"default:sword_", "default:pick_", "default:axe_", "default:shovel_"}
local tool_materials = {"stone", "steel", "bronze", "mese", "diamond"}

-- Add item to inv. Split item if count > stack_max using recursion
function give_initial_stuff.give_item(inv, item)
	-- Remove any lower tier tools of the same type as the item
	for _, tool in pairs(tools) do
		if item:get_name():find(tool) then -- Find what tool the new item is
			-- Get the 'tier' of the new item
			local newtier = table.indexof(tool_materials, item:get_name():sub(tool:len() + 1))

			for tier, mat in ipairs(tool_materials) do
				if tier < newtier then
					inv:remove_item("main", tool..mat) -- Will do nothing if tool doesn't exist
				elseif inv:contains_item("main", tool..mat) then
					return -- A higher tier tool is already in inventory
				end
			end
		end
	end

	-- Don't duplicate stacks
	if inv:contains_item("main", item:get_name()) then
		local safeguard = 0
		local itemcount = item:get_count()-5
		if itemcount < 0 then itemcount = 0 end

		-- Replace stack if it's smaller than what we want to add
		while not inv:contains_item("main", ("%s %d"):format(item:get_name(), itemcount)) do
			safeguard = safeguard + 1

			inv:add_item("main", item:get_name() .. " 5")

			if safeguard >= 500 then
				minetest.log("error", "[give_initial_stuff] Something went wrong when filling stack "..dump(item:get_name()))
				break
			end
		end

		return
	end

	inv:add_item("main", item:take_item(item:get_stack_max()))

	-- If item isn't empty, add the leftovers again
	if not item:is_empty() then
		give_initial_stuff.give_item(inv, item)
	end
end

setmetatable(give_initial_stuff, {
	__call = function(self, player, mode)
		minetest.log("action", "Giving initial stuff to player "
				.. player:get_player_name())
		local inv = player:get_inventory()

		if mode ~= "replace_tools" then
			inv:set_list("main",  {})
			inv:set_list("craft", {})

			inv:set_size("craft", 1)
			inv:set_size("craftresult", 0)
			inv:set_size("hand", 0)
		end

		local items = give_initial_stuff.get_stuff(player)

		for _, item in pairs(items) do
			if mode == "replace_tools" then
				for _, tool in pairs(tools) do
					if item:find(tool) then
						give_initial_stuff.give_item(inv, ItemStack(item))
					end
				end
			else
				give_initial_stuff.give_item(inv, ItemStack(item))
			end
		end
	end
})

local registered_stuff_providers = {}
function give_initial_stuff.register_stuff_provider(func, priority)
	table.insert(registered_stuff_providers,
		priority or (#registered_stuff_providers + 1),
		func)
end

function give_initial_stuff.get_stuff(player)
	local seen_stuff = {}

	local stuff = {}
	for i=1, #registered_stuff_providers do
		local new_stuff = registered_stuff_providers[i](player)
		assert(new_stuff)

		for j=1, #new_stuff do
			local name = ItemStack(new_stuff[j]):get_name()
			if not seen_stuff[name] then
				seen_stuff[name] = true
				stuff[#stuff + 1] = new_stuff[j]
			end
		end
	end
	return stuff
end

minetest.register_on_joinplayer(function(player)
	player:set_hp(player:get_properties().hp_max)
	give_initial_stuff(player)
end)
minetest.register_on_respawnplayer(give_initial_stuff)