Open main menu
local p = {}

local lang = mw.loadData("Module:languages/alldata")
local fam = mw.loadData("Module:families/data")
local etyl = mw.loadData("Module:etymology languages/data")

local function get_data(code)
	return lang[code] or etyl[code] or error("language code " .. code .. " not recognized")
end

local function get_sort_value(val)
	data = get_data(val)
	return data[1] or data.canonicalName
end

local function format(code)
	if lang[code] then
		return "<code>[[:Category:" .. lang[code][1] .. " language|" .. lang[code][1] .. " <span style=\"color:green;\">(" .. code .. ")</span>]]</code>"
	elseif etyl[code] then
		return "<code>[[:Category:" .. etyl[code].canonicalName .. "|" .. etyl[code].canonicalName .. " <span style=\"color:green;\">(" .. code .. ")</span>]] (etym-only)</code>"
	end
end

local function dump(data, prefix)
	if type(data) == "string" then
		return format(data)
	else
		local result = {}
		local branch = "├───"
		local next_level = prefix .. "│    "
		local length = #data
		for i, val in ipairs(data) do
			if i == length then
				branch = "└───"
				next_level = prefix .. "     "
			end
			if not val[1] then
				table.insert(result, prefix .. branch .. dump(val.name) .. "<br/>")
			else
				table.insert(result, "\n{| class=mw-collapsible style=border-collapse:collapse\n|")
				table.insert(result, prefix .. branch .. dump(val.name))
				table.insert(result, "\n|-\n|")
				table.insert(result, dump(val, next_level))
				table.insert(result, "\n|}\n")
			end
		end
		return table.concat(result)
	end
end

local function deep_sort(current)
	local result = {}
	local is_table = {}
	for key, val in pairs(current) do
		if type(key) == "number" then
			table.insert(result, val)
		else
			is_table[key] = true
			table.insert(result, key)
		end
	end
	
	table.sort(result, function(a, b)
		return get_sort_value(a) < get_sort_value(b)
	end)
	
	local i = 2
	while i < #result do
		while get_data(result[i - 1]) == get_data(result[i]) do
			table.remove(result, i)
		end
		i = i + 1
	end
	
	for i = 1, #result do
		if is_table[result[i]] then
			local name = result[i]
			result[i] = deep_sort(current[result[i]])
			result[i].name = name
		else
			result[i] = { name = result[i] }
		end
	end
	
	return result
end

local function find_code(t, code)
	for _, val in ipairs(t) do
		if val.name == code then -- "name" is really code
			return { val }
		else
			local result = find_code(val, code)
			if result then
				return result
			end
		end
	end
end

local function find_ancestors(origin, key, val)
	if val.ancestors then
		return val.ancestors
	elseif etyl[key] and val.parent and val.parent:match("%-pro$") then
		return { val.parent }
	elseif val[3] or val.family then
		while true do
			key = val[3] or val.family
			val = fam[key]
			if not key or key == "qfa-not" or key == "qfa-und" then
				return nil
			elseif val.protoLanguage and origin ~= val.protoLanguage then
				return { val.protoLanguage }
			elseif origin ~= key .. "-pro" and lang[key .. "-pro"] then
				return { key .. "-pro" }
			end
		end
	end
end

local function make_nested(data, children)
	local make_nil = {}
	for key, val in pairs(data) do
		if type(key) == "number" then
			if children[val] then
				data[val] = make_nested(children[val], children)
				table.insert(make_nil, key)
				children[val] = nil
			end
		else
			data[key] = make_nested(val, children)
		end
	end
	for _, key in ipairs(make_nil) do
		data[key] = nil
	end
	return data
end

local function get_children()
	local children = {}
	
	for _, data in ipairs { lang, etyl } do
		for key, val in pairs(data) do
			local ancestors = find_ancestors(key, key, val)
			if ancestors then
				for _, ancestor in ipairs(ancestors) do
					if ancestor ~= key then
						if children[ancestor] then
							table.insert(children[ancestor], key)
						else
							children[ancestor] = { key }
						end
					end
				end
			end
		end
	end
	
	return children
end

function p.show(frame)
	local children = get_children()
	
	local nested = make_nested(children, children)
	
	nested = deep_sort(nested)
	
	local descendants_of = frame.args[1]
	if descendants_of then
		if not lang[descendants_of] then
			error("The language code " .. descendants_of .. " is not a valid non-etymology language.")
		end
		
		nested = find_code(nested, descendants_of)
	end
	
	local result = {}
	for i = 1, #nested do
		table.insert(result, "\n\n\n{| class=mw-collapsible style=border-collapse:collapse\n|" .. format(nested[i].name) .. "\n|-\n|")
		table.insert(result, dump(nested[i], "  "))
		table.insert(result, "\n|}")
	end
	return table.concat(result)
end

return p