// morphology-akk.el - Akkadian morphology for the NLG engine.
//                     𒀭𒂗𒍪 — Akkadian (akkadû), the language of Babylon and Assyria.
//
// Implements Old Babylonian Akkadian verb conjugation (G-stem / Grundstamm),
// noun declension with mimation, and noun-phrase construction.
//
// Akkadian is the oldest attested Semitic language (ca. 2800–100 BCE).
// It uses cuneiform script; we work in standard Latin transliteration
// (Old Babylonian dialect — the classical prestige form).
//
// Language profile:
//   code=akk, name=Akkadian, morph_type=semitic, word_order=VSO/SOV,
//   script=cuneiform (transliterated), family=semitic/east-semitic
//
// Key grammatical facts:
//   - Semitic trilateral root system: words built from 3-consonant roots
//     by inserting vowel patterns (e.g. root p-r-s → iparras "he decides")
//   - Grammatical gender: masculine / feminine (no neuter)
//   - Cases: nominative (-um), accusative (-am), genitive (-im) — "mimation"
//   - Number: singular / plural (dual is vestigial in verbs)
//   - Verb stems: G (basic), D (intensive), Š (causative), N (passive);
//     this file implements G-stem throughout
//   - Two main tense/aspect systems:
//       Present-future (iparras pattern): action in progress or future
//       Perfect (iptaras pattern): completed action with present relevance
//       Stative (paris pattern): resultant state, often adjectival
//   - No definite or indefinite article; case endings convey
//     determination contextually
//   - Copula: bašû (to exist/be)
//
// Verb conjugation conventions:
//   person:  "first" | "second" | "third"
//   gender:  "m" | "f"
//   number:  "singular" | "plural"
//   tense:   "present" | "perfect" | "stative"
//
// Noun declension conventions:
//   gram_case: "nom" | "acc" | "gen"
//   number:    "singular" | "plural"
//   gender:    "m" | "f"   (passed to akk_decline for gender-specific forms)
//
// Verbs covered (G-stem infinitive, transliterated):
//   "bašû"   — to exist / be  (copula)
//   "alāku"  — to go
//   "amāru"  — to see
//   "qabû"   — to say
//   "epēšu"  — to do / make
//
// Nouns covered with known mimation forms:
//   "šarrum"  — king
//   "awīlum"  — man / person
//   "bītum"   — house
//   "ilum"    — god
//
// Depends on: morphology.el (str_eq, str_len, str_slice, str_ends_with)

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

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

fn akk_str_len(s: String) -> Int {
    return str_len(s)
}

fn akk_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)
}

// ── Slot index ─────────────────────────────────────────────────────────────────
//
// Maps person × number to a 0-based slot for table lookups.
// Akkadian verb agreement does not distinguish gender in 1st person,
// and the 2nd person often conflates masc/fem in some paradigms.
// We use a 6-cell paradigm matching the most common OB presentation:
//
//   0 = 1sg   (I)
//   1 = 2sg   (you sg)
//   2 = 3sg m (he)
//   3 = 3sg f (she)
//   4 = 1pl   (we)
//   5 = 3pl   (they)
//
// Note: 2pl is rare / vestigial in attested OB texts; omitted here.

fn akk_slot(person: String, number: String) -> Int {
    if str_eq(person, "first") {
        if str_eq(number, "plural") { return 4 }
        return 0
    }
    if str_eq(person, "second") {
        return 1
    }
    // third
    if str_eq(number, "plural") { return 5 }
    return 2   // default: 3sg masc; caller may override with gender check below
}

// akk_slot_g: gender-aware slot for third person singular.
// Returns 3 (3sg fem) when person=third, number=singular, gender=f.
fn akk_slot_g(person: String, gender: String, number: String) -> Int {
    let base: Int = akk_slot(person, number)
    if str_eq(person, "third") {
        if str_eq(number, "singular") {
            if str_eq(gender, "f") { return 3 }
        }
    }
    return base
}

// ── Copula: bašû — to exist / be ──────────────────────────────────────────────
//
// bašû is suppletive and highly irregular.
// Present:  ibašši (3sg m/f), abašši (1sg), tabašši (2sg)
// Stative:  bašī (3sg m), bašiat (3sg f), bašāku (1sg)
// Perfect:  not commonly attested in G-stem; use present forms as fallback.

