local nom = require("Module:gez-nominal")
local m_strutils = require("Module:string utilities")
local m_links = require("Module:links")
local lang = require("Module:languages").getByCode("gez")

local export = {}

local pronouns = {"1s", "2ms", "2fs", "3ms", "3fs", "1p", "2mp", "2fp", "3mp", "3fp"}

local numbers = {
    ["s"] = "singular",
    ["p"] = "plural"
}

local function infer_forms(forms)
    for _, section in pairs(forms) do
        local genders = (section["m_n_abs"] and section["f_n_abs"]) and {"m_", "f_"} or {""}
        for _, gender in ipairs(genders) do
            local is_plural = false
            if section["number"] == "p" then
                is_plural = true
            end
            if not section[gender .. "a_abs"] then
                section[gender .. "a_abs"] = nom.accusative_form(section[gender .. "n_abs"])
            end
            if not section[gender .. "n_con"] then
                section[gender .. "n_con"] = nom.construct_form(section[gender .. "n_abs"])
            end
            if not section[gender .. "a_con"] then
                section[gender .. "a_con"] = nom.accusative_form(section[gender .. "a_abs"])
            end
            local suf_base = section[gender .. "n_abs"]
            if section[gender .. "suf_base"] then
                suf_base = section[gender .. "suf_base"]
            end
            local acc_suf_base = suf_base
            if section[gender .. "acc_suf_base"] then
                acc_suf_base = section[gender .. "acc_suf_base"]
            end
            for _, pronoun in ipairs(pronouns) do
                section[gender .. "n_" .. pronoun] = nom.with_pronominal_suffix(suf_base, pronoun, is_plural)
                section[gender .. "a_" .. pronoun] = nom.with_pronominal_suffix(acc_suf_base, pronoun, is_plural, true)
            end
        end
    end
end

