local gender = require('Module:gender and number')
local m_links = require('Module:links')

local lang = require("Module:languages").getByCode("pl")

local export = {}

-- automatic declension

local inflectors = {}

inflectors["^((.+)([kg]))[ia]e?$"] = function (pargs, stem, substem, extra)
	local ending = { ["k"] = "cy"; ["g"] = "dzy"; }
	return {
		stem .. "i",
		stem .. "a",
		stem .. "ie",
		substem .. ending[extra],
		stem .. "ie",
		stem .. "iego",
		stem .. "iej",
		stem .. "ich",
		stem .. "iemu",
		stem .. "im",
		stem .. "ą",
		stem .. "im",
		stem .. "imi",
		(pargs.olddat and (stem .. "u") or nil)
	}
end

inflectors["^((.+)[npbfwsz]i)[ae]?$"] = function (pargs, stem, substem)
	return {
		stem,
		stem .. "a",
		stem .. "e",
		stem,
		stem .. "e",
		stem .. "ego",
		stem .. "ej",
		stem .. "ch",
		stem .. "emu",
		stem .. "m",
		stem .. "ą",
		stem .. "m",
		stem .. "mi"
	}
end

inflectors["^((.+)l)[iae]$"] = function (pargs, stem, substem)
	return {
		stem .. "i",
		stem .. "a",
		stem .. "e",
		stem .. "i",
		stem .. "e",
		stem .. "ego",
		stem .. "ej",
		stem .. "ich",
		stem .. "emu",
		stem .. "im",
		stem .. "ą",
		stem .. "im",
		stem .. "imi"
	}
end

inflectors["^(.+c)i[ae]?$"] = function (pargs, stem, substem)
	return {
		stem .. "i",
		stem .. "ia",
		stem .. "ie",
		stem .. "i",
		stem .. "ie",
		stem .. "iego",
		stem .. "iej",
		stem .. "ich",
		stem .. "iemu",
		stem .. "im",
		stem .. "ią",
		stem .. "im",
		stem .. "imi"
	}
end

inflectors["^((.+)([os])n)[yae]$"] = function (pargs, stem, substem, extra)
	local mpl_suffix

	if stem:match("zielon$") or stem:match("czerwon$") or stem:match("słon$") then
		-- probably the only exceptions
		mpl_suffix = "oni"
	else
		mpl_suffix = ({ ["o"] = "eni", ["s"] = "śni", ["z"] = "źni" })[extra]
	end

	return {
		stem .. "y",
		stem .. "a",
		stem .. "e",
		substem .. mpl_suffix,
		stem .. "e",
		stem .. "ego",
		stem .. "ej",
		stem .. "ych",
		stem .. "emu",
		stem .. "ym",
		stem .. "ą",
		stem .. "ym",
		stem .. "ymi",
	}
end
inflectors["^((.+[^crs])(z)n)[yae]$"] = inflectors["^((.+)([os])n)[yae]$"]

inflectors["^(.+[^osz]n)[yae]$"] = function (pargs, stem, substem, extra)
	return {
		stem .. "y",
		stem .. "a",
		stem .. "e",
		stem .. "i",
		stem .. "e",
		stem .. "ego",
		stem .. "ej",
		stem .. "ych",
		stem .. "emu",
		stem .. "ym",
		stem .. "ą",
		stem .. "ym",
		stem .. "ymi"
	}
end
inflectors["^(.+[crs]zn)[yae]$"] = inflectors["^(.+[^osz]n)[yae]$"]

inflectors["^(.+[wmpb])[yae]$"] = function (pargs, stem, substem)
	return {
		stem .. "y",
		stem .. "a",
		stem .. "e",
		stem .. "i",
		stem .. "e",
		stem .. "ego",
		stem .. "ej",
		stem .. "ych",
		stem .. "emu",
		stem .. "ym",
		stem .. "ą",
		stem .. "ym",
		stem .. "ymi"
	}
end

inflectors["^((.+s)z?)[yae]$"] = function (pargs, stem, substem)
	return {
		stem .. "y",
		stem .. "a",
		stem .. "e",
		substem .. "i",
		stem .. "e",
		stem .. "ego",
		stem .. "ej",
		stem .. "ych",
		stem .. "emu",
		stem .. "ym",
		stem .. "ą",
		stem .. "ym",
		stem .. "ymi"
	}
