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

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

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;

import de.duehl.basics.text.NumberString;
import de.duehl.swing.logic.LongTimeProcessInformer;
import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.ui.dialogs.base.ModalDialogBase;
import de.duehl.swing.ui.pages.DatasetsOnPages;
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.VocabularyTrainerLogic;
import de.duehl.vocabulary.japanese.logic.test.VocableListTesterLogic;
import de.duehl.vocabulary.japanese.ui.VocabularyTrainerGui;
import de.duehl.vocabulary.japanese.ui.components.bars.VocableBar;
import de.duehl.vocabulary.japanese.ui.data.FumikoUiObjects;
import de.duehl.vocabulary.japanese.ui.dialog.vocables.detail.VocableWithInternaDialog;
import de.duehl.vocabulary.japanese.ui.dialog.vocables.lister.VocabularyListerDialog;
import de.duehl.vocabulary.japanese.ui.dialog.vocables.single.SingleVocableVocabularyViewerDialog;
import de.duehl.vocabulary.japanese.ui.filter.VocableFilterInputs;
import de.duehl.vocabulary.japanese.ui.filter.VocableFilterPanel;

/**
 * Diese Klasse stellt den Dialog zur erweiterten Suche nach Vokabeln im Vokabel-Trainer dar.
 *
 * @version 1.01     2025-11-26
 * @author Christian Dühl
 */

public class ComplexVocableSearchDialog extends ModalDialogBase {

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

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

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

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

    /** Das Element mit den Filterkriterien für die Vokabeln. */
    private final VocableFilterPanel vocableFilter;

    /** Zur Anzeiger der Statistik. */
    private final JLabel statisticLabel;

    /** Zeigt die Vokabeln die den Filterkriterien entsprechen an. */
    private final DatasetsOnPages<Vocable> pages;

    /**
     * Die Eingaben der Suchkriterien. Diese werden zwischen zwei Aufrufen in der selben Sitzung
     * hier gespeichert.
     */
    private static VocableFilterInputs inputs;

    /**
     * Konstruktor.
     *
     * @param logic
     *            Die Logik des Vokabel-Trainers.
     * @param gui
     *            Die grafische Oberfläche des Vokabel-Trainers.
     * @param description
     *            Die Beschreibung der Menge von Vokabeln.
     * @param dataStructures
     *            Die Datenstrukturen des Vokabeltrainers.
     */
    public ComplexVocableSearchDialog(VocabularyTrainerLogic logic, VocabularyTrainerGui gui,
            FumikoDataStructures dataStructures, FumikoUiObjects uiObjects) {
        super(gui.getLocation(), gui.getProgramImage(), "Erweiterte Suche nach Vokabeln");
        addEscapeBehaviour();
        addClosingWindowListener(() -> storeSearchParametersAndCloseDialog());

        this.logic = logic;
        this.gui = gui;
        this.dataStructures = dataStructures;
        this.uiObjects = uiObjects;

        vocableFilter = new VocableFilterPanel(dataStructures,
                filteredVocablesList -> reactOnFilteredVocables(filteredVocablesList));

        statisticLabel = new JLabel();

        Options options = dataStructures.getOptions();
        int numberOfDatasetsPerSide = options.getNumberOfDatasetsPerPageOfOwnListEditor();
        int numberOfColumns = options.getNumberOfColumnsOfOwnListEditor();
        pages = new DatasetsOnPages<>(logic.collectVocablesOfAllVocabularies(),
                vocable -> createDatasetUi(vocable), numberOfDatasetsPerSide, numberOfColumns);

        if (null == inputs) {
            inputs = new VocableFilterInputs();
        }

        init();
        fillDialog();
    }

    /** Erstellt aus dem übergebenen Datensatz die Anzeige für die Gui. */
    private Component createDatasetUi(Vocable vocable) {
        VocableBar bar = new VocableBar(vocable, () -> showDetailDialog(vocable));
        bar.useButtonAsShowDetails();
        bar.createGui();
        return bar.getPanel();
    }