fn akk_copula_present(slot: Int) -> String {
    if slot == 0 { return "abašši" }     // 1sg
    if slot == 1 { return "tabašši" }    // 2sg
    if slot == 2 { return "ibašši" }     // 3sg m
    if slot == 3 { return "ibašši" }     // 3sg f (same form in attested OB)
    if slot == 4 { return "nibašši" }    // 1pl
    return "ibaššū"                       // 3pl
}

fn akk_copula_stative(slot: Int) -> String {
    if slot == 0 { return "bašāku" }     // 1sg  (stative 1sg: -āku suffix)
    if slot == 1 { return "bašāta" }     // 2sg  (-āta suffix)
    if slot == 2 { return "bašī" }       // 3sg m (unmarked base)
    if slot == 3 { return "bašiat" }     // 3sg f (-at suffix)
    if slot == 4 { return "bašānu" }     // 1pl  (-ānu suffix)
    return "bašū"                         // 3pl  (-ū suffix)
}

fn akk_is_copula(verb: String) -> Bool {
    if str_eq(verb, "bašû")  { return true }
    if str_eq(verb, "bashu") { return true }
    if str_eq(verb, "be")    { return true }
    return false
}

fn akk_conjugate_copula(tense: String, slot: Int) -> String {
    if str_eq(tense, "stative") { return akk_copula_stative(slot) }
    // present and perfect both fall back to present forms for bašû
    return akk_copula_present(slot)
}

// ── alāku — to go ─────────────────────────────────────────────────────────────
//
// Irregular: present stem is illak- (not the expected alakk-).
// Present: illak (3sg), allak (1sg), tallak (2sg), nillak (1pl), illaku (3pl)
// Perfect: ittalk- forms (less common, use illak- + perf marker)
// Stative: use present as proxy

fn akk_alaku_present(slot: Int) -> String {
    if slot == 0 { return "allak" }      // 1sg
    if slot == 1 { return "tallak" }     // 2sg
    if slot == 2 { return "illak" }      // 3sg m
    if slot == 3 { return "tallak" }     // 3sg f (same as 2sg — OB pattern)
    if slot == 4 { return "nillak" }     // 1pl
    return "illaku"                       // 3pl
}

fn akk_alaku_perfect(slot: Int) -> String {
    if slot == 0 { return "ittalak" }    // 1sg
    if slot == 1 { return "tattalak" }   // 2sg
    if slot == 2 { return "ittalak" }    // 3sg m
    if slot == 3 { return "tattalak" }   // 3sg f
    if slot == 4 { return "nittalak" }   // 1pl
    return "ittalku"                      // 3pl
}

// ── amāru — to see ────────────────────────────────────────────────────────────
//
// Present (immar-): immar (3sg), ammar (1sg), tammar (2sg)
// Perfect (imtamar-): imtamar (3sg), amtamar (1sg), tamtamar (2sg)

fn akk_amaru_present(slot: Int) -> String {
    if slot == 0 { return "ammar" }      // 1sg
    if slot == 1 { return "tammar" }     // 2sg
    if slot == 2 { return "immar" }      // 3sg m
    if slot == 3 { return "tammar" }     // 3sg f
    if slot == 4 { return "nimmar" }     // 1pl
    return "immaru"                       // 3pl
}

fn akk_amaru_perfect(slot: Int) -> String {
    if slot == 0 { return "amtamar" }    // 1sg
    if slot == 1 { return "tamtamar" }   // 2sg
    if slot == 2 { return "imtamar" }    // 3sg m
    if slot == 3 { return "tamtamar" }   // 3sg f
    if slot == 4 { return "nimtamar" }   // 1pl
    return "imtamaru"                     // 3pl
}

fn akk_amaru_stative(slot: Int) -> String {
    // amāru stative: 3sg "amir" (the one who saw / he has seen)
    if slot == 0 { return "amrāku" }
    if slot == 1 { return "amrāta" }
    if slot == 2 { return "amir" }
    if slot == 3 { return "amrat" }
    if slot == 4 { return "amrānu" }
    return "amrū"
}