end

inflectors["^(.+[^fwtdmrłnizshpb])[yae]$"] = function (pargs, stem, substem)
	return {
		stem .. "y",
		stem .. "a",
		stem .. "e",
		stem .. "y",
		stem .. "e",
		stem .. "ego",
		stem .. "ej",
		stem .. "ych",
		stem .. "emu",
		stem .. "ym",
		stem .. "ą",
		stem .. "ym",
		stem .. "ymi"
	}
end
inflectors["^(.+[cdr]z)[yae]$"] = inflectors["^(.+[^fwtdmrłnizshpb])[yae]$"]

inflectors["^((.+)([dr]))[yae]$"] = function (pargs, stem, substem, extra)
	local soft_mpl_cons = { ["ch"] = "si", ["h"] = "si", ["zł"] = "źli", ["sł"] = "śli", ["ł"] = "li", ["r"] = "rzy", ["t"] = "ci", ["st"] = "ści", ["d"] = "dzi" }
	
	return {
		stem .. "y",
		stem .. "a",
		stem .. "e",
		substem .. soft_mpl_cons[extra],
		stem .. "e",
		stem .. "ego",
		stem .. "ej",
		stem .. "ych",
		stem .. "emu",
		stem .. "ym",
		stem .. "ą",
		stem .. "ym",
		stem .. "ymi",
		(pargs.olddat and (stem .. "u") or nil)
	}
end
inflectors["^((.-)(c?h))[yae]$"] = inflectors["^((.+)([dr]))[yae]$"]
inflectors["^((.*[^s])(zł))[yae]$"] = inflectors["^((.+)([dr]))[yae]$"]
inflectors["^((.*)(sł))[yae]$"] = inflectors["^((.+)([dr]))[yae]$"]
inflectors["^((.*[^sz])(ł))[yae]$"] = inflectors["^((.+)([dr]))[yae]$"]
inflectors["^((.*sz)(ł))[yae]$"] = inflectors["^((.+)([dr]))[yae]$"]
inflectors["^((.*)(st))[yae]$"] = inflectors["^((.+)([dr]))[yae]$"]
inflectors["^((.*[^s])(t))[yae]$"] = inflectors["^((.+)([dr]))[yae]$"]

-- archaic forms
inflectors["^(.+[^i])en$"] = function (pargs, stem, substem, extra)
	return {
		stem .. "en",
		stem .. "na",
		stem .. "ne",
		stem .. "ni",
		stem .. "ne",
		stem .. "nego",
		stem .. "nej",
		stem .. "nych",
		stem .. "nemu",
		stem .. "nym",
		stem .. "ną",
		stem .. "nym",
		stem .. "nymi"
	}
end

inflectors["^(.+)on$"] = function (pargs, stem, substem, extra)
	return {
		stem .. "on",
		stem .. "ona",
		stem .. "one",
		stem .. "eni",
		stem .. "one",
		stem .. "onego",
		stem .. "onej",
		stem .. "onych",
		stem .. "onemu",
		stem .. "onym",
		stem .. "oną",
		stem .. "onym",
		stem .. "onymi"
	}
end

-- jakikolwiek, którykolwiek
inflectors["^(.+)kolwiek$"] = function (pargs, stem)
	local result = export.autoinflect(stem, pargs)
	if result then
		for i, item in ipairs(result) do
			result[i] = item .. "kolwiek"	
		end
		return result	
	end
end

function export.autoinflect(lemma, pargs)
	pargs = pargs or {}
	for pat, inflector in pairs(inflectors) do
		local st, en, stem, substem, extra = mw.ustring.find(lemma, pat)
		if st then
			return inflector(pargs, stem, substem, extra)
		end
	end
end

-- automatic comparatives

local comparators = {}

local function bardziej(pargs, stem)
	return "[[bardziej]] " .. stem, "[[najbardziej]] " .. stem	
end

comparators["^(.*ow[yaei])$"]           = bardziej
comparators["^(.*sk[ia]e?)$"]           = bardziej
comparators["^(.*scy)$"]                = bardziej
comparators["^(.*[^aeiou][wz]i[ae]?)$"] = bardziej
comparators["^(.*[^aeiou]l[iae]?)$"]    = bardziej

