local concat = table.concat
local insert = table.insert
local sprintf = string.format
local rep = string.rep

local minetest_example_header = [[
#    This file contains a list of all available settings and their default value for minetest.conf

#    By default, all the settings are commented and not functional.
#    Uncomment settings by removing the preceding #.

#    minetest.conf is read by default from:
#    ../minetest.conf
#    ../../minetest.conf
#    Any other path can be chosen by passing the path as a parameter
#    to the program, eg. "luanti.exe --config ../minetest.conf.example".

#    Further documentation:
#    https://docs.luanti.org/

]]

local group_format_template = [[
# %s = {
#    offset      = %s,
#    scale       = %s,
#    spread      = (%s, %s, %s),
#    seed        = %s,
#    octaves     = %s,
#    persistence = %s,
#    lacunarity  = %s,
#    flags       =%s
# }

]]

local function create_minetest_conf_example(settings)
	local result = { minetest_example_header }
	for _, entry in ipairs(settings) do
		if entry.type == "category" then
			if entry.level == 0 then
				insert(result, "#\n# " .. entry.name .. "\n#\n\n")
			else
				insert(result, rep("#", entry.level))
				insert(result, "# " .. entry.name .. "\n\n")
			end
		else -- any `type` as listed in `settingtypes.txt`
			local group_format = false
			if entry.noise_params and entry.values then
				if entry.type == "noise_params_2d" or entry.type == "noise_params_3d" then
					group_format = true
				end
			end

			local comment = entry.comment ~= "" and entry.comment
				or entry.readable_name -- fallback to the short description
			if comment ~= "" then
				for _, comment_line in ipairs(comment:split("\n", true)) do
					if comment_line == "" then
						insert(result, "#\n")
					else
						insert(result, "#    " .. comment_line .. "\n")
					end
				end
			end
			if entry.type == "key" then
				local line = "See https://docs.luanti.org/for-players/controls/"
				insert(result, "#    " .. line .. "\n")
			end
			insert(result, "#    type: " .. entry.type)
			if entry.min then
				insert(result, " min: " .. entry.min)
			end
			if entry.max then
				insert(result, " max: " .. entry.max)
			end
			if entry.values and entry.noise_params == nil then
				insert(result, " values: " .. concat(entry.values, ", "))
			end
			if entry.possible then
				insert(result, " possible values: " .. concat(entry.possible, ", "))
			end
			insert(result, "\n")
			if group_format == true then
				local flags = entry.values[10]
				if flags ~= "" then
					flags = " "..flags
				end
				insert(result, sprintf(group_format_template, entry.name, entry.values[1],
						entry.values[2], entry.values[3], entry.values[4], entry.values[5],
						entry.values[6], entry.values[7], entry.values[8], entry.values[9],
						flags))
			else
				local append
				if entry.default ~= "" then
					append = " " .. entry.default
				end
				insert(result, sprintf("# %s =%s\n\n", entry.name, append or ""))
			end
		end
	end
	return concat(result)
end

local translation_file_header = [[
// This file is automatically generated
// It contains a bunch of fake gettext calls, to tell xgettext about the strings in config files
// To update it, refer to the bottom of builtin/common/settings/init.lua

fake_function() {]]

local function add_translation_string(result, str, seen)
	if seen[str] then
		return
	end
	seen[str] = true

	-- Prevent gettext from interpreting e.g. "50% of volume" as C-formatted string
	-- Documentation: https://www.gnu.org/software/gettext/manual/html_node/c_002dformat-Flag.html
	local force_no_c_format = str:find("%", 1, true)
	local prefix = force_no_c_format and "/* xgettext:no-c-format */ " or ""

	local have_newlines = str:find("\n", 1, true)
	if have_newlines then
		-- Formatting as "%q" inserts literal newlines. But we want '\n'.
		-- Hence, use "%s" and escape relevant characters manually.
		str = str:gsub("\n", "\\n")
		str = str:gsub("\"", "\\\"")
		insert(result, sprintf("\t%sgettext(\"%s\");", prefix, str))
	else
		insert(result, sprintf("\t%sgettext(%q);", prefix, str))
	end
end

local function create_translation_file(settings)
	local seen = {} -- to deduplicate entries
	local result = { translation_file_header }
	for _, entry in ipairs(settings) do
		if entry.type == "category" then
			add_translation_string(result, entry.name, seen)
		else
			if entry.readable_name then
				add_translation_string(result, entry.readable_name, seen)
			end
			if entry.comment ~= "" then
				add_translation_string(result, entry.comment, seen)
			end
		end
	end
	insert(result, "}\n")
	return concat(result, "\n")
end

local file = assert(io.open("minetest.conf.example", "w"))
file:write(create_minetest_conf_example(settingtypes.parse_config_file(true, false)))
file:close()

file = assert(io.open("src/settings_translation_file.cpp", "w"))
-- If 'minetest.conf.example' appears in the 'bin' folder, the line below may have to be
-- used instead. The file will also appear in the 'bin' folder.
--file = assert(io.open("settings_translation_file.cpp", "w"))
-- We don't want hidden settings to be translated, so we set read_all to false.
file:write(create_translation_file(settingtypes.parse_config_file(false, false)))
file:close()
