// morphology-ang.el - Old English (Anglo-Saxon) morphology for the NLG engine.
//
// Implements Old English verb conjugation, noun declension, and the definite
// article/demonstrative pronoun.  Designed as a companion to morphology.el and
// called by the engine when the language profile code is "ang".
//
// Language profile: code=ang, name=Old English, morph_type=fusional,
//   word_order=SOV, question_strategy=intonation, script=latin, family=germanic.
//
// Typology note: Old English is a synthetic Germanic language with four
// grammatical cases (nominative, accusative, genitive, dative), three genders,
// and strong/weak noun and verb classes.  Strong verbs form their past tense by
// internal vowel change (ablaut); weak verbs use a dental (-de/-ode) suffix.
// Long vowels are marked with a macron (ā ē ī ō ū) and are preserved in all
// string literals; ǣ, æ, þ, ð, and ƿ (wynn) are used where historically
// appropriate.  V2 (verb-second) word order applies in main clauses but is not
// enforced by this module — the realizer handles constituent ordering.
//
// Verb conjugation covered:
//   Tenses:    present, past
//   Persons:   first/second/third × singular/plural (slots 0-5)
//   Classes:   weak (regular -ian), strong irregular table
//   Irregulars: wesan/beon (be), habban (have), gān (go), cuman (come),
//               secgan (say), sēon (see), dōn (do), willan (want), magan (can)
//   Canonical map: "be" -> "wesan" (past) / "beon" (present)
//
// Noun declension covered:
//   Strong masc a-stem  (cyning pattern): nom/acc -∅, gen -es, dat -e; pl -as/-a/-um
//   Strong neut a-stem  (word pattern):   sg same as masc; pl nom/acc -∅
//   Weak n-stem         (nama pattern):   sg nom -a, obl -an; pl -an/-ena/-um
//
// Article: simplified demonstrative/article forms for masculine, feminine,
//   neuter (se/sēo/þæt), fully declined.
//
// Depends on: morphology.el (str_ends_with, str_len, str_slice, str_eq)

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

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

fn ang_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 ang_str_last_char(s: String) -> String {
    let n: Int = str_len(s)
    if n == 0 {
        return ""
    }
    return str_slice(s, n - 1, n)
}

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

// ── Person/number slot ─────────────────────────────────────────────────────────
//
// Maps person × number to a 0-based index for paradigm tables.
//   0 = 1st singular  (ic)
//   1 = 2nd singular  (þū)
//   2 = 3rd singular  (hē/hēo/hit)
//   3 = 1st plural    (wē)
//   4 = 2nd plural    (gē)
//   5 = 3rd plural    (hīe)
//
// Old English also has a dual (wit, git) — not handled; dual falls through
// to plural.

fn ang_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
}

// ── Canonical verb mapping ─────────────────────────────────────────────────────
//
// The semantic layer may pass English canonical labels.  Map to Old English
// citation (infinitive) forms.  "be" maps to "beon" for present and "wesan"
// for past — the caller selects tense, so we map "be" to "beon" and handle
// the past-tense wesan forms inside the conjugation function.

fn ang_map_canonical(verb: String) -> String {
    if str_eq(verb, "be")    { return "beon" }
    if str_eq(verb, "have")  { return "habban" }
    if str_eq(verb, "go")    { return "gān" }
    if str_eq(verb, "come")  { return "cuman" }
    if str_eq(verb, "say")   { return "secgan" }
    if str_eq(verb, "see")   { return "sēon" }
    if str_eq(verb, "do")    { return "dōn" }
    if str_eq(verb, "want")  { return "willan" }
    if str_eq(verb, "will")  { return "willan" }
    if str_eq(verb, "can")   { return "magan" }
    if str_eq(verb, "know")  { return "witan" }
    if str_eq(verb, "give")  { return "giefan" }
    if str_eq(verb, "take")  { return "niman" }
    if str_eq(verb, "find")  { return "findan" }
    if str_eq(verb, "make")  { return "macian" }
    return verb
}

// ── Irregular: wesan (to be — past tense forms) ───────────────────────────────
//
// Past:    wæs wǣre wæs wǣron wǣron wǣron

fn ang_wesan_past(slot: Int) -> String {
    if slot == 0 { return "wæs" }
    if slot == 1 { return "wǣre" }
    if slot == 2 { return "wæs" }
    if slot == 3 { return "wǣron" }
    if slot == 4 { return "wǣron" }
    return "wǣron"
}

