// morphology-fi.el - Finnish morphology: noun case inflection and verb conjugation.
//
// Finnish is SOV, agglutinative, 15 grammatical cases, no grammatical gender,
// no articles.  Morphology is suffix-chain based.
//
// Key facts:
//   - Vowel harmony: suffixes harmonize with the stem's vowel class.
//     Back vowels (a, o, u) → back-harmony suffixes (-ssa, -sta, -an, etc.)
//     Front vowels (ä, ö, y) or neutral-only stems → front-harmony (-ssä, -stä, -än, etc.)
//   - Consonant gradation: alternation between strong and weak consonant grades.
//     Full gradation tables are large; this module implements the most common
//     patterns.  Irregular/exceptional stems must be supplied in inflected form.
//   - Verbs conjugate for person (1/2/3) and number (singular/plural), plus tense
//     (present/past) and polarity (affirmative/negative).
//   - Question suffix: -ko (back harmony) / -kö (front harmony), appended to verb.
//
// Depends on: (no dependencies - standalone morphology module)

// ── Vowel harmony ─────────────────────────────────────────────────────────────
//
// fi_harmony(word) -> "back" | "front"
//
// Scan the word right-to-left for the last unambiguously back (a, o, u) or
// front (ä, ö, y) vowel.  Neutral vowels (e, i) do not determine the class.
// If only neutral vowels are found, default to "front" (the conservative choice
// for borrowed words and those without clear back vowels).

fn fi_harmony(word: String) -> String {
    let n: Int = str_len(word)
    let i: Int = n - 1
    while i >= 0 {
        let c: String = str_slice(word, i, i + 1)
        // Back vowels
        if str_eq(c, "a") { return "back" }
        if str_eq(c, "o") { return "back" }
        if str_eq(c, "u") { return "back" }
        // Front vowels (UTF-8; Finnish ä is U+00E4, ö is U+00F6)
        // In a byte scan we may land mid-codepoint; we do a substring comparison
        // by checking two-byte sequences at position i-1 when c is a lead byte.
        if str_eq(c, "ä") { return "front" }
        if str_eq(c, "ö") { return "front" }
        if str_eq(c, "y") { return "front" }
        let i = i - 1
    }
    // Default: front (covers neutral-only and unknown stems)
    return "front"
}

// ── Suffix harmonization ──────────────────────────────────────────────────────
//
// fi_suffix(base, harmony) -> String
//
// Given a back-harmony base suffix, return the harmonized form.
// Only the a/ä alternation is handled here; all other vowels stay the same.
// E.g. fi_suffix("ssa", "front") -> "ssä"
//      fi_suffix("ssa", "back")  -> "ssa"

fn fi_suffix(base: String, harmony: String) -> String {
    if str_eq(harmony, "front") {
        // Replace every 'a' with 'ä' and every 'o' in suffix with 'ö'.
        // We handle only the common patterns used in Finnish case suffixes.
        if str_eq(base, "a")    { return "ä" }
        if str_eq(base, "ssa")  { return "ssä" }
        if str_eq(base, "sta")  { return "stä" }
        if str_eq(base, "an")   { return "än" }
        if str_eq(base, "aan")  { return "ään" }
        if str_eq(base, "lla")  { return "llä" }
        if str_eq(base, "lta")  { return "ltä" }
        if str_eq(base, "lle")  { return "lle" }
        if str_eq(base, "na")   { return "nä" }
        if str_eq(base, "ksi")  { return "ksi" }
        if str_eq(base, "tta")  { return "ttä" }
        if str_eq(base, "ta")   { return "tä" }
        if str_eq(base, "ja")   { return "jä" }
        if str_eq(base, "oja")  { return "öjä" }
        if str_eq(base, "issa") { return "issä" }
        if str_eq(base, "ista") { return "istä" }
        if str_eq(base, "ihin") { return "ihin" }
        if str_eq(base, "illa") { return "illä" }
        if str_eq(base, "ilta") { return "iltä" }
        if str_eq(base, "ille") { return "ille" }
        if str_eq(base, "ina")  { return "inä" }
        if str_eq(base, "itta") { return "ittä" }
        if str_eq(base, "ko")   { return "kö" }
        if str_eq(base, "pa")   { return "pä" }
        if str_eq(base, "va")   { return "vä" }
        if str_eq(base, "ma")   { return "mä" }
        if str_eq(base, "han")  { return "hän" }
        if str_eq(base, "lla")  { return "llä" }
        // Fall back: return the back-harmony form unchanged
        return base
    }
    // Back harmony: return as-is
    return base
}

