// Adjective Practice © 2025 by J. David Eisenberg is licensed under CC BY-NC-SA 4.0

/*
 * for testing:
 * aktuellesProblem = {"artikel": "der/die/das/die", "artikelTyp": 0, "nomen": "Messer/Messer/n", "adjektiv": "braun", "kasus": 1, "genus": "n", "nummer": 0, "modell": dingeAkkModell.next().value};
 */
const NOMINATIV = 0;
const AKKUSATIV = 1;
const DATIV = 2;
const kasusNamen = ["Nominativ", "Akkusativ", "Dativ"]

const SINGULAR = 0;
const PLURAL = 1;

const MASKULIN = 0;
const FEMININ = 1;
const NEUTRAL = 2;
const PLURALGENUS = 3;
const genusHash = {"m": MASKULIN, "f": FEMININ, "n": NEUTRAL};


const SCHWACH = 0;
const GEMISCHT = 1;

let bewertung = 0;
let nFrage = 0;
let nVersuch = 0;

let aktuellesProblem;


/*
 * Gibt random integer 0 <= resultat < max
 */
function getRandomInt(max) {
  return Math.floor(Math.random() * max);
}

function mischen(arr) {
    var n = arr.length;
    for (var i = 0; i < n - 1; i++) {
        j = i + getRandomInt(n - i);
        var temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

function* makeArrIterator(arr) {
    let index = 0;
    mischen(arr);
    while (true) {
        value = arr[index];
        index++;
        if (index == arr.length) {
            index = 0;
            mischen(arr);
        }
        yield value;
    }
}

let artikelTypMap = {
    "der": SCHWACH,
    "dieser": SCHWACH,
    "ein": GEMISCHT,
    "mein": GEMISCHT,
    "dein": GEMISCHT,
    "unser": GEMISCHT
};

/* Nom - Akk - Dat */
let schwachArtikelForme = {
    "der": [
        ["der", "die", "das", "die"],
        ["den", "die", "das", "die"],
        ["dem", "der", "dem", "den"]],
    "dieser": [
        ["dieser", "diese", "dieses", "diese"],
        ["diesen", "diese", "dieses", "diese"],
        ["diesem", "dieser", "diesem", "diesen"]]
};

let gemischtArtikelEndungen = [
    ["", "e", "", "e"], // Nom
    ["en", "e", "", "e"], // Akk
    ["em", "er", "em", "en"] // Dat
];


let schwachAdjEndungen = [
    ["e", "e", "e", "en"], // Nom
    ["en", "e", "e", "en"], // Akk
    ["en", "en", "en", "en"] // Dat
];

let gemischtAdjEndungen = [
    ["er", "e", "es", "en"], // Nom
    ["en", "e", "es", "en"], // Akk
    ["en", "en", "en", "en"] // Dat
];

let dingeArtikeln = makeArrIterator(["der/die/das/die", "dieser/diese/dieses/diese",
    "ein/eine/ein/--", "mein/meine/mein/meine",
    "dein/deine/dein/deine", "unser/unsere/unser/unsere"]);
let dingeNomen = makeArrIterator(["Buch/Bücher/n", "Hemd/Hemde/n", "Bluse/Blusen/f", "Tisch/Tische/m", "Löffel/m",
    "Gabel/Gabeln/f", "Messer/Messer/n", "Teller/Teller/m", "Tasse/Tassen/f", "Auto/Autos/n",
    "Bleistift/Bleistifte/m", "Kugelschreiber/Kugelschreiber/m",
    "Tastatur/Tastaturen/f", "Matratze/Matratzen/f", "Dose/Dosen/f",
    "Socke/Socken/f", "Schuh/Schuhe/m", "Zeitung/Zeitungen/f", "Blume/Blumen/f",
    "Fahrrad/Fahrräder/n", "Fußball/Fußbälle/m", "Sessel/Sessel/m"
]);

let dingeAdj = makeArrIterator(["alt", "neu", "billig", "teuer/teur", "groß", "klein", "rot", "blau", "grün",
    "gelb", "braun", "schwarz"]);
let dingeNomModell = makeArrIterator(["Hier ist _./Hier sind _.", "Das ist _./Das sind _."]);
let dingeAkkModell = makeArrIterator(["Ich habe _.", "Ihr wollt _.", "Wir sehen _."]);
let dingeDatModell = makeArrIterator(["Es liegt unter _.", "Wir stehen neben _."]);
/*
let abstrakte = ["Erklärung/Erklärungen/f", "Übung/Übungen/f", "Theorie/Theorien/f",
    "Erzählung/Erzählungen/f", "Darstellung/Darstellungen/f"];
let abstraktAdjektive = ["gut", "schlecht", "klar", "langweilig", "interssant"];
let abstraktNomModell = ["Das ist _"];
let abstrakt_acc_model = ["Ich verstehe _"];
*/

let familieArtikeln = makeArrIterator(["der/die/das/die", "mein/meine/mein/meine",
    "dein/deine/dein/deine", "unser/unsere/unser/unsere"]);
let familieNomen = makeArrIterator(["Kind/Kinder/n", "Sohn/Söhne/m",
    "Tochter/Töchter/f", "Onkel/Onkel/m", "Tante/Tanten/f", "Nichte/Nichten/f",
    "Neffe/Neffen/m"]);

let familieAdj = makeArrIterator(["älter", "jünger", "nett", "höflich",
    "interessant", "glücklich", "traurig", "hilfreich", "intelligent" ]);
let familieNomModell = makeArrIterator(["Wo ist _?/Wo sind _?", "Das ist _./Das sind _.", ]);
let familieAkkModell = makeArrIterator(["Wir grüßen _.", "Du suchst _."]);
let familieDatModell = makeArrIterator(["Er gibt _ das Buch.", "Ich bin mit _ befreundet.", "Du stehst neben _."]);

let personenArtikeln = makeArrIterator(["der/die/das/die", "ein/eine/ein/--"]);
let personenNomen = makeArrIterator(["Mechaniker/Mechaniker/m",
    "Mechanikerin/Mechanikerinnen/f", "Arzt/Ärtze/m", "Ärtzin/Ärtzinnen/f",
    "Verkäufer/Verkäufer/m", "Verkäuferin/Verkäuferinnen/f",
    "Kunde/Kunden/m", "Kundin/Kundinnen/f",
    "Kassier/Kassiere/m", "Kassierin/Kassierinnen/f"]);
let personenAdj = makeArrIterator(["älter", "jünger", "nett", "höflich", "interessant", "glücklich", "traurig", "hilfreich",
                "intelligent" ]);
let personenNomModell = makeArrIterator(["Hier kommt _./Hier kommen _", "Das ist _./Das sind _."]);
let personenAkkModell = makeArrIterator(["Wir kennen _.", "Du suchst _."]);
let personenDatModell = makeArrIterator(["Sie gibt _ das Buch.", "Ich bin mit _ befreundet.", "Du stehst neben _."]);

/*
 * These arrays are *not* indexed, because we are OK with getting multiple items
 * of the same case in a row.
 */
let nomenOptionen =
    [{"nomen": dingeNomen,
      "artikel": dingeArtikeln,
      "adjektive": dingeAdj,
      "modelle": [dingeNomModell, dingeAkkModell, dingeDatModell]},

    {"nomen": familieNomen,
    "artikel": familieArtikeln,
    "adjektive": familieAdj,
    "modelle": [familieNomModell, familieAkkModell, familieDatModell]},

    {"nomen": personenNomen,
     "artikel": personenArtikeln,
     "adjektive": personenAdj,
     "modelle": [personenNomModell, personenAkkModell, personenDatModell]}];

/*
 * Wir haben 2 Personen-optionen & 1 Ding-option; also wir
 * wählen zweimal Dings als Personen.
 *
 * [0, 1, 2, 3] -> [0, 0, 1, 2]
 */
function pseudoWähl(arr) {
    let n = getRandomInt(arr.length + 1);
    if (n > 1) {
        n = n - 1;
    }
    return arr[n];
}

function wählOption(arr) {
    let n = getRandomInt(arr.length);
    return arr[n];
}

/*
 * Return an object with:
 *  - artikel
 *  - artikel Typ (schwach/gemischt)
 *  - number
 *  - case
 *  - noun (includes gender)
 *  - adjective
 *  - Modellsatz
 */
function problemErstellen() {
    let welcherNomen = pseudoWähl(nomenOptionen);
    let artikel = welcherNomen.artikel.next().value;
    let artikelTyp = artikelTypMap[artikel.split("/")[0]];
    let nomen = welcherNomen.nomen.next().value;
    let genus = nomen.split("/")[2];
    let adj = welcherNomen.adjektive.next().value;
    let kasus = wählOption([NOMINATIV, AKKUSATIV, DATIV]);
    let modell = welcherNomen.modelle[kasus].next().value;
    let modellTeile = modell.split("/");
    let nummer = (artikel.startsWith("ein")) ? SINGULAR : getRandomInt(2);
    if (nummer == 1 && modellTeile.length == 2) {
        modell = modellTeile[PLURAL];
    } else {
        modell = modellTeile[SINGULAR];
    }
    return {
        "artikel": artikel,
        "artikelTyp": artikelTyp,
        "nomen": nomen,
        "adjektiv": adj,
        "kasus": kasus,
        "genus": genus,
        "nummer": nummer,
        "modell": modell
    };
}

function problemAnzeigen(problem) {
    const nummerStr = ["sg", "pl"];
    let nomenTeile = problem.nomen.split("/");
    let genusIndex = genusHash[problem.genus];
    let artikelTeile = problem.artikel.split("/");
    let artikel = "";
    let nomen = nomenTeile[problem.nummer];
    let adj = problem.adjektiv.split('/')[0];
    if (problem.nummer == SINGULAR) {
        artikel = artikelTeile[genusIndex];
        nomen = nomenTeile[problem.nummer];
    } else {
        artikel = artikelTeile[PLURAL]
    }

    insertText(artikel, "artikel");
    insertText(adj, "adjektiv");
    insertText(nomen, "nomen");
    insertText(`(${problem.genus} ${nummerStr[problem.nummer]})`, "genusNummer");

    let unterstreichIndex = problem.modell.indexOf("_");
    let vorSatz = problem.modell.substring(0, unterstreichIndex);
    let nachSatz = problem.modell.substring(unterstreichIndex + 1);
    insertText(vorSatz, "vorInput");
    insertText(nachSatz, "nachInput");
}

function machLoesung(problem) {
    /* Erst, der Artikel */
    let artikelTeile = problem.artikel.split("/");
    let artikel = artikelTeile[0]; // für jetzt...

    // let nomenTeile = problem.nomen.split("/");
    let nomen = problem.nomen.split("/")[problem.nummer];

    let adjektiv = problem.adjektiv;
    if (adjektiv.indexOf('/') >= 0) {
        adjektiv = problem.adjektiv.split('/')[1];
    }
    let position = (problem.nummer == SINGULAR) ? genusHash[problem.genus] : PLURALGENUS;

    if (problem.artikelTyp == SCHWACH) {
        let forme = schwachArtikelForme[artikel];
        artikel = forme[problem.kasus][position];
        adjektiv += schwachAdjEndungen[problem.kasus][position]
    } else {
        artikel = artikel + gemischtArtikelEndungen[problem.kasus][position];
        adjektiv += gemischtAdjEndungen[problem.kasus][position];
    }

    if ((problem.kasus == DATIV) && (problem.nummer == PLURAL) &&
        (!nomen.endsWith("n"))) {
        nomen += "n";
    }

    return {
        "artikel": artikel,
        "adjektiv": adjektiv,
        "nomen": nomen
    }
}

function ohneLeerZeichen(str) {
    return str.trim().replaceAll(/\s+/g, " ").replaceAll(/[/,.?]/g, "");
}

function canonical(str) {
    str = ohneLeerZeichen(str);
    str = str.replaceAll("ß", "ss");
    str = str.replaceAll("ä", "ae").replaceAll("ü", "ue").replaceAll("ö", "oe");
    str = str.replaceAll("Ä", "Ae").replaceAll("Ü", "Ue").replaceAll("Ö", "Oe");
    return str;
}

function machHinweise(problem, antwortTeile, artikelOK, adjOK, nomenOK) {
    let ergebnis = "<div class=\"hinweise\">";
    if (!artikelOK) {
        ergebnis += `<br />Der Kasus ist ${kasusNamen[problem.kasus]}.`;
    }

    if (!adjOK) {
        /* Hinweise für schwach */
        let adj = antwortTeile[1];
        let genus = genusHash[problem.genus];

        if (problem.artikelTyp == SCHWACH) {
            if ((problem.nummer == PLURAL) && (!adj.endsWith('en'))) {
                ergebnis += `<br />Im Plural steht immer „en“ am Ende des Adjektivs.`;
            } else if (problem.nummer == SINGULAR) {
                /* Singular */
                if (problem.kasus == NOMINATIV) {
                    if ((genus == MASKULIN && adj.endsWith('er')) ||
                        (genus == NEUTRAL && adj.endsWith('es'))) {
                        ergebnis +=
                        `<br />Der Artikel gibt uns den Genus; du brauchst nur „e“ am Ende.`;
                    }
                } else if (problem.kasus == AKKUSATIV) {
                    // console.log("genus is", genus, "adj", adj);
                    if ((genus == NEUTRAL && adj.endsWith('es'))) {
                        ergebnis +=
                        `<br />Der Artikel gibt uns den Genus; du brauchst nur „e“ am Ende.`;
                    }
                }
            }
        } else { /* Hinweise für gemischt */
            if ((problem.nummer == PLURAL) && (!adj.endsWith('en'))) {
                ergebnis += `<br />Im Plural steht immer „en“ am Ende des Adjektivs.`;
            } else if (problem.nummer == SINGULAR) {
                if ((problem.kasus == NOMINATIV && genus != FEMININ) ||
                   (problem.kasus == AKKUSATIV && genus == NEUTRAL)) {
                    ergebnis += "<br />Man benutzt hier die Endung des bestimmtes Artikels.";
                } else if (problem.kasus == DATIV) {
                    ergebnis += "<br />Im Dativ ist die Endung des Adjektivs immer „en“.";
                }
            }
        }
    }
    if (!nomenOK) {
        if ((problem.kasus == DATIV) && (problem.nummer == PLURAL) &&
            (!antwortTeile[2].endsWith('n')))
        {
            ergebnis += `<li>Vielleicht brauchst du „n“ am Ende des Nomens?</li>`;
        }
    }
    ergebnis += "</div>";
    return ergebnis;
}

function auswerten(evt) {
    evt.preventDefault();
    let antwort = ohneLeerZeichen(document.getElementById("antwort").value);
    let antwortTeile = antwort.split(/\s+/)
    let canonAntwort = canonical(antwort);
    let canonTeile = canonAntwort.split(/\s/);
    let artikelOK = true;
    let adjOK = true;
    let nomenOK = true;

    if (antwort == "") {
        insertText("Bitte geben Sie etwas ein.", "feedback")
    } else if (canonTeile.length < 3) {
        insertText("Bitte 3 Wörter eingeben.", "feedback");
    } else {
        let congrats = ["Richtig!", "Gut gemacht!", "Ach, wie gut!", "Exzellent!",
            "Ausgezeichnet!"];
        let lösung = machLoesung(aktuellesProblem);
        let feedbackStr = "";
        let hinweise = "";
        let klass = 'correct';
        let nFehler = 0;

        if (canonTeile[0] != canonical(lösung.artikel)) {
            nFehler++;
            klass = "wrong";
            artikelOK = false;
        }
        feedbackStr += `<span class=\"${klass}\">${antwortTeile[0]}</span> `;

        if (canonTeile[1] != canonical(lösung.adjektiv)) {
            nFehler++;
            klass = "wrong"
            adjOK = false;
        } else {
            klass = "correct";
        }
        feedbackStr += `<span class="${klass}">${antwortTeile[1]}</span> `;

        if (canonTeile[2] != canonical(lösung.nomen)) {
            nFehler++;
            klass = "wrong"
            nomenOK = false;
        } else {
            klass = "correct";
        }
        feedbackStr += `<span class="${klass}">${antwortTeile[2]}</span>`;
        if (nFehler == 0) {
            if (nVersuch == 0) {
                feedbackStr += `<br /><span class="normal">${congrats[getRandomInt(congrats.length)]}</span>`;
            } else {
                feedbackStr += `<br /><span class="normal">Richtig.</span>`;
            }

            if (nVersuch == 0) {
                bewertung += 1;
            } else {
                bewertung += 0.5;
            }

            insertText(euroFormat(bewertung), "korrect");
            insertText(nFrage, "total");
            insertText(feedbackStr, "feedback");

            document.getElementById("überprüfen").disabled = true;
        } else {
            hinweise = machHinweise(aktuellesProblem, antwortTeile, artikelOK, adjOK, nomenOK);
            feedbackStr += hinweise;
            if (nVersuch == 2) {
                feedbackStr = `Lösung: ${lösung.artikel} ${lösung.adjektiv} ${lösung.nomen}<br />${hinweise}`;
                document.getElementById("überprüfen").disabled = true;
            } else {
                nVersuch++;
            }
            insertText(feedbackStr, "feedback");
        };
    }
}


function insertText(text, id) {
    document.getElementById(id).innerHTML = text;
}

function euroFormat(n) {
    let ganzeZahl = Math.floor(n);
    let fraktion = n - ganzeZahl;
    if (fraktion == 0) {
        return n.toString();
    } else {
        return n.toString().replace('.', ',');
    }
}

function neuesProblem(evt) {
    if (evt != null) {
        evt.preventDefault();
    }
    nVersuch = 0;
    nFrage++;
    aktuellesProblem = problemErstellen();
    problemAnzeigen(aktuellesProblem);
    insertText("\u00a0", "feedback");
    document.getElementById("antwort").value = "";
    document.getElementById("überprüfen").disabled = false;
}


function setup() {
    neuesProblem(null);
    document.getElementById("überprüfen").addEventListener("click", auswerten);
    document.getElementById("weiter").addEventListener("click", neuesProblem);
    document.getElementById("inputForm").addEventListener("submit", auswerten);
}