// ── Irregular: beon (to be — present / habitual / future) ────────────────────
//
// Present: bēo bist biþ bēoþ bēoþ bēoþ
//
// The present indicative of "wesan" is eom/eart/is/sind — that paradigm is
// also provided below for completeness and for callers who specifically request
// wesan present.

fn ang_beon_present(slot: Int) -> String {
    if slot == 0 { return "bēo" }
    if slot == 1 { return "bist" }
    if slot == 2 { return "biþ" }
    if slot == 3 { return "bēoþ" }
    if slot == 4 { return "bēoþ" }
    return "bēoþ"
}

// ── Irregular: wesan present (eom/eart/is/sind) ───────────────────────────────
//
// Present:  eom eart is sind/sindon sind sind

fn ang_wesan_present(slot: Int) -> String {
    if slot == 0 { return "eom" }
    if slot == 1 { return "eart" }
    if slot == 2 { return "is" }
    if slot == 3 { return "sind" }
    if slot == 4 { return "sind" }
    return "sind"
}

// ── Irregular: habban (to have) ───────────────────────────────────────────────
//
// Present:  hæbbe hæfst hæfþ habbað habbað habbað
// Past:     hæfde hæfdest hæfde hæfdon hæfdon hæfdon

fn ang_habban_present(slot: Int) -> String {
    if slot == 0 { return "hæbbe" }
    if slot == 1 { return "hæfst" }
    if slot == 2 { return "hæfþ" }
    if slot == 3 { return "habbað" }
    if slot == 4 { return "habbað" }
    return "habbað"
}

fn ang_habban_past(slot: Int) -> String {
    if slot == 0 { return "hæfde" }
    if slot == 1 { return "hæfdest" }
    if slot == 2 { return "hæfde" }
    if slot == 3 { return "hæfdon" }
    if slot == 4 { return "hæfdon" }
    return "hæfdon"
}

// ── Irregular: gān (to go) ────────────────────────────────────────────────────
//
// Present:  gā gǣst gǣþ gāð gāð gāð
// Past:     ēode ēodest ēode ēodon ēodon ēodon

fn ang_gan_present(slot: Int) -> String {
    if slot == 0 { return "gā" }
    if slot == 1 { return "gǣst" }
    if slot == 2 { return "gǣþ" }
    if slot == 3 { return "gāð" }
    if slot == 4 { return "gāð" }
    return "gāð"
}

fn ang_gan_past(slot: Int) -> String {
    if slot == 0 { return "ēode" }
    if slot == 1 { return "ēodest" }
    if slot == 2 { return "ēode" }
    if slot == 3 { return "ēodon" }
    if slot == 4 { return "ēodon" }
    return "ēodon"
}

// ── Irregular: cuman (to come) ────────────────────────────────────────────────
//
// Present:  cume cymst cymþ cumað cumað cumað
// Past:     cōm cōme cōm cōmon cōmon cōmon

fn ang_cuman_present(slot: Int) -> String {
    if slot == 0 { return "cume" }
    if slot == 1 { return "cymst" }
    if slot == 2 { return "cymþ" }
    if slot == 3 { return "cumað" }
    if slot == 4 { return "cumað" }
    return "cumað"
}

fn ang_cuman_past(slot: Int) -> String {
    if slot == 0 { return "cōm" }
    if slot == 1 { return "cōme" }
    if slot == 2 { return "cōm" }
    if slot == 3 { return "cōmon" }
    if slot == 4 { return "cōmon" }
    return "cōmon"
}

// ── Irregular: secgan (to say) ────────────────────────────────────────────────
//
// Present:  secge sagast sagað secgað secgað secgað
// Past:     sægde sægdest sægde sægdon sægdon sægdon

fn ang_secgan_present(slot: Int) -> String {
    if slot == 0 { return "secge" }
    if slot == 1 { return "sagast" }
    if slot == 2 { return "sagað" }
    if slot == 3 { return "secgað" }
    if slot == 4 { return "secgað" }
    return "secgað"
}

fn ang_secgan_past(slot: Int) -> String {
    if slot == 0 { return "sægde" }
    if slot == 1 { return "sægdest" }
    if slot == 2 { return "sægde" }
    if slot == 3 { return "sægdon" }
    if slot == 4 { return "sægdon" }
    return "sægdon"
}

// ── Irregular: sēon (to see) ──────────────────────────────────────────────────
//
// Present:  sēo siehst siehþ sēoð sēoð sēoð
// Past:     seah sāwe seah sāwon sāwon sāwon

