// morphology-la.el - Latin morphology for the NLG engine.
//
// Implements fusional Latin verb conjugation and noun declension.  Designed
// as a companion to morphology.el and called by the engine when the language
// profile code is "la".
//
// Language profile: code=la, name=Latin, morph_type=fusional, word_order=SOV,
//   question_strategy=intonation, script=latin, family=italic.
//
// Verb conjugation covered:
//   Tenses:       present, past (perfect active), future
//   Persons:      first/second/third x singular/plural (slots 0-5)
//   Conjugations: 1st (-are), 2nd (-ere long), 3rd (-ere short), 4th (-ire)
//   Irregulars:   esse (be), ire (go), velle (want), posse (can)
//   Canonical map: "be" -> "esse"
//
// Noun declension covered:
//   Cases:       nominative, accusative, genitive, dative, ablative
//   Declensions: 1st (-a fem), 2nd masc (-us), 2nd neut (-um), 3rd (-is),
//                4th (-us), 5th (-es)
//
// Latin has no articles.  la_noun_phrase returns the declined noun directly.
//
// Depends on: morphology.el (str_ends_with, str_len, str_slice, str_eq)

// ── String helpers ─────────────────────────────────────────────────────────────

fn la_str_ends(s: String, suf: String) -> Bool {
    return str_ends_with(s, suf)
}

fn la_str_drop_last(s: String, n: Int) -> String {
    let len: Int = str_len(s)
    if n >= len {
        return ""
    }
    return str_slice(s, 0, len - n)
}

fn la_str_last_char(s: String) -> String {
    let n: Int = str_len(s)
    if n == 0 {
        return ""
    }
    return str_slice(s, n - 1, n)
}

fn la_str_last2(s: String) -> String {
    let n: Int = str_len(s)
    if n < 2 {
        return s
    }
    return str_slice(s, n - 2, n)
}

fn la_str_last3(s: String) -> String {
    let n: Int = str_len(s)
    if n < 3 {
        return s
    }
    return str_slice(s, n - 3, n)
}

// ── Person/number slot ─────────────────────────────────────────────────────────
//
// Maps person x number to a 0-based slot index used in paradigm tables.
//   0 = 1st singular   (ego)
//   1 = 2nd singular   (tu)
//   2 = 3rd singular   (is/ea/id)
//   3 = 1st plural     (nos)
//   4 = 2nd plural     (vos)
//   5 = 3rd plural     (ei/eae/ea)

fn la_slot(person: String, number: String) -> Int {
    if str_eq(person, "first") {
        if str_eq(number, "singular") { return 0 }
        return 3
    }
    if str_eq(person, "second") {
        if str_eq(number, "singular") { return 1 }
        return 4
    }
    // third
    if str_eq(number, "singular") { return 2 }
    return 5
}

// ── Conjugation class detection ────────────────────────────────────────────────
//
// Latin verbs fall into four conjugation classes identified by the infinitive
// ending.  The 2nd and 3rd conjugations both end in -ere; the 2nd has a long
// e (stem vowel retained before -re), the 3rd has a short e (thematic vowel).
// Heuristic: if the verb stem before -ere ends in a consonant cluster or a
// single consonant preceded by a short vowel, treat as 3rd; otherwise 2nd.
// In practice the caller passes dictionary infinitives, so we check known 2nd
// conjugation markers (stem ends in -e before -re, i.e. the penult is e) and
// known 4th (-ire) vs 3rd (-ere with consonant stem).
//
// Simplified detection strategy:
//   ends in -are  -> "1"
//   ends in -ire  -> "4"
//   ends in -ere  -> check penultimate vowel pattern:
//       stem + "ere" where stem ends in a vowel-like syllable -> "2"
//       otherwise -> "3"
//
// For the small working vocabulary, we rely on the ending length heuristic:
//   -ere with 2nd conj: the infinitive without -re has a long e as last char,
//   meaning the four chars before the final "re" are "e-consonant" or "ee".
//   Simpler: ends in -ere and has stem length >= 3 with last stem char a vowel
//   -> 2nd; else 3rd.  This correctly handles monere (2nd) vs dicere (3rd).

