-- LUALOCALS < ---------------------------------------------------------
local ItemStack, ipairs, minetest, nodecore
    = ItemStack, ipairs, minetest, nodecore
-- LUALOCALS > ---------------------------------------------------------

local cache = {}

local function handlepickups(player)
	local inv = player:get_inventory()
	local pname = player:get_player_name()
	local cached = cache[pname]
	if cached then
		local snap = {}
		for i = 1, #cached do snap[i] = ItemStack(cached[i]) end

		local excess = {}
		local dirty

		local widx = player:get_wield_index()
		for i in nodecore.inv_walk(player, widx, inv) do
			local cur = inv:get_stack("main", i)
			local old = snap[i]
			if old:is_empty() or cur:peek_item(1):to_string()
			== old:peek_item(1):to_string() then
				local def = minetest.registered_items[cur:get_name()]
				if not (def and def.virtual_item) then
					local cc = cur:get_count()
					local oc = old:get_count()
					if cc > oc then
						cur:set_count(cc - oc)
						for i = 1, #excess do
							cur = excess[i]:add_item(cur)
						end
						if not cur:is_empty() then
							excess[#excess + 1] = cur
						end
						dirty = dirty or i ~= widx
					else
						snap[i] = cur
					end
				end
			else
				snap[i] = cur
			end
		end

		if dirty then
			for i = 1, #excess do
				local v = excess[i]
				for j in nodecore.inv_walk(player, widx, inv) do
					v = snap[j]:add_item(v)
				end
				if not v:is_empty() then
					minetest.log("failed to reinsert item "
						.. v:get_name() .. " " .. v:get_count()
						.. " for " .. pname)
					dirty = nil
				end
			end
		end

		if dirty then
			dirty = nil
			for i = 1, #cached do
				dirty = dirty or inv:get_stack("main", i)
				:to_string() ~= snap[i]:to_string()
			end
		end

		if dirty then
			minetest.log("inventory rearranged for " .. pname)
			inv:set_list("main", snap)
		end
	end
	cache[pname] = inv:get_list("main")
end

minetest.register_globalstep(function()
		for _, p in ipairs(minetest.get_connected_players()) do
			handlepickups(p)
		end
	end)