local table_top = [===[<div><div class="NavFrame" style="display:inline-block;min-width:30em">
<div class="NavHead" align="left" style="min-width:30em">{title}</div>
<div class="NavContent" align="center" style="min-width:30em">
{| class="wikitable inflection-table" style="text-align:center;margin:0"
|-
! rowspan="2" | Number
! rowspan="2" colspan="2" | State
! colspan="2" | Case
|-
! nominative/genitive
! accusative
]===]

local table_top_gender = [===[<div><div class="NavFrame" style="display:inline-block;min-width:30em">
<div class="NavHead" align="left" style="min-width:30em">{title}</div>
<div class="NavContent" align="center" style="min-width:30em">
{| class="wikitable inflection-table" style="text-align:center;margin:0"
|-
! rowspan="4" | Number
! rowspan="4" colspan="2" | State
! colspan="4" | Gender
|-
! colspan="2" | masculine
! colspan="2" | feminine
|-
! colspan="2" | Case
! colspan="2" | Case
|-
! nominative/genitive
! accusative
! nominative/genitive
! accusative
]===]

local table_section_start = [===[|- style="border-block-start: 2px black solid;"
! rowspan="12" | {number}
! colspan="2" | absolute
| {abs}
|-
! colspan="2" | construct
| {con}
|-
! rowspan="10" style="writing-mode: vertical-rl" | With possessive pronouns
]===]

local table_section_start_gender = [===[|- style="border-block-start: 2px black solid;"
! rowspan="12" | {number}
! colspan="2" | absolute
| {m_abs}
| {f_abs}
|-
! colspan="2" | construct
| {m_con}
| {f_con}
|-
! rowspan="10" style="writing-mode: vertical-rl" | With possessive pronouns
]===]

local table_section_row = [===[! {header}
| {cell}
]===]

local table_section_row_gender = [===[! {header}
| {m_cell}
| {f_cell}
]===]

local table_bottom = [===[|}
</div></div></div>]===]

local table_cell_merged = [===[colspan="2" | {form} ]===]
local table_cell_separated = [===[{form_n} 
| {form_a} ]===]

local function make_link(x)
    if x == "-" then
        return "—" -- m-dash
    else
        return m_links.full_link({
            lang = lang,
            term = x,
            allow_self_link = false
        })
    end
end

local function make_cell(nom, acc)
    if nom == acc then
        return m_strutils.format(table_cell_merged, {
            ["form"] = nom
        })
    else
        return m_strutils.format(table_cell_separated, {
            ["form_n"] = nom,
            ["form_a"] = acc
        })
    end
end

local function make_table(forms)
    local gendered = forms[1]["m_n_abs"] and forms[1]["f_n_abs"]
    local title = ""
    if gendered then
        title = "Declension of " .. make_link(forms[1]["m_n_abs"]) .. " / " .. make_link(forms[1]["f_n_abs"])
    else
        title = "Declension of " .. make_link(forms[1]["n_abs"])
    end
    local output = {}
    table.insert(output, m_strutils.format(gendered and table_top_gender or table_top, {
        ["title"] = title
    }))
    for _, section in ipairs(forms) do
        if gendered then
            local section_start = {
                ["number"] = numbers[section["number"]],
                ["m_abs"] = make_cell(make_link(section["m_n_abs"] or "-"), make_link(section["m_a_abs"] or "-")),
                ["m_con"] = make_cell(make_link(section["m_n_con"] or "-"), make_link(section["m_a_con"] or "-")),
                ["f_abs"] = make_cell(make_link(section["f_n_abs"] or "-"), make_link(section["f_a_abs"] or "-")),
                ["f_con"] = make_cell(make_link(section["f_n_con"] or "-"), make_link(section["f_a_con"] or "-"))
            }
            table.insert(output, m_strutils.format(table_section_start_gender, section_start))
        else
            local section_start = {
                ["number"] = numbers[section["number"]],
                ["abs"] = make_cell(make_link(section["n_abs"] or "-"), make_link(section["a_abs"] or "-")),
                ["con"] = make_cell(make_link(section["n_con"] or "-"), make_link(section["a_con"] or "-"))
            }
            table.insert(output, m_strutils.format(table_section_start, section_start))
        end
        local rows = {}
        for _, pronoun in ipairs(pronouns) do
            local row_data = {
                ["header"] = pronoun
            }
            for _, gender in ipairs(gendered and {"m_", "f_"} or {""}) do
                local nom
                local form_name = gender .. "n_" .. pronoun
                if type(section[form_name]) == "table" then
                    local links = {}
                    for _, form in ipairs(section[form_name]) do
                        table.insert(links, make_link(form or "-"))
                    end
                    nom = table.concat(links, "; ")
                else
                    nom = make_link(section[form_name] or "-")
                end
                local acc
                form_name = gender .. "a_" .. pronoun
                if type(section[form_name]) == "table" then
                    local links = {}
                    for _, form in ipairs(section[form_name]) do
                        table.insert(links, make_link(form or "-"))
                    end
                    acc = table.concat(links, "; ")
                else
                    acc = make_link(section[form_name] or "-")
                end
                row_data[gender .. "cell"] = make_cell(nom, acc)
            end
            table.insert(rows, m_strutils.format(gendered and table_section_row_gender or table_section_row, row_data))
        end
        table.insert(output, table.concat(rows, "\n|-\n"))
    end
    table.insert(output, m_strutils.format(table_bottom, {}))
    return table.concat(output)
end

local function process_args(args)
    for key, val in pairs(args) do
        val = mw.text.trim(val)
        if val == "" then
            val = nil
        end
        args[key] = val
    end

    local forms = {}

    local section = {
        ["number"] = "s"
    }

    local gendered = args["m"] and args["f"]
    local genders = gendered and {"m_", "f_"} or {""}

    for _, gender in ipairs(genders) do
        local base_form = gendered and args[string.sub(gender, 1, 1)] or args[1]
        section[gender .. "n_abs"] = base_form
        if args[gender .. "sb"] then
            section[gender .. "suf_base"] = args[gender .. "sb"]
        end
        if args[gender .. "asb"] then
            section[gender .. "acc_suf_base"] = args[gender .. "asb"]
        end
    end
    table.insert(forms, section)

    for i = 1, 9 do
        local section = {
            ["number"] = "p"
        }
        local plural_found = false
        local n = (i == 1) and "" or tostring(i)
        for _, gender in ipairs(genders) do
            if args[gender .. "pl" .. n] then
                plural_found = true
                local form
                local base_form = gendered and args[string.sub(gender, 1, 1)] or args[1]
                if args[gender .. "pl" .. n] == "smp" then
                    form = nom.external_plural(base_form, "m")
                elseif args[gender .. "pl" .. n] == "sfp" then
                    form = nom.external_plural(base_form, "f")
                else
                    form = args[gender .. "pl" .. n]
                end
                section[gender .. "n_abs"] = form
                if args[gender .. "pl" .. n .. "_sb"] then
                    section[gender .. "suf_base"] = args[gender .. "pl" .. n .. "_sb"]
                end
                if args[gender .. "pl" .. n .. "_asb"] then
                    section[gender .. "acc_suf_base"] = args[gender .. "pl" .. n .. "_asb"]
                end
            end
        end
        if plural_found then
            table.insert(forms, section)
        end
    end
    return forms
end

function export.show(frame)
    local args = frame:getParent().args
    PAGENAME = mw.title.getCurrentTitle().text
    NAMESPACE = mw.title.getCurrentTitle().nsText

    local forms = nil
    forms = process_args(args)
    infer_forms(forms)
    return make_table(forms)
end

return export