// ── Noun case inflection ──────────────────────────────────────────────────────
//
// fi_noun_case(stem, gram_case, number, harmony) -> String
//
// Computes the inflected noun form from the oblique stem, case name, number
// ("singular" | "plural"), and vowel harmony class.
//
// The "stem" is the oblique/genitive stem (e.g. talo for talo, puhu for puhua).
// For most Type-1 nouns the oblique stem = dictionary form minus final vowel.
// For the singular nominative the dictionary form itself is used (passed directly).
//
// Cases and their suffixes (talo → back, stem "talo"):
//   nominative sg   : stem (no suffix)         → talo
//   nominative pl   : stem + t                 → talot
//   genitive sg     : stem + n                 → talon
//   genitive pl     : stem + jen / jen         → talojen
//   accusative sg   : stem + n (= gen sg)      → talon
//   accusative pl   : stem + t (= nom pl)      → talot
//   partitive sg    : stem + a/ä               → taloa
//   partitive pl    : stem + ja/jä             → taloja
//   inessive sg     : stem + ssa/ssä           → talossa
//   inessive pl     : stem + issa/issä         → taloissa
//   elative sg      : stem + sta/stä           → talosta
//   elative pl      : stem + ista/istä         → taloista
//   illative sg     : stem + vowel + n         → taloon (long vowel + n)
//   illative pl     : stem + ihin              → taloihin
//   adessive sg     : stem + lla/llä           → talolla
//   adessive pl     : stem + illa/illä         → taloilla
//   ablative sg     : stem + lta/ltä           → talolta
//   ablative pl     : stem + ilta/iltä         → taloilta
//   allative sg     : stem + lle               → talolle
//   allative pl     : stem + ille              → taloille
//   essive sg       : stem + na/nä             → talona
//   essive pl       : stem + ina/inä           → taloina
//   translative sg  : stem + ksi               → taloksi
//   translative pl  : stem + iksi              → taloiksi
//   instructive pl  : stem + in                → taloin  (plural only)
//   abessive sg     : stem + tta/ttä           → talotta
//   abessive pl     : stem + itta/ittä         → taloitta
//   comitative pl   : stem + ineen             → taloineen (plural only)

fn fi_noun_case(stem: String, gram_case: String, number: String, harmony: String) -> String {
    let sg: Bool = str_eq(number, "singular")

    if str_eq(gram_case, "nominative") {
        if sg  { return stem }
        return stem + "t"
    }

    if str_eq(gram_case, "genitive") {
        if sg  { return stem + "n" }
        return stem + "jen"
    }

    if str_eq(gram_case, "accusative") {
        if sg  { return stem + "n" }
        return stem + "t"
    }

    if str_eq(gram_case, "partitive") {
        if sg  { return stem + fi_suffix("a", harmony) }
        return stem + fi_suffix("ja", harmony)
    }

    if str_eq(gram_case, "inessive") {
        if sg  { return stem + fi_suffix("ssa", harmony) }
        return stem + fi_suffix("issa", harmony)
    }

    if str_eq(gram_case, "elative") {
        if sg  { return stem + fi_suffix("sta", harmony) }
        return stem + fi_suffix("ista", harmony)
    }

    if str_eq(gram_case, "illative") {
        if sg {
            // Singular illative: final vowel of stem is lengthened + n
            // e.g. talo → taloon, puu → puuhun, käsi → käteen
            // We take the last character of the stem and double it, then add n.
            let last: String = fi_str_last_char(stem)
            return stem + last + "n"
        }
        return stem + fi_suffix("ihin", harmony)
    }

    if str_eq(gram_case, "adessive") {
        if sg  { return stem + fi_suffix("lla", harmony) }
        return stem + fi_suffix("illa", harmony)
    }

    if str_eq(gram_case, "ablative") {
        if sg  { return stem + fi_suffix("lta", harmony) }
        return stem + fi_suffix("ilta", harmony)
    }

    if str_eq(gram_case, "allative") {
        // Allative is -lle for both numbers (only the stem differs)
        if sg  { return stem + "lle" }
        return stem + "ille"
    }

    if str_eq(gram_case, "essive") {
        if sg  { return stem + fi_suffix("na", harmony) }
        return stem + fi_suffix("ina", harmony)
    }

    if str_eq(gram_case, "translative") {
        if sg  { return stem + "ksi" }
        return stem + "iksi"
    }

    if str_eq(gram_case, "instructive") {
        // Instructive is plural only in modern Finnish
        return stem + "in"
    }

    if str_eq(gram_case, "abessive") {
        if sg  { return stem + fi_suffix("tta", harmony) }
        return stem + fi_suffix("itta", harmony)
    }

    if str_eq(gram_case, "comitative") {
        // Comitative is plural only
        return stem + "ineen"
    }

    // Unknown case: return stem unchanged
    return stem
}