fn ang_seon_present(slot: Int) -> String {
    if slot == 0 { return "sēo" }
    if slot == 1 { return "siehst" }
    if slot == 2 { return "siehþ" }
    if slot == 3 { return "sēoð" }
    if slot == 4 { return "sēoð" }
    return "sēoð"
}

fn ang_seon_past(slot: Int) -> String {
    if slot == 0 { return "seah" }
    if slot == 1 { return "sāwe" }
    if slot == 2 { return "seah" }
    if slot == 3 { return "sāwon" }
    if slot == 4 { return "sāwon" }
    return "sāwon"
}

// ── Irregular: dōn (to do) ────────────────────────────────────────────────────
//
// Present:  dō dēst dēþ dōð dōð dōð
// Past:     dyde dydest dyde dydon dydon dydon

fn ang_don_present(slot: Int) -> String {
    if slot == 0 { return "dō" }
    if slot == 1 { return "dēst" }
    if slot == 2 { return "dēþ" }
    if slot == 3 { return "dōð" }
    if slot == 4 { return "dōð" }
    return "dōð"
}

fn ang_don_past(slot: Int) -> String {
    if slot == 0 { return "dyde" }
    if slot == 1 { return "dydest" }
    if slot == 2 { return "dyde" }
    if slot == 3 { return "dydon" }
    if slot == 4 { return "dydon" }
    return "dydon"
}

// ── Irregular: willan (to want / will) ────────────────────────────────────────
//
// Present:  wille wilt wile willað willað willað
// Past:     wolde woldest wolde woldon woldon woldon

fn ang_willan_present(slot: Int) -> String {
    if slot == 0 { return "wille" }
    if slot == 1 { return "wilt" }
    if slot == 2 { return "wile" }
    if slot == 3 { return "willað" }
    if slot == 4 { return "willað" }
    return "willað"
}

fn ang_willan_past(slot: Int) -> String {
    if slot == 0 { return "wolde" }
    if slot == 1 { return "woldest" }
    if slot == 2 { return "wolde" }
    if slot == 3 { return "woldon" }
    if slot == 4 { return "woldon" }
    return "woldon"
}

// ── Irregular: magan (to be able / can) ──────────────────────────────────────
//
// Present:  mæg meaht mæg magon magon magon
// Past:     meahte meahtest meahte meahton meahton meahton

fn ang_magan_present(slot: Int) -> String {
    if slot == 0 { return "mæg" }
    if slot == 1 { return "meaht" }
    if slot == 2 { return "mæg" }
    if slot == 3 { return "magon" }
    if slot == 4 { return "magon" }
    return "magon"
}

fn ang_magan_past(slot: Int) -> String {
    if slot == 0 { return "meahte" }
    if slot == 1 { return "meahtest" }
    if slot == 2 { return "meahte" }
    if slot == 3 { return "meahton" }
    if slot == 4 { return "meahton" }
    return "meahton"
}

// ── Irregular: witan (to know) ────────────────────────────────────────────────
//
// Present:  wāt wāst wāt witon witon witon
// Past:     wisse/wiste wissest wisse wisson wisson wisson

fn ang_witan_present(slot: Int) -> String {
    if slot == 0 { return "wāt" }
    if slot == 1 { return "wāst" }
    if slot == 2 { return "wāt" }
    if slot == 3 { return "witon" }
    if slot == 4 { return "witon" }
    return "witon"
}

fn ang_witan_past(slot: Int) -> String {
    if slot == 0 { return "wisse" }
    if slot == 1 { return "wissest" }
    if slot == 2 { return "wisse" }
    if slot == 3 { return "wisson" }
    if slot == 4 { return "wisson" }
    return "wisson"
}

// ── Weak verb: present-tense endings ─────────────────────────────────────────
//
// Weak verbs with -ian infinitives form their present tense as:
//   stem + -e, -est, -eþ, -aþ, -aþ, -aþ
//
// The stem is the infinitive with -ian stripped (or -an for class-2 verbs).

fn ang_weak_present_ending(slot: Int) -> String {
    if slot == 0 { return "e" }
    if slot == 1 { return "est" }
    if slot == 2 { return "eþ" }
    if slot == 3 { return "aþ" }
    if slot == 4 { return "aþ" }
    return "aþ"
}

