local export = {}

local Array = require("Module:array")
local fun = require("Module:fun")
local m_table = require("Module:table")
local m_Unicode_data = require("Module:Unicode data")
local lookup_name = m_Unicode_data.lookup_name
local is_combining = m_Unicode_data.is_combining

local capitalize = m_table.listToSet { "latin", "greek", "coptic", "cyrillic",
	"armenian", "hebrew", "arabic", "syriac", "thaana", "samaritan", "mandaic",
	"devanagari", "bengali", } -- ...
local special = { ipa = "IPA", nko = "NKo", }

local function get_name(char)
	return lookup_name(mw.ustring.codepoint(char))
		:lower()
		:gsub("%S+",
			function(word)
				if capitalize[word] then
					return word:gsub("^%l", string.upper)
				else
					return special[word]
				end
			end)
end

function export.get_names(frame)
	local text = fun.map(get_name, frame.args[1])
	
	return table.concat(text, ", ")
end

local function exists(pagename)
	return mw.title.new(pagename).exists
end

local function safe_exists(pagename)
	local success, exists = pcall(exists, pagename)
	return success and exists
end

local output_mt = {}
function output_mt:insert(str)
	self.n = self.n + 1
	self[self.n] = str
end

function output_mt:insert_format(...)
	self:insert(string.format(...))
end

output_mt.join = table.concat

output_mt.__index = output_mt

local function Output()
	return setmetatable({ n = 0 }, output_mt)
end

function export.show_modules()
	local output = Output()
	
	output:insert [[

{| class="wikitable" style="text-align: center;"'
|+ Unicode name and image modules,<br>organized by first three digits of codepoint in hexadecimal base]]
	
	for i = -1, 0xF do
		if i >= 0 then
			output:insert_format('\n! %X', i)
		else
			output:insert '\n!'
		end
	end
	
	output:insert '\n|-'

	local function range(start, ending)
		if not (start and ending) then
			error("Expected two arguments")
		end
		
		local output = Array()
		for i = start, ending do
			output:insert(i)
		end
		return output
	end
	
	local function make_module_row(type, first_two_digits, attribute)
		local found_module = false
		local output = range(0x0, 0xF):map(
			function(i)
				local first_three_digits = first_two_digits * 0x10 + i
				local module = ('Module:Unicode data/' .. type .. '/%03X')
					:format(first_three_digits)
				local link
				if safe_exists(module) then
					link = ("[[%s|" .. type .. "]]"):format(module)
					found_module = true
				else
					link = "&nbsp;"
				end
				return ('| title="U+%Xxxx"%s | %s')
					:format(first_three_digits, attribute, link)
			end)
			:concat "\n"
		return output, found_module
	end
	
	local first = true
	for first_two_digits = 0x00, 0x0E do
		local attribute = not first and ' style="border-top-width: 3px;"'
			or ""
		local row1, found_module1 = make_module_row("names", first_two_digits, attribute)
		local row2, found_module2 = make_module_row("images", first_two_digits, "")
		
		if found_module1 or found_module2 then
			output:insert_format('\n|-\n! rowspan="2"%s | %02Xx\n%s\n|-\n%s',
				attribute, first_two_digits, row1, row2)
			first = false
		end
	end
	output:insert "\n|}"
	
	return output:join()
end

export.show = export.show_modules

return export