Module:fi-dialects/template


local export = {}
local m_dial = require("Module:fi-dialects")
local m_common = require("Module:fi-dialects/template/common")

local dialect_area_order = {
	"Varsinais-Suomi/Etelä",
	"Varsinais-Suomi/Pohjoinen",
	"Varsinais-Suomi/Ylämaa",
	"Varsinais-Suomi/Itä",
	"Satakunta/Etelä",
	"Satakunta/Länsi",
	"Satakunta/Pohjoinen",
	"Häme/Pohjoinen",
	"Häme/Etelä",
	"Häme/Kaakko",
	"Kymenlaakso",
	"Pohjanmaa/Etelä",
	"Pohjanmaa/Keski",
	"Pohjanmaa/Pohjoinen",
	"Peräpohjola",
	"Länsi-Pohja",

	"Kainuu",
	"Keski-Suomi/Pohjoinen",
	"Keski-Suomi/Länsi",
	"Keski-Suomi/Etelä",
	"Savo/Pohjoinen",
	"Savo/Etelä",
	"Karjala/Pohjoinen",
	"Karjala/Keski",
	"Karjala/Etelä",
	"Inkeri",
	"Vermlanti",
}

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

local function eager_wrap(text)
	return '<p style="white-space:pre-wrap;min-width:100%;max-width:min-content;">' .. text .. '</p>'
end

