// morphology-goh.el - Old High German morphology for the NLG engine.
//
// Implements Old High German verb conjugation, noun declension, and the
// demonstrative determiner.  Designed as a companion to morphology.el and
// called by the engine when the language profile code is "goh".
//
// Language profile: code=goh, name=Old High German, morph_type=fusional,
//   word_order=V2, question_strategy=inversion, script=latin,
//   family=germanic.
//
// Historical note: Old High German (ca. 750–1050 CE) is the earliest
// substantially attested stage of High German, preserved in texts such as the
// Hildebrandslied, the Muspilli, and Notker's translations.  It is the
// ancestor of Middle High German and thence Modern German.  The "High" refers
// to the geographical highlands of southern Germany, Austria, and Switzerland —
// distinct from the Low German / Old Saxon dialects spoken to the north.
//
// Defining feature — the Second Germanic Consonant Shift (Hochdeutsche
// Lautverschiebung), which distinguishes OHG from Gothic and Old English:
//   p → ff/pf  (Gothic "skip" → OHG "skif"; Goth "apan" → OHG "affo")
//   t → ss/z   (Goth "watan" → OHG "wazzer"; OE "tid" → OHG "zit")
//   k → ch     (Goth "mikan" → OHG "mihil")
// This shift only applies to inherited consonants and not to recent loanwords.
//
// Three genders (masculine, feminine, neuter), four cases (nominative,
// accusative, genitive, dative), and two numbers (singular, plural).
//
// Verb conjugation covered:
//   Tenses:   present indicative, past indicative
//   Persons:  first/second/third × singular/plural (slots 0-5)
//   Classes:  weak verbs (dental -ta past suffix — the most productive class)
//   Irregulars: wesan/sīn (be), habēn (have), gān (go), sehan (see),
//               quethan (say), tuon (do)
//   Canonical map: "be" -> "wesan"
//
// Noun declension covered:
//   Strong masc a-stem  (tag — day):   4 cases × sg/pl
//   Strong fem  ō-stem  (geba — gift): 4 cases × sg/pl
//   Strong neut a-stem  (wort — word): 4 cases × sg/pl
//   Weak masc   n-stem  (boto — messenger): 4 cases × sg/pl
//
// Demonstrative / definite article:
//   OHG uses the demonstrative dër/diu/daz (the) which doubles as a definite
//   determiner.  This implementation selects the correct nominative form by
//   inferred gender and passes it as a separate word before the noun.
//
// Depends on: morphology.el (str_eq, str_len, str_slice, str_ends_with)

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

import "morphology.el"
fn goh_str_ends(s: String, suf: String) -> Bool {
    return str_ends_with(s, suf)
}

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

// ── Person/number slot ─────────────────────────────────────────────────────────
//
// Maps person × number to a 0-based paradigm index.
//   0 = 1st singular  (ih)
//   1 = 2nd singular  (dū)
//   2 = 3rd singular  (ër/siu/ez)
//   3 = 1st plural    (wir)
//   4 = 2nd plural    (ir)
//   5 = 3rd plural    (sie)

fn goh_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 person
    if str_eq(number, "singular") { return 2 }
    return 5
}

// ── Canonical verb mapping ─────────────────────────────────────────────────────
//
// English semantic-layer labels are resolved to OHG dictionary infinitives
// before conjugation.

fn goh_map_canonical(verb: String) -> String {
    if str_eq(verb, "be")   { return "wesan" }
    if str_eq(verb, "have") { return "haben" }
    if str_eq(verb, "go")   { return "gan" }
    if str_eq(verb, "see")  { return "sehan" }
    if str_eq(verb, "say")  { return "quethan" }
    if str_eq(verb, "do")   { return "tuon" }
    if str_eq(verb, "make") { return "tuon" }
    if str_eq(verb, "come") { return "queman" }
    if str_eq(verb, "give") { return "geban" }
    if str_eq(verb, "know") { return "wizzan" }
    if str_eq(verb, "want") { return "wellan" }
    return verb
}