// ── Noun helper: str_last_char ─────────────────────────────────────────────────
//
// Return the last Unicode character of a string.
// Mirrors the helper in morphology.el; redefined here for standalone use.

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

// ── Full noun inflection (convenience wrapper) ────────────────────────────────
//
// fi_apply_case(noun, gram_case, number) -> String
//
// Accepts the nominative singular form (dictionary form), derives the harmony
// class, and produces the requested case form.
//
// For most regular nouns the oblique stem equals the dictionary form.  The
// illative singular is handled by appending the last vowel + n.

fn fi_apply_case(noun: String, gram_case: String, number: String) -> String {
    let harmony: String = fi_harmony(noun)
    // For nominative singular, return the noun as-is.
    if str_eq(gram_case, "nominative") {
        if str_eq(number, "singular") { return noun }
        return noun + "t"
    }
    // For all other cases, use the noun as the oblique stem.
    // (Callers that need consonant-gradated stems must pass the graded stem
    // directly via fi_noun_case.)
    return fi_noun_case(noun, gram_case, number, harmony)
}

// ── Verb stem extraction ──────────────────────────────────────────────────────
//
// fi_verb_stem(dict_form) -> String
//
// Strip the infinitive ending to get the present-tense stem.
//
// Type 1 verbs (most common): infinitive ends in -a/-ä, stem = infinitive - a/ä
//   puhua → puhu, juosta → juos (irregular, handled separately)
// Type 2 verbs: end in -da/-dä, stem = infinitive - da/dä
//   syödä → syö, juoda → juo
// Type 3 verbs: end in -la/-lä, -ra/-rä, -na/-nä, -sta/-stä
//   tulla → tul + l → stem "tull" (double consonant)
// Type 4 verbs: end in -ata/-ätä
//   tavata → tapaa (irregular lengthening, handled as irregular)
//
// For NLG purposes we handle Type 1 and Type 2 as the most frequent.

fn fi_verb_stem(dict_form: String) -> String {
    // Type 2: -da/-dä → drop 2 characters
    if str_ends_with(dict_form, "da") {
        return str_drop_last(dict_form, 2)
    }
    if str_ends_with(dict_form, "dä") {
        return str_drop_last(dict_form, 2)
    }
    // Type 3: -lla/-llä, -rra, -nna → drop last 2 chars (keeps double consonant)
    if str_ends_with(dict_form, "lla") {
        return str_drop_last(dict_form, 2)
    }
    if str_ends_with(dict_form, "llä") {
        return str_drop_last(dict_form, 2)
    }
    if str_ends_with(dict_form, "rra") {
        return str_drop_last(dict_form, 2)
    }
    if str_ends_with(dict_form, "nna") {
        return str_drop_last(dict_form, 2)
    }
    // Type 1 (and default): -a/-ä → drop 1 character
    if str_ends_with(dict_form, "a") {
        return str_drop_last(dict_form, 1)
    }
    if str_ends_with(dict_form, "ä") {
        return str_drop_last(dict_form, 1)
    }
    return dict_form
}

