local S = minetest.get_translator("unified_inventory")
local ui = unified_inventory

unified_inventory.registered_group_items = {}

function unified_inventory.extract_groupnames(groupname)
	local specname = ItemStack(groupname):get_name()
	if specname:sub(1, 6) ~= "group:" then
		return nil, 0
	end
	local group_names = specname:sub(7):split(",")
	return table.concat(group_names, S(" and ")), #group_names
end

-- This is used when displaying craft recipes, where an ingredient is
-- specified by group rather than as a specific item.  A single-item group
-- is represented by that item, with the single-item status signalled
-- in the "sole" field.  If the group contains no items at all, the item
-- field will be nil.
--
-- Within a multiple-item group, we prefer to use an item that has the
-- same specific name as the group, and if there are more than one of
-- those items we prefer the one registered for the group by a mod.
-- Among equally-preferred items, we just pick the one with the
-- lexicographically earliest name.
--
-- The parameter to this function isn't just a single group name.
-- It may be a comma-separated list of group names.  This is really a
-- "group:..." ingredient specification, minus the "group:" prefix.

-- TODO Replace this with the more efficient spec matcher (below)
local function compute_group_item(group_name_list)
	local group_names = group_name_list:split(",")
	local candidate_items = {}
	for itemname, itemdef in pairs(minetest.registered_items) do
		if (itemdef.groups.not_in_creative_inventory or 0) == 0 then
			local all = true
			for _, group_name in ipairs(group_names) do
				if (itemdef.groups[group_name] or 0) == 0 then
					all = false
				end
			end
			if all then table.insert(candidate_items, itemname) end
		end
	end
	local num_candidates = #candidate_items
	if num_candidates == 0 then
		return { sole = true }
	elseif num_candidates == 1 then
		return { item = candidate_items[1], sole = true }
	end
	local is_group = {}
	local registered_rep = {}
	for _, group_name in ipairs(group_names) do
		is_group[group_name] = true
		local rep = unified_inventory.registered_group_items[group_name]
		if rep then registered_rep[rep] = true end
	end
	local bestitem = ""
	local bestpref = 0
	for _, item in ipairs(candidate_items) do
		local pref
		if registered_rep[item] then
			pref = 4
		elseif string.sub(item, 1, 8) == "default:" and is_group[string.sub(item, 9)] then
			pref = 3
		elseif is_group[item:gsub("^[^:]*:", "")] then
			pref = 2
		else
			pref = 1
		end
		if pref > bestpref or (pref == bestpref and item < bestitem) then
			bestitem = item
			bestpref = pref
		end
	end
	return { item = bestitem, sole = false }
end


local group_item_cache = {}

function unified_inventory.get_group_item(group_name)
	if not group_item_cache[group_name] then
		group_item_cache[group_name] = compute_group_item(group_name)
	end
	return group_item_cache[group_name]
end

--[[
This is for filtering known items by groups
e.g. find all items that match "group:flower,yellow" (flower AND yellow groups)
]]
local spec_matcher = {}
function unified_inventory.init_matching_cache()
	for _, name in ipairs(ui.items_list) do
		-- we only need to care about groups, exact items are handled separately
		for group, value in pairs(minetest.registered_items[name].groups) do
			if value and value ~= 0 then
				if not spec_matcher[group] then
					spec_matcher[group] = {}
				end
				spec_matcher[group][name] = true
			end
		end
	end
end

--[[
Retrieves all matching items

Arguments:
	specname (string): Item name or group(s) to filter

Output:
	{
		matchingitem1 = true,
		...
	}
]]
function unified_inventory.get_matching_items(specname)
	if specname:sub(1, 6) ~= "group:" then
		return { [specname] = true }
	end

	local accepted = {}
	for i, group in ipairs(specname:sub(7):split(",")) do
		if i == 1 then
			-- First step: Copy all possible item names in this group
			for name, _ in pairs(spec_matcher[group] or {}) do
				accepted[name] = true
			end
		else
			-- Perform filtering
			if spec_matcher[group] then
				for name, _ in pairs(accepted) do
					accepted[name] = spec_matcher[group][name]
				end
			else
				-- No matching items
				return {}
			end
		end
	end
	return accepted
end
