// morphology-sga.el - Old Irish morphology for the NLG engine.
//
// Implements Old Irish verb conjugation, noun declension, and initial consonant
// lenition for the ca. 600-900 CE period.  Designed as a companion to
// morphology.el and called by the engine when the language profile code is "sga".
//
// Language profile: code=sga, name=Old Irish, morph_type=fusional, word_order=VSO,
//   question_strategy=particle, script=latin, family=celtic.
//
// Verb conjugation covered:
//   Tenses:   present, past
//   Persons:  first/second/third x singular/plural (slots 0-5)
//   Forms:    absolute (verb-initial position only; conjunct not implemented)
//   Irregulars: bith (to be, substantive), téit (to go), gaibid (to take/hold),
//               ad·cí (to see), as·beir (to say)
//   Copula:   "is" — invariant in 3sg; used as the default copular form
//   Canonical map: "be" -> copula "is" (3sg) / "bith" (substantive be)
//
// Noun declension covered:
//   o-stem masculine (like "fer" — man)
//   ā-stem feminine  (like "ben" — woman)
//   Cases: nominative, vocative, accusative, genitive, dative
//   Numbers: singular, plural
//   Definite article: simplified "in" prefix (article + noun)
//
// Lenition (initial mutation):
//   b->bh, c->ch, d->dh, f->fh, g->gh, m->mh, p->ph, s->sh, t->th
//   Other initial consonants and vowels are returned unchanged.
//
// Depends on: morphology.el (str_eq, str_len, str_slice, str_ends_with)

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

import "morphology.el"
fn sga_drop(s: String, n: Int) -> String {
    let len: Int = str_len(s)
    if n >= len { return "" }
    return str_slice(s, 0, len - n)
}

fn sga_first(s: String) -> String {
    if str_len(s) == 0 { return "" }
    return str_slice(s, 0, 1)
}

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

// ── Person/number slot ─────────────────────────────────────────────────────────
//
// Maps person x number to a 0-based paradigm slot.
//   0 = 1st singular   (mé)
//   1 = 2nd singular   (tú)
//   2 = 3rd singular   (é/sí/ed)
//   3 = 1st plural     (sní)
//   4 = 2nd plural     (sí)
//   5 = 3rd plural     (é/sí/ed)

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

// ── Lenition ───────────────────────────────────────────────────────────────────
//
// Initial consonant lenition (séimhiú) — the most common mutation in Old Irish.
// Applies after certain prepositions, the genitive of feminine nouns, and many
// other grammatical environments.  Simplified: only the first consonant is mutated.
//
// Lenition table (b c d f g m p s t):
//   b  -> bh    c  -> ch    d  -> dh    f  -> fh    g  -> gh
//   m  -> mh    p  -> ph    s  -> sh    t  -> th
// Vowels and other consonants: unchanged.

fn sga_lenite(word: String) -> String {
    let init: String = sga_first(word)
    let tail: String = sga_rest(word)
    if str_eq(init, "b") { return "bh" + tail }
    if str_eq(init, "c") { return "ch" + tail }
    if str_eq(init, "d") { return "dh" + tail }
    if str_eq(init, "f") { return "fh" + tail }
    if str_eq(init, "g") { return "gh" + tail }
    if str_eq(init, "m") { return "mh" + tail }
    if str_eq(init, "p") { return "ph" + tail }
    if str_eq(init, "s") { return "sh" + tail }
    if str_eq(init, "t") { return "th" + tail }
    // Vowels and unlenitable consonants (l, n, r, …): unchanged
    return word
}

// ── Copula paradigm ────────────────────────────────────────────────────────────
//
// The Old Irish copula "is" is largely invariant in the present tense in its
// most common (3rd singular assertive) use.  The full paradigm has variants
// (am, at, is in sg; ammi, adib, it in pl) but for NLG purposes we return "is"
// for 3sg and the simplified forms elsewhere.

fn sga_copula_present(slot: Int) -> String {
    if slot == 0 { return "am" }
    if slot == 1 { return "at" }
    if slot == 2 { return "is" }
    if slot == 3 { return "am" }
    if slot == 4 { return "adib" }
    return "it"
}

// ── Substantive "bith" (to be) ─────────────────────────────────────────────────
//
// "Bith" is the substantive/existential verb for being.  It differs from the
// copula "is" in that it can carry tense inflection and express existence rather
// than predication.
//
// Present: am, at, is, am, adib, at
// Past:    ba, ba, ba, bámmar, bádaid, batar

