local export = {}
local m_dial = require("Module:fi-dialects")

local function make_wikidata_item(item)
	return "[[d:" .. item .. "|item]]"
end

local function make_map_link(lat, lon)
	local lat_part = lat >= 0 and lat .. "_N" or -lat .. "_S"
	local lon_part = lon >= 0 and lon .. "_E" or -lon .. "_W"
	local link = "https://geohack.toolforge.org/geohack.php?params=" .. lat_part .. "_" .. lon_part .. "_globe:earth&language=en"
	return "[" .. link .. " geo]"
end

local function make_wikipedia_link_to(label, target)
	return "[[w:" .. target .. "|" .. label .. "]]"
end

local function make_wiktionary_link_to(label, target, anchor)
	anchor = anchor and "#" .. anchor or ""
	return "[[" .. target .. anchor .. "|" .. label .. "]]"
end

local function make_wikidata_sitelink_to(language, item)
	return "[[d:Special:GoToLinkedPage/" .. language .. "wiki/" .. item .. "|" .. language .. "]]"
end

local function format_parish(parish)
	local links = {}
	local wikidata = parish:getWikidataItem()
	local wikipedia = parish:getWikipediaArticle(true)

	if wikidata then
		table.insert(links, make_wikidata_item(wikidata))
	end

	if wikipedia and not mw.ustring.find(wikipedia, ":") then
		table.insert(links, make_wikipedia_link_to("en", wikipedia))
	end

	if wikidata then
		table.insert(links, make_wikidata_sitelink_to("fi", wikidata))
	end

	if parish:getCoordinates() then
		local lat, lon = parish:getCoordinates()
		table.insert(links, make_map_link(lat, lon))
	end
	
	table.insert(links, make_wiktionary_link_to("wikt", parish:getFinnishName():gsub("/.+", ""):gsub(" %(.+", ""), "Finnish"))

	if links then
		links = "(" .. table.concat(links, " • ") .. ")"
	end
	
	return parish:getFormattedName() .. (#links > 0 and " <small>" .. links .. "</small>" or links)
end

local area_order = {
	"Varsinais-Suomi",
	"Varsinais-Suomi/Etelä",
	"Varsinais-Suomi/Ylämaa",
	"Varsinais-Suomi/Pohjoinen",
	"Varsinais-Suomi/Itä",
	"Satakunta",
	"Satakunta/Etelä",
	"Satakunta/Länsi",
	"Satakunta/Pohjoinen",
	"Häme",
	"Häme/Pohjoinen",
	"Häme/Etelä",
	"Häme/Kaakko",
	"Kymenlaakso",
	"Pohjanmaa",
	"Pohjanmaa/Etelä",
	"Pohjanmaa/Keski",
	"Pohjanmaa/Pohjoinen",
	"Peräpohjola",
	"Länsi-Pohja",
	"Kainuu",
	"Savo",
	"Savo/Pohjoinen",
	"Savo/Etelä",
	"Keski-Suomi",
	"Keski-Suomi/Pohjoinen",
	"Keski-Suomi/Länsi",
	"Keski-Suomi/Etelä",
	"Vermlanti",
	"Karjala",
	"Karjala/Pohjoinen",
	"Karjala/Keski",
	"Karjala/Etelä",
	"Inkeri",
}

for v, k in ipairs(area_order) do area_order[k] = v end

local function get_groups()
	local groups = {"Southwest", "SouthwestTransitional", "Tavastia", "SouthOstrobothnia", "NorthOstrobothnia", "Lapland", "Savonia", "Southeast"}
	local groups_found = {}
	local refetch = false
	
	for _, group in ipairs(groups) do
		groups_found[group] = m_dial.getGroup(group)
	end

	for group, _ in pairs(require("Module:fi-dialects/data/area").groups) do
		if not groups_found[group] then
			table.insert(groups, group)
			refetch = true
		end
	end
	
	if refetch then
		error("update get_groups")
		for _, group in ipairs(groups) do
			groups_found[groups] = m_dial.getGroup(group)
		end
	end

	local groups_west = {}
	local groups_east = {}

	for _, group_code in ipairs(groups) do
		local group = groups_found[group_code]
		if group:getBranch() == "west" then
			table.insert(groups_west, group)
		elseif group:getBranch() == "east" then
			table.insert(groups_east, group)
		else
			error("Unrecognized branch")
		end
	end

	return groups_west, groups_east
end

local function get_areas_by_group()
	local areas_by_group = {}
	
	local function process_area(area)
		local group = area:getGroup()
		if not group then
			for _, subarea in pairs(area:getSubAreas()) do
				process_area(subarea)
			end
			return
		end
		local group_code = group:getCode()
		if not areas_by_group[group_code] then
			areas_by_group[group_code] = {}
		end
		table.insert(areas_by_group[group_code], area)
	end

	for area, _ in pairs(require("Module:fi-dialects/data/area").areas) do
		process_area(m_dial.getArea(area))
	end
	
	for _, areas in pairs(areas_by_group) do
		table.sort(areas, function (a, b) return area_order[a:getCode()] < area_order[b:getCode()] end)
	end

	return areas_by_group
end

local function get_parishes_by_area()
	local parishes = {}
	
	for parish, _ in pairs(require("Module:fi-dialects/data/parish").parishes) do
		table.insert(parishes, m_dial.getParish(parish))
	end

	local areas = {}
	
	for _, parish in ipairs(parishes) do
		local area_code = parish:getAreaCode()
		if not areas[area_code] then
			areas[area_code] = {}
		end
		table.insert(areas[area_code], parish)
	end

	for _, area in ipairs(areas) do
		table.sort(area, function (a, b) return a:getEnglishName() < b:getEnglishName() end)
	end
	
	return areas
end

local column_start = '<div class="ul-column-count" data-column-count="3">\n'
local column_end = "</div>\n"

local function format_area(area, parishes_by_area, level)
	local result = level .. area:getFormattedNameWithLink() .. level .. "\n\n"
	local parishes = {}
	
	for _, parish in ipairs(parishes_by_area[area:getCode()] or {}) do
		table.insert(parishes, parish)
	end
	table.sort(parishes, function (a, b) return a:getEnglishName() < b:getEnglishName() end)
	
	result = result .. column_start
	for _, parish in ipairs(parishes) do
		result = result .. "* " .. format_parish(parish) .. "\n"
	end
	result = result .. column_end
	
	local subareas = area:getSubAreas()
	
	local subarea_keys = {}
	for subarea_key, _ in pairs(subareas) do
		table.insert(subarea_keys, subarea_key)
	end
	table.sort(subarea_keys, function (a, b) return area_order[subareas[a]:getCode()] < area_order[subareas[b]:getCode()] end)
	
	for _, subarea_key in ipairs(subarea_keys) do
		result = result .. format_area(subareas[subarea_key], parishes_by_area, level .. "=")
	end
	
	return result
end

local function format_group(group, areas, parishes_by_area)
	local result = "==" .. group:getFormattedNameWithLink() .. "==\n\n"

	for _, area in ipairs(areas) do
		result = result .. format_area(area, parishes_by_area, "===")
	end

	return result
end

function export.dialect_list(frame)
	local groups_west, groups_east = get_groups()
	local areas_by_group = get_areas_by_group()
	local parishes_by_area = get_parishes_by_area()
	local result = "Each item is a parish, with some minor exceptions; see [[Appendix:Finnish dialects]].\n"

	result = result .. "=Western Finnish=\n\n"
	for _, group in ipairs(groups_west) do
		result = result .. format_group(group, areas_by_group[group:getCode()], parishes_by_area)
	end

	result = result .. "=Eastern Finnish=\n\n"
	for _, group in ipairs(groups_east) do
		result = result .. format_group(group, areas_by_group[group:getCode()], parishes_by_area)
	end
	
	return result
end

return export