// ── Weak verb: past-tense ending selection ────────────────────────────────────
//
// Class 1 (-ian with short stem): past -ede  (e.g. nerian -> nerede)
// Class 2 (-ian with long/heavy stem): past -ode  (e.g. macian -> macode)
// Class 3 (-ian, small group): past -de   (e.g. habban -> hæfde — irregular)
//
// Heuristic: if the stem length is 1 char, use -ede; otherwise use -ode.
// This is a simplification; correct assignment requires lexical class marking.
//
// For the past, all persons in the plural share -on, and all singulars share
// the same dental-suffixed stem.

fn ang_weak_past_stem(stem: String) -> String {
    let slen: Int = str_len(stem)
    if slen <= 2 {
        return stem + "ede"
    }
    return stem + "ode"
}

fn ang_weak_past(stem: String, slot: Int) -> String {
    let pstem: String = ang_weak_past_stem(stem)
    if slot == 0 { return pstem }
    if slot == 1 { return pstem + "st" }
    if slot == 2 { return pstem }
    if slot == 3 { return ang_str_drop_last(pstem, 1) + "on" }
    if slot == 4 { return ang_str_drop_last(pstem, 1) + "on" }
    return ang_str_drop_last(pstem, 1) + "on"
}

// ── Stem extraction for weak verbs ────────────────────────────────────────────
//
// Strip the infinitive ending to recover the stem:
//   -ian -> strip 3 chars (nerian -> ner-, macian -> mac-)
//   -an  -> strip 2 chars (habban -> habb-; fallback for non -ian)
//   otherwise: return as-is

fn ang_weak_stem(verb: String) -> String {
    if ang_str_ends(verb, "ian") {
        return ang_str_drop_last(verb, 3)
    }
    if ang_str_ends(verb, "an") {
        return ang_str_drop_last(verb, 2)
    }
    return verb
}

// ── ang_conjugate: main conjugation entry point ───────────────────────────────
//
// verb:   Old English infinitive or English canonical label
// tense:  "present" | "past"
// person: "first" | "second" | "third"
// number: "singular" | "plural"
//
// Strategy:
//   1. Map canonical English labels to OE verbs.
//   2. Check the full irregular table.
//   3. Fall back to weak conjugation for unknown -ian/-an verbs.
//   4. Return the base form if nothing matches.

fn ang_conjugate(verb: String, tense: String, person: String, number: String) -> String {
    let v: String = ang_map_canonical(verb)
    let slot: Int = ang_slot(person, number)

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

    // beon: present-tense "be" (habitual/future/general)
    if str_eq(v, "beon") {
        if str_eq(tense, "present") { return ang_beon_present(slot) }
        // past: use wesan past forms
        return ang_wesan_past(slot)
    }

    // wesan: past "be" and present "be" (existential/stative)
    if str_eq(v, "wesan") {
        if str_eq(tense, "present") { return ang_wesan_present(slot) }
        return ang_wesan_past(slot)
    }

    if str_eq(v, "habban") {
        if str_eq(tense, "present") { return ang_habban_present(slot) }
        return ang_habban_past(slot)
    }

    if str_eq(v, "gān") {
        if str_eq(tense, "present") { return ang_gan_present(slot) }
        return ang_gan_past(slot)
    }

    if str_eq(v, "cuman") {
        if str_eq(tense, "present") { return ang_cuman_present(slot) }
        return ang_cuman_past(slot)
    }

    if str_eq(v, "secgan") {
        if str_eq(tense, "present") { return ang_secgan_present(slot) }
        return ang_secgan_past(slot)
    }

    if str_eq(v, "sēon") {
        if str_eq(tense, "present") { return ang_seon_present(slot) }
        return ang_seon_past(slot)
    }

    if str_eq(v, "dōn") {
        if str_eq(tense, "present") { return ang_don_present(slot) }
        return ang_don_past(slot)
    }

    if str_eq(v, "willan") {
        if str_eq(tense, "present") { return ang_willan_present(slot) }
        return ang_willan_past(slot)
    }

    if str_eq(v, "magan") {
        if str_eq(tense, "present") { return ang_magan_present(slot) }
        return ang_magan_past(slot)
    }

    if str_eq(v, "witan") {
        if str_eq(tense, "present") { return ang_witan_present(slot) }
        return ang_witan_past(slot)
    }

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

    let stem: String = ang_weak_stem(v)

    if str_eq(tense, "present") {
        return stem + ang_weak_present_ending(slot)
    }

    if str_eq(tense, "past") {
        return ang_weak_past(stem, slot)
    }

    // Unknown tense: return infinitive
    return v
}

