local render = require("Module:akk-decl-noun/table")
local common = require("Module:akk-common")
local export = {}



local function map(table, func)
    return {
        ["nom.sg"] = func(table["nom.sg"]),
        ["nom.du"] = func(table["nom.du"]),
        ["nom.pl"] = func(table["nom.pl"]),
        ["gen.sg"] = func(table["gen.sg"]),
        ["gen.du"] = func(table["gen.du"]),
        ["gen.pl"] = func(table["gen.pl"]),
        ["acc.sg"] = func(table["acc.sg"]),
        ["bound"]  = func(table["bound"]),
        ["note"] = table.note
    }
end
local Feminine = {}
function Feminine:new(args)
    local object =  setmetatable({args = args}, {__index = self})
    object:set_stems()
    return object
end
function Feminine:remove_numbers(table) 
	if self.args.pl == "no" then 
		table["gen.pl"] = "–"
		table["nom.pl"] = "–"
	end	
	if self.args.du == "no" then 
		table["gen.du"] = "–"
		table["nom.du"] = "–"
	end
	if self.args.sg == "no" then 
		table["gen.sg"] = "–"
		table["nom.sg"] = "–"
		table["acc.sg"] = "–"
		table["bound"] = "–"
	end
end

function Feminine:set_plural() 
	local len = mw.ustring.len(self.singular)
	local ultima = mw.ustring.sub(self.singular, len, len)
	local penultima = mw.ustring.sub(self.singular, len-1, len-1)
	local stem
	local ends_in_at = false
	local ends_in = penultima..ultima
	if ends_in == "at" or ends_in == "et"  then
		stem = mw.ustring.sub(self.singular, 1, len-2)
		ends_in_at = true
	elseif ultima == "t" then
		stem = mw.ustring.sub(self.singular, 1, len-1)
	else 
		stem = self.singular
	end
	local syllables = common.syllabify_phonetic(stem)
	local len = #syllables
	local penultima = syllables[len - 1]
	local stem_is_open = mw.ustring.gmatch(stem, "[aeiuâêîûāīēū]$")()
	if stem_is_open then
		local last = syllables[len]
		local vowel = mw.ustring.sub(last, #last)
		last = mw.ustring.sub(last, 1, #last - 1)
		if vowel ~= "a" and vowel ~= "e" and vowel ~= "ā" and vowel ~= "ē" then 
			last = last..common.to_short(vowel)
		end
		syllables[len] = last

	elseif penultima and not common.syllable_is_closed(penultima) then 
		 local last = mw.ustring.gsub(syllables[len], "[aeiu]", "")
		 syllables[len] = last
	end
	plural = ""
	for _, syll in pairs(syllables) do
		plural = plural..syll
	end
	local vowel = mw.ustring.gmatch(stem, "[e|ē]" )() or "a"
	vowel = common.to_short(vowel)
	local long = common.to_long(vowel) 
	self.plural = plural..long.."t"
	self.plural = mw.ustring.gsub(self.plural, "āā", "â")
	self.plural = mw.ustring.gsub(self.plural, "īā", "iā")
	if (len == 1 or ends_in_at) and not stem_is_open  then
		self.bound = stem..vowel.."t"
	elseif common.is_vowel(last) then
		self.bound = stem.."t"
	else
		self.bound = stem.."ti"
	end
end
function Feminine:set_stems() 
	if self.args["stem"] then
		self.singular = self.args["stem"]
	else
		local name = mw.title.getCurrentTitle().text
		local len = mw.ustring.len(name)
		self.singular = mw.ustring.sub(name, 1, len - 2)
	end
	self:set_plural()
end

function Feminine:get_table()
	local o = self.args
	return {
		["nom.sg"] = o["nom.sg"] or self.singular.."um",
		["nom.du"] = o["nom.du"] or self.singular.."ān",
		["nom.pl"] = o["nom.pl"] or (self.args.pl or self.plural).."um",
		["gen.sg"] = o["gen.sg"] or self.singular.."im",
		["gen.du"] = o["gen.du"] or self.singular.."īn",
		["gen.pl"] = o["gen.pl"] or (self.args.pl or self.plural).."im",
		["acc.sg"] = o["acc.sg"] or self.singular.."am",
		["bound"] = o["cons"] or self.bound
	}
end


local Masculine = {}
function Masculine:bound(stem, weak) 
	local len = mw.ustring.len(stem)
	local ultima = mw.ustring.sub(stem, len, len)
	local penultima = mw.ustring.sub(stem, len - 1, len - 1)
	if self.args.cons then
		return self.args.cons
	elseif common.is_vowel(penultima) then 
		return stem
	elseif penultima == ultima then
		if weak == "ā" then
			return stem.."ê"
		elseif weak == "i" or weak == "ī" then
			return stem.."i"
		else
			return stem..mw.ustring.sub(stem, len - 2, len - 2)
		end
	else 
		local first = mw.ustring.sub(stem, 1, len - 2)
		local vowel = mw.ustring.sub(stem, len - 2, len - 2)
		return first..penultima..vowel..ultima
	end
end
function Masculine:new(args)
    return setmetatable({args = args}, {__index = self})
end
function Masculine:get_weakness() 
	 local title = mw.title.getCurrentTitle().text
	 if not self.args.stem and not mw.ustring.gmatch(title, "um$")() and not self.args.weak then
	 	error("The parameters for this entry cannot be inferred. Please specify a stem and a weakness type if necessary with the `stem` and `weak` parameters.")
	 else 
	 	return self.args.weak
	 end
end
function Masculine:get_stem() 
	if self.args.stem then 
		return self.args.stem
	else 
		local title = mw.title.getCurrentTitle().text
		local len = mw.ustring.len(title)
		return mw.ustring.sub(title, 1, len - 2)
	end
end

function Masculine:remove_numbers(table) 
	if self.args.pl == "no" then 
		table["gen.pl"] = "–"
		table["nom.pl"] = "–"
	end	
	if self.args.du == "no" then 
		table["gen.du"] = "–"
		table["nom.du"] = "–"
	end
	if self.args.sg == "no" then 
		table["gen.sg"] = "–"
		table["nom.sg"] = "–"
		table["acc.sg"] = "–"
	end
end
function Masculine:get_table() 
    local stem = self:get_stem()
    local weak = self:get_weakness()
    local o = self.args
	if not weak then 
		return {
			["nom.sg"] = o["nom.sg"] or stem.."um",
			["nom.du"] = o["nom.du"] or stem.."ān",
			["nom.pl"] = o["nom.pl"] or (o.pl or stem).."ū",
			["gen.sg"] = o["gen.sg"] or stem.."im",
			["gen.du"] = o["gen.du"] or stem.."īn",
			["gen.pl"] = o["gen.pl"] or (o.pl or stem).."ī",
			["acc.sg"] = o["acc.sg"] or stem.."am",
			["bound"] = self:bound(stem, weak)
		}
	elseif weak == "a" or weak == "u" or weak == "ū" then
		return {
			["nom.sg"] = o["nom.sg"] or stem.."ûm",
			["nom.du"] = o["nom.du"] or stem.."ân",
			["nom.pl"] = o["nom.pl"] or (o.pl or stem).."û",
			["gen.sg"] = o["gen.sg"] or stem.."îm",
			["gen.du"] = o["gen.du"] or stem.."în",
			["gen.pl"] = o["gen.pl"] or (o.pl or stem).."ī",
			["acc.sg"] = o["acc.sg"] or stem.."âm",
			["bound"] = self:bound(stem, weak)
		}
	elseif weak == "ā" then
		return {
			["nom.sg"] = o["nom.sg"] or stem.."ûm",
			["nom.du"] = o["nom.du"] or stem.."ân",
			["nom.pl"] = o["nom.pl"] or (o.pl or stem).."û",
			["gen.sg"] = o["gen.sg"] or stem.."êm",
			["gen.du"] = o["gen.du"] or stem.."ên",
			["gen.pl"] = o["gen.pl"] or (o.pl or stem).."ê",
			["acc.sg"] = o["acc.sg"] or stem.."âm",
			["bound"] = self:bound(stem, weak)
		}
	elseif weak == "i" or weak == "ī" then
		return {
			["nom.sg"] = o["nom.sg"] or stem.."ûm",
			["nom.du"] = o["nom.du"] or stem.."ian",
			["nom.pl"] = o["nom.pl"] or (o.pl or stem).."û",
			["gen.sg"] = o["gen.sg"] or stem.."îm",
			["gen.du"] = o["gen.du"] or stem.."în",
			["gen.pl"] = o["gen.pl"] or (o.pl or stem).."ī",
			["acc.sg"] = o["acc.sg"] or stem.."iam",
			["bound"] = self:bound(stem, weak)
		}
    else 
        error("Not a valid weakness pattern.")
    end
end

local function render_table(self) 
    local romanized = self:get_table()
    self:remove_numbers(romanized)
    local cuneiform = map(romanized, common.from_transcription)
    return render.render(cuneiform, romanized, "", self.args.note)
end

function export.main(frame) 
	local gender = frame.args.gender
	local args = frame:getParent().args
	args.note = frame.args.note
    local inflection
    if gender == "m" then 
    	inflection = Masculine:new(args)
    else
    	inflection = Feminine:new(args)
	end
    return render_table(inflection)
end

return export