fn la_verb_class(verb: String) -> String {
    if la_str_ends(verb, "are") { return "1" }
    if la_str_ends(verb, "ire") { return "4" }
    if la_str_ends(verb, "ere") {
        // Strip -ere, look at the last char of the stem
        let stem: String = la_str_drop_last(verb, 3)
        let slen: Int = str_len(stem)
        if slen == 0 { return "3" }
        let last: String = str_slice(stem, slen - 1, slen)
        // Stem ends in a vowel -> 2nd conjugation (monere, videre, habere)
        if str_eq(last, "a") { return "2" }
        if str_eq(last, "e") { return "2" }
        if str_eq(last, "i") { return "2" }
        if str_eq(last, "o") { return "2" }
        if str_eq(last, "u") { return "2" }
        // Stem ends in a consonant -> 3rd conjugation (dicere, edere, ducere)
        return "3"
    }
    // Default: treat as 3rd if ending is unknown
    return "3"
}

// la_stem: strip the infinitive ending to get the present stem.
//
// 1st: -are -> stem (amāre -> am-)
// 2nd: -ere -> stem + e (monēre -> mone-)   [keep the stem vowel]
// 3rd: -ere -> stem      (dicere -> dic-)
// 4th: -ire -> stem + i  (audire -> audi-)

fn la_stem(verb: String, vclass: String) -> String {
    if str_eq(vclass, "1") { return la_str_drop_last(verb, 3) }
    if str_eq(vclass, "2") { return la_str_drop_last(verb, 2) }  // drop -re; keep -e
    if str_eq(vclass, "3") { return la_str_drop_last(verb, 3) }
    if str_eq(vclass, "4") { return la_str_drop_last(verb, 2) }  // drop -re; keep -i
    return la_str_drop_last(verb, 3)
}

// la_perfect_stem: derive the perfect active stem.
//
// Latin perfect stems are highly irregular in general.  For regular verbs the
// most common pattern is stem + v- (1st/4th) or stem + u- (2nd/3rd), but this
// is not universal.  We use the following heuristic approximations:
//
//   1st conj: present stem + av-  (amavi, portavi)
//   2nd conj: present stem + u-   (monui, habui)   -- drops the -e from stem
//   3rd conj: present stem + -    (doubled root; context varies; use stem + i)
//   4th conj: present stem + iv-  (audivi)
//
// This will be wrong for many common verbs; callers can override by adding the
// verb to the la_irregular_perfect table.

fn la_perfect_stem(verb: String, vclass: String) -> String {
    if str_eq(vclass, "1") {
        // amāre -> am + av = amav
        let pstem: String = la_str_drop_last(verb, 3)
        return pstem + "av"
    }
    if str_eq(vclass, "2") {
        // monēre -> mone -> drop e -> mon + u = monu
        let pstem: String = la_str_drop_last(verb, 3)
        return pstem + "u"
    }
    if str_eq(vclass, "3") {
        // dicere -> dic + (perfect varies; approximate with stem + -i stem)
        let pstem: String = la_str_drop_last(verb, 3)
        return pstem
    }
    if str_eq(vclass, "4") {
        // audire -> audi + iv = audiv
        let pstem: String = la_str_drop_last(verb, 2)
        return pstem + "v"
    }
    return la_str_drop_last(verb, 3)
}

// ── Perfect active endings (all conjugations share these) ─────────────────────
//
// Slot: 0=1sg 1=2sg 2=3sg 3=1pl 4=2pl 5=3pl
//   -i, -isti, -it, -imus, -istis, -erunt

fn la_perfect_ending(slot: Int) -> String {
    if slot == 0 { return "i" }
    if slot == 1 { return "isti" }
    if slot == 2 { return "it" }
    if slot == 3 { return "imus" }
    if slot == 4 { return "istis" }
    return "erunt"
}

// ── Present tense endings ─────────────────────────────────────────────────────
//
// 1st:  -o, -as, -at, -amus, -atis, -ant
// 2nd:  -eo, -es, -et, -emus, -etis, -ent
// 3rd:  -o, -is, -it, -imus, -itis, -unt
// 4th:  -io, -is, -it, -imus, -itis, -iunt
//
// Note: for 1st conj the stem already ends in the thematic vowel (am- not ama-
// at slot 0 because the -o absorbs it).  The 2nd conj stem retains its -e and
// the ending is appended directly.