// ── Irregular verb table ──────────────────────────────────────────────────────
//
// Returns an 18-element list encoding essential irregular paradigm forms, or an
// empty list if the verb is regular.
//
// Slot layout (0-indexed):
//   0  inf        infinitive (dictionary form)
//   1  pres_1sg   present 1sg
//   2  pres_2sg   present 2sg
//   3  pres_3sg   present 3sg
//   4  pres_1pl   present 1pl
//   5  pres_2pl   present 2pl
//   6  pres_3pl   present 3pl
//   7  past_1sg   past 1sg
//   8  past_2sg   past 2sg
//   9  past_3sg   past 3sg
//   10 past_1pl   past 1pl
//   11 past_2pl   past 2pl
//   12 past_3pl   past 3pl
//   13 neg_stem   negative stem (used with en/et/ei/emme/ette/eivät)
//   14 cond_stem  conditional stem (for future use)
//   15 imp_2sg    imperative 2sg
//   16 part_pres  present participle stem
//   17 part_past  past participle

fn fi_irregular_verb(dict_form: String) -> [String] {
    let empty: [String] = []

    // olla — to be (the most irregular Finnish verb)
    if str_eq(dict_form, "olla") {
        let r: [String] = ["olla", "olen", "olet", "on", "olemme", "olette", "ovat",
                           "olin", "olit", "oli", "olimme", "olitte", "olivat",
                           "ole", "olis", "ole", "oleva", "ollut"]
        return r
    }

    // voida — can / to be able to
    if str_eq(dict_form, "voida") {
        let r: [String] = ["voida", "voin", "voit", "voi", "voimme", "voitte", "voivat",
                           "voin", "voit", "voi", "voimme", "voitte", "voivat",
                           "voi", "vois", "voi", "voiva", "voinut"]
        return r
    }

    // mennä — to go (Type 3 with irregularities)
    if str_eq(dict_form, "mennä") {
        let r: [String] = ["mennä", "menen", "menet", "menee", "menemme", "menette", "menevät",
                           "menin", "menit", "meni", "menimme", "menitte", "menivät",
                           "mene", "menis", "mene", "menevä", "mennyt"]
        return r
    }

    // tulla — to come (Type 3)
    if str_eq(dict_form, "tulla") {
        let r: [String] = ["tulla", "tulen", "tulet", "tulee", "tulemme", "tulette", "tulevat",
                           "tulin", "tulit", "tuli", "tulimme", "tulitte", "tulivat",
                           "tule", "tulis", "tule", "tuleva", "tullut"]
        return r
    }

    // tehdä — to do / make (Type 2, irregular)
    if str_eq(dict_form, "tehdä") {
        let r: [String] = ["tehdä", "teen", "teet", "tekee", "teemme", "teette", "tekevät",
                           "tein", "teit", "teki", "teimme", "teitte", "tekivät",
                           "tee", "tekis", "tee", "tekevä", "tehnyt"]
        return r
    }

    // nähdä — to see (Type 2, irregular)
    if str_eq(dict_form, "nähdä") {
        let r: [String] = ["nähdä", "näen", "näet", "näkee", "näemme", "näette", "näkevät",
                           "näin", "näit", "näki", "näimme", "näitte", "näkivät",
                           "näe", "näkis", "näe", "näkevä", "nähnyt"]
        return r
    }

    // saada — to get / to be able to (Type 2)
    if str_eq(dict_form, "saada") {
        let r: [String] = ["saada", "saan", "saat", "saa", "saamme", "saatte", "saavat",
                           "sain", "sait", "sai", "saimme", "saitte", "saivat",
                           "saa", "sais", "saa", "saava", "saanut"]
        return r
    }

    // pitää — must / to like (Type 1 with stem change)
    if str_eq(dict_form, "pitää") {
        let r: [String] = ["pitää", "pidän", "pidät", "pitää", "pidämme", "pidätte", "pitävät",
                           "pidin", "pidit", "piti", "pidimme", "piditte", "pitivät",
                           "pidä", "pitäis", "pidä", "pitävä", "pitänyt"]
        return r
    }

    // tietää — to know (Type 1 with stem change)
    if str_eq(dict_form, "tietää") {
        let r: [String] = ["tietää", "tiedän", "tiedät", "tietää", "tiedämme", "tiedätte", "tietävät",
                           "tiesin", "tiesit", "tiesi", "tiesimme", "tiesitte", "tiesivät",
                           "tiedä", "tietäis", "tiedä", "tietävä", "tiennyt"]
        return r
    }

    return empty
}