// ── Irregular verb: wesan/sīn (to be) ────────────────────────────────────────
//
// wesan is one of the most irregular verbs in OHG.  The present uses the
// bim/bin- forms (from Proto-Germanic *biju-); the past uses the was-/wāri-
// forms (from the strong past of wesan).
//
// Present indicative:
//   1sg bim/bin  2sg bist    3sg ist
//   1pl birum    2pl biruț   3pl sint
//
// Note: 2pl "biruț" — the ț represents an old dental fricative; rendered here
// as "birut" (common orthographic variant in OHG manuscripts).
//
// Past indicative:
//   1sg was    2sg wāri    3sg was
//   1pl wārum  2pl wāruț   3pl wārun

fn goh_wesan_present(slot: Int) -> String {
    if slot == 0 { return "bim" }
    if slot == 1 { return "bist" }
    if slot == 2 { return "ist" }
    if slot == 3 { return "birum" }
    if slot == 4 { return "birut" }
    return "sint"
}

fn goh_wesan_past(slot: Int) -> String {
    if slot == 0 { return "was" }
    if slot == 1 { return "wari" }
    if slot == 2 { return "was" }
    if slot == 3 { return "warum" }
    if slot == 4 { return "warut" }
    return "warun"
}

// ── Irregular verb: habēn (to have) ───────────────────────────────────────────
//
// habēn is an athematic preterite-present verb.
//
// Present indicative:
//   1sg habem   2sg habest  3sg habet
//   1pl habemes 2pl habet   3pl habent
//
// Past (weak past -ta on stem hab-):
//   1sg habeta  2sg habetos 3sg habeta
//   1pl habetom 2pl habetot 3pl habeton

fn goh_haben_present(slot: Int) -> String {
    if slot == 0 { return "habem" }
    if slot == 1 { return "habest" }
    if slot == 2 { return "habet" }
    if slot == 3 { return "habemes" }
    if slot == 4 { return "habet" }
    return "habent"
}

fn goh_haben_past(slot: Int) -> String {
    if slot == 0 { return "habeta" }
    if slot == 1 { return "habetos" }
    if slot == 2 { return "habeta" }
    if slot == 3 { return "habetom" }
    if slot == 4 { return "habetot" }
    return "habeton"
}

// ── Irregular verb: gān (to go) ───────────────────────────────────────────────
//
// gān is an anomalous contracted verb.
//
// Present indicative:
//   1sg gan    2sg gest   3sg get
//   1pl games  2pl gat    3pl gant
//
// Past (reduplicating/suppletive — giang- forms):
//   1sg giang  2sg giangi  3sg giang
//   1pl giangum 2pl giangun 3pl giangun

fn goh_gan_present(slot: Int) -> String {
    if slot == 0 { return "gan" }
    if slot == 1 { return "gest" }
    if slot == 2 { return "get" }
    if slot == 3 { return "games" }
    if slot == 4 { return "gat" }
    return "gant"
}

fn goh_gan_past(slot: Int) -> String {
    if slot == 0 { return "giang" }
    if slot == 1 { return "giangi" }
    if slot == 2 { return "giang" }
    if slot == 3 { return "giangum" }
    if slot == 4 { return "giangun" }
    return "giangun"
}

// ── Irregular verb: sehan (to see) ────────────────────────────────────────────
//
// Strong class 5 (ablaut e/a).  Present has i-mutation in sg forms.
//
// Present indicative:
//   1sg sihu    2sg sihist  3sg sihit
//   1pl sehemes 2pl sehet   3pl sehent
//
// Past (strong ablaut: e → a):
//   1sg sah    2sg sahi    3sg sah
//   1pl sahum  2pl sahut   3pl sahun

fn goh_sehan_present(slot: Int) -> String {
    if slot == 0 { return "sihu" }
    if slot == 1 { return "sihist" }
    if slot == 2 { return "sihit" }
    if slot == 3 { return "sehemes" }
    if slot == 4 { return "sehet" }
    return "sehent"
}

fn goh_sehan_past(slot: Int) -> String {
    if slot == 0 { return "sah" }
    if slot == 1 { return "sahi" }
    if slot == 2 { return "sah" }
    if slot == 3 { return "sahum" }
    if slot == 4 { return "sahut" }
    return "sahun"
}

