"इस मॉड्यूल हेतु प्रलेख मॉड्यूल:User:Dragonoid76/sa-verb/doc पर बनाया जा सकता है"

local export = {}

local m_para = require("Module:parameters")
local m_links = require("Module:links")
local m_scripts = require("Module:scripts")
local m_utils = require("Module:utilities")
local lang = require("Module:languages").getByCode("sa")
local m_script_utils = require("Module:script utilities")
local sa_verb_data = require("Module:User:Dragonoid76/sa-verb/data")
local SLP_to_IAST = require("Module:sa-utilities/translit/SLP1-to-IAST")
local sa_utils_translit = require("Module:sa-utilities/translit")
local PAGENAME = mw.title.getCurrentTitle().text

local gsub = mw.ustring.gsub
local match = mw.ustring.match
local split = mw.text.split
local tenses = sa_verb_data.tenses

local sa_utils = require("Module:sa-utilities")

local function not_has_voice(args, voice)
    return (voice == "av" and not match(args.n, "a")) or (voice == "mv" and not match(args.n, "m")) or
        (voice == "pv" and not match(args.n, "p"))
end

local function add_to_forms(forms, args, stem, es, q)
    if q == nil then
        q = 0
    end
    if type(es) == "string" then
        es = {es}
    end
    for i, e in ipairs(es) do
        local note
        if type(e) == "table" then
            note = e.note
            e = e[1]
        end

        local form = ""
        if args.auto_sandhi == true then
            if type(e) == "function" then
                form = e(stem, true)
            else
                form =
                    sa_utils.internal_sandhi(
                    {
                        stem = stem,
                        ending = e,
                        non_final = true
                    }
                )
            end
        else
            if type(e) == "function" then
                form = e(stem, false)
            else
                form = stem .. e
            end
        end
        local skip
        for _, preexisting in ipairs(forms) do
            if preexisting == form then
                skip = true
            end
        end
        if not skip then
            forms[q + i] = form
            if note then
                forms["note" .. (q + i)] = note
            end
        end
    end
end

