package de.duehl.vocabulary.japanese.ui.tabs;

import java.awt.BorderLayout;
import java.awt.Component;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;
import javax.swing.JTabbedPane;

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.logic.VocabularyTrainerLogic;
import de.duehl.vocabulary.japanese.logic.ownlists.OwnLists;
import de.duehl.vocabulary.japanese.tools.VocabularyTools;
import de.duehl.vocabulary.japanese.ui.VocabularyTrainerGui;
import de.duehl.vocabulary.japanese.ui.components.bars.VocabularyBar;
import de.duehl.vocabulary.japanese.ui.data.FumikoUiObjects;

/**
 * Diese Klasse stellt den Panel mit den Reitern für die Wahl zwischen den Vokabularien und den
 * eigenen Listen dar.
 *
 * Die Vokabularien sind unveränderlich (während das Programm läuft). Aber ihre Anordnung kann
 * variieren. Dafür muss man aber nur innerhalb der einzelnen Unterkategorien die Bars anders
 * anordnen.
 *
 * Anders ist es bei den OwnLists. Da kann sich auch die Anordnung ändern, aber in den folgenden
 * Fällen ist ein Neuaufbau ab hier notwendig:
 *     - Eine neue Liste wurde angelegt.
 *     - Eine Liste wurde gelöscht.
 *     - eine ownListCategory hat sich geändert
 *     - eine ownListSubCategory hat sich geändert
 * Daher muss in diesen Fällen ab hier der Teil für die eigenen Listen neu aufgebaut werden.
 *
 * In beiden Fällen werden aber CategoryTabs, SubCategoryTabs und zur Anzeige dort die
 * VocabularyBar benutzt. Die anderen Bars kommen erst in den Dialogen zum Verwalten und
 * Bearbeiten der eigenen Listen zum Einsatz.
 *
 * @version 1.01     2025-11-20
 * @author Christian Dühl
 */

public class MainTabs {

    /** Die Logik des Vokabel-Trainers. */
    private final VocabularyTrainerLogic logic;

    /** Die grafische Oberfläche des Vokabel-Trainers. */
    private final VocabularyTrainerGui gui;

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

    /** Der Panel mit den Reitern für die Wahl zwischen den Vokabularien und den eigenen Listen. */
    private final JTabbedPane mainTabs;

    /** Der Inhalt des Reiters mit den Vokabularien. */
    private final JPanel vocabulariesTabPanel;

    /** Der Inhalt des Reiters mit den eigenen Listen. */
    private final JPanel ownListsTabPanel;

    /**
     * Der Inhalt des Reiters mit den beiden automatisch gepflegten Listen mit falsch abgefragten
     * Vokabeln aus Gruppen und anderen Vokabularien.
     */
    private final JPanel wrongTestedVocablesTabPanel;

    /** Der erzeugte CategoryTabs für die Vokabularien. */
    private CategoryTabs vocabularyCategoryTabs;

    /** Der erzeugte CategoryTabs für die eigenen Listen. */
    private CategoryTabs ownListCategoryTabs;

    /**
     * Die grafische Oberfläche mit den beiden automatisch gepflegten Listen mit falsch abgefragten
     * Vokabeln aus Gruppen und anderen Vokabularien.
     */
    private WrongTestedVocablesUi wrongTestedVocablesUi;

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

    /**
     * Konstruktor.
     *
     * @param logic
     *            Die Logik des Vokabel-Trainers.
     * @param gui
     *            Die grafische Oberfläche des Vokabel-Trainers.
     * @param uiObjects
     *            Die häufig verwendeten Funktionen der grafischen Oberfläche des Vokabeltrainers.
     */
    public MainTabs(VocabularyTrainerLogic logic, VocabularyTrainerGui gui,
            FumikoUiObjects uiObjects) {
        this.logic = logic;
        this.gui = gui;
        this.uiObjects = uiObjects;

        mainTabs = new JTabbedPane();
        vocabulariesTabPanel = new JPanel();
        ownListsTabPanel = new JPanel();
        wrongTestedVocablesTabPanel = new JPanel();
        init();
    }

    private void init() {
        initMainTabs();
        initTabPanels();
    }