// ── qabû — to say / speak ─────────────────────────────────────────────────────
//
// Present: iqabbi (3sg), aqabbi (1sg), taqabbi (2sg)
// Perfect: iqtabi (3sg), aqtabi (1sg), taqtabi (2sg)

fn akk_qabu_present(slot: Int) -> String {
    if slot == 0 { return "aqabbi" }     // 1sg
    if slot == 1 { return "taqabbi" }    // 2sg
    if slot == 2 { return "iqabbi" }     // 3sg m
    if slot == 3 { return "taqabbi" }    // 3sg f
    if slot == 4 { return "niqabbi" }    // 1pl
    return "iqabbû"                       // 3pl
}

fn akk_qabu_perfect(slot: Int) -> String {
    if slot == 0 { return "aqtabi" }     // 1sg
    if slot == 1 { return "taqtabi" }    // 2sg
    if slot == 2 { return "iqtabi" }     // 3sg m
    if slot == 3 { return "taqtabi" }    // 3sg f
    if slot == 4 { return "niqtabi" }    // 1pl
    return "iqtabû"                       // 3pl
}

fn akk_qabu_stative(slot: Int) -> String {
    if slot == 0 { return "qabāku" }
    if slot == 1 { return "qabāta" }
    if slot == 2 { return "qabi" }
    if slot == 3 { return "qabiat" }
    if slot == 4 { return "qabānu" }
    return "qabû"
}

// ── epēšu — to do / make ──────────────────────────────────────────────────────
//
// Present (ieppuš / eppuš): ieppuš (3sg), eppuš (1sg), teppuš (2sg)
// Perfect: iptešu forms

fn akk_epesu_present(slot: Int) -> String {
    if slot == 0 { return "eppuš" }      // 1sg
    if slot == 1 { return "teppuš" }     // 2sg
    if slot == 2 { return "ieppuš" }     // 3sg m
    if slot == 3 { return "teppuš" }     // 3sg f
    if slot == 4 { return "neppuš" }     // 1pl
    return "ieppušu"                      // 3pl
}

fn akk_epesu_perfect(slot: Int) -> String {
    if slot == 0 { return "iptešu" }     // 1sg  (irregular: root ʿ-p-š)
    if slot == 1 { return "taptešu" }    // 2sg
    if slot == 2 { return "iptešu" }     // 3sg m
    if slot == 3 { return "taptešu" }    // 3sg f
    if slot == 4 { return "niptešu" }    // 1pl
    return "iptešū"                       // 3pl
}

fn akk_epesu_stative(slot: Int) -> String {
    if slot == 0 { return "epšāku" }
    if slot == 1 { return "epšāta" }
    if slot == 2 { return "epuš" }
    if slot == 3 { return "epšat" }
    if slot == 4 { return "epšānu" }
    return "epšū"
}

// ── Regular G-stem paradigms (iparras model) ──────────────────────────────────
//
// For regular verbs not in the irregular table, we apply the standard
// OB G-stem paradigm using a caller-supplied present stem and perfect stem.
// The stems must be pre-computed by the caller (or vocabulary layer).
//
// iparras (present) endings by slot:
//   1sg:  a- prefix
//   2sg:  ta- prefix
//   3sg m: i- prefix
//   3sg f: ta- prefix  (same prefix as 2sg)
//   1pl:  ni- prefix
//   3pl:  i- prefix + -ū suffix
//
// For the generic fallback we use "iparras" as the model template.

fn akk_regular_present(stem: String, slot: Int) -> String {
    // stem is the 3sg m form (i-prefix already present in conventional citation)
    // We rebuild from the bare root portion by stripping/adding prefixes.
    // Simplification: return prefixed forms using the provided present-3sg string.
    if slot == 0 { return "a" + stem }   // 1sg: a + stem (strip i-, add a-)
    if slot == 1 { return "ta" + stem }  // 2sg
    if slot == 2 { return "i" + stem }   // 3sg m
    if slot == 3 { return "ta" + stem }  // 3sg f
    if slot == 4 { return "ni" + stem }  // 1pl
    return "i" + stem + "u"              // 3pl: i + stem + -ū
}