-- -ały, -ała, -ałe (singular)
comparators["^(.-)(i?a)ł([aye])$"] = function (pargs, stem, vowl, suf)
	local shift = { ["ia"] = "ie", ["a"] = "a" }
	return stem .. shift[vowl] .. "lsz" .. suf, "naj" .. stem .. shift[vowl] .. "lsz" .. suf
end

-- -ali (virile plural)
comparators["^(.-)(i?a)li$"] = function (pargs, stem, vowl, suf)
	local shift = { ["ia"] = "ie", ["a"] = "a" }
	return stem .. shift[vowl] .. "lsi", "naj" .. stem .. shift[vowl] .. "lsi"
end

function export.autocompare(lemma, pargs)
	for pat, comparator in pairs(comparators) do
		local st, en, stem, substem, suf = mw.ustring.find(lemma, pat)
		if st then
			return comparator(pargs, stem, substem, suf)
		end
	end
end

-- template functions

local function make_table(items, title, curtitle, width)
	local itemlinks = {}
	
	local maxl = 0
	for i, item in ipairs(items) do
		table.insert(itemlinks, item and m_links.full_link({lang = lang, term = item}))
		if not width then -- speed
			local l = mw.ustring.len(m_links.remove_links(item))
			if maxl < l then
				maxl = l
			end
		end
	end

	if not width then
		width = math.floor(maxl * 0.78) -- number obtained by anecdotal evidence
		width = (width < 10) and 10 or width
		width = 11 + (width * 5)
	end
 
	return ([=[<div class="NavFrame inflection-table-adj" style="width: %uem">
<div class="NavHead" align=left>%s</div>
<div class="NavContent">
{| class="wikitable inflection-table" style="width: %uem; margin: 0; border: none;"
 ! rowspan="2" style="width: 11em;" | case
 ! colspan="4" scope="colgroup" | singular
 ! colspan="3" scope="colgroup" | plural
 |-
 ! style="min-width: 8em;" scope="col" | %s
 ! style="min-width: 8em;" scope="col" | %s
 ! style="min-width: 8em;" scope="col" | %s
 ! style="min-width: 8em;" scope="col" | %s
 ! style="min-width: 8em;" scope="col" | %s
 ! style="min-width: 8em;" scope="col" | other 
 |-
 ! title="mianownik (jaki? jaka? jakie?), wołacz (o!)" scope="row" | nominative, vocative
 | colspan="2" | %s
 | %s
 | %s
 | %s
 | %s
 |-
 ! title="dopełniacz (jakiego? jakiej?)" scope="row" | genitive
 | colspan="3" | %s
 | rowspan="2" | %s
 | colspan="2" | %s
 |-
 ! title="celownik (jakiemu? jakiej?)" scope="row" | dative
 | colspan="3" | %s
 | colspan="2" | %s
 |-
 ! title="biernik (jakiego? jaki? jaką? jakie?)" scope="row" | accusative
 | %s
 | %s
 | %s
 | rowspan="2" | %s
 | %s
 | %s
 |-
 ! title="narzędnik (jakim? jaką?)" scope="row" | instrumental
 | colspan="3" rowspan="2" | %s
 | colspan="2" | %s
 |-
 ! title="miejscownik (jakim? jakiej?)" scope="row" | locative
 | %s
 | colspan="2" | %s%s
 |}
</div>
</div>]=]):format(
		width, title, width,
		gender.format_list({ "m-pr", "m-an" }),
		gender.format_list({ "m-in" }),
		gender.format_list({ "n" }),
		gender.format_list({ "f" }),
		gender.format_list({ "m-pr" }),
		itemlinks[1],
		itemlinks[3],
		itemlinks[2],
		itemlinks[4],
		itemlinks[5],
		itemlinks[6],
		itemlinks[7], 
		itemlinks[8], 
		itemlinks[9], 
		itemlinks[10],
		itemlinks[6],
		itemlinks[1],
		itemlinks[3],
		itemlinks[11],
		itemlinks[8],
		itemlinks[5],
		itemlinks[12],
		itemlinks[13],
		itemlinks[7],
		itemlinks[8],
		itemlinks[14] and ([=[

 |-
 ! bgcolor="#CCCCFF" title="stary celownik (po jakiemu?)" scope="row" | old dative
 | lang="pl" xml:lang="pl" | %s
]=]):format(itemlinks[14]) or ""
	)
end