// ── Noun declension class detection ───────────────────────────────────────────
//
// Infer the declension class from the nominative singular form and an optional
// gender hint.  Without a full lexicon, ending-based heuristics are used:
//
//   ends in -a            -> weak n-stem (nama pattern)
//   ends in -e (long)     -> may be various; default to strong masc a-stem
//   any other ending      -> strong a-stem; gender distinguishes masc vs neut
//
// The caller may pass gender as a hint:
//   "masculine" | "feminine" | "neuter" | ""  (empty = infer)
//
// For simplicity this module handles three paradigms:
//   "strong_masc" — a-stem masculine (cyning, mann)
//   "strong_neut" — a-stem neuter (word, scip)
//   "weak"        — n-stem (nama, ēage)

fn ang_declension(noun: String, gender: String) -> String {
    if ang_str_ends(noun, "a") { return "weak" }
    if str_eq(gender, "neuter") { return "strong_neut" }
    return "strong_masc"
}

// ── Strong masculine a-stem (cyning pattern) ──────────────────────────────────
//
// Stem: the noun as given (nom sg lacks an inflectional ending in this class).
//
// Singular: nom -∅   acc -∅   gen -es   dat -e
// Plural:   nom -as  acc -as  gen -a    dat -um

fn ang_decline_strong_masc(noun: String, gram_case: String, number: String) -> String {
    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return noun }
        if str_eq(gram_case, "accusative") { return noun }
        if str_eq(gram_case, "genitive")   { return noun + "es" }
        if str_eq(gram_case, "dative")     { return noun + "e" }
        return noun
    }
    // plural
    if str_eq(gram_case, "nominative") { return noun + "as" }
    if str_eq(gram_case, "accusative") { return noun + "as" }
    if str_eq(gram_case, "genitive")   { return noun + "a" }
    if str_eq(gram_case, "dative")     { return noun + "um" }
    return noun + "as"
}

// ── Strong neuter a-stem (word pattern) ───────────────────────────────────────
//
// Singular: same as strong masc
// Plural:   nom/acc -∅   gen -a   dat -um

fn ang_decline_strong_neut(noun: String, gram_case: String, number: String) -> String {
    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return noun }
        if str_eq(gram_case, "accusative") { return noun }
        if str_eq(gram_case, "genitive")   { return noun + "es" }
        if str_eq(gram_case, "dative")     { return noun + "e" }
        return noun
    }
    // plural: neuters have zero ending in nom/acc
    if str_eq(gram_case, "nominative") { return noun }
    if str_eq(gram_case, "accusative") { return noun }
    if str_eq(gram_case, "genitive")   { return noun + "a" }
    if str_eq(gram_case, "dative")     { return noun + "um" }
    return noun
}

// ── Weak n-stem (nama pattern) ────────────────────────────────────────────────
//
// The nom sg ends in -a; the oblique stem is formed by stripping -a and adding
// -an.  Plural genitive is -ena.
//
// Singular: nom -a   acc -an  gen -an  dat -an
// Plural:   nom -an  acc -an  gen -ena dat -um

fn ang_decline_weak(noun: String, gram_case: String, number: String) -> String {
    // Oblique stem: strip the final -a
    let stem: String = ang_str_drop_last(noun, 1)

    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return noun }
        if str_eq(gram_case, "accusative") { return stem + "an" }
        if str_eq(gram_case, "genitive")   { return stem + "an" }
        if str_eq(gram_case, "dative")     { return stem + "an" }
        return noun
    }
    // plural
    if str_eq(gram_case, "nominative") { return stem + "an" }
    if str_eq(gram_case, "accusative") { return stem + "an" }
    if str_eq(gram_case, "genitive")   { return stem + "ena" }
    if str_eq(gram_case, "dative")     { return stem + "um" }
    return stem + "an"
}

// ── ang_decline: main declension entry point ──────────────────────────────────
//
// noun:      nominative singular Old English noun (e.g. "cyning", "word", "nama")
// gram_case: "nominative" | "accusative" | "genitive" | "dative"
// number:    "singular" | "plural"
// gender:    "masculine" | "neuter" | "feminine" | ""  (empty triggers inference)
//
// Returns the inflected form.  Falls back to the nominative singular for any
// unrecognised combination.

