This module needs documentation.
Please document this module by describing its purpose and usage on the documentation page.

local m_utilities = require("Module:utilities")
local m_links = require("Module:links")
local m_common = require("Module:ine-common")

local export = {}

local lang = require("Module:languages").getByCode("ine-pro")

local endings = {
	["actv_pri"] = {
		["1sg"] = {athem = {"mi"}, them = {"h₂"}},
		["2sg"] = {"si"},
		["3sg"] = {"ti"},
		["1du"] = {"wós"},
		["2du"] = {"tés"},
		["3du"] = {"tés"},
		["1pl"] = {"mós"},
		["2pl"] = {"té"},
		["3pl"] = {{stressed = "énti", unstressed = "n̥ti"}},
		},
	
	["actv_sec"] = {
		["1sg"] = {"m̥"},
		["2sg"] = {"s"},
		["3sg"] = {"t"},
		["1du"] = {"wé"},
		["2du"] = {"tóm"},
		["3du"] = {"tā́m"},
		["1pl"] = {"mé"},
		["2pl"] = {"té"},
		["3pl"] = {{stressed = "ént", unstressed = "n̥t"}},
		},
	
	["actv_impr"] = {
		["2sg"] = {athem = {"", "dʰí"}, them = {""}},
		["3sg"] = {"tu"},
		["2du"] = {"tóm"},
		["3du"] = {"tā́m"},
		["2pl"] = {"té"},
		["3pl"] = {{stressed = "éntu", unstressed = "n̥tu"}},
		},
	
	["midl_pri"] = {
		["1sg"] = {"h₂ér"},
		["2sg"] = {"th₂ér"},
		["3sg"] = {athem = {"tór", "ór"}, them = {"tór"}},
		["1du"] = {"wósdʰh₂"},
		["2du"] = "?",
		["3du"] = "?",
		["1pl"] = {"mósdʰh₂"},
		["2pl"] = {"dʰh₂wé"},
		["3pl"] = {"rór", "n̥tór"},
		},
	
	["midl_sec"] = {
		["1sg"] = {"h₂é"},
		["2sg"] = {"th₂é"},
		["3sg"] = {athem = {"tó", "ó"}, them = {"tó"}},
		["1du"] = {"wédʰh₂"},
		["2du"] = "?",
		["3du"] = "?",
		["1pl"] = {"médʰh₂"},
		["2pl"] = {"dʰh₂wé"},
		["3pl"] = {"ró", "n̥tó"},
		},
	
	["midl_impr"] = {
		["2sg"] = {"só"},
		["3sg"] = "?",
		["2du"] = "?",
		["3du"] = "?",
		["2pl"] = {"dʰh₂wé"},
		["3pl"] = "?",
		},
	
	["stative"] = {
		["1sg"] = {"h₂e"},
		["2sg"] = {"th₂e"},
		["3sg"] = {"e"},
		["1du"] = {"wé"},
		["2du"] = "?",
		["3du"] = "?",
		["1pl"] = {"mé"},
		["2pl"] = {"é"},
		["3pl"] = {"ḗr"},
		},
}

local them_vowel = {
	["1sg"] = "ó",
	["2sg"] = "é",
	["3sg"] = "é",
	["1du"] = "ó",
	["2du"] = "é",
	["3du"] = "é",
	["1pl"] = "ó",
	["2pl"] = "é",
	["3pl"] = "ó",
}


local function inflect(data, prefix, endings, stem1, stem2)
	stem2 = stem2 or stem1
	
	-- Is the stem thematic?
	local them = mw.clone(them_vowel)
	
	if mw.ustring.find(stem1, "[eoēōéóḗṓ]$") then
		if mw.ustring.find(stem1, "[ēōḗṓ]$") then
			for key, val in pairs(them) do
				them[key] = m_common.lengthen(val)
			end
		end
		
		if mw.ustring.find(stem1, "[eoēō]$") then
			for key, val in pairs(them) do
				them[key] = m_common.destress(val)
			end
		end
		
		stem1 = mw.ustring.gsub(stem1, "[eoēōéóḗṓ]$", "")
		stem2 = stem1
	else
		for key, val in pairs(them) do
			them[key] = ""
		end
	end
	
	-- Go over each person-number combination
	for p, pendings in pairs(endings) do
		if pendings == "?" then
			data.forms[prefix .. "_" .. p] = {"?"}
		elseif pendings then
			data.forms[prefix .. "_" .. p] = {}
			
			if pendings.athem then
				if them[p] == "" then
					pendings = pendings.athem
				else
					pendings = pendings.them
				end
			end
			
			for _, pending in ipairs(pendings) do
				local stem = stem1
				
				-- Use stem2 if the ending can be stressed, stem1 otherwise
				if pending.stressed or mw.ustring.find(pending, "[áéíĺḿńóŕúḗṓ́]") then
					stem = stem2
				end
				
				local ending
				local ending_unstr
				
				-- Thematic endings are never stressed
				if them[p] ~= "" then
					ending = pending.unstressed or m_common.destress(pending.stressed or pending)
				else
					ending = pending.stressed or pending
					ending_unstr = pending.unstressed
				end
				
				table.insert(data.forms[prefix .. "_" .. p], m_common.add_ending(stem .. them[p], ending, ending_unstr))
			end
		end
	end