// ── Irregular verb: quethan (to say) ──────────────────────────────────────────
//
// Strong class 5 (ablaut e/a).  Present sg has i-mutation (quid-).
//
// Present indicative:
//   1sg quidu   2sg quidist  3sg quidit
//   1pl quethumes 2pl quethet 3pl quethent
//
// Past (strong ablaut: e → a):
//   1sg quad    2sg quadi   3sg quad
//   1pl quadum  2pl quadut  3pl quadun

fn goh_quethan_present(slot: Int) -> String {
    if slot == 0 { return "quidu" }
    if slot == 1 { return "quidist" }
    if slot == 2 { return "quidit" }
    if slot == 3 { return "quethumes" }
    if slot == 4 { return "quethet" }
    return "quethent"
}

fn goh_quethan_past(slot: Int) -> String {
    if slot == 0 { return "quad" }
    if slot == 1 { return "quadi" }
    if slot == 2 { return "quad" }
    if slot == 3 { return "quadum" }
    if slot == 4 { return "quadut" }
    return "quadun"
}

// ── Irregular verb: tuon (to do/make) ─────────────────────────────────────────
//
// tuon is a contracted athematic verb.
//
// Present indicative:
//   1sg tuom   2sg tuost  3sg tuot
//   1pl tuomes 2pl tuot   3pl tuont
//
// Past (weak past -ta on stem tā-):
//   1sg teta   2sg tetos  3sg teta
//   1pl tetom  2pl tetot  3pl teton

fn goh_tuon_present(slot: Int) -> String {
    if slot == 0 { return "tuom" }
    if slot == 1 { return "tuost" }
    if slot == 2 { return "tuot" }
    if slot == 3 { return "tuomes" }
    if slot == 4 { return "tuot" }
    return "tuont"
}

fn goh_tuon_past(slot: Int) -> String {
    if slot == 0 { return "teta" }
    if slot == 1 { return "tetos" }
    if slot == 2 { return "teta" }
    if slot == 3 { return "tetom" }
    if slot == 4 { return "tetot" }
    return "teton"
}

// ── Weak verb regular paradigm ─────────────────────────────────────────────────
//
// Weak verbs are the productive OHG class — new verbs are regularly formed on
// this pattern.  The infinitive ends in -en or -on; the weak past uses the
// dental suffix -ta/-to (a reflex of Proto-Germanic *-dō-).
//
// Class 1 weak (most common; infinitive -en, stem ends in consonant):
//
// Present indicative (stem + ending):
//   1sg -u     2sg -ist   3sg -it
//   1pl -emēs  2pl -et    3pl -ent
//
// Past indicative (stem + dental suffix):
//   1sg -ta    2sg -tōs   3sg -ta
//   1pl -tōm   2pl -tōt   3pl -tōn
//
// The stem for a regular weak verb is obtained by dropping -en (2 bytes).

fn goh_weak_present(stem: String, slot: Int) -> String {
    if slot == 0 { return stem + "u" }
    if slot == 1 { return stem + "ist" }
    if slot == 2 { return stem + "it" }
    if slot == 3 { return stem + "emes" }
    if slot == 4 { return stem + "et" }
    return stem + "ent"
}

fn goh_weak_past(stem: String, slot: Int) -> String {
    if slot == 0 { return stem + "ta" }
    if slot == 1 { return stem + "tos" }
    if slot == 2 { return stem + "ta" }
    if slot == 3 { return stem + "tom" }
    if slot == 4 { return stem + "tot" }
    return stem + "ton"
}

// goh_verb_stem: strip the infinitive ending to expose the productive stem.
//   -en endings: drop 2 bytes (most weak verbs: sagēn → sag-)
//   -on endings: drop 2 bytes (class 2 weak: lobōn → lob-)
//   -an endings: drop 2 bytes (strong verbs handled as irregular; fallback)

fn goh_verb_stem(verb: String) -> String {
    return goh_drop(verb, 2)
}

// ── goh_conjugate: main conjugation entry point ───────────────────────────────
//
// verb:   OHG infinitive (e.g. "sagēn", "lobōn") or English canonical label
// tense:  "present" | "past"
// person: "first" | "second" | "third"
// number: "singular" | "plural"
//
// Returns the inflected form.  Unknown tenses fall back to the infinitive.