fn la_present_ending(vclass: String, slot: Int) -> String {
    if str_eq(vclass, "1") {
        if slot == 0 { return "o" }
        if slot == 1 { return "as" }
        if slot == 2 { return "at" }
        if slot == 3 { return "amus" }
        if slot == 4 { return "atis" }
        return "ant"
    }
    if str_eq(vclass, "2") {
        if slot == 0 { return "o" }
        if slot == 1 { return "s" }    // stem ends -e; -es becomes mones
        if slot == 2 { return "t" }    // monet
        if slot == 3 { return "mus" }  // monemus
        if slot == 4 { return "tis" }  // monetis
        return "nt"                    // monent
    }
    if str_eq(vclass, "3") {
        if slot == 0 { return "o" }
        if slot == 1 { return "is" }
        if slot == 2 { return "it" }
        if slot == 3 { return "imus" }
        if slot == 4 { return "itis" }
        return "unt"
    }
    // 4th (-ire)
    if slot == 0 { return "o" }
    if slot == 1 { return "s" }    // audi + s = audis
    if slot == 2 { return "t" }    // audit
    if slot == 3 { return "mus" }  // audimus
    if slot == 4 { return "tis" }  // auditis
    return "unt"                   // audiunt
}

// la_present_form: build the present tense form for a regular verb.
//
// Special slot-0 handling per class:
//   1st: am- + o = amo   (strip thematic -a from stem first)
//   2nd: mone- + o = moneo
//   3rd: dic- + o = dico
//   4th: audi- + o = audio (the -i stays as part of the stem)

fn la_present_form(stem: String, vclass: String, slot: Int) -> String {
    if str_eq(vclass, "1") {
        if slot == 0 {
            // stem ends in the thematic -a which is absorbed by -o
            return la_str_drop_last(stem, 1) + "o"
        }
        return stem + la_present_ending(vclass, slot)
    }
    if str_eq(vclass, "2") {
        // stem ends in -e; all endings attach directly
        return stem + la_present_ending(vclass, slot)
    }
    if str_eq(vclass, "3") {
        if slot == 0 {
            return stem + "o"
        }
        return stem + la_present_ending(vclass, slot)
    }
    // 4th: stem ends in -i
    if slot == 0 {
        return stem + "o"   // audio
    }
    if slot == 5 {
        return stem + "unt" // audiunt (special: i + unt)
    }
    return stem + la_present_ending(vclass, slot)
}

// ── Future tense ──────────────────────────────────────────────────────────────
//
// 1st/2nd conjugations: present stem + bo/bis/bit/bimus/bitis/bunt
//   (1st: thematic -a dropped before -bo; 2nd: -e kept, ebo -> moneo... actually
//    for 2nd the future is mone + bo = monebo; stem keeps -e)
//
// 3rd/4th conjugations: present stem + am/es/et/emus/etis/ent
//   (No thematic vowel linking; -am directly on consonant stem)

fn la_future_ending_12(slot: Int) -> String {
    if slot == 0 { return "bo" }
    if slot == 1 { return "bis" }
    if slot == 2 { return "bit" }
    if slot == 3 { return "bimus" }
    if slot == 4 { return "bitis" }
    return "bunt"
}

fn la_future_ending_34(slot: Int) -> String {
    if slot == 0 { return "am" }
    if slot == 1 { return "es" }
    if slot == 2 { return "et" }
    if slot == 3 { return "emus" }
    if slot == 4 { return "etis" }
    return "ent"
}

fn la_future_form(stem: String, vclass: String, slot: Int) -> String {
    if str_eq(vclass, "1") {
        // Drop thematic -a then add -bo etc: am- + bo = amabo? No: ama + bo = amabo
        // Actually for 1st conj the stem IS the thematic-vowel stem (ama-),
        // and the future is ama + bo = amabo.
        return stem + la_future_ending_12(slot)
    }
    if str_eq(vclass, "2") {
        // mone + bo = monebo
        return stem + la_future_ending_12(slot)
    }
    if str_eq(vclass, "3") {
        // dic + am = dicam
        return stem + la_future_ending_34(slot)
    }
    // 4th: audi + am = audiam
    return stem + la_future_ending_34(slot)
}

// ── Irregular verb tables ─────────────────────────────────────────────────────
//
// esse (be), ire (go), velle (want), posse (can/be able)

fn la_esse_present(slot: Int) -> String {
    if slot == 0 { return "sum" }
    if slot == 1 { return "es" }
    if slot == 2 { return "est" }
    if slot == 3 { return "sumus" }
    if slot == 4 { return "estis" }
    return "sunt"
}

