package de.duehl.vocabulary.japanese.logic;

import java.util.ArrayList;
import java.util.List;

import javax.swing.SwingUtilities;

import de.duehl.basics.collections.CollectionsHelper;
import de.duehl.basics.io.FileHelper;
import de.duehl.basics.logic.ErrorHandler;
import de.duehl.basics.system.SystemTools;
import de.duehl.basics.version.Version;
import de.duehl.swing.logic.Quitter;
import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.version.NewsHelper;
import de.duehl.vocabulary.japanese.VocabularyTrainerVersion;
import de.duehl.vocabulary.japanese.common.data.VocabularySortOrder;
import de.duehl.vocabulary.japanese.common.persistence.GrammarTableHeaderPersistenceHelper;
import de.duehl.vocabulary.japanese.common.persistence.Options;
import de.duehl.vocabulary.japanese.common.persistence.SessionManager;
import de.duehl.vocabulary.japanese.common.website.update.vocables.VocablesActualityChecker;
import de.duehl.vocabulary.japanese.data.FumikoDataStructures;
import de.duehl.vocabulary.japanese.data.KanjiSet;
import de.duehl.vocabulary.japanese.data.Vocable;
import de.duehl.vocabulary.japanese.data.Vocabulary;
import de.duehl.vocabulary.japanese.data.symbol.Hiragana;
import de.duehl.vocabulary.japanese.data.symbol.Kanji;
import de.duehl.vocabulary.japanese.grammar.AdjectiveSearchWords;
import de.duehl.vocabulary.japanese.grammar.VerbSearchWords;
import de.duehl.vocabulary.japanese.io.AllKanjiSetsWriter;
import de.duehl.vocabulary.japanese.logic.wrongtested.WrongTestedVocables;
import de.duehl.vocabulary.japanese.startup.logic.StartupLoader;
import de.duehl.vocabulary.japanese.ui.VocabularyTrainerGui;
import de.duehl.vocabulary.japanese.website.update.ownlists.GroupsOfOwnListsFromWebsiteImporter;
import de.duehl.vocabulary.japanese.website.update.ownlists.OwnListGroupsUpdater;
import de.duehl.vocabulary.japanese.website.update.ownlists.interest.OwnListVersionsAndInterestUpdater;
import de.duehl.vocabulary.japanese.website.update.program.ProgramUpdatableChecker;
import de.duehl.vocabulary.japanese.website.update.vocables.VocablesUpdatableChecker;

import static de.duehl.vocabulary.japanese.startup.logic.steps.Step02CheckVocabelIntegrety.GODAN_VERB;

/**
 * Diese Klasse stellt die Logik des Vokabel-Trainers dar.
 *
 * @version 1.01     2026-06-30
 * @author Christian Dühl
 */

public class VocabularyTrainerLogic implements Quitter {

    /**
     * Die Version des Vokabel-Trainers (erhältlich mit
     * new VocabularyTrainerVersion().getVersion()).
     */
    private final Version vocabularyTrainerVersion;

    /** Der Sessionmanager für Daten, die zwischen Sessions erhalten bleiben. */
    private final SessionManager sessionManager;

    /** Die Programmoptionen. */
    private final Options options;

    /** Die grafische Oberfläche. */
    private final VocabularyTrainerGui gui;

    /** Die Datenstrukturen des Vokabeltrainers. */
    private FumikoDataStructures dataStructures;

    /**
     * Die Nachricht mit der Anzahl Vokabeln und Vokabularien vom StartupLoader, die nach dem Laden
     * in der Statuszeile der Oberfläche angezeigt werden soll.
     */
    private String vocabularyLoadMessage;

    /** Der Text, der beim Starten des Vokabeltrainers angezeigt wurde. */
    private String startupLog;

    /** Die Meldungen zu Warnungen, die beim Start auftreten. */
    private List<String> startupWarnings;

    /**
     * Gibt an, dass die Prüfungen auf neue Versionen beim Start des Programms stattfinden.
     * Anderenfalls hat der Benutzer sie über das Update-Menü angestoßen.
     */
    private boolean checkingNewVersionsOnStartUp;

    /** Gibt an, ob beim Startup ein Fehler in Form einer Ausnahme auftrat. */
    private boolean startupExceptionOccured;