fn goh_conjugate(verb: String, tense: String, person: String, number: String) -> String {
    let v: String = goh_map_canonical(verb)
    let slot: Int = goh_slot(person, number)

    // ── Irregular: wesan (to be) ──────────────────────────────────────────────
    if str_eq(v, "wesan") {
        if str_eq(tense, "present") { return goh_wesan_present(slot) }
        if str_eq(tense, "past")    { return goh_wesan_past(slot) }
        return v
    }

    // ── Irregular: habēn / haben (to have) ───────────────────────────────────
    if str_eq(v, "haben") {
        if str_eq(tense, "present") { return goh_haben_present(slot) }
        if str_eq(tense, "past")    { return goh_haben_past(slot) }
        return v
    }

    // Also match the macron-spelled infinitive if passed directly
    if str_eq(v, "haben") {
        if str_eq(tense, "present") { return goh_haben_present(slot) }
        if str_eq(tense, "past")    { return goh_haben_past(slot) }
        return v
    }

    // ── Irregular: gān / gan (to go) ─────────────────────────────────────────
    if str_eq(v, "gan") {
        if str_eq(tense, "present") { return goh_gan_present(slot) }
        if str_eq(tense, "past")    { return goh_gan_past(slot) }
        return v
    }

    // ── Irregular: sehan (to see) ─────────────────────────────────────────────
    if str_eq(v, "sehan") {
        if str_eq(tense, "present") { return goh_sehan_present(slot) }
        if str_eq(tense, "past")    { return goh_sehan_past(slot) }
        return v
    }

    // ── Irregular: quethan (to say) ───────────────────────────────────────────
    if str_eq(v, "quethan") {
        if str_eq(tense, "present") { return goh_quethan_present(slot) }
        if str_eq(tense, "past")    { return goh_quethan_past(slot) }
        return v
    }

    // ── Irregular: tuon (to do/make) ──────────────────────────────────────────
    if str_eq(v, "tuon") {
        if str_eq(tense, "present") { return goh_tuon_present(slot) }
        if str_eq(tense, "past")    { return goh_tuon_past(slot) }
        return v
    }

    // ── Regular weak conjugation ──────────────────────────────────────────────
    let stem: String = goh_verb_stem(v)

    if str_eq(tense, "present") { return goh_weak_present(stem, slot) }
    if str_eq(tense, "past")    { return goh_weak_past(stem, slot) }

    // Final fallback: return the infinitive
    return v
}

// ── Noun stem type detection ───────────────────────────────────────────────────
//
// OHG nouns are grouped by historical stem class.  This engine supports four:
//
//   "masc_a"  — strong masculine a-stem   (tag, fisc, arm)
//   "fem_o"   — strong feminine ō-stem    (geba, zala, burg)
//   "neut_a"  — strong neuter a-stem      (wort, kind, tier)
//   "masc_n"  — weak masculine n-stem     (boto, hazo, namo)
//
// Detection heuristic from the citation form (nominative singular):
//   ends in -o  → masc_n  (weak masc nouns: boto, hazo)
//   ends in -a  → fem_o   (strong fem: geba, zala)
//   ends in -t, -d, -n (common neuter endings) → neut_a
//   otherwise   → masc_a  (default strong masculine)
//
// Callers may override by passing the stem type directly when the heuristic
// would produce the wrong class (e.g. for monosyllables with ambiguous endings).

fn goh_stem_type(noun: String) -> String {
    if goh_str_ends(noun, "o")  { return "masc_n" }
    if goh_str_ends(noun, "a")  { return "fem_o" }
    if goh_str_ends(noun, "t")  { return "neut_a" }
    if goh_str_ends(noun, "d")  { return "neut_a" }
    if goh_str_ends(noun, "nd") { return "neut_a" }
    return "masc_a"
}

// goh_extract_stem: derive the bare stem used as the base for all case endings.
//
//   masc_a: citation form is nom sg without ending (tag = tag, fisc = fisc)
//           — citation IS the stem; no stripping needed
//   fem_o:  strip final -a  (geba → geb-)
//   neut_a: citation form is nom/acc sg without ending (wort = wort)
//           — citation IS the stem; no stripping needed
//   masc_n: strip final -o  (boto → bot-)