fn sga_bith_present(slot: Int) -> String {
    if slot == 0 { return "am" }
    if slot == 1 { return "at" }
    if slot == 2 { return "is" }
    if slot == 3 { return "am" }
    if slot == 4 { return "adib" }
    return "at"
}

fn sga_bith_past(slot: Int) -> String {
    if slot == 0 { return "ba" }
    if slot == 1 { return "ba" }
    if slot == 2 { return "ba" }
    if slot == 3 { return "bámmar" }
    if slot == 4 { return "bádaid" }
    return "batar"
}

// ── Irregular verb tables ──────────────────────────────────────────────────────

// téit (to go) — strong verb, highly suppletive in the past
//   Present: tíagu, téit, téit, tíagmai, tíagid, tíagat
//   Past:    lod, lod, luid, lodmar, lodaid, lotar

fn sga_teit_present(slot: Int) -> String {
    if slot == 0 { return "tíagu" }
    if slot == 1 { return "téit" }
    if slot == 2 { return "téit" }
    if slot == 3 { return "tíagmai" }
    if slot == 4 { return "tíagid" }
    return "tíagat"
}

fn sga_teit_past(slot: Int) -> String {
    if slot == 0 { return "lod" }
    if slot == 1 { return "lod" }
    if slot == 2 { return "luid" }
    if slot == 3 { return "lodmar" }
    if slot == 4 { return "lodaid" }
    return "lotar"
}

// gaibid (to take/hold) — AI class strong verb
//   Present: gaibim, gaibi, gaibid, gaibmi, gaibthe, gaibid

fn sga_gaibid_present(slot: Int) -> String {
    if slot == 0 { return "gaibim" }
    if slot == 1 { return "gaibi" }
    if slot == 2 { return "gaibid" }
    if slot == 3 { return "gaibmi" }
    if slot == 4 { return "gaibthe" }
    return "gaibid"
}

// ad·cí (to see) — compound verb with deuterotonic stress
//   Present: ad·ciu, ad·cí, ad·cí, ad·cími, ad·cíthe, ad·ciat

fn sga_adci_present(slot: Int) -> String {
    if slot == 0 { return "ad·ciu" }
    if slot == 1 { return "ad·cí" }
    if slot == 2 { return "ad·cí" }
    if slot == 3 { return "ad·cími" }
    if slot == 4 { return "ad·cíthe" }
    return "ad·ciat"
}

// as·beir (to say) — compound verb
//   Present: as·biur, as·beir, as·beir, as·beram, as·berid, as·berat

fn sga_asbeir_present(slot: Int) -> String {
    if slot == 0 { return "as·biur" }
    if slot == 1 { return "as·beir" }
    if slot == 2 { return "as·beir" }
    if slot == 3 { return "as·beram" }
    if slot == 4 { return "as·berid" }
    return "as·berat"
}

// ── Canonical verb mapping ─────────────────────────────────────────────────────
//
// Maps English semantic labels to Old Irish infinitives / citation forms.
// Old Irish verbs are cited by 3sg present absolute (the most stable form).

fn sga_map_canonical(verb: String) -> String {
    if str_eq(verb, "be")   { return "is" }
    if str_eq(verb, "go")   { return "téit" }
    if str_eq(verb, "take") { return "gaibid" }
    if str_eq(verb, "hold") { return "gaibid" }
    if str_eq(verb, "see")  { return "ad·cí" }
    if str_eq(verb, "say")  { return "as·beir" }
    return verb
}

// ── Regular AI-class verb conjugation ─────────────────────────────────────────
//
// The AI (first) conjugation is the most productive class in Old Irish.
// Stems ending in a broad consonant take broad endings; slender take slender
// endings.  For simplicity this module uses a single generic set.
//
// Present absolute (verb-initial position):
//   Sg: 1st -aim, 2nd -ai, 3rd -aid
//   Pl: 1st -am,  2nd -aid, 3rd -at
//
// There is no regular past tense for AI verbs in the simple sense — the engine
// falls back to the citation form when past is requested for unknown verbs.

fn sga_ai_present(stem: String, slot: Int) -> String {
    if slot == 0 { return stem + "aim" }
    if slot == 1 { return stem + "ai" }
    if slot == 2 { return stem + "aid" }
    if slot == 3 { return stem + "am" }
    if slot == 4 { return stem + "aid" }
    return stem + "at"
}