    /** Konstruktor. */
    public VocabularyTrainerLogic() {
        vocabularyTrainerVersion = new VocabularyTrainerVersion().getVersion();
        sessionManager = new SessionManager(vocabularyTrainerVersion);
        options = loadOptions();
        checkForChangedGrammarTableForms();

        gui = new VocabularyTrainerGui(this, options);
        checkingNewVersionsOnStartUp = false;
    }

    /** Lädt die Optionen. */
    private Options loadOptions() {
        try {
            return sessionManager.load();
        }
        catch (Exception exception) {
            if (gui == null) {
                System.out.println("Fehler beim Laden der Optionen: " + exception.getMessage());
                return new Options(vocabularyTrainerVersion);
            }
            else {
                ErrorHandler error = gui.createErrorHandler();
                error.error(exception.getMessage(), exception);
                return new Options(vocabularyTrainerVersion);
            }
        }
    }

    private void checkForChangedGrammarTableForms() {
        GrammarTableHeaderPersistenceHelper.checkForChangedMyForms(
                options.getGrammarTableVerbHeadersMap(),
                "den Verben",
                VerbSearchWords.SORTED_VERB_SEARCH_WORDS);
        GrammarTableHeaderPersistenceHelper.checkForChangedMyForms(
                options.getGrammarTableIAdjectiveHeadersMap(),
                "den I-Adjektiven",
                AdjectiveSearchWords.SORTED_I_ADJEKTIVE_SEARCH_WORDS);
        GrammarTableHeaderPersistenceHelper.checkForChangedMyForms(
                options.getGrammarTableNaAdjectiveHeadersMap(),
                "den NA-Adjektiven",
                AdjectiveSearchWords.SORTED_NA_ADJEKTIVE_SEARCH_WORDS);
    }

    /** Speichert die Optionen. */
    public void saveOptions() {
        try {
            tryToSaveOption();
        }
        catch (Exception exception) {
            if (gui != null) {
                ErrorHandler error = gui.createErrorHandler();
                error.error(exception.getMessage(), exception);
            }
            else {
                throw new RuntimeException("Fehler beim Speichern der Optionen!", exception);
            }
        }
    }

    private void tryToSaveOption() {
        storeToOptions();

        /* Falls beim Aufbau des Menüs was schief geht, noch nicht definiert: */
        if (sessionManager != null) {
            sessionManager.save();
        }
    }