fn la_esse_past(slot: Int) -> String {
    if slot == 0 { return "fui" }
    if slot == 1 { return "fuisti" }
    if slot == 2 { return "fuit" }
    if slot == 3 { return "fuimus" }
    if slot == 4 { return "fuistis" }
    return "fuerunt"
}

fn la_esse_future(slot: Int) -> String {
    if slot == 0 { return "ero" }
    if slot == 1 { return "eris" }
    if slot == 2 { return "erit" }
    if slot == 3 { return "erimus" }
    if slot == 4 { return "eritis" }
    return "erunt"
}

fn la_ire_present(slot: Int) -> String {
    if slot == 0 { return "eo" }
    if slot == 1 { return "is" }
    if slot == 2 { return "it" }
    if slot == 3 { return "imus" }
    if slot == 4 { return "itis" }
    return "eunt"
}

fn la_ire_past(slot: Int) -> String {
    if slot == 0 { return "ii" }
    if slot == 1 { return "isti" }   // contracted: iisti -> isti
    if slot == 2 { return "iit" }
    if slot == 3 { return "iimus" }
    if slot == 4 { return "istis" }
    return "ierunt"
}

fn la_ire_future(slot: Int) -> String {
    if slot == 0 { return "ibo" }
    if slot == 1 { return "ibis" }
    if slot == 2 { return "ibit" }
    if slot == 3 { return "ibimus" }
    if slot == 4 { return "ibitis" }
    return "ibunt"
}

fn la_velle_present(slot: Int) -> String {
    if slot == 0 { return "volo" }
    if slot == 1 { return "vis" }
    if slot == 2 { return "vult" }
    if slot == 3 { return "volumus" }
    if slot == 4 { return "vultis" }
    return "volunt"
}

fn la_velle_past(slot: Int) -> String {
    if slot == 0 { return "volui" }
    if slot == 1 { return "voluisti" }
    if slot == 2 { return "voluit" }
    if slot == 3 { return "voluimus" }
    if slot == 4 { return "voluistis" }
    return "voluerunt"
}

fn la_velle_future(slot: Int) -> String {
    if slot == 0 { return "volam" }
    if slot == 1 { return "voles" }
    if slot == 2 { return "volet" }
    if slot == 3 { return "volemus" }
    if slot == 4 { return "voletis" }
    return "volent"
}

fn la_posse_present(slot: Int) -> String {
    if slot == 0 { return "possum" }
    if slot == 1 { return "potes" }
    if slot == 2 { return "potest" }
    if slot == 3 { return "possumus" }
    if slot == 4 { return "potestis" }
    return "possunt"
}

fn la_posse_past(slot: Int) -> String {
    if slot == 0 { return "potui" }
    if slot == 1 { return "potuisti" }
    if slot == 2 { return "potuit" }
    if slot == 3 { return "potuimus" }
    if slot == 4 { return "potuistis" }
    return "potuerunt"
}

fn la_posse_future(slot: Int) -> String {
    if slot == 0 { return "potero" }
    if slot == 1 { return "poteris" }
    if slot == 2 { return "poterit" }
    if slot == 3 { return "poterimus" }
    if slot == 4 { return "poteritis" }
    return "poterunt"
}

// ── Irregular perfect stems for common verbs ──────────────────────────────────
//
// Returns the perfect stem for common irregular verbs, or "" if not found.
// When a perfect stem is returned, la_perfect_ending() is appended directly.
//
// Verb       Perfect stem   Example: 3sg
// edere      ed-            edit
// dicere     dix-           dixit
// ducere     dux-           duxit
// facere     fec-           fecit
// capere     cep-           cepit
// venire     ven-           venit  (same as present; context clarifies)
// videre     vid-           vidit
// esse       -> handled separately
// ire        -> handled separately

