local S = minetest.get_translator("unified_inventory")

unified_inventory.registered_categories = {}
unified_inventory.registered_category_items = {}
unified_inventory.category_list = {}

local function char_to_sort_index(char_code)
	if char_code <= 32 then
		-- Command codes, no thanks
		return 0
	end
	if char_code <= 64 then
		-- Sorts numbers, and some punctuation, after letters
		return char_code
	end
	if char_code >= 158 then
		-- Out of sortable range
		return 0
	end
	if char_code > 122 then
		-- Avoids overlap with {, |, } and ~
		return char_code - 58
	end
	if char_code > 96 then
		-- Normalises lowercase with uppercase
		return char_code - 96
	end
	return char_code - 64
end

local function string_to_sort_index(str)
	local max_chars = 5
	local power = 100
	local index = 0
	for i = 1, math.min(#str, max_chars) do
		index = index + (char_to_sort_index(string.byte(str, i)) / (power ^ i))
	end
	return index
end

function update_category_list()
	local category_list = {}
	table.insert(category_list, {
		name = "all",
		label = S("All Items"),
		symbol = "sbz_resources:the_core",
		index = -2,
	})

	table.insert(category_list, {
		name = "uncategorized",
		label = S("Miscellaneous"),
		symbol = "sbz_resources:matter_dust",
		index = -1,
	})
	for category, def in pairs(unified_inventory.registered_categories) do
		table.insert(category_list, {
			name = category,
			label = def.label or category,
			symbol = def.symbol,
			index = def.index or   -- sortby defined order
				string_to_sort_index(category) -- or do a rudimentary alphabetical sort
		})
	end
	table.sort(category_list, function(a, b)
		return a.index < b.index
	end)
	unified_inventory.category_list = category_list
end

local function ensure_category_exists(category_name)
	if not unified_inventory.registered_categories[category_name] then
		unified_inventory.registered_categories[category_name] = {
			symbol = "unknown_item.png",
			label = category_name
		}
	end
	if not unified_inventory.registered_category_items[category_name] then
		unified_inventory.registered_category_items[category_name] = {}
	end
end

function unified_inventory.register_category(category_name, config)
	ensure_category_exists(category_name)
	config = config or {}
	if config.symbol then
		unified_inventory.set_category_symbol(category_name, config.symbol)
	end
	if config.label then
		unified_inventory.set_category_label(category_name, config.label)
	end
	if config.index then
		unified_inventory.set_category_index(category_name, config.index)
	end
	if config.items then
		unified_inventory.add_category_items(category_name, config.items)
	end
	update_category_list()
end

-- TODO: Mark these for removal. They are pretty much useless

function unified_inventory.set_category_symbol(category_name, symbol)
	ensure_category_exists(category_name)
	unified_inventory.registered_categories[category_name].symbol = symbol
	update_category_list()
end

function unified_inventory.set_category_label(category_name, label)
	ensure_category_exists(category_name)
	unified_inventory.registered_categories[category_name].label = label
	update_category_list()
end

function unified_inventory.set_category_index(category_name, index)
	ensure_category_exists(category_name)
	unified_inventory.registered_categories[category_name].index = index
	update_category_list()
end

function unified_inventory.add_category_item(category_name, item)
	if type(item) ~= "string" then
		minetest.log("warning", "[unified_inventory] Cannot register category item: " .. dump(item))
		return
	end

	ensure_category_exists(category_name)
	unified_inventory.registered_category_items[category_name][item] = true
end

function unified_inventory.add_category_items(category_name, items)
	for _, item in ipairs(items) do
		unified_inventory.add_category_item(category_name, item)
	end
end

function unified_inventory.remove_category_item(category_name, item)
	unified_inventory.registered_category_items[category_name][item] = nil
end

function unified_inventory.remove_category(category_name)
	unified_inventory.registered_categories[category_name] = nil
	unified_inventory.registered_category_items[category_name] = nil
	update_category_list()
end

function unified_inventory.find_category(item)
	-- Returns the first category the item exists in
	-- Best for checking if an item has any category at all
	for category, items in pairs(unified_inventory.registered_category_items) do
		if items[item] then return category end
	end
end

function unified_inventory.find_categories(item)
	-- Returns all the categories the item exists in
	-- Best for listing all categories
	local categories = {}
	for category, items in pairs(unified_inventory.registered_category_items) do
		if items[item] then
			table.insert(categories, category)
		end
	end
	return categories
end