function export.template_decl(frame)
	local title = mw.title.getCurrentTitle()
	local pargs = frame:getParent().args

	return make_table({
		pargs[ 1] or "{{{1}}}",
		pargs[ 2] or "{{{2}}}",
		pargs[ 3] or "{{{3}}}",
		pargs[ 4] or "{{{4}}}",
		pargs[ 5] or "{{{5}}}",
		pargs[ 6] or "{{{6}}}",
		pargs[ 7] or "{{{7}}}",
		pargs[ 8] or "{{{8}}}",
		pargs[ 9] or "{{{9}}}",
		pargs[10] or "{{{10}}}",
		pargs[11] or "{{{11}}}",
		pargs[12] or "{{{12}}}",
		pargs[13] or "{{{13}}}",
		pargs[14] or ((title.fullText == frame:getParent():getTitle()) and "{{{14}}}"),
	}, pargs.title or ('declension of <i class="Latn mention" lang="pl" xml:lang="pl">%s</i>'):format(pargs.head or ((title.namespace == 0) and title.text or (pargs[1] or "{{{1}}}"))), title.fullText)
end

function export.template_decl_y(frame)
	local title = mw.title.getCurrentTitle()
	local pargs = frame:getParent().args
	local lemma = pargs[1] and (pargs[1] .. 'y') or ((title.namespace == 0) and title.text or 'darmowy')
	if pargs[2] then
		if declinfo[4] ~= pargs[2] then
			cat = '[[Category:Polish terms needing attention]]'
		end
		declinfo[4] = pargs[2]
	end

	if mw.isSubsting() then
		return ("{{pl-decl-adj|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s%s}}"):format(
			declinfo[1],
			declinfo[2],
			declinfo[3],
			declinfo[4],
			declinfo[5],
			declinfo[6],
			declinfo[7],
			declinfo[8],
			declinfo[9],
			declinfo[10],
			declinfo[11],
			declinfo[12],
			declinfo[13],
			declinfo[14] and (("|%s"):format(declinfo[14])) or ""
		)
	else
		local table_title = pargs.title or (lemma and ('declension of <i class="Latn mention" lang="pl" xml:lang="pl">%s</i>'):format(lemma)) or "declension table"
		return make_table(declinfo, table_title, title.fullText) .. cat
	end
end

function export.template_decl_auto(frame)
	local title = mw.title.getCurrentTitle()
	local pargs = frame:getParent().args
	local lemma = frame.args[1] or pargs[1]; if lemma == "" then lemma = nil end
	lemma = lemma or (title.namespace == 0 and title.text) or (title.nsText == "Template" and "-ni")
	
	local declinfo
	
	if title.fullText == frame:getParent():getTitle() then
		declinfo = {
			"[[-ski]], [[-ty]]",
			"[[-ska]], [[-ta]]",
			"[[-skie]], [[-te]]",
			"[[-scy]], [[-ci]]",
			"[[-skie]], [[-te]]",
			"[[-skiego]], [[-tego]]",
			"[[-skiej]], [[-tej]]",
			"[[-skich]], [[-tych]]",
			"[[-skiemu]], [[-temu]]",
			"[[-skim]], [[-tym]]",
			"[[-ską]], [[-tą]]",
			"[[-skim]], [[-tym]]",
			"[[-skimi]], [[-tymi]]",
			"[[-sku]], [[-tu]]"
		}
	else
		declinfo = export.autoinflect(lemma, pargs) or error(("No declension pattern matches '%s'"):format(lemma))
		
		if not (pargs.olddat and pargs.olddat ~= "") then
			declinfo[14] = nil
		end
	end

	if mw.isSubsting() then
		return ("{{pl-decl-adj|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s%s}}"):format(
			declinfo[1],
			declinfo[2],
			declinfo[3],
			declinfo[4],
			declinfo[5],
			declinfo[6],
			declinfo[7],
			declinfo[8],
			declinfo[9],
			declinfo[10],
			declinfo[11],
			declinfo[12],
			declinfo[13],
			declinfo[14] and (("|%s"):format(declinfo[14])) or ""
		)
	else
		local table_title = pargs.title or (lemma and ('declension of <i class="Latn mention" lang="pl" xml:lang="pl">%s</i>'):format(lemma)) or "declension table"
		return make_table(declinfo, table_title, title.fullText)
	end
end

return export