    private void storeToOptions() {
        /* Falls beim Aufbau des Menüs was schief geht, noch nicht definiert: */
        if (null != options && !startupExceptionOccured) {
            if (gui != null) {
                options.setStringSelectionFontSize(gui.getStringSelectionFontSize());
                options.setStringSelectionLabelFontSize(gui.getStringSelectionLabelFontSize());
                gui.storeShownTabIndices();
            }
        }
    }

//    /** Getter für den Index des zuletzt betrachteten Reiters in der Oberfläche. */
//    public int getLastShownMainTabIndex() {
//        if (null != options) {
//            return options.getLastShownMainTabIndex();
//        }
//        else {
//            return new Options(vocabularyTrainerVersion).getLastShownMainTabIndex();
//        }
//    }

//    /**
//     * Getter für den Index des zuletzt betrachteten Kategorie-Reiters (Vokabularien / eigene
//     * Listen) in der Oberfläche.
//     */
//    public int getLastShownVocabularyCategoryTabIndex() {
//        if (null != options) {
//            return options.getLastShownVocabularyCategoryTabIndex();
//        }
//        else {
//            return new Options(vocabularyTrainerVersion).getLastShownVocabularyCategoryTabIndex();
//        }
//    }

//    /**
//     * Getter für den Index des zuletzt betrachteten Kategorie-Reiters (Vokabularien / eigene
//     * Listen) in der Oberfläche.
//     */
//    public int getLastShownOwnListCategoryTabIndex() {
//        if (null != options) {
//            return options.getLastShownOwnListCategoryTabIndex();
//        }
//        else {
//            return new Options(vocabularyTrainerVersion).getLastShownOwnListCategoryTabIndex();
//        }
//    }

//    /**
//     * Getter für den Index des zuletzt betrachteten Unterkategorie-Reiters (Vokabularien / eigene
//     * Listen) in der Oberfläche.
//     */
//    public int getLastShownVocabularySubCategoryTabIndex() {
//        if (null != options) {
//            return options.getLastShownVocabularySubCategoryTabIndex();
//        }
//        else {
//            return new Options(vocabularyTrainerVersion).getLastShownVocabularySubCategoryTabIndex();
//        }
//    }

//    /**
//     * Getter für den Index des zuletzt betrachteten Unterkategorie-Reiters (Vokabularien / eigene
//     * Listen) in der Oberfläche.
//     */
//    public int getLastShownOwnListSubCategoryTabIndex() {
//        if (null != options) {
//            return options.getLastShownOwnListSubCategoryTabIndex();
//        }
//        else {
//            return new Options(vocabularyTrainerVersion).getLastShownOwnListSubCategoryTabIndex();
//        }
//    }

//    /** Getter für die Anzahl der Spalten mit den Vokabularien. */
//    public int getNumberOfVocabularyBarColumns() {
//        if (null != options) {
//            return options.getNumberOfVocabularyBarColumns();
//        }
//        else {
//            return new Options(vocabularyTrainerVersion).getNumberOfVocabularyBarColumns();
//        }
//    }

//    /**
//     * Getter für die Richtung, in die übersetzt wird (Japanisch - Deutsch oder Deutsch -
//     * Japanisch).
//     */
//    public TranslationDirection getTranslationDirection() {
//        if (null != options) {
//            return options.getTranslationDirection();
//        }
//        else {
//            return new Options(vocabularyTrainerVersion).getTranslationDirection();
//        }
//    }

//    /**
//     * Gibt an, wieviele Tage 'eine Weile' ist, um Vokabeln anzuzeigen oder abzufragen, die 'lange
//     * nicht mehr abgefragt' wurden.
//     */
//    public int getNumberOfDaysForAWhile() {
//        if (null != options) {
//            return options.getNumberOfDaysForAWhile();
//        }
//        else {
//            return new Options(vocabularyTrainerVersion).getNumberOfDaysForAWhile();
//        }
//    }

//    /**
//     * Gibt an wieviele Tage seit des ersten Auftretens eine Vokabel noch als 'neu' gilt, um die
//     * 'neuesten' Vokabeln abzufragen oder anzuzeigen.
//     */
//    public int getNumberOfDaysAVocableIsNew() {
//        if (null != options) {
//            return options.getNumberOfDaysAVocableIsNew();
//        }
//        else {
//            return new Options(vocabularyTrainerVersion).getNumberOfDaysAVocableIsNew();
//        }
//    }

    /** Führt den Vokabeltrainer aus. */
    public void run() {
        if (isInitialDirectoryChoosingNecessary()) {
            initialDirectoryChoosing();
        }

        startup();

        if (!startupExceptionOccured) {
            perhapsShowNews();

            actualizeStringSelectionSize();
            actualizeStringSelectionLabelSize();

            initGuiWithDataStructures();
            showLoadMessageInStatusBar();
            gui.setVisible(true);

            checksForNewVersionsAtStart();

            if (options.isShowWarningsAfterStart()) {
                gui.showStartupWarningsIfAny();
            }
        }
    }

    private boolean isInitialDirectoryChoosingNecessary() {
        String vocabulariesPath = options.getVocabulariesPath();
        return vocabulariesPath.isBlank()
                || !FileHelper.isDirectory(vocabulariesPath);
    }

    private void initialDirectoryChoosing() {
        String homeDir = SystemTools.getHomeDirectory();
        String vocabulariesPath = GuiTools.openDirectory(homeDir,
                "Bitte das Verzeichnis mit den Vokabeln wählen",
                gui.getWindowAsComponent());

        if (FileHelper.isDirectory(vocabulariesPath)) {
            options.setVocabulariesPath(vocabulariesPath);
        }
        else {
            System.exit(1);
        }
    }

    private void startup() {
        startupExceptionOccured = false;
        reallyStartup();
    }

    private void reallyStartup() {
        try {
            tryStartUp();
        }
        catch (Exception exception) {
            reallyQuit();
            gui.createErrorHandler().error("Es trat ein Fehler beim Startup auf", exception);
            startupExceptionOccured = true;
        }
    }

    private void tryStartUp() {
        StartupLoader loader = new StartupLoader(gui, options);
        loader.load();
        dataStructures = loader.getDataStructures();
        vocabularyLoadMessage = loader.getVocabularyLoadMessage();
        startupLog = loader.getStartUpLog();
        startupWarnings = loader.getStartupWarnings();
    }