// ── sga_conjugate: main conjugation entry point ───────────────────────────────
//
// verb:   Old Irish citation form or English canonical label
// tense:  "present" | "past"
// person: "first" | "second" | "third"
// number: "singular" | "plural"
//
// Returns the inflected absolute form.  Unknown tenses fall back to the citation
// form.  Conjunct forms (after particles) are not implemented; only absolute
// forms are produced.

fn sga_conjugate(verb: String, tense: String, person: String, number: String) -> String {
    let v: String = sga_map_canonical(verb)
    let slot: Int = sga_slot(person, number)

    // ── Copula "is" ───────────────────────────────────────────────────────────
    if str_eq(v, "is") {
        if str_eq(tense, "present") { return sga_copula_present(slot) }
        // Past copula: "ba" is the standard past (invariant simplification)
        return "ba"
    }

    // ── Substantive bith ──────────────────────────────────────────────────────
    if str_eq(v, "bith") {
        if str_eq(tense, "present") { return sga_bith_present(slot) }
        if str_eq(tense, "past")    { return sga_bith_past(slot) }
        return v
    }

    // ── téit (to go) ──────────────────────────────────────────────────────────
    if str_eq(v, "téit") {
        if str_eq(tense, "present") { return sga_teit_present(slot) }
        if str_eq(tense, "past")    { return sga_teit_past(slot) }
        return v
    }

    // ── gaibid (to take/hold) ─────────────────────────────────────────────────
    if str_eq(v, "gaibid") {
        if str_eq(tense, "present") { return sga_gaibid_present(slot) }
        // Past gaibid: gab- preterite (simplified to slot 2 form for all)
        return "gab"
    }

    // ── ad·cí (to see) ────────────────────────────────────────────────────────
    if str_eq(v, "ad·cí") {
        if str_eq(tense, "present") { return sga_adci_present(slot) }
        // Past: ro·acc forms exist but are complex; return citation form
        return v
    }

    // ── as·beir (to say) ──────────────────────────────────────────────────────
    if str_eq(v, "as·beir") {
        if str_eq(tense, "present") { return sga_asbeir_present(slot) }
        return v
    }

    // ── Regular AI-class verb ─────────────────────────────────────────────────
    //
    // Citation form for AI verbs ends in -id (3sg pres abs).  Strip -id to get
    // the stem, then apply AI endings.  If the verb doesn't end in -id, use the
    // whole form as the stem (best-effort).
    if str_ends_with(v, "id") {
        let stem: String = sga_drop(v, 2)
        if str_eq(tense, "present") { return sga_ai_present(stem, slot) }
        // No regular past implemented: return citation form
        return v
    }

    // Unknown verb: return citation form unchanged
    return v
}

// ── o-stem masculine declension ("fer" — man) ─────────────────────────────────
//
// The o-stem is the most common masculine declension class.
//
// Singular:  nom fer   voc a fhir  acc fer   gen fir   dat fiur
// Plural:    nom fir   voc a firu  acc firu  gen fer   dat feraib

fn sga_decline_ostem(noun: String, gram_case: String, number: String) -> String {
    if str_eq(noun, "fer") {
        if str_eq(number, "singular") {
            if str_eq(gram_case, "nominative") { return "fer" }
            if str_eq(gram_case, "vocative")   { return "fhir" }
            if str_eq(gram_case, "accusative") { return "fer" }
            if str_eq(gram_case, "genitive")   { return "fir" }
            if str_eq(gram_case, "dative")     { return "fiur" }
            return "fer"
        }
        if str_eq(gram_case, "nominative") { return "fir" }
        if str_eq(gram_case, "vocative")   { return "firu" }
        if str_eq(gram_case, "accusative") { return "firu" }
        if str_eq(gram_case, "genitive")   { return "fer" }
        if str_eq(gram_case, "dative")     { return "feraib" }
        return "fir"
    }

    // Generic o-stem masculine: append case endings to noun base.
    // Nom/Acc sg take no ending; Gen sg syncopates (approximated by +a removed);
    // the engine uses safe suffix-only approximations here.
    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return noun }
        if str_eq(gram_case, "vocative")   { return sga_lenite(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 + "u" }
        return noun
    }
    if str_eq(gram_case, "nominative") { return noun + "i" }
    if str_eq(gram_case, "vocative")   { return noun + "u" }
    if str_eq(gram_case, "accusative") { return noun + "u" }
    if str_eq(gram_case, "genitive")   { return noun }
    if str_eq(gram_case, "dative")     { return noun + "aib" }
    return noun + "i"
}