end

function export.eventive(frame)
	local params = {
		[1] = {required = true},
		[2] = {},
		["v"] = {},
		}
	
	local args = require("Module:parameters").process(frame:getParent().args, params)
	
	if mw.title.getCurrentTitle().nsText == "Template" then
		args[1] = args[1] or (frame.args[1] == "impf" and "linékʷ" or "léykʷ")
		args[2] = args[2] or (frame.args[2] == "impf" and "linkʷ" or "likʷ")
	end
	
	args[2] = args[2] or args[1]
	
	local data = {forms = {}, title = nil, categories = {}}
	data.info = (frame.args[1] == "impf" and "Imperfective" or "Perfective")
	
	if mw.ustring.find(args[1], "[eoéó]$") then
		data.info = data.info .. ", thematic"
	else
		data.info = data.info .. ", athematic"
	end
	
	data.v = args["v"]
	
	if data.v == "actv" then
		data.info = data.info .. ", active only"
	elseif data.v == "midl" then
		data.info = data.info .. ", middle only"
	end
	
	local subj_stem, opta_stem1, opta_stem2
	
	-- Is the stem thematic?
	if mw.ustring.find(args[1], "[eoéó]$") then
		subj_stem = mw.ustring.gsub(args[1], "([eoéó])$", {["e"] = "ē", ["o"] = "ō", ["é"] = "ḗ", ["o"] = "ṓ"})
		opta_stem1 = mw.ustring.gsub(args[1], "([eoéó])$", {["e"] = "oyh₁", ["o"] = "oyh₁", ["é"] = "óyh₁", ["o"] = "óyh₁"})
		opta_stem2 = opta_stem1
	else
		subj_stem = args[1] .. "e"
		opta_stem1 = m_common.add_ending(args[2], "yéh₁")
		opta_stem2 = m_common.add_ending(args[2], "ih₁")
	end
	
	-- Create the forms
	if frame.args[1] == "impf" then
		data.tenses = true
		
		inflect(data, "pres_indc_actv", endings["actv_pri"], args[1], args[2])
		inflect(data, "past_indc_actv", endings["actv_sec"], args[1], args[2])
		
		inflect(data, "pres_indc_midl", endings["midl_pri"], args[2], args[2])
		inflect(data, "past_indc_midl", endings["midl_sec"], args[2], args[2])
		
		local suffix_type
		
		if mw.ustring.find(args[1], "[eoéó]$") then
			if mw.ustring.find(mw.ustring.sub(args[1], 1, -2), "[eoéó]") then
				if mw.ustring.find(args[1], "o.+éye$") then
					--Removed, redundant to [[:Category:Proto-Indo-European words suffixed with *-éyeti]]
					--suffix_type = " eye-causative/iterative"
				elseif mw.ustring.find(args[1], "é.+ye$") then
					suffix_type = " ye-present"
				elseif mw.ustring.find(args[1], "[eo].+yé$") then
					suffix_type = " ye-denominative"
				elseif mw.ustring.find(args[1], "é.+dʰh₁e$") then
					suffix_type = " dʰh₁e-present"
				else
					suffix_type = " root thematic"
				end
			else
				if mw.ustring.find(args[1], "sḱé$") then
					suffix_type = " sḱe-present"
				elseif mw.ustring.find(args[1], "syé$") then
					suffix_type = " sye-desiderative"
				elseif mw.ustring.find(args[1], "yé$") then
					suffix_type = " zero-grade ye-present"
				elseif mw.ustring.find(args[1], "í.+e$") then
					suffix_type = " i-reduplicated root thematic"
				else
					suffix_type = " zero-grade root thematic"
				end
			end
		else
			if mw.ustring.find(args[1], "éh₁$") then
				suffix_type = " eh₁-stative"
			elseif mw.ustring.find(args[1], "néw$") then
				suffix_type = " nu-present"
			elseif mw.ustring.find(args[1], "né") and not mw.ustring.find(args[2], "né") then
				suffix_type = " nasal-infixed present"
			elseif mw.ustring.find(args[1], "í.+e.+") and not mw.ustring.find(args[2], "í.+e.+") then
				suffix_type = " i-reduplicated root athematic"
			elseif mw.ustring.find(args[1], "é.+e.+") and not mw.ustring.find(args[2], "é.+e.+") then
				suffix_type = " reduplicated root athematic"
			elseif mw.ustring.find(args[1], "ḗ") then
				suffix_type = " lengthened-grade root athematic"
			else
				suffix_type = " root athematic"
			end
		end
		
		if suffix_type then
			table.insert(data.categories, lang:getCanonicalName() .. suffix_type .. " verbs")
		end
	else
		inflect(data, "indc_actv", endings["actv_sec"], args[1], args[2])
		
		inflect(data, "indc_midl", endings["midl_sec"], args[2], args[2])
		
		local suffix_type = {}

		if mw.ustring.find(args[1], "[eoéó]$") then
			if not mw.ustring.find(mw.ustring.sub(args[1], 1, -2), "[eoéó]") then
				suffix_type = " zero-grade root thematic aorist"
			elseif mw.ustring.find(args[1], "é.+e$") and mw.ustring.find(args[2], "é.+e$") then
				suffix_type = " reduplicated root thematic aorist"
			else			
				suffix_type = " root thematic aorist"
			end
		else
			if mw.ustring.find(args[1], "ḗ.+s$") then
				suffix_type = " s-aorist"
			elseif not mw.ustring.find(args[1], "[eoéó]") then
				suffix_type = " zero-grade root athematic aorist"
			elseif mw.ustring.find(args[1], "é.+") and mw.ustring.find(args[2], "é.+") then
				suffix_type = " reduplicated root athematic aorist"
			else
				suffix_type = " root athematic aorist"
			end
		end
		
		table.insert(data.categories, lang:getCanonicalName() .. suffix_type .. " verbs")
	
	end
	
	inflect(data, "impr_actv", endings["actv_impr"], args[1], args[2])
	inflect(data, "subj_actv", endings["actv_pri"], subj_stem)
	inflect(data, "opta_actv", endings["actv_sec"], opta_stem1, opta_stem2)
	
	inflect(data, "impr_midl", endings["midl_impr"], args[2], args[2])
	inflect(data, "subj_midl", endings["midl_pri"], subj_stem)
	inflect(data, "opta_midl", endings["midl_sec"], opta_stem2, opta_stem2)
	
	local o_grade = mw.ustring.gsub(args[2], "[eé]$", {["e"] = "o", ["é"] = "ó"})
	data.forms["actv_ptcp"] = {m_common.add_ending(o_grade, "ónts", "n̥ts")}
	data.forms["midl_ptcp"] = {m_common.add_ending(o_grade, "m̥h₁nós")}

	return make_table(data)
