#!/usr/bin/lua5.3
-- Generate built-in apk help from scdoc manpages

local function splittokens(s)
	local res = {}
	for w in s:gmatch("%S+") do
		res[#res+1] = w
	end
	return res
end

local function textwrap(text, linewidth)
	local spaceleft = linewidth
	local res = {}
	local line = {}

	for _, word in ipairs(splittokens(text)) do
		if #word + 1 > spaceleft then
			table.insert(res, table.concat(line, ' '))
			line = { word }
			spaceleft = linewidth - #word
		else
			table.insert(line, word)
			spaceleft = spaceleft - (#word + 1)
		end
	end
	table.insert(res, table.concat(line, ' '))
	return res
end

local function upperfirst(s)
	return s:sub(1,1):upper() .. s:sub(2):lower()
end

local function render_options(doc, out, options)
	local width = doc.width
	local nindent = 24

	table.insert(out, ("%s options:\n"):format(upperfirst(options.name)))
	for _, opt in ipairs(options) do
		local indent = (" "):rep(nindent)
		k, v = opt[1], opt[2]
		if #k > nindent - 4 then
			table.insert(out, ("  %s\n"):format(k, "", v))
			table.insert(out, indent)
		else
			local fmt = ("  %%-%ds  "):format(nindent - 4)
			table.insert(out, fmt:format(k, v))
		end

		v = table.concat(v, " ")
		local i = v:find("%.%s")
		if not i then i = v:find("%.$") end
		if i then v = v:sub(1, i-1) end
		v = textwrap(v, width - nindent - 1)

		table.insert(out, v[1])
		table.insert(out, "\n")
		for i = 2, #v do
			table.insert(out, indent)
			table.insert(out, v[i])
			table.insert(out, "\n")
		end
	end
end

local function render_footer(doc, out)
	table.insert(out, ("\nFor more information: man %s %s\n"):format(doc.mansection, doc.manpage))
end

local function render_optgroups(doc, out, groups)
	for _, options in ipairs(doc.optgroup) do
		if #options > 0 and (options.name == doc.applet) ~= groups then
			table.insert(out, options.name .. "\0")
			render_options(doc, out, options)
			if options.name == doc.applet then
				render_footer(doc, out)
			end
			table.insert(out, "\0")
		end
	end
end

local function render(doc, out, enabled_applets)
	local width = doc.width

	if not doc.applet then return end
	table.insert(out, doc.applet .. "\0")
	table.insert(out, table.concat(doc.usage, "\n"))
	table.insert(out, "\n")
	local header = nil
	if #doc.commands > 0 then
		for _, cmd in ipairs(doc.commands) do
			if type(cmd) == "string" then
				header = "\n" .. cmd .. ":\n"
			elseif enabled_applets[cmd[1]] then
				if header then
					table.insert(out, header)
					header = nil
				end
				table.insert(out, ("  %-10s %s\n"):format(cmd[1], cmd[2]))
			end
		end
	elseif #doc.description > 0 then
		table.insert(out, "\nDescription:\n")
		for _, ln in ipairs(textwrap(table.concat(doc.description, ' '), width - 2)) do
			table.insert(out, ("  %s\n"):format(ln))
		end
	end
	if #doc.notes > 0 then
		table.insert(out, "\n")
		table.insert(out, table.concat(doc.notes, "\n"))
		if doc.manpage == "apk" then render_footer(doc, out)
		else table.insert(out, "\n") end
	end
	table.insert(out, "\0")
end

local function generate_debug(data)
	return data:gsub("\0", "@nil@\n")
end

local function generate_help(data, app)
	local out, name, width = {}, "help", 16
	local cout = app:compress(data)
	table.insert(out, "/* Automatically generated by genhelp.lua. Do not modify. */")
	table.insert(out, ("static const unsigned int uncompressed_%s_size = %d;"):format(name, #data))
	table.insert(out, ("static const unsigned char compressed_%s[] = { /* %d bytes */"):format(name, #cout))
	local line = { "\t" }
	for i = 1, #cout do
		table.insert(line, ("0x%02x,"):format(cout:byte(i)))
		if i % width == 0 or i == #cout then
			table.insert(out, table.concat(line))
			line = { "\t" }
		end
	end
	table.insert(out, "};")
	return table.concat(out, "\n")
end

local M = {}

function M:generate(app, docs)
	local out = {}
	for _, doc in ipairs(docs) do render(doc, out, app.enabled_applets) end
	for _, doc in ipairs(docs) do render_optgroups(doc, out, true) end
	for _, doc in ipairs(docs) do render_optgroups(doc, out, false) end
	table.insert(out, "\0")

	local gen = app.debug and generate_debug or generate_help
	return gen(table.concat(out), app)
end

return M