// ── ā-stem feminine declension ("ben" — woman) ────────────────────────────────
//
// The ā-stem is the most common feminine declension class.  It shows heavy
// syncope and internal change (ben -> mná in oblique forms).
//
// Singular:  nom ben   voc a ben   acc bein  gen mná    dat mnáib
// Plural:    nom mná   voc a mná   acc mná   gen ban     dat mnáib

fn sga_decline_astem(noun: String, gram_case: String, number: String) -> String {
    if str_eq(noun, "ben") {
        if str_eq(number, "singular") {
            if str_eq(gram_case, "nominative") { return "ben" }
            if str_eq(gram_case, "vocative")   { return "ben" }
            if str_eq(gram_case, "accusative") { return "bein" }
            if str_eq(gram_case, "genitive")   { return "mná" }
            if str_eq(gram_case, "dative")     { return "mnáib" }
            return "ben"
        }
        if str_eq(gram_case, "nominative") { return "mná" }
        if str_eq(gram_case, "vocative")   { return "mná" }
        if str_eq(gram_case, "accusative") { return "mná" }
        if str_eq(gram_case, "genitive")   { return "ban" }
        if str_eq(gram_case, "dative")     { return "mnáib" }
        return "mná"
    }

    // Generic ā-stem feminine: suffix-based approximation.
    if str_eq(number, "singular") {
        if str_eq(gram_case, "nominative") { return noun }
        if str_eq(gram_case, "vocative")   { return noun }
        if str_eq(gram_case, "accusative") { return noun + "i" }
        if str_eq(gram_case, "genitive")   { return noun + "e" }
        if str_eq(gram_case, "dative")     { return noun + "aib" }
        return noun
    }
    if str_eq(gram_case, "nominative") { return noun + "a" }
    if str_eq(gram_case, "vocative")   { return noun + "a" }
    if str_eq(gram_case, "accusative") { return noun + "a" }
    if str_eq(gram_case, "genitive")   { return noun }
    if str_eq(gram_case, "dative")     { return noun + "aib" }
    return noun + "a"
}

// ── Gender detection heuristic ─────────────────────────────────────────────────
//
// Ideally gender is supplied by the lexicon.  Known exemplars are routed
// explicitly; everything else defaults to o-stem masculine.

fn sga_detect_gender(noun: String) -> String {
    if str_eq(noun, "ben")  { return "feminine" }
    if str_eq(noun, "mná")  { return "feminine" }
    // Default: masculine o-stem
    return "masculine"
}

// ── sga_decline: main declension entry point ──────────────────────────────────
//
// noun:      nominative singular Old Irish noun (e.g. "fer", "ben")
// gram_case: "nominative" | "vocative" | "accusative" | "genitive" | "dative"
// number:    "singular" | "plural"
//
// Returns the inflected form.  Falls back to nominative singular on unknown input.

fn sga_decline(noun: String, gram_case: String, number: String) -> String {
    let gender: String = sga_detect_gender(noun)

    if str_eq(gender, "masculine") { return sga_decline_ostem(noun, gram_case, number) }
    if str_eq(gender, "feminine")  { return sga_decline_astem(noun, gram_case, number) }

    // Fallback
    return noun
}

// ── sga_noun_phrase: noun phrase builder ──────────────────────────────────────
//
// Old Irish has a definite article that agrees in case, number, and gender.
// The article has many allomorphs (int, in, ind, na, …).  For NLG purposes this
// module uses the simplified invariant form "in" for all definite contexts,
// placed before the declined noun.
//
// Indefinite nouns carry no article (like Latin).
//
// noun:      nominative singular Old Irish noun
// gram_case: "nominative" | "vocative" | "accusative" | "genitive" | "dative"
// number:    "singular" | "plural"
// definite:  "true" | "false"
//
// Returns the complete noun phrase string.

fn sga_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String {
    let base: String = sga_decline(noun, gram_case, number)

    if !str_eq(definite, "true") { return base }

    // Definite: prepend simplified article "in"
    return "in " + base
}