    private void showDetailDialog(Vocable vocable) {
        VocableWithInternaDialog viewer = new VocableWithInternaDialog(vocable, dataStructures,
                uiObjects, getLocation());
        viewer.setVisible(true);
    }

    private void init() {
        initVocableFilter();
        initStatistic();
    }

    private void initVocableFilter() {
        vocableFilter.arrangeForComplexVocableSearchDialog();
        vocableFilter.loadInputs(inputs);
    }

    private void initStatistic() {
        updateStatistic(vocableFilter.createFilteredVocablesList());
    }

    private void reactOnFilteredVocables(List<Vocable> filteredVocablesList) {
        updateStatistic(filteredVocablesList);
        if (filteredVocablesList.isEmpty()) {
            /*
             * Damit kommt der ListNavigator nicht klar, weil er dann keinen aktuellen Datensatz
             * zurückgeben kann.
             *
             * Das wäre schön, wenn die Liste dann einfach leer wäre, ist aber ein größerer
             * Eingriff...
             */
            GuiTools.informUser(getWindowAsComponent(), "Keine Vokabeln gefunden",
                    "Die Suchkriterien führen zu einer leeren Liste von Vokabeln, daher wird "
                            + "diese nicht angezeigt.");
        }
        else {
            pages.setOtherDatasets(filteredVocablesList);
        }
    }

    /** Baut die Gui auf. */
    @Override
    protected void populateDialog() {
        add(createSearchAndStatisticPart(), BorderLayout.CENTER);
        add(createButtonPart(), BorderLayout.SOUTH);
    }