local function conjugate_inner(args, data, strong_lemma, weak_lemma, passive_lemma, mood, voice, is_thematic)
    local t = args.tense
    local tense_data = tenses[t]

    local endings = {""}
    if mood ~= "part" then
        endings = {"1-s", "2-s", "3-s", "1-d", "2-d", "3-d", "1-p", "2-p", "3-p"}
    end
    for _, ending in ipairs(endings) do
        local tag = t .. "-" .. mood .. "-" .. voice
        if mood ~= "part" then
            tag = tag .. "-" .. ending
        end
        if not_has_voice(args, voice) then
            data.forms[tag] = nil
        else
            local stem = tense_data.get_stem(strong_lemma, weak_lemma, passive_lemma, mood, voice, ending, is_thematic)
            local es = tense_data.get_endings(strong_lemma, weak_lemma, passive_lemma, mood, voice, ending, is_thematic)
            if es == nil then
                data.forms[tag] = nil
            else
                local forms = data.forms[tag] or {}
                add_to_forms(forms, args, stem, es, #forms)
                data.forms[tag] = forms
            end
        end
    end
end

local function conjugate(args, data)
    local strong_lemmas = args.lemma
    local weak_lemmas = args[3] or nil
    local passive_lemmas = args[4] or nil
    local is_thematic = (weak_lemmas == nil)

    local t = args.tense
    local tense_data = tenses[t]

    local mood_pairs = tense_data.moods
    if t == "nonf" then
        local strong_lemmas_table = split(strong_lemmas, ",")
        if strong_lemmas_table[2] then
            error("cannot handle multiple stems in the non-finite table - override values independently")
        end
        local infinitive = strong_lemmas_table[1]
        local weak_lemmas_table = {[1] = ""}
        if weak_lemmas ~= nil then
            weak_lemmas_table = split(weak_lemmas, ",")
        end
        if weak_lemmas_table[2] then
            error("cannot handle multiple stems in the non-finite table - override values independently")
        end
        local part = weak_lemmas_table[1]
        if part == "" then
            part = nil
        end
        for _, mood_pair in ipairs(mood_pairs) do
            local mood, _ = mood_pair[1], mood_pair[2]
            local tag = t .. "-" .. mood

            local stem = tense_data.get_stem(infinitive, part, mood, is_thematic)
            local es = tense_data.get_endings(infinitive, part, mood, is_thematic)
            if es == nil then
                data.forms[tag] = nil
            else
                local forms = data.forms[tag] or {}
                add_to_forms(forms, args, stem, es, #forms)
                data.forms[tag] = forms
            end
        end
        return
    end

    local voices = tense_data.voices

    for _, mood_pair in ipairs(mood_pairs) do
        local mood, mood_name = mood_pair[1], mood_pair[2]
        for _, voice in ipairs(voices) do
            local strong_lemmas_table = split(strong_lemmas, ",")
            local weak_lemmas_table = {[1] = ""}
            if weak_lemmas ~= nil then
                weak_lemmas_table = split(weak_lemmas, ",")
            end
            local passive_lemmas_table = {[1] = ""}
            if passive_lemmas ~= nil then
                passive_lemmas_table = split(passive_lemmas, ",")
            end
            for _, strong_lemma in ipairs(strong_lemmas_table) do
                for _, weak_lemma in ipairs(weak_lemmas_table) do
                    if weak_lemma == "" then
                        weak_lemma = nil
                    end
                    for _, passive_lemma in ipairs(passive_lemmas_table) do
                        if passive_lemma == "" then
                            passive_lemma = nil
                        end
                        conjugate_inner(args, data, strong_lemma, weak_lemma, passive_lemma, mood, voice, is_thematic)
                    end
                end
            end
        end
    end
end

local accent = "[/\\]"

local super_nums = {
    [1] = "¹",
    [2] = "²",
    [3] = "³",
    [4] = "⁴",
    [5] = "⁵",
    [6] = "⁶",
    [7] = "⁷",
    [8] = "⁸",
    [9] = "⁹",
    [0] = "⁰"
}

local function to_super(num)
    local annotation = gsub(num, ".", super_nums)
    return annotation
end

local function get_form_note_tags(form_notes, data)
    local output = {}
    if type(form_notes) ~= "table" then
        form_notes = {form_notes}
    end
    for _, form_note in ipairs(form_notes) do
        if type(data.form_notes[form_note]) ~= "number" then
            table.insert(data.form_notes_out, form_note)
            data.form_notes[form_note] = #data.form_notes_out
        end
        table.insert(output, to_super(data.form_notes[form_note]))
    end
    return table.concat(output)
end

local function make_header(args, data, title_models, sc_cache)
    local width = 70
    local title = tenses[args.tense].name .. ": " .. title_models

    local header = {
        '{| class="inflection-table vsSwitcher" data-toggle-category="inflection" style="background:#F9F9F9; text-align:center; border: 1px solid #CCC; width: ' ..
            width .. 'em"\n'
    }
    table.insert(header, '|- style="background: #d9ebff;"\n')
    table.insert(
        header,
        '! class="vsToggleElement" style="text-align: left;" colspan="' .. args.colspan .. '" | ' .. title .. "\n"
    )
    table.insert(header, '|- class="vsHide"\n')
    table.insert(header, '! style="background:#eff7ff" rowspan="2" |\n')

    table.insert(header, '! colspan="3" style="background:#eff7ff" | Active\n')
    if args.tense == "pres" or args.tense == "impf" then
        table.insert(header, '! colspan="3" style="background:#eff7ff" | Middle\n')
        table.insert(header, '! colspan="3" style="background:#eff7ff" | Passive\n')
    else
        table.insert(header, '! colspan="3" style="background:#eff7ff" | Middle/Passive\n')
    end

    table.insert(header, '|- class="vsHide"\n')
    local size = 2
    if args.tense == "pres" or args.tense == "impf" then
        size = 3
    end
    for i = 1, size, 1 do
        table.insert(header, '! style="background:#eff7ff" | Singular\n')
        table.insert(header, '! style="background:#eff7ff" | Dual\n')
        table.insert(header, '! style="background:#eff7ff" | Plural\n')
    end

    return table.concat(header)
end

local function make_cell(args, data, tag, arg_tag, sc_cache, col_span)
    local forms, links, trs = {}, {}, {}
    if args[arg_tag] then
        forms = mw.text.split(sc_cache.tr(args[arg_tag]), "%s*[,]%s*")
    else
        forms = data.forms[tag]
    end

    if not forms then
        return "| -\n"
    end
    for i, form in ipairs(forms) do
        local form_note_tag = get_form_note_tags(forms["note" .. i] or {}, data)
        table.insert(
            links,
            m_links.full_link({term = sc_cache.reverse_tr(form), tr = "-", lang = lang, sc = sc_cache.sc}) ..
                form_note_tag
        )
        table.insert(trs, SLP_to_IAST.tr(form) .. form_note_tag)
    end

    return table.concat {
        "| ",
        "colspan = ",
        col_span,
        " | ",
        table.concat(links, " / "),
        "<br/>",
        m_script_utils.tag_translit(table.concat(trs, " / "), lang, "default", 'style="color: #888;"'),
        "\n"
    }
end

local function format_notes(args, data)
    local output = {
        '|- class="vsHide"',
        '| style="background-color:#eff7ff; font-style:italic;border-top:double #888;" | Notes',
        '| style="text-align:left;border-top:double #888;" colspan="' .. args.colspan .. '" |'
    }
    if #data.form_notes_out > 0 or #data.general_notes > 0 or #args.note > 0 then
        for i, form_note in ipairs(data.form_notes_out) do
            table.insert(output, "* " .. to_super(i) .. form_note)
        end
        for _, general_note in ipairs(data.general_notes) do
            table.insert(output, "* " .. general_note)
        end
        for _, note in ipairs(args.note) do
            table.insert(output, "* " .. note)
        end
        return table.concat(output, "\n") .. "\n"
    else
        return ""
    end
end

local function make_nonf_table(args, data, output, sc_cache)
    local moods = tenses[args.tense].moods
    local title_models = ""
        local arg_before = false
        local tag = args.tense .. "-" .. moods[1][1]
        local arg_tag = moods[1][1]
        local forms
        if args[arg_tag] then
            forms = mw.text.split(sc_cache.tr(args[arg_tag]), "%s*[,]%s*")
        else
            forms = data.forms[tag]
        end
        for i, title_lem in ipairs(forms) do
            if i > 1 then
                title_models = title_models .. " or "
            end
            title_models =
                title_models ..
                m_links.full_link(
                    {
                        term = nil,
                        alt = sc_cache.reverse_tr(title_lem),
                        tr = SLP_to_IAST.tr(title_lem),
                        lang = lang,
                        sc = sc_cache.sc
                    }
                )
        end
        local title = tenses[args.tense].name .. ": " .. title_models
        table.insert(
            output,
            '{| class="inflection-table vsSwitcher" data-toggle-category="inflection" style="background:#F9F9F9; text-align:center; border: 1px solid #CCC; width: 40em"\n'
        )
        table.insert(output, '|- style="background: #d9ebff;"\n')
        table.insert(output, '! class="vsToggleElement" style="text-align: left;" colspan="4" | ' .. title .. "\n")
        table.insert(output, '|- class="vsHide"\n')
        table.insert(output, '! style="background:#eff7ff" |\n')

        table.insert(output, '! colspan="2" style="background:#eff7ff" | Undeclinable\n')
        table.insert(output, '|- class="vsHide"\n')
        for _, mood_pair in ipairs(moods) do
            local mood, mood_name = mood_pair[1], mood_pair[2]
            if mood == "gerundive_mn" then
                table.insert(output, '! style="background:#eff7ff" |\n')
                table.insert(output, '! colspan="2" style="background:#eff7ff" | Participles\n')
                table.insert(output, '|- class="vsHide"\n')
            end
            table.insert(output, '! style="background-color:#eff7ff;" | ' .. mood_name .. "\n")
            local tag = args.tense .. "-" .. mood
            local arg_tag = mood
            table.insert(output, make_cell(args, data, tag, arg_tag, sc_cache, 1))
            table.insert(output, '|- class="vsHide"\n')
        end
        table.insert(output, format_notes(args, data))
        table.insert(output, "|}")
        if not args.nocat and #data.categories > 0 then
            table.insert(output, m_utils.format_categories(data.categories, lang))
        end
        return output
end

local function make_voice(title_models, args, data, sc_cache, voice)
    local moods = tenses[args.tense].moods
    local tag = args.tense .. "-" .. moods[1][1] .. "-" .. voice .. "-3-s"
    local arg_tag = moods[1][1] .. "_" .. voice .. "_3_s"
    local forms
    if args[arg_tag] then
        forms = mw.text.split(sc_cache.tr(args[arg_tag]), "%s*[,]%s*")
    else
        forms = data.forms[tag]
    end
    if #title_models > 0 then
        table.insert(title_models, ", ")
    end
    for i, title_lem in ipairs(forms) do
        if i > 1 then
            table.insert(title_models, " or ")
        end
        table.insert(title_models, 
            m_links.full_link(
                {
                    term = nil,
                    alt = sc_cache.reverse_tr(title_lem),
                    tr = SLP_to_IAST.tr(title_lem),
                    lang = lang,
                    sc = sc_cache.sc
                }
            )
        )
    end
end

local function make_table_verb(args, data, output, sc_cache)
    local moods = tenses[args.tense].moods

    if args.tense == "nonf" then
        return make_nonf_table(args, data, output, sc_cache)
    end

    local voices = tenses[args.tense].voices
    local numbers = {"s", "d", "p"}
    local people = {{"3", "Third"}, {"2", "Second"}, {"1", "First"}}

    local title_models = {}
    if match(args.n, "a") then
        make_voice(title_models, args, data, sc_cache, "av")
    end
    if match(args.n, "m") then
        make_voice(title_models, args, data, sc_cache, "mv")
    end
    if match(args.n, "p") then
        make_voice(title_models, args, data, sc_cache, "pv")
    end

    table.insert(output, make_header(args, data, table.concat(title_models, ""), sc_cache))
    table.insert(output, '|- class="vsHide"\n')

    for _, mood_pair in ipairs(moods) do
        local mood, mood_name = mood_pair[1], mood_pair[2]
        table.insert(output, '! style="background:#eff7ff" colspan="' .. args.colspan .. '" | ' .. mood_name .. "\n")
        table.insert(output, '|- class="vsHide"\n')
        if mood == "part" then
            table.insert(output, '| style="background-color:#eff7ff; font-style:italic;" | \n')
            for _, voice in ipairs(voices) do
                local tag = args.tense .. "-" .. mood .. "-" .. voice
                local arg_tag = mood .. "_" .. voice
                table.insert(output, make_cell(args, data, tag, arg_tag, sc_cache, 3))
            end
        else
            for i, person_pair in ipairs(people) do
                local person, person_name = person_pair[1], person_pair[2]
                table.insert(output, '| style="background-color:#eff7ff; font-style:italic;" | ' .. person_name .. "\n")
                for _, voice in ipairs(voices) do
                    for _, number in ipairs(numbers) do
                        local tag = args.tense .. "-" .. mood .. "-" .. voice .. "-" .. person .. "-" .. number
                        local arg_tag = mood .. "_" .. voice .. "_" .. person .. "_" .. number
                        table.insert(output, make_cell(args, data, tag, arg_tag, sc_cache, 1))
                    end
                end
                table.insert(output, '|- class="vsHide"\n')
            end
        end
    end

    table.insert(output, format_notes(args, data))
    table.insert(output, "|}")
    if not args.nocat and #data.categories > 0 then
        table.insert(output, m_utils.format_categories(data.categories, lang))
    end
    return output
end

local function get_sc_details(args)
    local sc, scCode
    if args.sc then
        sc = m_scripts.getByCode(args.sc)
        scCode = args.sc
    else
        sc = m_scripts.findBestScript(args.lemma, lang)
        scCode = sc:getCode()
        if scCode == "None" then
            sc = m_scripts.findBestScript(PAGENAME, lang)
            scCode = sc:getCode()
            if scCode == "None" then
                error("Script code was not specified or detected.")
            end
        end
    end

    local tr, reverse_tr = sa_utils_translit.retrieve_tr_modules(scCode)
    return {tr = tr, reverse_tr = reverse_tr, sc = sc, scCode = scCode}
end

function export.show(frame)
    local params = {
        lemma = {default = PAGENAME},
        n = {default = "amp"},
        sc = {},
        [1] = {required = true},
        [2] = {alias_of = "lemma"},
        [3] = {},
        [4] = {},
        auto_sandhi = {default = true},
        set = {default = true}, -- set vs anit roots
        note = {list = true}
    }
    local numbers = {"1_s", "2_s", "3_s", "1_d", "2_d", "3_d", "1_p", "2_p", "3_p"}
    for t, tense_data in pairs(tenses) do
        for _, mood_pair in pairs(tense_data.moods) do
            if t == "nonf" then
                params[mood_pair[1]] = {}
            else
                for _, voice in ipairs(tense_data.voices) do
                    for _, number in ipairs(numbers) do
                        params[mood_pair[1] .. "_" .. voice .. "_" .. number] = {}
                    end
                end
            end
        end
    end
    local data = {
        forms = {},
        categories = {},
        decl_type = nil,
        form_notes = {},
        form_notes_out = {},
        general_notes = {}
    }
    local args = m_para.process(frame:getParent().args, params)

    local sc_cache = get_sc_details(args)
    args.lemma = sc_cache.tr(args.lemma)

    local output = {}
    args.tense = args[1]
    conjugate(args, data)
    args.colspan = 7
    if args.tense == "pres" or args.tense == "impf" then
        args.colspan = 10
    else
        args.n = gsub(args.n, "p", "")
    end
    make_table_verb(args, data, output, sc_cache)
    return table.concat(output)
end

return export