package de.duehl.vocabulary.japanese.grammar.table;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import de.duehl.basics.collections.CollectionsHelper;
import de.duehl.basics.text.Text;
import de.duehl.swing.ui.GuiTools;
import de.duehl.vocabulary.japanese.common.persistence.Options;
import de.duehl.vocabulary.japanese.data.FumikoDataStructures;
import de.duehl.vocabulary.japanese.data.OwnList;
import de.duehl.vocabulary.japanese.data.Vocable;
import de.duehl.vocabulary.japanese.data.Vocabulary;
import de.duehl.vocabulary.japanese.grammar.AdjectivePositivePresenceFinder;
import de.duehl.vocabulary.japanese.grammar.VerbWoerterbuchformFinder;
import de.duehl.vocabulary.japanese.grammar.find.RelatedVocablesFinder;
import de.duehl.vocabulary.japanese.grammar.table.data.GrammarTableType;
import de.duehl.vocabulary.japanese.ui.data.FumikoUiObjects;
import de.duehl.vocabulary.japanese.ui.dialog.grammar.subdialogs.GrammarTableBaseFormUserSelectionDialog;
import de.duehl.vocabulary.japanese.ui.dialog.grammar.subdialogs.GrammarTableBaseFormUserSortDialog;

import static de.duehl.vocabulary.japanese.grammar.GrammarTools.PART_OF_SPEECH_ADJECTIVE;
import static de.duehl.vocabulary.japanese.grammar.GrammarTools.PART_OF_SPEECH_VERB;

/**
 * Diese Klasse erzeugt die Listen von Vokabeln für die Zeilen in grammatikalischen Tabellen von
 * Verben oder Adjektiven.
 *
 * @version 1.01     2025-11-29
 * @author Christian Dühl
 */

class GrammarTableRowCreator {

    /** Die Konstante für noch nicht eingeplegte Verbformen. */
    public static final Vocable NOT_FOUND_FORM = new Vocable().setKanji("NICHT GEFUNDENE WORTFORM");


    /** Die Liste mit den bekannten Vokabularien. */
    private final List<Vocabulary> vocabularies;

    /** Die eigene Liste, aus der die Verben bzw. Adjektive genommen werden. */
    private final OwnList ownList;

    /** Die Art der Tabelle. Möglich sind Verben, I- und Na-Adjektive. */
    private final GrammarTableType grammarTableType;

    /**
     * Die Suchbegriffe, welch die Spalten der Tabelle darstellen.
     *
     * Diese sind entweder Verb- oder Adjektivformen.
     */
    private final List<String> searchWords;

    /** Die häufig verwendeten Funktionen der grafischen Oberfläche des Vokabeltrainers. */
    private final FumikoUiObjects uiObjects;

    /**
     * Gibt ab, ob zu Verben / Adjektiven, die nicht in Wörterbuch- bzw. Grundform vorliegen, diese
     * gesucht werden soll.
     *
     * Ist diese Variable false, werden nur solche in der Liste verwendet, die schon in Wörterbuch-
     * bzw. Grundform vorliegen
     */
    private final boolean searchForBaseForms;

    /** Die Listen von Vokabeln, die in der Tabelle zeilenweise dargestellt werden. */
    private List<List<Vocable>> listOfVocablesInRow;

    /** Gibt an, ob wirklich eine Tabelle erstellt wurde. */
    private boolean created;

    /**
     * Konstruktor.
     *
     * @param dataStructures
     *            Die Datenstrukturen des Vokabeltrainers.
     * @param ownList
     *            Die eigene Liste, aus der die Verben bzw. Adjektive genommen werden.
     * @param grammarTableType
     *            Die Art der Tabelle. Möglich sind Verben, I- und Na-Adjektive.
     * @param searchWords
     *            Die Suchbegriffe, welch die Spalten der Tabelle darstellen.
     * @param uiObjects
     *            Die häufig verwendeten Funktionen der grafischen Oberfläche des Vokabeltrainers.
     */
    public GrammarTableRowCreator(FumikoDataStructures dataStructures, OwnList ownList,
            GrammarTableType grammarTableType, List<String> searchWords, FumikoUiObjects uiObjects) {
        this.ownList = ownList;
        this.grammarTableType = grammarTableType;
        this.searchWords = searchWords;
        this.uiObjects = uiObjects;

        vocabularies = dataStructures.getVocabularies();
        Options options = dataStructures.getOptions();
        searchForBaseForms = options.isGrammarTableSearchForBaseForms();
    }