    private Component createSearchAndStatisticPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());

        panel.add(createSearchAndFoundVocablesPart(), BorderLayout.CENTER);
        panel.add(createStatisticsPart(), BorderLayout.SOUTH);

        return panel;
    }

    private Component createSearchAndFoundVocablesPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());

        panel.add(createSearchPart(), BorderLayout.WEST);
        panel.add(createFoundVocablesPart(), BorderLayout.CENTER);

        return panel;
    }

    private Component createSearchPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        GuiTools.createTitle("Kriterien", panel);

        panel.add(vocableFilter.getPanel(), BorderLayout.NORTH);

        return panel;
    }

    private Component createFoundVocablesPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        GuiTools.createTitle("Treffer", panel);

        panel.add(pages.getPanel(), BorderLayout.CENTER);

        return panel;
    }

    private Component createStatisticsPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        GuiTools.createTitle("Statistik", panel);

        panel.add(statisticLabel, BorderLayout.CENTER);

        return panel;
    }

    private Component createButtonPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(1, 0, 5, 0));

        panel.add(createTestVocablesButton());
        panel.add(createEinzeldarsetllungButton());
        panel.add(createListendarstellungButton());
        panel.add(createVokabelblattdarstellungButton());
        panel.add(createSaveListButton());
        panel.add(createCloseDialogButton());

        return panel;
    }

    private Component createTestVocablesButton() {
        JButton button = new JButton("Vokabeln abfragen");
        button.addActionListener(e -> testVocables());
        return button;
    }

    private void testVocables() {
        List<Vocable> foundVocables = vocableFilter.createFilteredVocablesList();
        if (foundVocables.isEmpty()) {
            informNoVocablesMatchSearchCriteria();
        }
        else {
            storeSearchParametersAndCloseDialog();
            String vocablesTitle = "Vokabeln aus der erweiterten Suche";
            VocableListTesterLogic tester = new VocableListTesterLogic(foundVocables, vocablesTitle,
                    logic, dataStructures, uiObjects);
            tester.test();

            gui.setCorrectForegroundColorOfVocabularyBarsLater();
            gui.setMessageLater("Getestet wurden: " + vocablesTitle);
        }
    }

    private Component createEinzeldarsetllungButton() {
        JButton button = new JButton("Einzeldarstellung");
        button.addActionListener(e -> showAsEinzeldarstellung());
        return button;
    }

    private void showAsEinzeldarstellung() {
        List<Vocable> foundVocables = vocableFilter.createFilteredVocablesList();
        if (foundVocables.isEmpty()) {
            informNoVocablesMatchSearchCriteria();
        }
        else {
            storeSearchParametersAndCloseDialog();
            SingleVocableVocabularyViewerDialog dialog = new SingleVocableVocabularyViewerDialog(
                    foundVocables, "Gefundene Vokabeln", dataStructures, uiObjects);
            dialog.setVisible(true);
            gui.setMessageLater("Die gefundenen Vokabeln wurden angezeigt.");
        }
    }

    private void informNoVocablesMatchSearchCriteria() {
        String message = "Es gibt keine Vokabeln, die auf die Suchkriterien passen.";
        GuiTools.informUser(getWindowAsComponent(), "Hinweis", message);
        gui.setMessageLater(message);
    }

    private Component createListendarstellungButton() {
        JButton button = new JButton("Listendarstellung");
        button.addActionListener(e -> showAsListendarstellung());
        return button;
    }

    private void showAsListendarstellung() {
        List<Vocable> foundVocables = vocableFilter.createFilteredVocablesList();
        if (foundVocables.isEmpty()) {
            informNoVocablesMatchSearchCriteria();
        }
        else {
            storeSearchParametersAndCloseDialog();
            VocabularyListerDialog dialog = new VocabularyListerDialog(foundVocables,
                    "Vokabeln aus der erweiterten Suche", dataStructures, uiObjects, getLocation(),
                    (LongTimeProcessInformer) gui);
            dialog.setVisible(true);
            gui.setMessageLater(
                    "Die Vokabeln aus der erweiterten Suche wurden als Liste angezeigt.");
        }
    }

    private Component createVokabelblattdarstellungButton() {
        JButton button = new JButton("Darstellung als Vokabelblatt");
        button.addActionListener(e -> showAsVokabelblattdarstellung());
        return button;
    }

    private void showAsVokabelblattdarstellung() {
        List<Vocable> foundVocables = vocableFilter.createFilteredVocablesList();
        if (foundVocables.isEmpty()) {
            informNoVocablesMatchSearchCriteria();
        }
        else {
            storeSearchParametersAndCloseDialog();
            gui.sheetWithVocables(foundVocables, "Vokabeln aus der erweiterten Suche");
            gui.setMessageLater(
                    "Die Vokabeln aus der erweiterten Suche wurden als Blatt angezeigt.");
        }
    }

    private Component createSaveListButton() {
        JButton button = new JButton("Gefundene Vokabeln als Liste speichern");
        button.addActionListener(e -> saveList());
        return button;
    }

    private Component createCloseDialogButton() {
        JButton button = new JButton("Schließen");
        button.addActionListener(e -> storeSearchParametersAndCloseDialog());
        return button;
    }

    /** Der Anfang des Namens für abgespeicherte Listen aus der erweiterten Suche. */
    public static final String COMPLEX_SEARCH_NAME_START = "Erweiterte Suche";

    private void saveList() {
        List<Vocable> foundVocables = vocableFilter.createFilteredVocablesList();
        if (foundVocables.isEmpty()) {
            informNoVocablesMatchSearchCriteria();
        }
        else {
            gui.saveAsList(foundVocables, COMPLEX_SEARCH_NAME_START);
        }
    }

    private void updateStatistic(List<Vocable> filteredVocablesList) {
        statisticLabel
                .setText("Anzahl Vokabeln: " + NumberString.taupu(filteredVocablesList.size()));
    }

    private void storeSearchParametersAndCloseDialog() {
        vocableFilter.storeInputs(inputs);
        closeDialog();
    }

}