fn ang_decline(noun: String, gram_case: String, number: String, gender: String) -> String {
    let decl: String = ang_declension(noun, gender)

    if str_eq(decl, "strong_masc") {
        return ang_decline_strong_masc(noun, gram_case, number)
    }

    if str_eq(decl, "strong_neut") {
        return ang_decline_strong_neut(noun, gram_case, number)
    }

    if str_eq(decl, "weak") {
        return ang_decline_weak(noun, gram_case, number)
    }

    // Unknown: return nominative unchanged
    return noun
}

// ── Definite article / demonstrative: se/sēo/þæt ─────────────────────────────
//
// Old English used the demonstrative pronoun se/sēo/þæt as a definite article.
// The full paradigm (gender × case × number) is given below.
//
// Masculine:
//   sg: nom se   acc þone  gen þæs   dat þǣm
//   pl: nom þā   acc þā    gen þāra  dat þǣm
//
// Feminine:
//   sg: nom sēo  acc þā    gen þǣre  dat þǣre
//   pl: nom þā   acc þā    gen þāra  dat þǣm
//
// Neuter:
//   sg: nom þæt  acc þæt   gen þæs   dat þǣm
//   pl: nom þā   acc þā    gen þāra  dat þǣm

fn ang_article_masculine(gram_case: String, number: String) -> String {
    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return "se" }
        if str_eq(gram_case, "accusative") { return "þone" }
        if str_eq(gram_case, "genitive")   { return "þæs" }
        if str_eq(gram_case, "dative")     { return "þǣm" }
        return "se"
    }
    // plural
    if str_eq(gram_case, "nominative") { return "þā" }
    if str_eq(gram_case, "accusative") { return "þā" }
    if str_eq(gram_case, "genitive")   { return "þāra" }
    if str_eq(gram_case, "dative")     { return "þǣm" }
    return "þā"
}

fn ang_article_feminine(gram_case: String, number: String) -> String {
    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return "sēo" }
        if str_eq(gram_case, "accusative") { return "þā" }
        if str_eq(gram_case, "genitive")   { return "þǣre" }
        if str_eq(gram_case, "dative")     { return "þǣre" }
        return "sēo"
    }
    // plural
    if str_eq(gram_case, "nominative") { return "þā" }
    if str_eq(gram_case, "accusative") { return "þā" }
    if str_eq(gram_case, "genitive")   { return "þāra" }
    if str_eq(gram_case, "dative")     { return "þǣm" }
    return "þā"
}

fn ang_article_neuter(gram_case: String, number: String) -> String {
    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return "þæt" }
        if str_eq(gram_case, "accusative") { return "þæt" }
        if str_eq(gram_case, "genitive")   { return "þæs" }
        if str_eq(gram_case, "dative")     { return "þǣm" }
        return "þæt"
    }
    // plural
    if str_eq(gram_case, "nominative") { return "þā" }
    if str_eq(gram_case, "accusative") { return "þā" }
    if str_eq(gram_case, "genitive")   { return "þāra" }
    if str_eq(gram_case, "dative")     { return "þǣm" }
    return "þā"
}

fn ang_article(gender: String, gram_case: String, number: String) -> String {
    if str_eq(gender, "masculine") { return ang_article_masculine(gram_case, number) }
    if str_eq(gender, "feminine")  { return ang_article_feminine(gram_case, number) }
    // neuter
    return ang_article_neuter(gram_case, number)
}

// ── Gender inference from noun form ───────────────────────────────────────────
//
// A last-resort heuristic when the caller provides no gender hint.
// -a ending strongly suggests weak masculine or neuter (but most -a nouns are
// masculine weak).  Without a full lexicon, masculine is the safe default.

fn ang_infer_gender(noun: String) -> String {
    if ang_str_ends(noun, "u") { return "feminine" }
    if ang_str_ends(noun, "e") { return "feminine" }
    return "masculine"
}

// ── ang_noun_phrase: noun phrase builder ──────────────────────────────────────
//
// Produces a declined noun with optional definite article (demonstrative)
// prepended.  When gender is empty ("") it is inferred from the noun form.
//
// noun:      nominative singular Old English noun
// gram_case: "nominative" | "accusative" | "genitive" | "dative"
// number:    "singular" | "plural"
// definite:  "true" | "false"

fn ang_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String {
    let gender: String = ang_infer_gender(noun)
    let declined: String = ang_decline(noun, gram_case, number, gender)
    if str_eq(definite, "true") {
        let art: String = ang_article(gender, gram_case, number)
        return art + " " + declined
    }
    return declined
}