    /** Die anzuzeigenden Vokabeln in ihrer Wörterbuch-/Grundform. */
    private List<Vocable> baseFormVocables;

    /** Gibt an, ob irgendein Dialog durch Abbruch beendet wurde. */
    private boolean notAppliedDialog;

    /** Erstellt die Liste von den Vokabeln, die in den Zeilen der Tabelle dargestellt werden. */
    public void create() {
        created = false;
        notAppliedDialog = false;

        determineBaseFormVocablesFromOwnList();
        if (baseFormVocables.isEmpty()) {
            informEmtpy("Ermittlung der entsprechenden Vokabeln in Grundform aus der Liste");
        }
        else {
            makeBaseFormsDiscjunctAndSortThem();
            letUserSelectWantedBaseForms();
            if (baseFormVocables.isEmpty()) {
                informEmtpy("nach Auswahl der Grundformen durch Sie");
            }
            else {
                letUserSortWantedBaseForms();
                if (baseFormVocables.isEmpty()) {
                    informEmtpy("nach Sortierung der Grundformen durch Sie");
                }
                else {
                    searchWantedVocableForms();
                    created = true;
                }
            }
        }
    }

    /**
     * Hier werden die Vokabeln aus der Liste bestimmt, die die passende Wortart ("Verb" oder
     * "Adjektiv") haben und entweder in der Wörterbuch-/Grundform vorliegen, oder, falls
     * gewünscht, zu denen die entsprechende Wörterbuch-/Grundform gesucht wird.
     */
    private void determineBaseFormVocablesFromOwnList() {
        List<Vocable> vocablesFromOwnList = determineVerbsOrAdjectivesFromOwnList();

        baseFormVocables = determineBaseForms(vocablesFromOwnList);

        if (searchForBaseForms) {
            List<Vocable> notBaseFormVocablesFromOwnList = new ArrayList<>();
            notBaseFormVocablesFromOwnList.addAll(vocablesFromOwnList);
            notBaseFormVocablesFromOwnList.removeAll(baseFormVocables);

            List<Vocable> baseFormVocablesFromNotBaseFormVocables =
                    createBaseFormVocablesFromNotBaseFormVocables(notBaseFormVocablesFromOwnList);
            /*
             * Das ist eigentlich nicht nötig, da die Suche RelatedVocablesFinder auch für
             * nicht-Grundform Vokabeln funktioniert.
             *
             * Da wir aber dem Benutzer die Grundformen zur Auswahl anbieten, muss man sie auf
             * diese Weise ermitteln.
             */

            baseFormVocables.addAll(baseFormVocablesFromNotBaseFormVocables);
        }
    }

    /**
     * Hier werden alle Verben bzw. Adjektive aus der Liste herausgesucht, egal in welcher Form sie
     * enthalten sind.
     */
    private List<Vocable> determineVerbsOrAdjectivesFromOwnList() {
        List<Vocable> vocablesFromOwnList = new ArrayList<>();

        for (Vocable vocable : ownList.getVocables()) {
            List<String> partsOfSpeech = vocable.getPartsOfSpeech();
            if (containsPartsOfSpeachOneOfTheRightForms(partsOfSpeech)) {
                vocablesFromOwnList.add(vocable);
            }
        }

        return vocablesFromOwnList;
    }