fn goh_extract_stem(noun: String, stype: String) -> String {
    if str_eq(stype, "fem_o")  { return goh_drop(noun, 1) }
    if str_eq(stype, "masc_n") { return goh_drop(noun, 1) }
    // masc_a and neut_a: citation IS the stem
    return noun
}

// ── Strong masculine a-stem declension (tag — day) ────────────────────────────
//
// The strong masculine a-stem is the most common OHG masculine class.
// It corresponds to the Gothic a-stem and the Latin 2nd-declension masculine.
//
// Paradigm (stem = tag-):
//   Singular:  nom tag       acc tag       gen tages     dat tage
//   Plural:    nom taga      acc taga      gen tago      dat tagum

fn goh_decline_masc_a_sg(stem: String, gram_case: String) -> String {
    if str_eq(gram_case, "nominative") { return stem }
    if str_eq(gram_case, "accusative") { return stem }
    if str_eq(gram_case, "genitive")   { return stem + "es" }
    if str_eq(gram_case, "dative")     { return stem + "e" }
    return stem
}

fn goh_decline_masc_a_pl(stem: String, gram_case: String) -> String {
    if str_eq(gram_case, "nominative") { return stem + "a" }
    if str_eq(gram_case, "accusative") { return stem + "a" }
    if str_eq(gram_case, "genitive")   { return stem + "o" }
    if str_eq(gram_case, "dative")     { return stem + "um" }
    return stem + "a"
}

// ── Strong feminine ō-stem declension (geba — gift) ──────────────────────────
//
// The ō-stem feminines are the standard OHG feminine class.
// They correspond to the Gothic o-stem and Latin 1st-declension nouns.
//
// Paradigm (stem = geb-):
//   Singular:  nom geba      acc geba      gen gebā      dat gebu
//   Plural:    nom gebā      acc gebā      gen gebōno    dat gebōm

fn goh_decline_fem_o_sg(stem: String, gram_case: String) -> String {
    if str_eq(gram_case, "nominative") { return stem + "a" }
    if str_eq(gram_case, "accusative") { return stem + "a" }
    if str_eq(gram_case, "genitive")   { return stem + "a" }
    if str_eq(gram_case, "dative")     { return stem + "u" }
    return stem + "a"
}

fn goh_decline_fem_o_pl(stem: String, gram_case: String) -> String {
    if str_eq(gram_case, "nominative") { return stem + "a" }
    if str_eq(gram_case, "accusative") { return stem + "a" }
    if str_eq(gram_case, "genitive")   { return stem + "ono" }
    if str_eq(gram_case, "dative")     { return stem + "om" }
    return stem + "a"
}

// ── Strong neuter a-stem declension (wort — word) ─────────────────────────────
//
// Strong neuter nouns share the a-stem pattern but have identical nom/acc
// throughout (a pan-Germanic neuter feature).  The plural differs from the
// masculine in the nom/acc: neuters use stem alone rather than stem + -a.
//
// Paradigm (stem = wort):
//   Singular:  nom wort      acc wort      gen wortes    dat worte
//   Plural:    nom wort      acc wort      gen worto     dat wortum

fn goh_decline_neut_a_sg(stem: String, gram_case: String) -> String {
    if str_eq(gram_case, "nominative") { return stem }
    if str_eq(gram_case, "accusative") { return stem }
    if str_eq(gram_case, "genitive")   { return stem + "es" }
    if str_eq(gram_case, "dative")     { return stem + "e" }
    return stem
}

fn goh_decline_neut_a_pl(stem: String, gram_case: String) -> String {
    if str_eq(gram_case, "nominative") { return stem }
    if str_eq(gram_case, "accusative") { return stem }
    if str_eq(gram_case, "genitive")   { return stem + "o" }
    if str_eq(gram_case, "dative")     { return stem + "um" }
    return stem
}

// ── Weak masculine n-stem declension (boto — messenger) ───────────────────────
//
// Weak nouns (n-stems) are characterised by the nasal -n- appearing in all
// forms except the nominative singular.  They correspond to the Gothic n-stem
// and the Old English weak noun class.
//
// Paradigm (stem = bot-):
//   Singular:  nom boto      acc boton     gen boton     dat boton
//   Plural:    nom boton     acc boton     gen botōno    dat botōm