    private void initMainTabs() {
        mainTabs.add("vorgegebene Vokabularien", GuiTools.createScrollPane(vocabulariesTabPanel));
        mainTabs.add("eigene Vokabellisten", GuiTools.createScrollPane(ownListsTabPanel));
        mainTabs.add("falsch übersetzte Vokabeln",
                GuiTools.createScrollPane(wrongTestedVocablesTabPanel));
    }

    private void initTabPanels() {
        vocabulariesTabPanel.setLayout(new BorderLayout());
        ownListsTabPanel.setLayout(new BorderLayout());
        wrongTestedVocablesTabPanel.setLayout(new BorderLayout());
    }

    /**
     * Initialisiert zu Beginn des Programms mit den eingelesenen Vokabularien und eigenen Listen.
     *
     * @param dataStructures
     *            Die Datenstrukturen des Vokabeltrainers..
     */
    public void initWithDataStructures(FumikoDataStructures dataStructures) {
        this.dataStructures = dataStructures;
        initWithDataStructures();
    }

    private void initWithDataStructures() {
        createVocabularyTabFromScratch();
        createOwnListsTabFromScratch();
        createWrongTestedVocablesTabFromScratch();

        showTabsWeViewedLastTime();
    }

    /**
     * Erzeugt den Aufbau der Reiter für die Kategorien und Unterkategorien für die Vokabularien.
     *
     * Dies ist nur einmal beim Start des Programms nötig, alle Umsortierungen etc. finden dann
     * innerhalb der Unterkategorien statt, die Struktur des Aufbaus bleibt aber gleich..
     */
    private void createVocabularyTabFromScratch() {
        List<Vocabulary> vocabularies = dataStructures.getVocabularies();
        List<String> vocabularyCategories = VocabularyTools.determineCategories(vocabularies);

        vocabulariesTabPanel.removeAll();

        vocabularyCategoryTabs = new CategoryTabs(vocabularyCategories, vocabularies, logic, gui,
                dataStructures, uiObjects);

        vocabulariesTabPanel.add(vocabularyCategoryTabs.getCategoryTabs(), BorderLayout.CENTER);
    }

    /**
     * Erzeugt den Aufbau der Reiter für die Kategorien und Unterkategorien für die Vokabularien.
     *
     * Dies ist zunächst beim Start des Programms nötig, alle Umsortierungen etc. finden dann
     * innerhalb der Unterkategorien statt, die Struktur des Aufbaus bleibt aber gleich. Allerdings
     * muss in den folgenden Fällen der Aufbau erneut vollzogen werden:
     *     - Eine neue Liste wurde angelegt.
     *     - Eine Liste wurde gelöscht.
     *     - eine ownListCategory hat sich geändert
     *     - eine ownListSubCategory hat sich geändert
     * In diesen Fällen hat sich nämlich die ownLists.getOwnLists() geändert.
     */
    public void createOwnListsTabFromScratch() {
        OwnLists ownLists = dataStructures.getOwnLists();

        List<String> ownListsCategories = VocabularyTools.determineCategories(
                ownLists.getOwnLists());

        ownListsTabPanel.removeAll();

        ownListCategoryTabs = new CategoryTabs(ownListsCategories, ownLists.getOwnLists(), logic,
                gui, dataStructures, uiObjects);

        ownListsTabPanel.add(ownListCategoryTabs.getCategoryTabs(), BorderLayout.CENTER);
    }

    /**
     * Erzeugt den Aufbau des Reiters mit den beiden automatisch gepflegten Listen mit falsch
     * abgefragten Vokabeln aus Gruppen und anderen Vokabularien.
     *
     * Dies ist nur einmal beim Start des Programms nötig, alle Umsortierungen etc. finden dann
     * innerhalb der Unterkategorien statt, die Struktur des Aufbaus bleibt aber gleich..
     */
    private void createWrongTestedVocablesTabFromScratch() {
        wrongTestedVocablesTabPanel.removeAll();

        wrongTestedVocablesUi = new WrongTestedVocablesUi(logic, gui, dataStructures, uiObjects);

        wrongTestedVocablesTabPanel.add(wrongTestedVocablesUi.getPanel(), BorderLayout.CENTER);
    }

    static final String VOCABULARY_LAST_SHOWN_TAB_INDEX_TYPE = "Vocabulary";
    static final String OWN_LIST_LAST_SHOWN_TAB_INDEX_TYPE = "OwnList";