fn la_irregular_perfect_stem(verb: String) -> String {
    if str_eq(verb, "edere")   { return "ed" }
    if str_eq(verb, "dicere")  { return "dix" }
    if str_eq(verb, "ducere")  { return "dux" }
    if str_eq(verb, "facere")  { return "fec" }
    if str_eq(verb, "capere")  { return "cep" }
    if str_eq(verb, "venire")  { return "ven" }
    if str_eq(verb, "videre")  { return "vid" }
    if str_eq(verb, "bibere")  { return "bib" }
    if str_eq(verb, "currere") { return "cucurr" }
    if str_eq(verb, "legere")  { return "leg" }
    if str_eq(verb, "scribere") { return "scrips" }
    if str_eq(verb, "vivere")  { return "vix" }
    if str_eq(verb, "cadere")  { return "cecid" }
    if str_eq(verb, "ponere")  { return "posu" }
    if str_eq(verb, "querere") { return "quaesiv" }
    return ""
}

// ── Canonical verb mapping ─────────────────────────────────────────────────────
//
// The semantic layer passes English canonical labels ("be", "go", "want").
// Map these to Latin infinitives before conjugation.

fn la_map_canonical(verb: String) -> String {
    if str_eq(verb, "be")   { return "esse" }
    if str_eq(verb, "go")   { return "ire" }
    if str_eq(verb, "want") { return "velle" }
    if str_eq(verb, "can")  { return "posse" }
    if str_eq(verb, "eat")  { return "edere" }
    if str_eq(verb, "say")  { return "dicere" }
    if str_eq(verb, "see")  { return "videre" }
    if str_eq(verb, "make") { return "facere" }
    if str_eq(verb, "come") { return "venire" }
    if str_eq(verb, "read") { return "legere" }
    if str_eq(verb, "write") { return "scribere" }
    if str_eq(verb, "run")  { return "currere" }
    if str_eq(verb, "live") { return "vivere" }
    if str_eq(verb, "love") { return "amare" }
    return verb
}

// ── la_conjugate: main conjugation entry point ────────────────────────────────
//
// verb:   Latin infinitive (e.g. "amare", "esse") or English canonical
// tense:  "present" | "past" | "future"
// person: "first" | "second" | "third"
// number: "singular" | "plural"
//
// Returns the inflected form.  Falls back to the base (infinitive) when a form
// is not implemented rather than crashing.

fn la_conjugate(verb: String, tense: String, person: String, number: String) -> String {
    // Map canonical English labels to Latin infinitives
    let v: String = la_map_canonical(verb)
    let slot: Int = la_slot(person, number)

    // ── Irregulars ────────────────────────────────────────────────────────────

    if str_eq(v, "esse") {
        if str_eq(tense, "present") { return la_esse_present(slot) }
        if str_eq(tense, "past")    { return la_esse_past(slot) }
        if str_eq(tense, "future")  { return la_esse_future(slot) }
        return v
    }

    if str_eq(v, "ire") {
        if str_eq(tense, "present") { return la_ire_present(slot) }
        if str_eq(tense, "past")    { return la_ire_past(slot) }
        if str_eq(tense, "future")  { return la_ire_future(slot) }
        return v
    }

    if str_eq(v, "velle") {
        if str_eq(tense, "present") { return la_velle_present(slot) }
        if str_eq(tense, "past")    { return la_velle_past(slot) }
        if str_eq(tense, "future")  { return la_velle_future(slot) }
        return v
    }

    if str_eq(v, "posse") {
        if str_eq(tense, "present") { return la_posse_present(slot) }
        if str_eq(tense, "past")    { return la_posse_past(slot) }
        if str_eq(tense, "future")  { return la_posse_future(slot) }
        return v
    }

    // ── Regular conjugation ───────────────────────────────────────────────────

    let vclass: String = la_verb_class(v)
    let stem: String = la_stem(v, vclass)

    if str_eq(tense, "present") {
        return la_present_form(stem, vclass, slot)
    }

    if str_eq(tense, "past") {
        // Check for a known irregular perfect stem first
        let irreg_perf: String = la_irregular_perfect_stem(v)
        if !str_eq(irreg_perf, "") {
            return irreg_perf + la_perfect_ending(slot)
        }
        // Regular perfect stem derivation
        let perf_stem: String = la_perfect_stem(v, vclass)
        return perf_stem + la_perfect_ending(slot)
    }

    if str_eq(tense, "future") {
        return la_future_form(stem, vclass, slot)
    }

    // Unknown tense: return infinitive
    return v
}