fn akk_regular_perfect(stem: String, slot: Int) -> String {
    // Perfect (iptaras) — uses infix -ta- after first root consonant.
    // stem here is the 3sg perfect form; we apply person endings.
    if slot == 0 { return "a" + stem }   // 1sg
    if slot == 1 { return "ta" + stem }  // 2sg
    if slot == 2 { return "i" + stem }   // 3sg m
    if slot == 3 { return "ta" + stem }  // 3sg f
    if slot == 4 { return "ni" + stem }  // 1pl
    return "i" + stem + "u"              // 3pl
}

fn akk_regular_stative(stem: String, slot: Int) -> String {
    // Stative (paris): 3sg m has zero ending; others take person suffixes.
    if slot == 0 { return stem + "āku" }  // 1sg
    if slot == 1 { return stem + "āta" }  // 2sg
    if slot == 2 { return stem }           // 3sg m: bare stem
    if slot == 3 { return stem + "at" }   // 3sg f
    if slot == 4 { return stem + "ānu" }  // 1pl
    return stem + "ū"                      // 3pl
}

// ── Known-verb dispatcher ─────────────────────────────────────────────────────

fn akk_known_verb(verb: String, tense: String, slot: Int) -> String {
    // bašû — to be / exist
    if str_eq(verb, "bašû") {
        return akk_conjugate_copula(tense, slot)
    }
    if str_eq(verb, "bashu") {
        return akk_conjugate_copula(tense, slot)
    }

    // alāku — to go
    if str_eq(verb, "alāku") {
        if str_eq(tense, "perfect") { return akk_alaku_perfect(slot) }
        if str_eq(tense, "stative") { return akk_alaku_present(slot) }
        return akk_alaku_present(slot)
    }
    if str_eq(verb, "alaku") {
        if str_eq(tense, "perfect") { return akk_alaku_perfect(slot) }
        return akk_alaku_present(slot)
    }

    // amāru — to see
    if str_eq(verb, "amāru") {
        if str_eq(tense, "perfect") { return akk_amaru_perfect(slot) }
        if str_eq(tense, "stative") { return akk_amaru_stative(slot) }
        return akk_amaru_present(slot)
    }
    if str_eq(verb, "amaru") {
        if str_eq(tense, "perfect") { return akk_amaru_perfect(slot) }
        if str_eq(tense, "stative") { return akk_amaru_stative(slot) }
        return akk_amaru_present(slot)
    }

    // qabû — to say
    if str_eq(verb, "qabû") {
        if str_eq(tense, "perfect") { return akk_qabu_perfect(slot) }
        if str_eq(tense, "stative") { return akk_qabu_stative(slot) }
        return akk_qabu_present(slot)
    }
    if str_eq(verb, "qabu") {
        if str_eq(tense, "perfect") { return akk_qabu_perfect(slot) }
        if str_eq(tense, "stative") { return akk_qabu_stative(slot) }
        return akk_qabu_present(slot)
    }

    // epēšu — to do / make
    if str_eq(verb, "epēšu") {
        if str_eq(tense, "perfect") { return akk_epesu_perfect(slot) }
        if str_eq(tense, "stative") { return akk_epesu_stative(slot) }
        return akk_epesu_present(slot)
    }
    if str_eq(verb, "epesu") {
        if str_eq(tense, "perfect") { return akk_epesu_perfect(slot) }
        if str_eq(tense, "stative") { return akk_epesu_stative(slot) }
        return akk_epesu_present(slot)
    }

    return ""
}

// ── Main conjugation entry point ──────────────────────────────────────────────
//
// akk_conjugate: conjugate an Akkadian verb (G-stem).
//
// verb:   G-stem infinitive (transliterated, e.g. "alāku", "amāru")
// tense:  "present" | "perfect" | "stative"
// person: "first" | "second" | "third"
// number: "singular" | "plural"
//
// Returns:
//   - Inflected form for known verbs
//   - verb unchanged as safe fallback for unknown verbs

fn akk_conjugate(verb: String, tense: String, person: String, number: String) -> String {
    let slot: Int = akk_slot(person, number)

    // Copula shortcut
    if akk_is_copula(verb) {
        return akk_conjugate_copula(tense, slot)
    }

    // Known-verb table
    let known: String = akk_known_verb(verb, tense, slot)
    if !str_eq(known, "") {
        return known
    }

    // Unknown verb: safe fallback
    return verb
}