    private void showLoadMessageInStatusBar() {
        gui.setMessageLater(vocabularyLoadMessage);
    }

    /** Getter für den Text, der beim Starten des Vokabeltrainers angezeigt wurde. */
    public String getStartupLog() {
        return startupLog;
    }

    /** Getter für die Meldungen zu Warnungen, die beim Start auftreten. */
    public List<String> getStartupWarnings() {
        return startupWarnings;
    }

    private void perhapsShowNews() {
        NewsHelper newsHelper = new NewsHelper(vocabularyTrainerVersion,
                options.getLastUsedVersion(), gui.getChanges(), gui.getProgramImage(),
                gui.getLocation());
        newsHelper.perhapsShowNews();
    }

    private void actualizeStringSelectionSize() {
        int stringSelectionFontSize = options.getStringSelectionFontSize();
        if (stringSelectionFontSize > -1) {
            gui.setStringSelectionFontSize(stringSelectionFontSize);
        }
    }

    private void actualizeStringSelectionLabelSize() {
        int stringSelectionLabelFontSize = options.getStringSelectionLabelFontSize();
        if (stringSelectionLabelFontSize > -1) {
            gui.setStringSelectionLabelFontSize(stringSelectionLabelFontSize);
        }
    }

    private void initGuiWithDataStructures() {
        gui.initGuiWithDataStructure(dataStructures);
    }

    /** Beendet das Programm (von der Gui aus). */
    @Override
    public void quit() {
        if (GuiTools.askUserToQuit(gui.getWindowAsComponent())) {
            reallyQuit();
        }
    }

    /** Beendet das Programm ohne Nachfrage beim Benutzer. */
    public void reallyQuit() {
        saveOptions();
        if (null != dataStructures && !startupExceptionOccured) {
            List<KanjiSet> kanjiSets = dataStructures.getKanjiSets();
            if (null != kanjiSets) { // Bei Fehlern im Menü ist die Gui noch nicht definiert!
                saveKanjiSets();
            }
        }
        if (null != gui && !startupExceptionOccured) {
            gui.saveManualVocabularyOrder(); // Bei Fehlern im Menü ist die Gui noch nicht definiert!
            gui.quit();
        }
    }

    /** Speichert die Kanji-Mengen ab. */
    public void saveKanjiSets() {
        List<KanjiSet> kanjiSets = dataStructures.getKanjiSets();
        AllKanjiSetsWriter.write(kanjiSets);
    }

    /** Stellt eine Liste mit den Vokabeln aus allen Vokabularen zusammen. */
    public List<Vocable> collectVocablesOfAllVocabularies() {
        List<Vocable> allVocables = new ArrayList<>();

        List<Vocabulary> vocabularies = dataStructures.getVocabularies();
        for (Vocabulary vocabulary : vocabularies ) {
            allVocables.addAll(vocabulary.getVocables());
        }

        return allVocables;
    }

    /**
     * Setzt in den Optionen die Art die Vokabularien in der Oberfläche zu sortieren und speichert
     * sie hinterher.
     */
    public void setVocabularySortOrder(VocabularySortOrder sortOrder) {
        options.setVocabularySortOrder(sortOrder);
        saveOptions();
    }

    /**
     * Prüft beim Start des Vokabeltrainers ggf. auf neue Versionen der Vokabeln und der Gruppen
     * mit eigenen Listen.
     *
     * Nach neuen Programmversionen wird nicht mehr gesucht, da dies inzwischen der Launcher
     * übernimmt.
     *
     * Nach neuen Versionen der Vokabeln wird nicht mehr gesucht, da dies inzwischen der Launcher
     * übernimmt. Daher ist das auch bei einer "Neuinstallation" kein Problem.
     *
     * Man muss Vorsichtig sein mit dem Update der Gruppen von eigenen Listen, denn wenn die
     * Vokabeln nicht aktuell sind, dann werden dort sofort wieder Einträge entfernt.
     * Da aber vorher der Launcher läuft, kann man hoffen, dass sie aktuell sind.
     */
    private void checksForNewVersionsAtStart() {
        checkingNewVersionsOnStartUp = true;

        if (options.isCheckNewOwnListsVersionAtStart()) {
            checkNewOwnListsVersion();
        }
        else {
            OwnListVersionsAndInterestUpdater.updateOwnListVersionsAndInterestsForNewOwnLists();
        }

        checkingNewVersionsOnStartUp = false;
    }