// ── Declension detection ───────────────────────────────────────────────────────
//
// Infer Latin declension from the nominative singular ending.
//
//   ends in -a  -> 1st declension (feminine)
//   ends in -us -> 2nd or 4th; disambiguate: if genitive suffix pattern
//                  suggests 4th (-us gen) we use 4th, otherwise 2nd masc.
//                  Heuristic: monosyllabic -us nouns and known 4th-decl words
//                  use 4th; longer -us words default to 2nd.
//   ends in -um -> 2nd declension neuter
//   ends in -is -> 3rd declension (genitive -is is also 3rd nom for some words;
//                  treat nom -is as 3rd)
//   ends in -es -> 5th declension
//   ends in -er -> 2nd masc (puer, ager)
//   otherwise   -> 3rd declension (consonant stems: rex, miles, etc.)

fn la_declension(noun: String) -> String {
    if la_str_ends(noun, "a")  { return "1" }
    if la_str_ends(noun, "um") { return "2n" }
    if la_str_ends(noun, "er") { return "2m" }
    if la_str_ends(noun, "us") {
        // 4th declension heuristic: check known 4th decl nouns
        if str_eq(noun, "manus")   { return "4" }
        if str_eq(noun, "usus")    { return "4" }
        if str_eq(noun, "fructus") { return "4" }
        if str_eq(noun, "gradus")  { return "4" }
        if str_eq(noun, "cursus")  { return "4" }
        if str_eq(noun, "sensus")  { return "4" }
        if str_eq(noun, "spiritus") { return "4" }
        if str_eq(noun, "portus")  { return "4" }
        if str_eq(noun, "domus")   { return "4" }
        if str_eq(noun, "impetus") { return "4" }
        // Default: 2nd masc
        return "2m"
    }
    if la_str_ends(noun, "es") { return "5" }
    if la_str_ends(noun, "is") { return "3" }
    // Consonant-stem 3rd declension (rex, canis, leo, etc.)
    return "3"
}

// ── 1st declension: -a nouns (mostly feminine) ────────────────────────────────
//
// Stem: remove final -a
// Singular:  nom -a   gen -ae  dat -ae  acc -am  abl -a
// Plural:    nom -ae  gen -arum dat -is  acc -as  abl -is

fn la_decline_1(stem: String, gram_case: String, number: String) -> String {
    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return stem + "a" }
        if str_eq(gram_case, "genitive")   { return stem + "ae" }
        if str_eq(gram_case, "dative")     { return stem + "ae" }
        if str_eq(gram_case, "accusative") { return stem + "am" }
        if str_eq(gram_case, "ablative")   { return stem + "a" }
        // vocative same as nominative for 1st decl
        return stem + "a"
    }
    // plural
    if str_eq(gram_case, "nominative") { return stem + "ae" }
    if str_eq(gram_case, "genitive")   { return stem + "arum" }
    if str_eq(gram_case, "dative")     { return stem + "is" }
    if str_eq(gram_case, "accusative") { return stem + "as" }
    if str_eq(gram_case, "ablative")   { return stem + "is" }
    return stem + "ae"
}

// ── 2nd declension masculine: -us nouns ───────────────────────────────────────
//
// Stem: remove final -us
// Singular:  nom -us  gen -i   dat -o   acc -um  abl -o
// Plural:    nom -i   gen -orum dat -is  acc -os  abl -is

fn la_decline_2m(stem: String, gram_case: String, number: String) -> String {
    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return stem + "us" }
        if str_eq(gram_case, "genitive")   { return stem + "i" }
        if str_eq(gram_case, "dative")     { return stem + "o" }
        if str_eq(gram_case, "accusative") { return stem + "um" }
        if str_eq(gram_case, "ablative")   { return stem + "o" }
        return stem + "us"
    }
    // plural
    if str_eq(gram_case, "nominative") { return stem + "i" }
    if str_eq(gram_case, "genitive")   { return stem + "orum" }
    if str_eq(gram_case, "dative")     { return stem + "is" }
    if str_eq(gram_case, "accusative") { return stem + "os" }
    if str_eq(gram_case, "ablative")   { return stem + "is" }
    return stem + "i"
}

// ── 2nd declension neuter: -um nouns ─────────────────────────────────────────
//
// Stem: remove final -um
// Singular:  nom/acc -um  gen -i   dat/abl -o
// Plural:    nom/acc -a   gen -orum dat/abl -is