end

function export.stative(frame)
	local params = {
		[1] = {required = true},
		[2] = {},
		}
	
	local args = require("Module:parameters").process(frame:getParent().args, params)
	
	if mw.title.getCurrentTitle().nsText == "Template" then
		args[1] = args[1] or "lelóykʷ"
		args[2] = args[2] or "lelikʷ"
	end
	
	args[2] = args[2] or args[1]
	
	local data = {forms = {}, title = nil, categories = {}}
	data.info = "Stative"
	data.v = "actv"
	
	local subj_stem, opta_stem1, opta_stem2
	
	-- Is the stem thematic?
	if mw.ustring.find(args[1], "[eoéó]$") then
		error("Stative verbs must be athematic.")
	else
		subj_stem = mw.ustring.gsub(args[1], "ó", "é") .. "e"
		opta_stem1 = m_common.add_ending(args[2], "yéh₁")
		opta_stem2 = m_common.add_ending(args[2], "ih₁")
	end
	
	-- Create the forms
	inflect(data, "indc_actv", endings["stative"], args[1], args[2])
	inflect(data, "impr_actv", endings["actv_impr"], args[1], args[2])
	inflect(data, "subj_actv", endings["actv_pri"], subj_stem)
	inflect(data, "opta_actv", endings["actv_sec"], opta_stem1, opta_stem2)
	
	data.forms["actv_ptcp"] = {m_common.add_ending(args[2], "wṓs")}
	
	local suffix_type = {}

	if mw.ustring.find(args[1], "e.+ó.+") and not mw.ustring.find(args[2], "e.+ó.+") then
		suffix_type = " reduplicated root perfect"
	else
		suffix_type = " root perfect"
	end
	
	table.insert(data.categories, lang:getCanonicalName() .. suffix_type .. " verbs")

	return make_table(data)
end