    /** Prüft auf eine neue Programmversion. */
    public void checkNewProgramVersion() {
        gui.startLongTimeProcess("Prüfe auf aktuellere Programmversion");
        new Thread(() -> checkNewProgramVersionInOwnThread()).start();
    }

    private void checkNewProgramVersionInOwnThread() {
        String versionNumberRunning = VocabularyTrainerVersion.getOnlyVersion();
        ProgramUpdatableChecker updater = new ProgramUpdatableChecker(versionNumberRunning,
                gui.getWindowAsComponent(), gui.getLocation(), gui.getProgramImage());
        updater.update();

        SwingUtilities.invokeLater(() -> gui.endLongTimeProcess());
    }

    /**
     * Prüft auf eine neue Version der Vokabularien.
     *
     * @return Gibt an, ob die Vokabeln zum Zeitpunkt der Überprüfung bereits aktuell waren.
     */
    public void checkNewVocabulariesVersion() {
        gui.startLongTimeProcess("Prüfe auf aktuellere Version der Vokabeln");
        new Thread(() -> checkNewVocabulariesVersionInOwnThread()).start();
    }

    private void checkNewVocabulariesVersionInOwnThread() {
        VocablesUpdatableChecker checker = new VocablesUpdatableChecker(
                options.getVocabulariesPath(), gui);
        checker.check();

        SwingUtilities.invokeLater(() -> gui.endLongTimeProcess());
    }

    /** Prüft auf eine neue Version der Gruppen mit . */
    public void checkNewOwnListsVersion() {
        VocablesActualityChecker checker = new VocablesActualityChecker(
                options.getVocabulariesPath());
        checker.check();
        if (checker.isVersionOnWebserverDetermined() && checker.isActual()) {
            reallyCheckNewOwnListsVersion();
        }
        else {
            informAboutNoOwnListUpdateBecauseOfNewVocablesOnWebserver(
                    "Updateprüfung der eigenen Listen");
        }
    }

    private void informAboutNoOwnListUpdateBecauseOfNewVocablesOnWebserver(String usage) {
        String title = usage + ": Neuere Vokabeln auf dem Webserver!";
        String message = ""
                + usage + " entfällt, denn es gibt neuere Vokabeln auf dem Webserver!\n\n"
                + "Solange es neuere Vokabeln gibt, macht der Downonload der Gruppen von\n"
                + "eigenen Listen keinen Sinn, denn wenn die Vokabeln nicht aktuell sind,\n"
                + "dann werden dort sofort wieder Einträge entfernt.\n\n"
                + "Bitte aktualisieren Sie zunächst die Vokabeln.";
        GuiTools.informUser(gui.getWindowAsComponent(),  title, message);
    }

    private void reallyCheckNewOwnListsVersion() {
        OwnListGroupsUpdater updater = new OwnListGroupsUpdater(dataStructures, gui.getUiObjects(),
                checkingNewVersionsOnStartUp);
        updater.update();
        /*
         * Innerhalb dieser Klasse werden Dialoge angezeigt, die GlassPane über der Gui erfolgt
         * dort erst später.
         */
    }

    /** Fügt eine Vokabel zur passenden Liste hinzu. */
    public void addVocableToWrongTestedVocablesList(Vocable vocable) {
        WrongTestedVocables wrongTestedVocables = dataStructures.getWrongTestedVocables();
        wrongTestedVocables.addVocableToWrongTestedVocablesList(vocable);
        gui.setCorrectForegroundColorOfVocabularyBarsLater(); // Färbt den Text
        gui.showTranslationDirectionOnBarButtons(); // aktualisiert die Buttons (inaktiv / aktiv)
    }

    /** Entfernt eine Vokabel aus der passenden Liste. */
    public void removeVocableFromWrongTestedVocablesList(Vocable vocable) {
        WrongTestedVocables wrongTestedVocables = dataStructures.getWrongTestedVocables();
        wrongTestedVocables.removeVocableFromWrongTestedVocablesList(vocable);
        gui.setCorrectForegroundColorOfVocabularyBarsLater(); // Färbt den Text
        gui.showTranslationDirectionOnBarButtons(); // aktualisiert die Buttons (inaktiv / aktiv)
    }