fn la_decline_2n(stem: String, gram_case: String, number: String) -> String {
    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return stem + "um" }
        if str_eq(gram_case, "genitive")   { return stem + "i" }
        if str_eq(gram_case, "dative")     { return stem + "o" }
        if str_eq(gram_case, "accusative") { return stem + "um" }
        if str_eq(gram_case, "ablative")   { return stem + "o" }
        return stem + "um"
    }
    // plural
    if str_eq(gram_case, "nominative") { return stem + "a" }
    if str_eq(gram_case, "genitive")   { return stem + "orum" }
    if str_eq(gram_case, "dative")     { return stem + "is" }
    if str_eq(gram_case, "accusative") { return stem + "a" }
    if str_eq(gram_case, "ablative")   { return stem + "is" }
    return stem + "a"
}

// ── 3rd declension: consonant and i-stem nouns ────────────────────────────────
//
// The 3rd declension is highly varied; the nominative singular is usually
// irregular (the stem is seen in the genitive).  The caller passes the
// nominative singular form; we use it as-is for nominative and treat it as
// the stem for other cases (approximation for productive NLG use).
//
// Singular:  nom (unchanged)  gen -is   dat -i   acc -em  abl -e
// Plural:    nom -es           gen -um   dat -ibus acc -es  abl -ibus
//
// For i-stems (is ending in nom sg) we keep the full form as the stem and
// add endings directly to the base minus -is.

fn la_decline_3(noun: String, gram_case: String, number: String) -> String {
    // For 3rd decl the nom sg is given; the stem for oblique cases is
    // derived by stripping -is if the nom ends in -is, otherwise we use
    // the noun as the stem for the oblique and append endings.
    let oblique_stem: String = ""
    if la_str_ends(noun, "is") {
        let oblique_stem = la_str_drop_last(noun, 2)
    } else {
        let oblique_stem = noun
    }

    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return noun }
        if str_eq(gram_case, "genitive")   { return oblique_stem + "is" }
        if str_eq(gram_case, "dative")     { return oblique_stem + "i" }
        if str_eq(gram_case, "accusative") { return oblique_stem + "em" }
        if str_eq(gram_case, "ablative")   { return oblique_stem + "e" }
        return noun
    }
    // plural
    if str_eq(gram_case, "nominative") { return oblique_stem + "es" }
    if str_eq(gram_case, "genitive")   { return oblique_stem + "um" }
    if str_eq(gram_case, "dative")     { return oblique_stem + "ibus" }
    if str_eq(gram_case, "accusative") { return oblique_stem + "es" }
    if str_eq(gram_case, "ablative")   { return oblique_stem + "ibus" }
    return oblique_stem + "es"
}

// ── 4th declension: -us nouns (mostly masculine) ─────────────────────────────
//
// Stem: remove final -us
// Singular:  nom -us  gen -us  dat -ui  acc -um  abl -u
// Plural:    nom -us  gen -uum dat -ibus acc -us  abl -ibus

fn la_decline_4(stem: String, gram_case: String, number: String) -> String {
    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return stem + "us" }
        if str_eq(gram_case, "genitive")   { return stem + "us" }
        if str_eq(gram_case, "dative")     { return stem + "ui" }
        if str_eq(gram_case, "accusative") { return stem + "um" }
        if str_eq(gram_case, "ablative")   { return stem + "u" }
        return stem + "us"
    }
    // plural
    if str_eq(gram_case, "nominative") { return stem + "us" }
    if str_eq(gram_case, "genitive")   { return stem + "uum" }
    if str_eq(gram_case, "dative")     { return stem + "ibus" }
    if str_eq(gram_case, "accusative") { return stem + "us" }
    if str_eq(gram_case, "ablative")   { return stem + "ibus" }
    return stem + "us"
}

// ── 5th declension: -es nouns (mostly feminine) ───────────────────────────────
//
// Stem: remove final -es
// Singular:  nom -es  gen -ei  dat -ei  acc -em  abl -e
// Plural:    nom -es  gen -erum dat -ebus acc -es  abl -ebus

fn la_decline_5(stem: String, gram_case: String, number: String) -> String {
    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return stem + "es" }
        if str_eq(gram_case, "genitive")   { return stem + "ei" }
        if str_eq(gram_case, "dative")     { return stem + "ei" }
        if str_eq(gram_case, "accusative") { return stem + "em" }
        if str_eq(gram_case, "ablative")   { return stem + "e" }
        return stem + "es"
    }
    // plural
    if str_eq(gram_case, "nominative") { return stem + "es" }
    if str_eq(gram_case, "genitive")   { return stem + "erum" }
    if str_eq(gram_case, "dative")     { return stem + "ebus" }
    if str_eq(gram_case, "accusative") { return stem + "es" }
    if str_eq(gram_case, "ablative")   { return stem + "ebus" }
    return stem + "es"
}