local names = {
	["actv"] = "Active voice",
	["midl"] = "Middle voice",
	
	["indc"] = "Indicative",
	["pres_indc"] = "Present indicative",
	["past_indc"] = "Past indicative",
	["impr"] = "Imperative",
	["subj"] = "Subjunctive",
	["opta"] = "Optative",
	
	["1sg"] = "1st singular",
	["2sg"] = "2nd singular",
	["3sg"] = "3rd singular",
	["1du"] = "1st dual",
	["2du"] = "2nd dual",
	["3du"] = "3rd dual",
	["1pl"] = "1st plural",
	["2pl"] = "2nd plural",
	["3pl"] = "3rd plural",
}

-- Make the table
function make_table(data)
	local function show_form(form)
		if not form then
			return "—"
		end
		
		if type(form) ~= "table" then
			error("a non-table value was given in the list of inflected forms.")
		end
		
		local ret = {}
		
		for key, subform in ipairs(form) do
			table.insert(ret, m_links.full_link({lang = lang, alt = "*" .. subform}))
		end
		
		return table.concat(ret, ", ")
	end
	
	local function repl(param)
		if param == "lemma" then
			return m_links.full_link({lang = lang, alt = mw.title.getCurrentTitle().text}, "term")
		elseif param == "info" then
			return data.info or ""
		else
			return show_form(data.forms[param])
		end
	end
	
	local voices = data.v and {data.v} or {"actv", "midl"}
	local moods = data.tenses and {"pres_indc", "past_indc", "impr", "subj", "opta"} or {"indc", "impr", "subj", "opta"}
	
	local wikicode = {}
	
	table.insert(wikicode, "{| class=\"inflection-table vsSwitcher\" data-toggle-category=\"inflection\" style=\"background: #FAFAFA; border: 1px solid #d0d0d0; text-align: left;\" cellspacing=\"1\" cellpadding=\"2\"")
	table.insert(wikicode, "|- style=\"background: #CCCCFF;\"\n! class=\"vsToggleElement\" colspan=\"" .. (#moods + 1) .. "\" | {{{info}}}")
	table.insert(wikicode, "|- class=\"vsShow\" style=\"background: #F2F2FF;\"")
	table.insert(wikicode, "! style=\"min-width: 8em; background: #E6E6FF;\" | " .. names["3sg"] .. "\n| style=\"min-width: 11em;\" | {{{" .. (data.tenses and "pres_indc" or "indc") .. "_" .. (data.v or "actv") .. "_3sg}}}")
	table.insert(wikicode, "|- class=\"vsShow\" style=\"background: #F2F2FF;\"")
	table.insert(wikicode, "! style=\"min-width: 8em; background: #E6E6FF;\" | " .. names["3pl"] .. "\n| style=\"min-width: 11em;\" | {{{" .. (data.tenses and "pres_indc" or "indc") .. "_" .. (data.v or "actv") .. "_3pl}}}")
	
	for _, voice in ipairs(voices) do
		table.insert(wikicode, "|- class=\"vsHide\" style=\"background: #CCCCFF;\"\n! " .. names[voice])
		
		for _, mood in ipairs(moods) do
			table.insert(wikicode, "! style=\"min-width: 11em; background: #CCCCFF;\" | " .. names[mood])
		end
		
		for _, pn in ipairs({"1sg", "2sg", "3sg", "1du", "2du", "3du", "1pl", "2pl", "3pl"}) do
			table.insert(wikicode, "|- class=\"vsHide\" style=\"background-color: #F2F2FF;\"\n! style=\"min-width: 8em; background-color: #E6E6FF;\" | " .. names[pn])
			
			for _, mood in ipairs(moods) do
				table.insert(wikicode, "| {{{" .. mood .. "_" .. voice .. "_" .. pn .. "}}}")
			end
		end
		
		table.insert(wikicode, "|- class=\"vsHide\" style=\"background-color: #CCCCFF;\"\n| colspan=\"" .. (#moods + 1) .. "\" |")
		table.insert(wikicode, "|- class=\"vsHide\" style=\"background-color: #F2F2FF;\"\n! style=\"background-color: #E6E6FF;\" | Participle")
		table.insert(wikicode, "| {{{" .. voice .. "_ptcp}}}")
		table.insert(wikicode, "| style=\"background-color: #CCCCFF;\" colspan=\"" .. (#moods - 1) .. "\" |")
	end
	
	table.insert(wikicode, [=[|}]=])
	
	wikicode = table.concat(wikicode, "\n")
	
	return (mw.ustring.gsub(wikicode, "{{{([a-z0-9_]+)}}}", repl)) .. m_utilities.format_categories(data.categories, lang)
end

return export