    /** Importiert Gruppen von eigenen Vokabellisten von der Webseite. */
    public void importGroupsOfOwnListsFromWebsite() {
        VocablesActualityChecker checker = new VocablesActualityChecker(
                options.getVocabulariesPath());
        checker.check();
        if (checker.isVersionOnWebserverDetermined() && checker.isActual()) {
            new Thread(() -> importGroupsOfOwnListsFromWebsiteInOwnThread()).start();
        }
        else {
            informAboutNoOwnListUpdateBecauseOfNewVocablesOnWebserver("Import von eigenen Listen");
        }
    }

    private void importGroupsOfOwnListsFromWebsiteInOwnThread() {
        GroupsOfOwnListsFromWebsiteImporter importer = new GroupsOfOwnListsFromWebsiteImporter(
                dataStructures, gui.getUiObjects());
        importer.importGroups();
    }

    /** Erzeugt eine Liste der Vokabeln, welche Verben in Wörterbuchform sind. */
    public List<Vocable> createListOfVerbsInWoerterbuchform() {
        List<Vocable> verbsInWoerterbuchform = new ArrayList<>();

        List<Vocabulary> vocabularies = dataStructures.getVocabularies();
        for (Vocabulary vocabulary : vocabularies) {
            for (Vocable vocable : vocabulary.getVocables()) {
                if (vocable.isVerbInWoerterbuchform()) {
                    verbsInWoerterbuchform.add(vocable);
                }
            }
        }

        return verbsInWoerterbuchform;
    }

    /** Erzeugt eine Liste der Vokabeln, welche Adjektive in Grundform (positive Gegenwart) sind. */
    public List<Vocable> createListOfAdjectivesInPositivePresence() {
        List<Vocable> adjectivesInPositivePresence = new ArrayList<>();

        List<Vocabulary> vocabularies = dataStructures.getVocabularies();
        for (Vocabulary vocabulary : vocabularies) {
            for (Vocable vocable : vocabulary.getVocables()) {
                if (vocable.isAdjectivInPositivePresence()) {
                    adjectivesInPositivePresence.add(vocable);
                }
            }
        }

        return adjectivesInPositivePresence;
    }

    /**
     * Erzeugt eine Liste der Vokabeln, welche い-Adjektive in Grundform (positive Gegenwart) sind.
     */
    public List<Vocable> createListOfIAdjectivesInPositivePresence() {
        List<Vocable> iAdjectivesInPositivePresence = new ArrayList<>();

        List<Vocabulary> vocabularies = dataStructures.getVocabularies();
        for (Vocabulary vocabulary : vocabularies) {
            for (Vocable vocable : vocabulary.getVocables()) {
                if (vocable.isIAdjectivInPositivePresence()) {
                    iAdjectivesInPositivePresence.add(vocable);
                }
            }
        }

        return iAdjectivesInPositivePresence;
    }

    /**
     * Erzeugt eine Liste der Vokabeln, welche な-Adjektive in Grundform (positive Gegenwart) sind.
     */
    public List<Vocable> createListOfNaAdjectivesInPositivePresence() {
        List<Vocable> naAdjectivesInPositivePresence = new ArrayList<>();

        List<Vocabulary> vocabularies = dataStructures.getVocabularies();
        for (Vocabulary vocabulary : vocabularies) {
            for (Vocable vocable : vocabulary.getVocables()) {
                if (vocable.isNaAdjectivInPositivePresence()) {
                    naAdjectivesInPositivePresence.add(vocable);
                }
            }
        }

        return naAdjectivesInPositivePresence;
    }

    /** Erzeugt eine Liste der Vokabeln, welche な-Adjektive sind, die auf い enden. */
    public List<Vocable> createListOfNaAdjectivesPositivePresenceEndsWithI() {
        List<Vocable> naAdjectivesInPositivePresenceEndingWithI = new ArrayList<>();

        List<Vocabulary> vocabularies = dataStructures.getVocabularies();
        for (Vocabulary vocabulary : vocabularies) {
            for (Vocable vocable : vocabulary.getVocables()) {
                if (vocable.isNaAdjectivInPositivePresence()
                        && vocable.endWithHiragana(Hiragana.I)) {
                    naAdjectivesInPositivePresenceEndingWithI.add(vocable);
                }
            }
        }

        return naAdjectivesInPositivePresenceEndingWithI;
    }