    private boolean containsPartsOfSpeachOneOfTheRightForms(List<String> partsOfSpeech) {
        switch (grammarTableType) {
            case VERB:
                return partsOfSpeech.contains(PART_OF_SPEECH_VERB);
            case I_ADJEKTIV:
            case NA_ADJEKTIV:
                return partsOfSpeech.contains(PART_OF_SPEECH_ADJECTIVE);
            default:
                throw new RuntimeException("Unbekanne Tabellenart " + grammarTableType);
        }
    }

    /**
     * Hier wird aus der übergebenen Liste von Vokabeln diejenigen herausgesucht, welche in
     * Wörterbuchform bzw. Grundform vorliegen.
     */
    private List<Vocable> determineBaseForms(List<Vocable> vocables) {
        List<Vocable> baseFormVocables = new ArrayList<>();

        for (Vocable vocable : vocables) {
            if (hasVocableBaseForm(vocable)) {
                baseFormVocables.add(vocable);
            }
        }

        return baseFormVocables;
    }

    private boolean hasVocableBaseForm(Vocable vocable){
        switch (grammarTableType) {
            case VERB:
                return vocable.isVerbInWoerterbuchform();
            case I_ADJEKTIV:
                return vocable.isIAdjectivInPositivePresence();
            case NA_ADJEKTIV:
                return vocable.isNaAdjectivInPositivePresence();
            default:
                throw new RuntimeException("Unbekanne Tabellenart " + grammarTableType);
        }
    }

    private List<Vocable> createBaseFormVocablesFromNotBaseFormVocables(
            List<Vocable> notBaseFormVocablesFromOwnList) {
        List<Vocable> baseFormVocablesFromNotBaseFormVocables = new ArrayList<>();

        for (Vocable vocable : notBaseFormVocablesFromOwnList) {
            switch (grammarTableType) {
                case VERB:
                    Vocable woerterbuchform = VerbWoerterbuchformFinder
                            .determineWoerterbuchform(vocable, vocabularies);
                    if (!woerterbuchform.equals(VerbWoerterbuchformFinder.NO_WOERTERBUCHFORM_FOUND)) {
                        baseFormVocablesFromNotBaseFormVocables.add(woerterbuchform);
                    }
                case I_ADJEKTIV:
                case NA_ADJEKTIV:
                    Vocable positivePresence =
                    AdjectivePositivePresenceFinder.determinePositivePresence(vocable,
                            vocabularies);
                    if (!positivePresence.equals(
                            AdjectivePositivePresenceFinder.NO_POSITIVE_PRESENCE_FOUND)) {
                        baseFormVocablesFromNotBaseFormVocables.add(positivePresence);
                    }
                    break;
                default:
                    throw new RuntimeException("Unbekanne Tabellenart " + grammarTableType);
            }
        }

        return baseFormVocablesFromNotBaseFormVocables;
    }

    private void sortBaseForms(List<Vocable> baseFormVocables) {
        Collections.sort(baseFormVocables, new Comparator<Vocable>() {

            @Override
            public int compare(Vocable v1, Vocable v2) {
                String k1 = v1.getKana();
                String k2 = v2.getKana();
                return k1.compareTo(k2);
            }
        });
        /*
         * Vielleicht möchte man irgendwann auch nach Hirokos Verbenklassen I, II, III
         * sortieren, dann muss ich mal schauen...
         */
    }

    private void makeBaseFormsDiscjunctAndSortThem() {
        baseFormVocables = CollectionsHelper.createDisjunctList(baseFormVocables);
        sortBaseForms(baseFormVocables);
    }

    /**
     * Nachdem nun alle Grundformen ermittelt wurden, werden sie hier dem Benutzer noch einmal zur
     * Auswahl angeboten. Per Default sind alle Grundformen ausgewählt.
     *
     * Hier kann der Benutzer die Begriffe also noch reduzieren.
     */
    private void letUserSelectWantedBaseForms() {
        GrammarTableBaseFormUserSelectionDialog dialog =
                new GrammarTableBaseFormUserSelectionDialog(baseFormVocables, uiObjects);
        dialog.setVisible(true);
        if (dialog.isApplied()) {
            // Nichts zu tun, baseFormVocables wurde im Dialog entsprechend geändert.
        }
        else {
            notAppliedDialog = true;
            baseFormVocables.clear();
        }
    }