local function get_parishes_by_area(parishes)
	local seen_areas = {}
	local areas = {}
	
	for _, parish in ipairs(parishes) do
		local area_code = parish:getAreaCode()
		local area = seen_areas[area_code]
		if not area then
			area = { ["area"] = parish:getArea(), ["parishes"] = { } }
			table.insert(areas, area)
			seen_areas[area_code] = area
		end
		table.insert(area.parishes, parish)
	end
	
	for _, area in ipairs(areas) do
		table.sort(area.parishes, function (a, b) return a:getEnglishName() < b:getEnglishName() end)
	end
	
	table.sort(areas, function (a, b) 
		return (dialect_area_order[a.area:getCode()] or #dialect_area_order)
		     < (dialect_area_order[b.area:getCode()] or #dialect_area_order)
	end)
	return areas
end

local function parish_map(frame, term, parishes_by_name, extra)
	local parishes = {}
	
	for _, parish in ipairs(parishes_by_name) do
		local result = m_dial.getParish(mw.text.trim(parish), true)
		if result then table.insert(parishes, result) end
	end
	
	local areas = get_parishes_by_area(parishes)
	
	local areas_text = {}
	
	-- construct list of parishes grouped by areas
	for _, area in ipairs(areas) do
		local formatted_parishes = {}
		
		for _, parish in ipairs(area.parishes) do
			table.insert(formatted_parishes, parish:getFormattedName())
		end
		
		formatted_parishes = table.concat(formatted_parishes, ", ")
		table.insert(areas_text, "* " .. area.area:getFormattedName() .. " <small>[" .. area.area:getGroup():getEnglishName() .. "]</small>\n** " .. formatted_parishes)
	end
	
	local list_by_area = table.concat(areas_text, "\n")
	
	local function peg(parish, top, left)
		-- simple peg
		return tostring(mw.html.create('div')
					:attr('class', 'peg_outer')
					:css('position', 'absolute')
					:css('top', top)		-- positioning
					:css('left', left)
					:css('line-height', '0')
					:tag('div')
						:css('position', 'relative')
						:css('left', '-3px')		-- center (6px / 2 = 3px)
						:css('top', '-3px')
						:attr('title', parish:getFormattedName())
						:wikitext('[[File:Red pog.svg|6x6px|link=]]')
						:done())
	end

	return [[{| cellspacing="2" cellpadding="2" class="wikitable vsSwitcher" data-toggle-category="dialectal data"
|-
! class="vsToggleElement" colspan="2" | ]] .. "Dialectal distribution of " .. m_common.mention(term, extra.gloss) .. '\n' .. [[|- class="vsHide"
|<div style="text-align: left; min-width: 25em;">
]] .. frame:preprocess(list_by_area) .. [[
</div>
|
]] .. require("Module:fi-dialects/map").show{frame = frame, parishes = parishes, peg = peg, size = "550px"} .. [[

|- class="vsHide"
| colspan="2" | ]] .. eager_wrap("''" .. m_common.disclaimer .. "''") .. [[
]] .. m_common.format_sources(extra.source) .. [[

|}]]
end

local function format_synonym(term, data)
	if data.semantic then
		return data.labels[term] or term
	else
		return data.nolink and m_common.tag(term) or m_common.make_link(term)
	end
end

local function format_synonyms(syns, data)
	if type(syns) == "table" then
		local result = {}
		for _, syn in ipairs(syns) do
			table.insert(result, format_synonym(syn, data))
		end
		return table.concat(result, "\n")
	else
		return format_synonym(syns, data)
	end
end

local function synonym_table(frame, term, word_id, data)
	local synonyms = data.syns
	local source = data.source and (type(data.source) == "table" and data.source or { data.source }) or { }
	
	local areas_text = ""
	
	for _, special_key in ipairs(m_common.specials) do
		if synonyms[special_key] then
			areas_text = areas_text .. '|- class="vsHide" \n'
			areas_text = areas_text .. '|colspan="2"|' .. m_common.special[special_key] .. '\n'
			areas_text = areas_text .. '|' .. format_synonyms(synonyms[special_key], data) .. '\n'
		end
	end
	
	local parishes = {}
	
	for parish, _ in pairs(synonyms) do
		if not m_common.special[parish] then
			local result = m_dial.getParish(parish, true)
			if result then table.insert(parishes, result) end
		end
	end
	
	local areas = get_parishes_by_area(parishes)
	
	for _, area in ipairs(areas) do
		local areasyns = {}
		local areasyns_parishes = {}
		local parish_indexes = {}

		local function visit(syn, parish)
			if not areasyns_parishes[syn] then
				table.insert(areasyns, syn)
				areasyns_parishes[syn] = {}
			end
			table.insert(areasyns_parishes[syn], parish)
		end

		-- gather all synonyms and parishes that have them
		for index, parish in ipairs(area.parishes) do
			local parish_code = parish:getCode()
			local parish_text = parish:getFormattedName()
			parish_indexes[parish_text] = index
			local syns = synonyms[parish_code]
			if type(syns) == 'table' then
				for _, syn in ipairs(syns) do
					visit(syn, parish_text)
				end
			else
				visit(syns, parish_text)
			end
		end
		
		-- if the list of parishes is equivalent between any two pairs of synonyms, merge them
		local mergers = {}
		local areasyns_original = {}
		for i, syn in ipairs(areasyns) do
			local result = table.concat(areasyns_parishes[syn], "\n")
			for i = 1, #areasyns_original do
				if result == areasyns_original[i] then
					if not mergers[areasyns[i]] then
						mergers[areasyns[i]] = {}
					end
					table.insert(mergers[areasyns[i]], syn)
					break
				end
			end
			areasyns_original[i] = result
		end

		-- apply mergers
		for syn1, syns in pairs(mergers) do
			local parishes = areasyns_parishes[syn1]
			local merged_syns = {syn1}
			areasyns_parishes[syn1] = nil
			for _, syn in ipairs(syns) do
				table.insert(merged_syns, syn)
				areasyns_parishes[syn] = nil
			end
			table.sort(parishes, function (a, b) return parish_indexes[a] < parish_indexes[b] end)
			areasyns_parishes[merged_syns] = parishes
			table.insert(areasyns, merged_syns)
		end

		table.sort(areasyns, function (a, b) return #(areasyns_parishes[a] or {}) > #(areasyns_parishes[b] or {}) end)

		local result_text = ""
		local rows = 0
		for _, syn in ipairs(areasyns) do
			if areasyns_parishes[syn] then
				if #result_text > 0 then result_text = result_text .. '|- class="vsHide"\n' end
				result_text = result_text .. '| style="max-width:32em;" |' .. table.concat(areasyns_parishes[syn], ', ') .. '\n'
				result_text = result_text .. '|' .. format_synonyms(syn, data) .. '\n'
				rows = rows + 1
			end
		end
		areas_text = areas_text .. '|- class="vsHide"\n|rowspan="' .. rows .. '"|' .. area.area:getFormattedName():gsub(" %(", "\n(") .. "<br><small>[" .. area.area:getGroup():getEnglishName() .. "]</small>\n" .. result_text
	end
	
	return [[{| cellspacing="2" cellpadding="2" class="wikitable vsSwitcher" data-toggle-category="dialectal data"
|-
! class="vsToggleElement" colspan="3" | ]] .. "Dialectal " .. (data.semantic and "meanings" or "synonyms") .. " for " .. m_common.mention(term, data.gloss) .. ' ([[Template:fi-dial-map/' .. word_id .. '|map]])\n' .. areas_text .. [[
|- class="vsHide"
| colspan="3" | ]] .. eager_wrap("''" .. m_common.disclaimer .. "''") .. [[<div style="float:right;"><small>]] .. '([[Module:fi-dialects/data/word/' .. word_id .. '|edit data]])' .. [[</small></div>
]] .. m_common.format_sources(source) .. [[

|}]]
end

function export.show(frame)
	local title = mw.title.getCurrentTitle().text
	local params = {
		[1] = { default = title },
		[2] = { default = nil },
		
		["p"] = { default = nil },
		["ref"] = { default = nil },
		["t"] = { default = nil }
	}
	
	local args = require("Module:parameters").process(frame:getParent().args, params)
	local parishes = {}
	
	if args["p"] then
		return parish_map(frame, args[1], mw.text.split(args["p"], ",%s*"), { source = args["ref"] and mw.text.split(args["ref"], ",%s*") or {}, gloss = args["t"] })
	else
		local word_id = args[1]
		if args[2] then
			word_id = word_id .. " (" .. args[2] .. ")"
		end
		local data_ok, data = pcall(function() return mw.loadData("Module:fi-dialects/data/word/" .. word_id) end)
		if not data_ok then
			return "<div><em>No data found. See [[Template:fi-dial]] for more information on how to use this template.</em></div>" .. require("Module:utilities").format_categories({"fi-dial missing data"}, m_common.lang)
		end
		return synonym_table(frame, args[1], word_id, data)
	end
end

return export