// ── Noun declension ────────────────────────────────────────────────────────────
//
// akk_decline: decline an Akkadian noun for gram_case and number.
//
// Mimation: OB nouns bear final -m in all case endings (mimation).
// The base noun (dictionary form) is the nominative singular with mimation.
// We strip the nominative -um ending (if present) to obtain the bare stem,
// then apply the requested ending.
//
// Masculine case endings (singular):
//   Nominative: -um
//   Accusative: -am
//   Genitive:   -im
//
// Masculine case endings (plural):
//   Nominative: -ūtum  (or -ū in construct)
//   Accusative/Genitive: -ātim  (or -ī in construct)
//
// Feminine nouns (identified by -tum nom sg ending):
//   Sg nominative: -tum, accusative: -tam, genitive: -tim
//   Pl nominative: -ātum, genitive/accusative: -ātim
//
// Known irregular stems (the vocabulary layer should pass dictionary forms):
//   šarrum  → stem: šarr-
//   awīlum  → stem: awīl-
//   bītum   → stem: bīt-
//   ilum    → stem: il-

fn akk_strip_nom(noun: String) -> String {
    // Strip -um (masc nom sg mimation ending) to get bare stem
    if akk_str_ends(noun, "um") {
        return akk_str_drop_last(noun, 2)
    }
    // Strip -tum (fem nom sg)
    if akk_str_ends(noun, "tum") {
        return akk_str_drop_last(noun, 3)
    }
    // Already a bare stem or unusual form: return as-is
    return noun
}

fn akk_is_fem(noun: String) -> Bool {
    // Feminine nouns in OB typically end in -tum (nom sg)
    if akk_str_ends(noun, "tum") { return true }
    if akk_str_ends(noun, "tam") { return true }
    if akk_str_ends(noun, "tim") { return true }
    return false
}

fn akk_decline(noun: String, gram_case: String, number: String) -> String {
    let fem: Bool = akk_is_fem(noun)
    let stem: String = akk_strip_nom(noun)

    if str_eq(number, "singular") {
        if fem {
            if str_eq(gram_case, "nom") { return stem + "tum" }
            if str_eq(gram_case, "acc") { return stem + "tam" }
            if str_eq(gram_case, "gen") { return stem + "tim" }
            return stem + "tum"
        }
        // Masculine
        if str_eq(gram_case, "nom") { return stem + "um" }
        if str_eq(gram_case, "acc") { return stem + "am" }
        if str_eq(gram_case, "gen") { return stem + "im" }
        return stem + "um"
    }

    // Plural
    if fem {
        if str_eq(gram_case, "nom") { return stem + "ātum" }
        // acc and gen merge in the oblique plural
        return stem + "ātim"
    }
    // Masculine plural
    if str_eq(gram_case, "nom") { return stem + "ūtum" }
    return stem + "ātim"
}

// ── Noun phrase ────────────────────────────────────────────────────────────────
//
// akk_noun_phrase: produce the surface noun phrase.
//
// Akkadian has no definite or indefinite article.  Determination is conveyed
// by context, word order, and the genitive construct chain (status constructus).
// The definite parameter is accepted but has no surface effect: the declined
// noun is returned in either case.
//
// noun:      dictionary form (nominative singular with mimation, e.g. "šarrum")
// gram_case: "nom" | "acc" | "gen"
// number:    "singular" | "plural"
// definite:  "true" | "false"  (no surface effect in Akkadian)

fn akk_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String {
    return akk_decline(noun, gram_case, number)
}

// ── Canonical verb mapping ─────────────────────────────────────────────────────
//
// akk_map_canonical: map cross-lingual English canonical verb labels to
// their Akkadian G-stem infinitive equivalents.

fn akk_map_canonical(verb: String) -> String {
    if str_eq(verb, "be")    { return "bašû"  }
    if str_eq(verb, "go")    { return "alāku" }
    if str_eq(verb, "see")   { return "amāru" }
    if str_eq(verb, "say")   { return "qabû"  }
    if str_eq(verb, "speak") { return "qabû"  }
    if str_eq(verb, "do")    { return "epēšu" }
    if str_eq(verb, "make")  { return "epēšu" }
    return verb
}