    /**
     * Hier werden die persistent gespeicherten zuletzt aktiven Tabs sowohl bei den Vokabularien
     * als auch bei den eigenen Listen wieder angezeigt.
     *
     * Das Gegenstück zu dieser Methode ist storeShownTabIndices().
     */
    public void showTabsWeViewedLastTime() {
        Options options = dataStructures.getOptions();
        int lastShownMainTabIndex = options.getLastShownMainTabIndex();
        if (lastShownMainTabIndex >= 0 && lastShownMainTabIndex < mainTabs.getTabCount()) {
            mainTabs.setSelectedIndex(lastShownMainTabIndex);
        }
        vocabularyCategoryTabs.showTabsWeViewedLastTime(VOCABULARY_LAST_SHOWN_TAB_INDEX_TYPE);
        ownListCategoryTabs.showTabsWeViewedLastTime(OWN_LIST_LAST_SHOWN_TAB_INDEX_TYPE);
    }

    /**
     * Speichert die Indices des aktuell angezeigten Tabs in jeder Kategorie in den Optionen.
     *
     * Das Gegenstück zu dieser Methode ist showTabsWeViewedLastTime().
     */
    public void storeShownTabIndices(Options options) {
        /*
         * Bei Fehlern beim Startup wurde vocabularyCategoryTabs und ownListCategoryTabs noch nicht
         * initialisiert und diese Methode wird von reallyQuit() in der Logik aufgerufen. Daher
         * prüfe ich:
         */
        if (null != vocabularyCategoryTabs) {
            int lastShownMainTabIndex = mainTabs.getSelectedIndex();
            options.setLastShownMainTabIndex(lastShownMainTabIndex);
            vocabularyCategoryTabs.storeShownTabIndices(options,
                    VOCABULARY_LAST_SHOWN_TAB_INDEX_TYPE);
            ownListCategoryTabs.storeShownTabIndices(options, OWN_LIST_LAST_SHOWN_TAB_INDEX_TYPE);
        }
    }

    /** Ermittelt die Liste der Vokabeln aus den ausgewählten Vokabularien. */
    public List<Vocable> collectVocablesOfSelectedVocabularies() {
        List<Vocable> vocables = new ArrayList<>();

        vocables.addAll(vocabularyCategoryTabs.collectVocablesOfSelectedVocabularies());
        vocables.addAll(ownListCategoryTabs.collectVocablesOfSelectedVocabularies());

        return vocables;
    }

    /** Sortiert die angezeigten Vokabularien so, wie es in den Optionen eingetragen ist. */
    public void showVocabularyBarsInWantedOrder() {
        vocabularyCategoryTabs.showVocabularyBarsInWantedOrder();
        ownListCategoryTabs.showVocabularyBarsInWantedOrder();
    }

    /**
     * Zeigt oder versteckt die Buttons zur individuellen Sortierung der Vokabularien.
     *
     * Wird nur aufgerufen, wenn die Vokabularien auch in der individuellen Sortierung angezeigt
     * werden.
     */
    public void showOrHideIndividualVocabularySortModeMoveButtons() {
        vocabularyCategoryTabs.showOrHideIndividualVocabularySortModeMoveButtons();
        ownListCategoryTabs.showOrHideIndividualVocabularySortModeMoveButtons();
    }

    /**
     * Gibt die echten Vokabularien (keine OwnLists) in der benutzerdefinierten Reihenfolge zurück.
     *
     * Wird nur aufgerufen, wenn die Vokabularien auch in der individuellen Sortierung angezeigt
     * werden.
     */
    public List<Vocabulary> getVocabulariesInIndividualOrder() {
        /*
         * Bei Fehlern beim Startup wurde vocabularyCategoryTabs noch nicht initialisiert
         * und diese Methode wird von reallyQuit() in der Logik aufgerufen. Daher prüfe ich:
         */
        if (null == vocabularyCategoryTabs) {
            return new ArrayList<>();
        }
        else {
            return vocabularyCategoryTabs.getVocabulariesInIndividualOrder();
        }
    }