    /** Erzeugt eine Liste der Vokabeln, welche いる/える-Ausnahme-Verben sind. */
    public List<Vocable> createListOfIruEruExceptionVerbsInWoerterbuchform() {
        List<Vocable> iruEruExceptionVerbsInWoerterbuchform = new ArrayList<>();

        List<Vocabulary> vocabularies = dataStructures.getVocabularies();
        for (Vocabulary vocabulary : vocabularies) {
            for (Vocable vocable : vocabulary.getVocables()) {
                if (vocable.isVerbInWoerterbuchform() && vocable.hasIruEruEnd()) {
                    String comment = vocable.getComment();
                    if (comment.contains(GODAN_VERB)) {
                        iruEruExceptionVerbsInWoerterbuchform.add(vocable);
                    }
                }
            }
        }

        return iruEruExceptionVerbsInWoerterbuchform;
    }

    /** Ermittelt alle Kanji mit der übergebenen ON-Lesung. */
    public List<Kanji> findAllKanjiWithOnLesung(String onLesung) {
        List<Kanji> kanjiList = new ArrayList<>();

        for (Kanji kanji : Kanji.getAllKanjiAsList()) {
            if (CollectionsHelper.containedInAnyListElementIgnoreCase(onLesung,
                    kanji.getOnLesungen())) {
                kanjiList.add(kanji);
            }
        }

        return kanjiList;
    }

    /** Ermittelt alle Kanji mit der übergebenen kun-Lesung. */
    public List<Kanji> findAllKanjiWithKunLesung(String kunLesung) {
        List<Kanji> kanjiList = new ArrayList<>();

        for (Kanji kanji : Kanji.getAllKanjiAsList()) {
            if (CollectionsHelper.containedInAnyListElementIgnoreCase(kunLesung,
                    kanji.getKunLesungen())) {
                kanjiList.add(kanji);
            }
        }

        return kanjiList;
    }

    /** Ermittelt alle Kanji mit der übergebenen ON- oder kun-Lesung. */
    public List<Kanji> findAllKanjiWithAnyLesung(String anyLesung) {
        List<Kanji> kanjiList = new ArrayList<>();

        for (Kanji kanji : Kanji.getAllKanjiAsList()) {
            if (CollectionsHelper.containedInAnyListElementIgnoreCase(anyLesung, kanji.getOnLesungen())
                    || CollectionsHelper.containedInAnyListElementIgnoreCase(anyLesung,
                            kanji.getKunLesungen())) {
                kanjiList.add(kanji);
            }
        }

        return kanjiList;
    }

    /** Ermittelt alle Kanji mit der übergebenen deutschen Bedeutung. */
    public List<Kanji> findAllKanjiWithGermanMeaning(String germanMeaning) {
        List<Kanji> kanjiList = new ArrayList<>();

        for (Kanji kanji : Kanji.getAllKanjiAsList()) {
            if (CollectionsHelper.containedInAnyListElementIgnoreCase(germanMeaning,
                    kanji.getGermanMeanings())) {
                kanjiList.add(kanji);
            }
        }

        return kanjiList;
    }

    /** Ermittelt alle Kanji mit dem übergebenen beliebigen Inhalt (Lesung, deutsche Bedeutung). */
    public List<Kanji> findAllKanjiWithAny(String any) {
        List<Kanji> kanjiList = new ArrayList<>();

        for (Kanji kanji : Kanji.getAllKanjiAsList()) {
            if (CollectionsHelper.containedInAnyListElementIgnoreCase(any, kanji.getOnLesungen())
                    || CollectionsHelper.containedInAnyListElementIgnoreCase(any,
                            kanji.getKunLesungen())
                    || CollectionsHelper.containedInAnyListElementIgnoreCase(any,
                            kanji.getGermanMeanings())
                    || kanji.getCharacter().equals(any)
                    ) {
                kanjiList.add(kanji);
            }
        }

        return kanjiList;
    }

    /** Getter für die Datenstrukturen des Vokabeltrainers. */
    public FumikoDataStructures getDataStructures() {
        return dataStructures;
    }

}