    /**
     * Nachdem der Benutzer die Auswahl getroffen hat, kann er die verbleibenden Grundformen hier
     * manuell sortieren.
     */
    private void letUserSortWantedBaseForms() {
        GrammarTableBaseFormUserSortDialog dialog = new GrammarTableBaseFormUserSortDialog(
                baseFormVocables, uiObjects);
        dialog.setVisible(true);
        if (dialog.isApplied()) {
            // Nichts zu tun, baseFormVocables wurde im Dialog entsprechend geändert.
        }
        else {
            notAppliedDialog = true;
            baseFormVocables.clear();
        }
    }

    /**
     * Erzeugt zu den Grundformen von Verben oder Adjektive jeweils eine Liste von Vokabeln mit den
     * gewünschten Formen dieser Verben bzw. Adjektive.
     */
    private void searchWantedVocableForms() {
        listOfVocablesInRow = new ArrayList<>();

        for (Vocable baseFormVocable : baseFormVocables) {
            List<Vocable> vocablesInRow = buildWantedForms(baseFormVocable);
            listOfVocablesInRow.add(vocablesInRow);
        }
    }

    /**
     * Erzeugt zu einer Grundform eines Verb oder Adjektivs eine Liste von Vokabeln mit den
     * gewünschten Formen dieses Verbs bzw. Adjektivs.
     *
     * Es sind nicht immer alle gewünschten Formen in den Vokabularien vorhanden. Fehlen diese,
     * wird das Element NOT_FOUND_FORM eingefügt. Bei der Erstellung des HTML-Codes wird daraus
     * dann je nach der Einstellung in den Optionen ein entsprechender Hinweis oder die
     * Tabellenzelle wird leer gelassen.
     */
    private List<Vocable> buildWantedForms(Vocable baseFormVocable) {
        RelatedVocablesFinder finder = new RelatedVocablesFinder(vocabularies, baseFormVocable,
                v -> isVocableRelated(v));
        finder.find();
        List<Vocable> relatedVocables = finder.getRelatedVocables();

        List<Vocable> vocablesInRow = new ArrayList<>();
        for (String searchWord : searchWords) {
            boolean found = false;
            for (Vocable vocable : relatedVocables) {
                List<String> searchWordsOfVocable = vocable.getSearchWords();
                if (searchWordsOfVocable.contains(searchWord)) {
                    vocablesInRow.add(vocable);
                    found = true;
                    break;
                }
            }
            if (!found) {
                vocablesInRow.add(NOT_FOUND_FORM);
            }
        }

        return vocablesInRow;
    }

    private boolean isVocableRelated(Vocable v) {
        switch (grammarTableType) {
            case VERB:
                return v.isVerb();
            case I_ADJEKTIV:
                return v.isIAdjective();
            case NA_ADJEKTIV:
                return v.isNaAdjective();
            default:
                throw new RuntimeException("Unbekanne Tabellenart " + grammarTableType);
        }
    }

    private void informEmtpy(String before) {
        if (!notAppliedDialog) {
            String text = "Es liegen nach " + before + " keine Vokabeln in Grundform vor, aus "
                    + "denen die Tabelle erstellt werden könnte";
            GuiTools.informUser("Keine Tabellen-Erzeugung möglich", Text.addLineBreaks(text, 60));
        }
    }

    /** Gibt an, ob wirklich eine Tabelle erstellt wurde. */
    public boolean isCreated() {
        return created;
    }

    /** Getter für die Listen von Vokabeln, die in der Tabelle zeilenweise dargestellt werden. */
    public List<List<Vocable>> getListOfVocablesInRow() {
        return listOfVocablesInRow;
    }

}