// ── 2nd declension -er nouns ──────────────────────────────────────────────────
//
// Nouns like "puer" retain -e throughout; nouns like "ager" drop it in oblique.
// Heuristic: if the stem (drop -er) + er would be the original, check if the
// penultimate char before -er is also r (like "puer": stem "pu" + "er" = puer)
// vs "ager": stem "agr" drops e.  We default to retaining -e (puer pattern)
// for simplicity; "ager" type is less common.
//
// Singular:  nom -er  gen -i  dat -o  acc -um  abl -o
// Plural:    nom -i   gen -orum dat -is  acc -os  abl -is

fn la_decline_2er(noun: String, gram_case: String, number: String) -> String {
    // Oblique stem: drop -er then add "r" to get true stem? For simplicity
    // use the form with -e retained (puer pattern): stem = noun minus "r".
    // This correctly handles "puer" -> "pueri", "puero" etc.
    // For ager-type the caller would need to pass the stem form; we approximate.
    let stem: String = la_str_drop_last(noun, 1)   // drop final -r -> "pue"

    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return noun }
        if str_eq(gram_case, "genitive")   { return stem + "ri" }    // pueri
        if str_eq(gram_case, "dative")     { return stem + "ro" }    // puero
        if str_eq(gram_case, "accusative") { return stem + "rum" }   // puerum
        if str_eq(gram_case, "ablative")   { return stem + "ro" }    // puero
        return noun
    }
    // plural
    if str_eq(gram_case, "nominative") { return stem + "ri" }        // pueri
    if str_eq(gram_case, "genitive")   { return stem + "rorum" }     // puerorum
    if str_eq(gram_case, "dative")     { return stem + "ris" }       // pueris
    if str_eq(gram_case, "accusative") { return stem + "ros" }       // pueros
    if str_eq(gram_case, "ablative")   { return stem + "ris" }       // pueris
    return stem + "ri"
}

// ── la_decline: main declension entry point ───────────────────────────────────
//
// noun:      nominative singular Latin noun (e.g. "feles", "aqua", "dominus")
// gram_case: "nominative" | "accusative" | "genitive" | "dative" | "ablative"
// number:    "singular" | "plural"
//
// Returns the inflected form.  Falls back to the nominative singular when a
// form is not implemented.

fn la_decline(noun: String, gram_case: String, number: String) -> String {
    let decl: String = la_declension(noun)

    if str_eq(decl, "1") {
        let stem: String = la_str_drop_last(noun, 1)
        return la_decline_1(stem, gram_case, number)
    }

    if str_eq(decl, "2m") {
        let stem: String = la_str_drop_last(noun, 2)
        return la_decline_2m(stem, gram_case, number)
    }

    if str_eq(decl, "2n") {
        let stem: String = la_str_drop_last(noun, 2)
        return la_decline_2n(stem, gram_case, number)
    }

    if str_eq(decl, "2er") {
        return la_decline_2er(noun, gram_case, number)
    }

    if str_eq(decl, "3") {
        return la_decline_3(noun, gram_case, number)
    }

    if str_eq(decl, "4") {
        let stem: String = la_str_drop_last(noun, 2)
        return la_decline_4(stem, gram_case, number)
    }

    if str_eq(decl, "5") {
        let stem: String = la_str_drop_last(noun, 2)
        return la_decline_5(stem, gram_case, number)
    }

    // Unknown declension: return nominative unchanged
    return noun
}

// ── la_noun_phrase: noun phrase builder ───────────────────────────────────────
//
// Latin has no definite or indefinite articles.  The declined noun form is the
// complete noun phrase.  The definite parameter is accepted for interface
// compatibility with other language modules but has no effect.
//
// noun:      nominative singular Latin noun
// gram_case: "nominative" | "accusative" | "genitive" | "dative" | "ablative"
// number:    "singular" | "plural"
// definite:  ignored (Latin has no articles)

fn la_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String {
    return la_decline(noun, gram_case, number)
}