// ── Present-tense personal endings ───────────────────────────────────────────
//
// Type-1 verb present tense endings (suffix onto stem):
//   1sg: -n     2sg: -t     3sg: stem-final vowel lengthened (no suffix)
//   1pl: -mme   2pl: -tte   3pl: -vat/-vät
//
// The 3sg form doubles the final vowel of the stem (puhu → puhuu, tule → tulee).
// The 3pl uses the harmony-dependent -vat/-vät suffix.

fn fi_present_ending(stem: String, person: String, number: String, harmony: String) -> String {
    if str_eq(number, "singular") {
        if str_eq(person, "first")  { return stem + "n" }
        if str_eq(person, "second") { return stem + "t" }
        if str_eq(person, "third") {
            // 3sg: lengthen final vowel
            let last: String = fi_str_last_char(stem)
            return stem + last
        }
    }
    if str_eq(number, "plural") {
        if str_eq(person, "first")  { return stem + "mme" }
        if str_eq(person, "second") { return stem + "tte" }
        if str_eq(person, "third")  { return stem + fi_suffix("vat", harmony) }
    }
    return stem
}

// ── Past-tense forms ──────────────────────────────────────────────────────────
//
// Type-1 verbs form the past by inserting -i- between the stem and the personal
// ending.  The stem-final vowel may contract before -i-.
//
// puhua: puhu + i → puhui + n → puhuin (1sg past)
// Common contraction: stem final -a/-ä drops before -i-
//   puhua stem puhu → puhu + i = puhui (no drop needed, -u not -a)
//   tavata → contraction gives tavasi (handled as irregular or Type 4)
// For Type-1 verbs with -u/-y final stem the rule is simple concatenation.

fn fi_past_stem(stem: String) -> String {
    // If stem ends in a or ä, they may contract.  For Type-1 verbs where the
    // infinitive is -aa/-ää the stem ends in -a/-ä; before -i- that often
    // gives -oi-/-öi- (e.g. puhua: puhu → puhui, but sanoa: sano → sanoi).
    // The heuristic: if the stem already ends in a vowel other than a/ä, just
    // append i.  If it ends in a/ä, convert to o/ö + i (common pattern).
    let last: String = fi_str_last_char(stem)
    if str_eq(last, "a") {
        return str_drop_last(stem, 1) + "oi"
    }
    if str_eq(last, "ä") {
        return str_drop_last(stem, 1) + "öi"
    }
    return stem + "i"
}

fn fi_past_ending(stem: String, person: String, number: String, harmony: String) -> String {
    let pstem: String = fi_past_stem(stem)
    if str_eq(number, "singular") {
        if str_eq(person, "first")  { return pstem + "n" }
        if str_eq(person, "second") { return pstem + "t" }
        if str_eq(person, "third")  { return str_drop_last(pstem, 1) }
    }
    if str_eq(number, "plural") {
        if str_eq(person, "first")  { return pstem + "mme" }
        if str_eq(person, "second") { return pstem + "tte" }
        if str_eq(person, "third")  { return pstem + fi_suffix("vat", harmony) }
    }
    return pstem
}

// ── Negative forms ────────────────────────────────────────────────────────────
//
// Finnish negation: negative auxiliary ei (conjugated for person/number) +
// verb in the connective (negative) stem = infinitive stem without personal ending.
//
// Negative auxiliary conjugation:
//   1sg: en   2sg: et   3sg: ei
//   1pl: emme  2pl: ette  3pl: eivät
//
// The negative stem for most verbs = present stem (the form without any ending).

fn fi_neg_aux(person: String, number: String) -> String {
    if str_eq(number, "singular") {
        if str_eq(person, "first")  { return "en" }
        if str_eq(person, "second") { return "et" }
        if str_eq(person, "third")  { return "ei" }
    }
    if str_eq(number, "plural") {
        if str_eq(person, "first")  { return "emme" }
        if str_eq(person, "second") { return "ette" }
        if str_eq(person, "third")  { return "eivät" }
    }
    return "ei"
}

