function exchange_shop.has_access(meta, player_name)
	local owner = meta:get_string("owner")
	if player_name == owner or owner == "" then
		return true
	end
	local privs = minetest.get_player_privs(player_name)
	return privs.protection_bypass
end


-- Tool wear aware replacement for contains_item.
function exchange_shop.list_contains_item(inv, listname, stack)
	local count = stack:get_count()
	if count == 0 then
		return true
	end

	local list = inv:get_list(listname)
	local name = stack:get_name()
	local wear = stack:get_wear()
	for _, list_stack in pairs(list) do
		if list_stack:get_name() == name and
		   list_stack:get_wear() <= wear then
			if list_stack:get_count() >= count then
				return true
			else
				count = count - list_stack:get_count()
			end
		end
	end
end

-- Tool wear aware replacement for remove_item.
function exchange_shop.list_remove_item(inv, listname, stack)
	local wanted = stack:get_count()
	if wanted == 0 then
		return stack
	end

	local list = inv:get_list(listname)
	local name = stack:get_name()
	local wear = stack:get_wear()
	local remaining = wanted
	local removed_wear = 0

	for index, list_stack in pairs(list) do
		if list_stack:get_name() == name and
		   list_stack:get_wear() <= wear then
			local taken_stack = list_stack:take_item(remaining)
			inv:set_stack(listname, index, list_stack)

			removed_wear = math.max(removed_wear, taken_stack:get_wear())
			remaining = remaining - taken_stack:get_count()
			if remaining == 0 then
				break
			end
		end
	end

	-- Todo: Also remove kebab
	local removed_stack = ItemStack(name)
	removed_stack:set_count(wanted - remaining)
	removed_stack:set_wear(removed_wear)
	return removed_stack
end

function exchange_shop.exchange_action(player_inv, shop_inv)
	if not shop_inv:is_empty("cust_ej")
			or not shop_inv:is_empty("custm_ej") then
		return "One or multiple ejection fields are filled. "..
			"Please empty them or contact the shop owner."
	end
	local owner_wants = shop_inv:get_list("cust_ow")
	local owner_gives = shop_inv:get_list("cust_og")

	-- Check validness of stack "owner wants"
	for i1, item1 in pairs(owner_wants) do
		local name1 = item1:get_name()
		for i2, item2 in pairs(owner_wants) do
			if name1 == "" then
				break
			end
			if i1 ~= i2 and name1 == item2:get_name() then
				return "The field 'Owner needs' can not contain multiple "..
					"times the same items. Please contact the shop owner."
			end
		end
	end

	-- Check validness of stack "owner gives"
	for i1, item1 in pairs(owner_gives) do
		local name1 = item1:get_name()
		for i2, item2 in pairs(owner_gives) do
			if name1 == "" then
				break
			end
			if i1 ~= i2 and name1 == item2:get_name() then
				return "The field 'Owner gives' can not contain multiple "..
					"times the same items. Please contact the shop owner."
			end
		end
	end

	-- Check for space in the shop
	for i, item in pairs(owner_wants) do
		if not shop_inv:room_for_item("custm", item) then
			return "The stock in this shop is full. "..
				"Please contact the shop owner."
		end
	end

	local list_contains_item = exchange_shop.list_contains_item

	-- Check availability of the shop's items
	for i, item in pairs(owner_gives) do
		if not list_contains_item(shop_inv, "stock", item) then
			return "This shop is sold out."
		end
	end

	-- Check for space in the player's inventory
	for i, item in pairs(owner_gives) do
		if not player_inv:room_for_item("main", item) then
			return "You do not have enough space in your inventory."
		end
	end

	-- Check availability of the player's items
	for i, item in pairs(owner_wants) do
		if not list_contains_item(player_inv, "main", item) then
			return "You do not have the required items."
		end
	end

	local list_remove_item = exchange_shop.list_remove_item

	-- Conditions are ok: (try to) exchange now
	local fully_exchanged = true
	for i, item in pairs(owner_wants) do
		local stack = list_remove_item(player_inv, "main", item)
		if shop_inv:room_for_item("custm", stack) then
			shop_inv:add_item("custm", stack)
		else
			-- Move to ejection field
			shop_inv:add_item("custm_ej", stack)
			fully_exchanged = false
		end
	end
	for i, item in pairs(owner_gives) do
		local stack = list_remove_item(shop_inv, "stock", item)
		if player_inv:room_for_item("main", stack) then
			player_inv:add_item("main", stack)
		else
			-- Move to ejection field
			shop_inv:add_item("cust_ej", stack)
			fully_exchanged = false
		end
	end
	if not fully_exchanged then
		return "Warning! Stacks are overflowing somewhere!", true
	end
end