    /**
     * Gibt die OwnLists in der benutzerdefinierten Reihenfolge zurück.
     *
     * Wird nur aufgerufen, wenn die Vokabularien auch in der individuellen Sortierung angezeigt
     * werden.
     */
    public List<OwnList> getOwnListsInIndividualOrder() {
        /*
         * Bei Fehlern beim Startup wurde ownListCategoryTabs noch nicht initialisiert
         * und diese Methode wird von reallyQuit() in der Logik aufgerufen. Daher prüfe ich:
         */
        if (null == ownListCategoryTabs) {
            return new ArrayList<>();
        }
        else {
            return ownListCategoryTabs.getOwnListsInIndividualOrder();
        }
    }

    /** Ermittelt die auf dem aktuellen Reiter angezeigten VocabularyBars. */
    public List<VocabularyBar> determineBarsOfSelectedTab() {
        int selectedIndex = mainTabs.getSelectedIndex();
        if (selectedIndex == 0) {
            return vocabularyCategoryTabs.determineBarsOfSelectedTab();
        }
        else if (selectedIndex == 1) {
            return ownListCategoryTabs.determineBarsOfSelectedTab();
        }
        else if (selectedIndex == 2) {
            return wrongTestedVocablesUi.getVocabularyBars();
        }
        else {
            throw new RuntimeException("Unbekannter Tab Index " + selectedIndex);
        }
    }

    /**
     * Setzt die Vordergrundfarbe auf den nach den Optionen und ggf. dem Durchschnitt der
     * erfolgreichen Abfrage der Vokabeln aus dem Vokabular in allen Vokabularien richtig.
     */
    public void setCorrectForegroundColorOfVocabularyBars() {
        vocabularyCategoryTabs.setCorrectForegroundColorOfVocabularyBars();
        ownListCategoryTabs.setCorrectForegroundColorOfVocabularyBars();

        for (VocabularyBar bar : wrongTestedVocablesUi.getVocabularyBars()) {
            bar.setCorrectForegroundColor();
        }
    }

    /** Aktualisiert den angezeigten Text in allen Vokabularien je nach den Optionen. */
    public void setCorrectTextOfVocabularyBars() {
        vocabularyCategoryTabs.setCorrectTextOfVocabularyBars();
        ownListCategoryTabs.setCorrectTextOfVocabularyBars();

        for (VocabularyBar bar : wrongTestedVocablesUi.getVocabularyBars()) {
            bar.setCorrectText();
        }
    }

    /**
     * Aktualisiert den angezeigten Text zum Prozent des Erfolges in allen Vokabularien je nach den
     * Optionen.
     */
    public void showOrHidePercentInVocabularyBarsLater() {
        vocabularyCategoryTabs.showOrHidePercentInVocabularyBarsLater();
        ownListCategoryTabs.showOrHidePercentInVocabularyBarsLater();
    }

    /** Getter für den Panel. */
    public Component getPanel() {
        return mainTabs;
    }

    /**
     * Zeigt auf dem Übersetzen-Buttons aller Bars das richtige Icon für die Richtung der
     * Übersetzung an.
     */
    public void showTranslationDirectionOnBarButtons() {
        vocabularyCategoryTabs.showTranslationDirectionOnBarButtons();
        ownListCategoryTabs.showTranslationDirectionOnBarButtons();

        for (VocabularyBar bar : wrongTestedVocablesUi.getVocabularyBars()) {
            bar.showTranslationDirectionOnBarButtons();
        }
    }

    /** Buttons zum Exportieren, Bearbeiten und Löschen von eigene Listen ein oder ausschalten. */
    public void toggleShowOwnListButtons() {
        vocabularyCategoryTabs.showTestViewListSheetButtons(); // da gibt es keine solchen!
        ownListCategoryTabs.toggleShowOwnListButtons(); // schaltet wirklich um!
    }

    /** Zeigt die normalen Button zum Abfragen und betrachten der Vokabularien an. */
    public void showTestViewListSheetButtons() {
        vocabularyCategoryTabs.showTestViewListSheetButtons();
        ownListCategoryTabs.showTestViewListSheetButtons();
    }

    /** Zeigt bei eigenen Listen die Buttons zum Löschen, Bearbeiten und Exportieren an. */
    public void showOwnListExtraButtons() {
        ownListCategoryTabs.showOwnListExtraButtons();
    }

    /** Zeigt den Reiter mit den eigenen Listen an. */
    public void showTabWithOwnLists() {
        mainTabs.setSelectedIndex(1);
    }

}
