Unicode name and image modules,
organized by first three digits of codepoint in hexadecimal base
0 1 2 3 4 5 6 7 8 9 A B C D E F
00x names names names names names           names     names   names
images images images images images images images images images images images images       images
01x names names names names names   names   names   names names names names names names
images images images images images   images images images   images images images images images images
02x                             names names
images images images images images images images images images images images images images images images images
03x                                
images images images                          
0Ex names                              
                               

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