package de.duehl.vocabulary.japanese.ui.dialog.vocables.sheet;

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

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import de.duehl.basics.system.SystemTools;
import de.duehl.vocabulary.japanese.common.data.VocablesShuffleType;
import de.duehl.vocabulary.japanese.common.persistence.Options;
import de.duehl.vocabulary.japanese.data.FumikoDataStructures;
import de.duehl.vocabulary.japanese.data.Vocable;
import de.duehl.vocabulary.japanese.logic.internal.InternalDataRequester;
import de.duehl.vocabulary.japanese.tools.VocableListShuffler;
import de.duehl.vocabulary.japanese.ui.components.VocableViewer;
import de.duehl.vocabulary.japanese.ui.data.FumikoUiObjects;
import de.duehl.vocabulary.japanese.ui.dialog.vocables.detail.VocableWithInternaViewer;
import de.duehl.vocabulary.japanese.ui.dialog.vocables.sheet.data.WithVocablePanesStartable;

/**
 * Diese Klasse erzeugt für eine Menge von Vokabeln, die man in Form eines Vokabelblattes anzeigen
 * möchte, die passenden Komponenten für jede Vokabel.
 *
 * Vorher war es einfach eine normale Schleife in der Klasse VocabularySheetDialog, dann gab es
 * aber plötzlich manchmal nicht reproduzierbare Probleme in den Tiefen von Swing, dort wo ich am
 * Ende des Dokuments weitere Texte anhänge, welche die Anzeige des Dialogs verhinderten.
 *
 * Daher mache ich es nun hier nach und nach in Einzelschritten für jede Vokabel einzeln im EDT.
 *
 * Damit die GlassPane bei großen Mengen auch die Stoppuhr hochzählt, läuft das ganze noch in
 * einem eigenen Thread mit einem winzigen Sleep dazwischen.
 *
 * @version 1.01     2025-11-20
 * @author Christian Dühl
 */

public class VocableSheetsCreator {

    /** Die Liste mit den anzuzeigenden Vokabeln. */
    private final List<Vocable> vocables;

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

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

    /** Startet etwas, dem die hier generierte Liste von Komponenten der Vokabeln übergeben wird. */
    private final WithVocablePanesStartable startable;

    /** Die Position des Rahmens der Oberfläche, vor der dieser Dialog erzeugt wird. */
    private final Point parentLocation;

    /** Die Liste mit den Paneln der dargestellten Vokabeln. */
    private List<Component> vocablePanes;

    /**
     * Konstruktor.
     *
     * @param vocables
     *            Die Liste mit den anzuzeigenden Vokabeln.
     * @param dataStructures
     *            Die Datenstrukturen des Vokabeltrainers.
     * @param uiObjects
     *            Die häufig verwendeten Funktionen der grafischen Oberfläche des Vokabeltrainers.
     * @param startable
     *            Startet etwas, dem die hier generierte Liste von Komponenten der Vokabeln
     *            übergeben wird.
     * @param parentLocation
     *            Position des Rahmens der Oberfläche, vor der dieser Dialog erzeugt wird.
     */
    public VocableSheetsCreator(List<Vocable> vocables, FumikoDataStructures dataStructures,
            FumikoUiObjects uiObjects, WithVocablePanesStartable startable, Point parentLocation) {
        this.dataStructures = dataStructures;
        this.uiObjects = uiObjects;
        this.startable = startable;
        this.parentLocation = parentLocation;

        Options options = dataStructures.getOptions();
        if (options.isUseVocablesShuffleTypeForShowingListsAndSheetsToo()) {
            VocablesShuffleType type = options.getVocablesShuffleType();
            VocableListShuffler shuffler = new VocableListShuffler(vocables, type, dataStructures);
            shuffler.shuffle();
            this.vocables = shuffler.getVocables();
        }
        else {
            this.vocables = vocables;
        }
    }

    /** Die in einzelnen Schritten im EDT auszuführenden Arbeitsschritte. */
    private List<Runnable> runnables;

    /**
     * Erstellt die Komponenten für die Gui.
     *
     * Erzeugt die Liste der Panes für die Vokabeln nach und nach jeweils in einem Loop des EDT.
     * Hinterher wird der eigentliche Dialog mit den ggf. zufällig sortierten Vokabeln und den hier
     * erzeugten Komponenten zur Anzeige der Vokabeln gestartet.
     *
     * Vorher war die Erzeugung dieser Komponenten einfach eine normale Schleife in der Klasse
     * VocabularySheetDialog, dann gab es aber plötzlich manchmal nicht reproduzierbare Probleme in
     * den Tiefen von Swing, dort wo ich am Ende des Dokuments weitere Texte anhänge.
     *
     * Daher mache ich es nun hier nach und nach im EDT. Da das ganze von außen immer in eigenen
     * Threads aufgerufen wird, muss ich hier keinen eigenen starten.
     */
    public void create() {
        init();
        createRunnables();
        createVocablePanesInEdtLoop();
    }

    private void init() {
        vocablePanes = new ArrayList<>();
    }

    private void createRunnables() {
        runnables = new ArrayList<>();

        for (Vocable vocable : vocables) {
            runnables.add(() -> createAndAddVocablePane(vocable));
        }

        runnables.add(() -> startable.runWithVocablePanes(vocables, vocablePanes));
    }

    private void createAndAddVocablePane(Vocable vocable) {
        Component vocablePane = createVocablePane(vocable);
        vocablePanes.add(vocablePane);
    }

    private Component createVocablePane(Vocable vocable) {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());

        panel.add(createPureVocablePane(vocable), BorderLayout.CENTER);
        Options options = dataStructures.getOptions();
        if (options.isShowDetailsButtonInVokabelblattDarstellung()) {
            panel.add(createDetailsButton(vocable), BorderLayout.SOUTH);
        }

        return panel;
    }

    private Component createPureVocablePane(Vocable vocable) {
        Options options = dataStructures.getOptions();
        InternalDataRequester requester = dataStructures.getInternalDataRequester();

        VocableViewer viewer = new VocableViewer(options);
        viewer.showVocable(vocable, requester.getInternalDataForVocable(vocable));
        return viewer.getPanel();
    }

    private Component createDetailsButton(Vocable vocable) {
        JButton button = new JButton("Details anzeigen");
        button.addActionListener(e -> showDetails(vocable));
        return button;
    }

    private void showDetails(Vocable vocable) {
        VocableWithInternaViewer viewer = new VocableWithInternaViewer(vocable, dataStructures,
                uiObjects, parentLocation);
        viewer.setVisible(true);
    }

    /**
     * Führt die einzelnen Tasks zur Erzeugung der Komponenten und am Ende dem Aufruf des
     * eigentlichen Dialogs in Einzelschritten im EDT aus.
     *
     * Damit die GlassPane bei großen Mengen auch die Stoppuhr hochzählt, läuft das ganze noch in
     * einem eigenen Thread mit einem winzigen Sleep dazwischen.
     *
     * Falls das nicht reicht, könnte ich das nächste invokeLater erst am Ende des jeweiligen Tasks
     * davor aufrufen....
     *
     * Es sieht aber so aus, als passt das so. Leider zuckt es nach dem Öffnen. Aber es
     * funktioniert wieder.
     */
    private void createVocablePanesInEdtLoop() {
        for (Runnable runnable : runnables) {
            SwingUtilities.invokeLater(runnable);
            SystemTools.sleep(5); // Millisekunden
        }
    }

}