fn fi_negative(verb: String, person: String, number: String) -> String {
    let irreg: [String] = fi_irregular_verb(verb)
    let aux: String = fi_neg_aux(person, number)
    if native_list_len(irreg) > 0 {
        let neg_stem: String = native_list_get(irreg, 13)
        return aux + " " + neg_stem
    }
    let stem: String = fi_verb_stem(verb)
    return aux + " " + stem
}

// ── Main conjugation entry point ──────────────────────────────────────────────
//
// fi_conjugate(verb, tense, person, number) -> String
//
// tense:  "present" | "past"
// person: "first" | "second" | "third"
// number: "singular" | "plural"

fn fi_conjugate(verb: String, tense: String, person: String, number: String) -> String {
    let harmony: String = fi_harmony(verb)

    // Check irregular table first
    let irreg: [String] = fi_irregular_verb(verb)
    if native_list_len(irreg) > 0 {
        if str_eq(tense, "present") {
            if str_eq(number, "singular") {
                if str_eq(person, "first")  { return native_list_get(irreg, 1) }
                if str_eq(person, "second") { return native_list_get(irreg, 2) }
                if str_eq(person, "third")  { return native_list_get(irreg, 3) }
            }
            if str_eq(number, "plural") {
                if str_eq(person, "first")  { return native_list_get(irreg, 4) }
                if str_eq(person, "second") { return native_list_get(irreg, 5) }
                if str_eq(person, "third")  { return native_list_get(irreg, 6) }
            }
        }
        if str_eq(tense, "past") {
            if str_eq(number, "singular") {
                if str_eq(person, "first")  { return native_list_get(irreg, 7) }
                if str_eq(person, "second") { return native_list_get(irreg, 8) }
                if str_eq(person, "third")  { return native_list_get(irreg, 9) }
            }
            if str_eq(number, "plural") {
                if str_eq(person, "first")  { return native_list_get(irreg, 10) }
                if str_eq(person, "second") { return native_list_get(irreg, 11) }
                if str_eq(person, "third")  { return native_list_get(irreg, 12) }
            }
        }
    }

    // Regular verbs
    let stem: String = fi_verb_stem(verb)

    if str_eq(tense, "present") {
        return fi_present_ending(stem, person, number, harmony)
    }

    if str_eq(tense, "past") {
        return fi_past_ending(stem, person, number, harmony)
    }

    return stem
}

// ── Question suffix ───────────────────────────────────────────────────────────
//
// Finnish questions are formed by appending -ko (back harmony) or -kö (front
// harmony) directly to the verb (or sometimes another focus word).

fn fi_question_suffix(harmony: String) -> String {
    if str_eq(harmony, "front") { return "kö" }
    return "ko"
}

// ── Question formation ────────────────────────────────────────────────────────
//
// fi_make_question: append the appropriate question suffix to a verb form.

fn fi_make_question(verb_form: String, harmony: String) -> String {
    return verb_form + fi_question_suffix(harmony)
}

// ── Convenience: inflect a noun through all 15 cases ─────────────────────────
//
// Returns a 30-element list: [case_name, sg_form, case_name, pl_form, ...]
// for all 15 cases.  Plural-only cases (instructive, comitative) have an
// empty string for the singular slot.

fn fi_full_paradigm(noun: String) -> [String] {
    let harmony: String = fi_harmony(noun)
    let r: [String] = []
    let cases: [String] = ["nominative", "genitive", "accusative", "partitive",
                           "inessive", "elative", "illative", "adessive",
                           "ablative", "allative", "essive", "translative",
                           "instructive", "abessive", "comitative"]
    let n: Int = native_list_len(cases)
    let i: Int = 0
    while i < n {
        let c: String = native_list_get(cases, i)
        let r = native_list_append(r, c)
        // Singular
        if str_eq(c, "instructive") {
            let r = native_list_append(r, "")
        } else {
            if str_eq(c, "comitative") {
                let r = native_list_append(r, "")
            } else {
                let r = native_list_append(r, fi_noun_case(noun, c, "singular", harmony))
            }
        }
        // Plural
        let r = native_list_append(r, fi_noun_case(noun, c, "plural", harmony))
        let i = i + 1
    }
    return r
}