fn goh_decline_masc_n_sg(stem: String, gram_case: String) -> String {
    if str_eq(gram_case, "nominative") { return stem + "o" }
    if str_eq(gram_case, "accusative") { return stem + "on" }
    if str_eq(gram_case, "genitive")   { return stem + "on" }
    if str_eq(gram_case, "dative")     { return stem + "on" }
    return stem + "o"
}

fn goh_decline_masc_n_pl(stem: String, gram_case: String) -> String {
    if str_eq(gram_case, "nominative") { return stem + "on" }
    if str_eq(gram_case, "accusative") { return stem + "on" }
    if str_eq(gram_case, "genitive")   { return stem + "ono" }
    if str_eq(gram_case, "dative")     { return stem + "om" }
    return stem + "on"
}

// ── goh_decline: main declension entry point ──────────────────────────────────
//
// noun:      OHG nominative singular form (e.g. "tag", "geba", "wort", "boto")
// gram_case: "nominative" | "accusative" | "genitive" | "dative"
// number:    "singular" | "plural"
//
// Returns the inflected form.  Unknown stem types return the citation form
// unchanged as a safe fallback.

fn goh_decline(noun: String, gram_case: String, number: String) -> String {
    let stype: String = goh_stem_type(noun)
    let stem: String = goh_extract_stem(noun, stype)

    if str_eq(stype, "masc_a") {
        if str_eq(number, "singular") { return goh_decline_masc_a_sg(stem, gram_case) }
        return goh_decline_masc_a_pl(stem, gram_case)
    }

    if str_eq(stype, "fem_o") {
        if str_eq(number, "singular") { return goh_decline_fem_o_sg(stem, gram_case) }
        return goh_decline_fem_o_pl(stem, gram_case)
    }

    if str_eq(stype, "neut_a") {
        if str_eq(number, "singular") { return goh_decline_neut_a_sg(stem, gram_case) }
        return goh_decline_neut_a_pl(stem, gram_case)
    }

    if str_eq(stype, "masc_n") {
        if str_eq(number, "singular") { return goh_decline_masc_n_sg(stem, gram_case) }
        return goh_decline_masc_n_pl(stem, gram_case)
    }

    // Unknown: return citation form unchanged
    return noun
}

// ── Demonstrative article ──────────────────────────────────────────────────────
//
// OHG uses the demonstrative pronoun dër/diu/daz as a definite determiner.
// Full declension of this pronoun is complex; this implementation provides the
// nominative forms used as determiners before nouns.
//
// Nominative forms (the most common slot for a determiner):
//   Masculine sg: der    Feminine sg: diu    Neuter sg: daz
//   Plural (all genders): die
//
// Gender is inferred from the stem type:
//   masc_a  → masculine → "der"
//   fem_o   → feminine  → "diu"
//   neut_a  → neuter    → "daz"
//   masc_n  → masculine → "der"

fn goh_demo_article(stype: String, number: String) -> String {
    if str_eq(number, "plural") { return "die" }
    if str_eq(stype, "fem_o")  { return "diu" }
    if str_eq(stype, "neut_a") { return "daz" }
    return "der"
}

// ── goh_noun_phrase: noun phrase builder ──────────────────────────────────────
//
// Assembles a declined noun with an optional OHG demonstrative article.
//
// noun:      OHG nominative singular (e.g. "tag", "geba", "wort", "boto")
// gram_case: "nominative" | "accusative" | "genitive" | "dative"
// number:    "singular" | "plural"
// definite:  "true" to prepend the demonstrative determiner; any other value omits it
//
// Note: the demonstrative is given in its nominative singular form for
// simplicity.  Full agreement would require a separate declined demonstrative
// paradigm; the NLG layer should implement that when case-agreement on the
// determiner is required.

fn goh_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String {
    let stype: String = goh_stem_type(noun)
    let declined: String = goh_decline(noun, gram_case, number)

    if str_eq(definite, "true") {
        let art: String = goh_demo_article(stype, number)
        return art + " " + declined
    }

    return declined
}
