-- LUALOCALS < ---------------------------------------------------------
local ItemStack, getmetatable, minetest, setmetatable
    = ItemStack, getmetatable, minetest, setmetatable
-- LUALOCALS > ---------------------------------------------------------

local invplayer = {}
setmetatable(invplayer, {__mode = "k"})

local invgiving = {}
setmetatable(invgiving, {__mode = "k"})

local function newadd(inv, item, player)
	item = ItemStack(item)
	local size = inv:get_size("main")

	-- Combine any existing stack first
	for i = 1, size do
		local stack = inv:get_stack("main", i)
		if not stack:is_empty() then
			item = stack:add_item(item)
			inv:set_stack("main", i, stack)
			if item:is_empty() then return item end
		end
	end

	-- Try to put in selected slot second
	local idx = player:get_wield_index()
	local stack = inv:get_stack("main", idx)
	item = stack:add_item(item)
	inv:set_stack("main", idx, stack)

	-- Add to leftmost open slot last
	return inv:add_item("main", item)
end

local function wrapinv(inv, player)
	local meta = getmetatable(inv)
	meta = meta and meta.__index or meta
	local oldadd = meta.add_item
	function meta:add_item(listname, stack, ...)
		if invgiving[self] then return oldadd(self, listname, stack, ...) end
		invgiving[self] = true
		local function helper(...)
			invgiving[self] = nil
			return ...
		end
		local found = invplayer[self]
		if found then
			return helper(newadd(self, stack, player))
		else
			return helper(oldadd(self, listname, stack, ...))
		end
	end
	wrapinv = function(i, p)
		if i then invplayer[i] = p end
		return i
	end
	return wrapinv(inv, player)
end

local function patchplayers()
	local player = (minetest.get_connected_players())[1]
	if not player then
		return minetest.after(0, patchplayers)
	end

	local meta = getmetatable(player)
	meta = meta and meta.__index or meta
	if not meta.get_inventory then return end

	local getraw = meta.get_inventory
	function meta:get_inventory(...)
		return wrapinv(getraw(self, ...), self)
	end
end
minetest.after(0, patchplayers)
