package de.duehl.vocabulary.japanese.ui.components.bars;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import de.duehl.basics.text.NumberString;
import de.duehl.swing.logic.LongTimeProcessInformer;
import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.ui.move.VerticalMoveButtons;
import de.duehl.swing.ui.move.data.MovingGui;
import de.duehl.swing.ui.move.data.VerticalMoveButtonsUser;
import de.duehl.vocabulary.japanese.common.data.TranslationDirection;
import de.duehl.vocabulary.japanese.common.persistence.Options;
import de.duehl.vocabulary.japanese.common.ui.resources.IconDefinitions;
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.success.VocabularyTestSuccesssCalculator;
import de.duehl.vocabulary.japanese.logic.test.VocableListTesterLogic;
import de.duehl.vocabulary.japanese.tools.VocabularyTools;
import de.duehl.vocabulary.japanese.ui.VocabularyTrainerGui;
import de.duehl.vocabulary.japanese.ui.components.bars.data.VocabularyBarShownButtonsType;
import de.duehl.vocabulary.japanese.ui.data.FumikoUiObjects;
import de.duehl.vocabulary.japanese.ui.dialog.vocables.lister.VocabularyListerDialog;
import de.duehl.vocabulary.japanese.ui.dialog.vocables.sheet.VocableSheetsCreator;
import de.duehl.vocabulary.japanese.ui.dialog.vocables.sheet.VocabularySheetDialog;
import de.duehl.vocabulary.japanese.ui.dialog.vocables.single.SingleVocableVocabularyViewerDialog;
import de.duehl.vocabulary.japanese.ui.tools.VocabularyTrainerUiTools;

/**
 * Diese Klasse stellt die grafische Oberfläche der Anzeige eines Vokabulars in der Übersicht dar.
 *
 * @version 1.01     2026-01-19
 * @author Christian Dühl
 */

public class VocabularyBar implements VerticalMoveButtonsUser {

    private static final String TEST_VIEW_LIST_SHEET_BUTTONS_NAME = "TEST-VIEW-LIST-SHEET-BUTTONS";
    private static final String MOVE_UP_AND_DOWN_BUTTONS_NAME = "MOVE-UP-AND-DOWN-BUTTONS";
    private static final String OWN_LISTS_EXTRA_BUTTONS_NAME = "OWN-LISTS-EXTRA-BUTTONS";


    /** Das Vokabular zu welchem die Bar angezeigt wird. */
    private final Vocabulary vocabulary;

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

    /** Die grafische Oberfläche. */
    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;

    /** Die grafische Oberfläche die Bars bewegt und angeben kann, ob die Bewegung zulässig ist. */
    private final MovingGui<VocabularyBar> movingGui;

    /** Der Panel auf dem das Vokabular dargestellt wird. */
    private final JPanel panel;

    /** Der Panel mit den Buttons zum Ansehen und Anfragen des Vokabulars. */
    private final JPanel testViewListSheetButtonsPanel;

    /**
     * Der Panel der entweder den Panel mit den Buttons zum Ansehen und Anfragen des Vokabulars
     * oder aber den Panel mit Buttons zum Verschieben nach oben und unten anzeigen kann.
     */
    private final JPanel switchingContentButtonPanel;

    /** Das Layout für den Panel mit den wechselnden Inhalten (switchingContentButtonPanel). */
    private final CardLayout cardLayout;

    /** Der Panel mit Buttons zum Verschieben nach oben und unten. */
    private final VerticalMoveButtons moveButtons;

    /** Der Panel mit Buttons zum Exportieren, Bearbeiten und Löschen von eigenen Listen. */
    private final JPanel ownListsExtraButtonsPanel;

    /** Hiermit kann man die Vokabeln dieses Vokabulars auswählen. */
    private final JCheckBox selectionCheckBox;

    /** Das Label mit der Beschreibung des Vokabulars. */
    private final JLabel vocabularyLabel;

    /** Die ggf. um den Anfang gekürzte Beschreibung des Vokabulars. */
    private String perhapsShortenedVocabularyDescription;

    /** Der Panel mit dem Titel und vielleicht der Prozentzahl des Erfolges. */
    private final JPanel vocabularyAndPercentPanel;

    /** Das Label mit dem Erfolg bei den letzten zehn Abfragen der Vokabeln. */
    private final JLabel successPercentLabel;

    /** Gibt an, welche Buttons gerade angezeigt werden. */
    private VocabularyBarShownButtonsType shownButtonsType;

    /**
     * Konstruktor.
     *
     * @param vocabulary
     *            Das Vokabular zu welchem die Bar angezeigt wird.
     * @param logic
     *            Die Logik des Vokabel-Trainers.
     * @param gui
     *            Die grafische Oberfläche.
     * @param dataStructures
     *            Die Datenstrukturen des Vokabeltrainers.
     * @param uiObjects
     *            Die häufig verwendeten Funktionen der grafischen Oberfläche des Vokabeltrainers.
     * @param movingGui
     *            Die grafische Oberfläche die Bars bewegt und angeben kann, ob die Bewegung
     *            zulässig ist.
     */
    public VocabularyBar(Vocabulary vocabulary, VocabularyTrainerLogic logic,
            VocabularyTrainerGui gui, FumikoDataStructures dataStructures,
            FumikoUiObjects uiObjects, MovingGui<VocabularyBar> movingGui) {
        this.vocabulary = vocabulary;
        this.logic = logic;
        this.gui = gui;
        this.dataStructures = dataStructures;
        this.uiObjects = uiObjects;
        this.movingGui = movingGui;

        panel = new JPanel();
        moveButtons = new VerticalMoveButtons((VerticalMoveButtonsUser) this);
        selectionCheckBox = new JCheckBox();

        vocabularyLabel = new JLabel();
        successPercentLabel = new JLabel();

        testViewListSheetButtonsPanel = new JPanel();
        switchingContentButtonPanel = new JPanel();
        cardLayout = new CardLayout();
        perhapsShortenedVocabularyDescription = buildPerhapsShortenedVocabularyDescription();
        vocabularyAndPercentPanel = new JPanel();
        ownListsExtraButtonsPanel = new JPanel();

        shownButtonsType = VocabularyBarShownButtonsType.TEST_VIEW_LIST_SHEET_BUTTONS;

        setLabelText();
        initSuccessPercentLabel();
        initViewTestListSheet();
        initOwnListsExtraButtons();
        initSwitchingContentButtonPanel();
        initOtherElements();
        fillPanel();
        setCorrectForegroundColor();
        fillVocabularyAndPercentPanel();
    }

    private void initSuccessPercentLabel() {
        GuiTools.setMonospacedFont(successPercentLabel);
        successPercentLabel.setBorder(new EmptyBorder(0, 5, 0, 5));
    }

    private String buildPerhapsShortenedVocabularyDescription() {
        String description = vocabulary.getDescription();
        Options options = dataStructures.getOptions();
        if (options.isHideStartOfVocabularyDescription()) {
            description = VocabularyTools.hideStartOfVocabularyDescription(description,
                    options.isHideLessStartOfVocabularyDescriptionForVhs());
        }
        int maxLength = options.getNumberOfCharsInVocableBarTitleShown();
        if (description.length() > maxLength) {
            description = description.substring(0, maxLength - 4) + " ...";
        }
        return description;
    }

    private void setLabelText() {
        vocabularyLabel.setText(perhapsShortenedVocabularyDescription);
    }

    private void initViewTestListSheet() {
        testViewListSheetButtonsPanel.setLayout(new FlowLayout());
        populateViewTestListSheet();
    }

    private void populateViewTestListSheet() {
        testViewListSheetButtonsPanel.removeAll();

        testViewListSheetButtonsPanel.add(createTestButton());
        testViewListSheetButtonsPanel.add(createViewButton());
        testViewListSheetButtonsPanel.add(createListButton());
        testViewListSheetButtonsPanel.add(createSheetButton());

        testViewListSheetButtonsPanel.repaint();
        testViewListSheetButtonsPanel.validate();
        testViewListSheetButtonsPanel.invalidate();
    }

    private Component createViewButton() {
        JButton button = VocabularyTrainerUiTools.createPicturedButton(
                IconDefinitions.VIEW_VOCABULARY, e -> viewVocabulary(), "Vokabeln anzeigen");
        if (vocabulary.getVocables().isEmpty()) {
            button.setEnabled(false);
        }
        return button;
    }

    private void viewVocabulary() {
        SingleVocableVocabularyViewerDialog dialog = new SingleVocableVocabularyViewerDialog(
                vocabulary.getVocables(), "Gesamtanzeige '" + vocabulary.getDescription() + "'",
                dataStructures, uiObjects);
        dialog.setVisible(true);
        gui.setMessageLater("Das Vokabular \"" + vocabulary.getDescription()
                + "\" wurde in Einzeldarstellung angezeigt.");
    }

    private Component createTestButton() {
        String pictureIdentifier;
        Options options = dataStructures.getOptions();
        TranslationDirection direction = options.getTranslationDirection();
        if (direction == TranslationDirection.JAPANESE_TO_GERMAN) {
            pictureIdentifier = IconDefinitions.TEST_VOCABULARY_JAPANESE_TO_GERMAN;
        }
        else if (direction == TranslationDirection.GERMAN_TO_JAPANESE) {
            pictureIdentifier = IconDefinitions.TEST_VOCABULARY_GERMAN_TO_JAPANESE;
        }
        else {
            throw new RuntimeException("Unbekannte TranslationDirection " + direction + ".");
        }

        JButton button = VocabularyTrainerUiTools.createPicturedButton(
                pictureIdentifier, e -> testVocabulary(), "Vokabeln abfragen");
        if (vocabulary.getVocables().isEmpty()) {
            button.setEnabled(false);
        }
        return button;
    }

    private void testVocabulary() {
        VocableListTesterLogic tester = new VocableListTesterLogic(vocabulary.getVocables(),
                "Gesamtabfrage '" + vocabulary.getDescription() + "'", logic, dataStructures,
                uiObjects);
        tester.test();

        setCorrectForegroundColor();
        gui.setMessageLater(
                "Das Vokabular \"" + vocabulary.getDescription() + "\" wurde abgefragt.");
    }

    private Component createListButton() {
        JButton button = VocabularyTrainerUiTools.createPicturedButton(
                IconDefinitions.SHOW_VOCABULARY_LIST, e -> listVocabulary(),
                "Vokabeln als Liste anzeigen");
        if (vocabulary.getVocables().isEmpty()) {
            button.setEnabled(false);
        }
        return button;
    }

    private void listVocabulary() {
        VocabularyListerDialog dialog = new VocabularyListerDialog(vocabulary.getVocables(),
                vocabulary.getDescription(), dataStructures, uiObjects, uiObjects.getGuiLocation(),
                (LongTimeProcessInformer) gui);
        dialog.setVisible(true);
        gui.setMessageLater("Das Vokabular \"" + vocabulary.getDescription()
                + "\" wurde als Liste angezeigt.");
    }

    private Component createSheetButton() {
        JButton button = VocabularyTrainerUiTools.createPicturedButton(
                IconDefinitions.SHOW_VOCABULARY_SHEET, e -> listVocabularyAsSheet(),
                "Vokabeln auf einem Blatt anzeigen");
        if (vocabulary.getVocables().isEmpty()) {
            button.setEnabled(false);
        }
        return button;
    }

    private void listVocabularyAsSheet() {
        gui.startLongTimeProcess("Öffne Blattdarstellung mit "
                + NumberString.taupu(vocabulary.getVocables().size())
                + " Vokabeln");
        new Thread(() -> listVocabularyAsSheetInOwnThread()).start();
    }

    private void listVocabularyAsSheetInOwnThread() {
        try {
            tryToListVocabularyAsSheetInOwnThread();
        }
        catch (Exception exception) {
            GuiTools.informUser("Hier ging etwas schief", "Bei der Erzeugng der Panel für die "
                    + "Blattdarstellung gab es ein Problem.");
            gui.endLongTimeProcess();
        }
    }

    private void tryToListVocabularyAsSheetInOwnThread() {
        VocableSheetsCreator creator = new VocableSheetsCreator(vocabulary.getVocables(),
                dataStructures, uiObjects,
                (vocables, vocablePanes) -> listVocabularyAsSheetInEdt(vocables, vocablePanes),
                uiObjects.getGuiLocation());
        creator.create();
    }

    private void listVocabularyAsSheetInEdt(List<Vocable> vocables, List<Component> vocablePanes) {
        try {
            tryToListVocabularyAsSheetInEdt(vocables, vocablePanes);
        }
        catch (Exception exception) {
            GuiTools.informUser("Hier ging etwas schief", "Bei der Anzeige der Blattdarstellung "
                    + "gab es ein Problem.");
            gui.endLongTimeProcess();
        }
    }

    private void tryToListVocabularyAsSheetInEdt(List<Vocable> vocables,
            List<Component> vocablePanes) {
        VocabularySheetDialog dialog = new VocabularySheetDialog(vocables, vocablePanes,
                vocabulary.getDescription(), dataStructures, uiObjects, uiObjects.getInformer(),
                uiObjects.getGuiLocation());
        dialog.setVisible(true);
        dialog.requestFocus();
        gui.endLongTimeProcess();
        gui.setMessageLater("Das Vokabular \"" + vocabulary.getDescription()
                + "\" wurde als Blatt angezeigt.");
    }

    /** Gibt an, ob die Bar eines Vokabulars nach oben bewegt werden kann. */
    @Override
    public boolean canMoveButtonsUserMoveUp() {
        return movingGui.canBarMoveUp(this);
    }

    /** Gibt an, ob die Bar eines Vokabulars nach unten bewegt werden kann. */
    @Override
    public boolean canMoveButtonsUserMoveDown() {
        return movingGui.canBarMoveDown(this);
    }

    /** Verschiebt die Bar eines Vokabulars an die erste Stelle. */
    @Override
    public void moveMoveButtonsUserToFirst() {
        movingGui.moveBarToFirst(this);
    }

    /** Verschiebt die Bar eines Vokabulars nach oben. */
    @Override
    public void moveMoveButtonsUserUp() {
        movingGui.moveBarUp(this);
    }

    /** Verschiebt die Bar eines Vokabulars nach unten. */
    @Override
    public void moveMoveButtonsUserDown() {
        movingGui.moveBarDown(this);
    }

    /** Verschiebt die Bar eines Vokabulars an die letzte Stelle. */
    @Override
    public void moveMoveButtonsUserToLast() {
        movingGui.moveBarToLast(this);
    }

    /**
     * Setzt die Farben und Darstellung der Buttons abhängig davon, ob sie verschoben werden
     * können.
     */
    public void setUpAndDownButtonColorsAndEnabled() {
        moveButtons.setUpAndDownButtonColorsAndEnabled();
    }

    private void initOwnListsExtraButtons() {
        ownListsExtraButtonsPanel.setLayout(new GridLayout(1, 0, 2, 2));

        ownListsExtraButtonsPanel.add(createDeleteButton());
        ownListsExtraButtonsPanel.add(createEditButton());
        ownListsExtraButtonsPanel.add(createExportButton());
        ownListsExtraButtonsPanel.add(new JLabel());
    }

    private Component createDeleteButton() {
        JButton button = VocabularyTrainerUiTools.createPicturedButton(
                IconDefinitions.DELETE_OWN_LIST,
                e -> gui.deleteOwnList(getVocabularyAsOwnList()),
                "Vokabelliste löschen");
        button.setForeground(Color.RED);
        return button;
    }

    private Component createEditButton() {
        JButton button = VocabularyTrainerUiTools.createPicturedButton(
                IconDefinitions.EDIT_OWN_LIST,
                e -> gui.editOwnList(getVocabularyAsOwnList()),
                "Vokabelliste bearbeiten");
        return button;
    }

    private Component createExportButton() {
        JButton button = VocabularyTrainerUiTools.createPicturedButton(
                IconDefinitions.EXPORT_OWN_LIST,
                e -> gui.exportListOwnList(getVocabularyAsOwnList()),
                "Vokabelliste exportieren");
        return button;
    }

    private void initSwitchingContentButtonPanel() {
        switchingContentButtonPanel.setLayout(cardLayout);

        switchingContentButtonPanel.add(testViewListSheetButtonsPanel,
                TEST_VIEW_LIST_SHEET_BUTTONS_NAME);
        switchingContentButtonPanel.add(moveButtons.getPanel(),
                MOVE_UP_AND_DOWN_BUTTONS_NAME);
        switchingContentButtonPanel.add(ownListsExtraButtonsPanel,
                OWN_LISTS_EXTRA_BUTTONS_NAME);
    }

    private void initOtherElements() {
        GuiTools.biggerFont(vocabularyLabel, 7);
        //vocabularyLabel.setHorizontalTextPosition(JLabel.CENTER); // bringt nichts.
        vocabularyLabel.setHorizontalAlignment(JLabel.CENTER);
    }

    private void fillPanel() {
        panel.setLayout(new BorderLayout());
        GuiTools.createTitle(panel);

        panel.add(vocabularyAndPercentPanel, BorderLayout.CENTER);
        panel.add(createButtonBar(), BorderLayout.EAST);
    }

    private void fillVocabularyAndPercentPanel() {
        vocabularyAndPercentPanel.setLayout(new BorderLayout());

        vocabularyAndPercentPanel.removeAll();

        vocabularyAndPercentPanel.add(vocabularyLabel, BorderLayout.CENTER);
        Options options = dataStructures.getOptions();
        if (options.isShowSuccessPercentInVocabularyBar()) {
            vocabularyAndPercentPanel.add(createSuccessPercentLabelPart(), BorderLayout.EAST);
        }
    }

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

        panel.add(successPercentLabel, BorderLayout.CENTER);

        return panel;
    }

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

        panel.add(switchingContentButtonPanel, BorderLayout.CENTER);
        panel.add(selectionCheckBox, BorderLayout.EAST);

        return panel;
    }

    /**
     * Setzt die Vordergrundfarbe auf den nach den Optionen und ggf. dem Durchschnitt der
     * erfolgreichen Abfrage der Vokabeln aus dem Vokabular richtig.
     */
    public void setCorrectForegroundColor() {
        VocabularyTestSuccesssCalculator calculator =
                new VocabularyTestSuccesssCalculator(vocabulary, dataStructures);
        calculator.calculate();

        Color foregroundColor = calculator.getForegroundColor();
        vocabularyLabel.setForeground(foregroundColor);
        vocabularyLabel.repaint();
        vocabularyLabel.invalidate();
        vocabularyLabel.validate();
        successPercentLabel.setText(calculator.getPercentText());
    }

    /** Aktualisiert den angezeigten Text je nach den Optionen. */
    public void setCorrectText() {
        perhapsShortenedVocabularyDescription = buildPerhapsShortenedVocabularyDescription();
        setLabelText();
        vocabularyLabel.repaint();
        vocabularyLabel.invalidate();
        vocabularyLabel.validate();
    }

    /** Aktualisiert den angezeigten Text zum Prozent des Erfolges je nach den Optionen. */
    public void showOrHidePercent() {
        fillVocabularyAndPercentPanel();
    }

    /** Getter für den Panel auf dem das Vokabular dargestellt wird. */
    public Component getPanel() {
        return panel;
    }

    /** Gibt an, ob die Vokabeln dieses Vokabulars ausgewählt wurden. */
    public boolean isSelected() {
        return selectionCheckBox.isSelected();
    }

    /** Legt fest, ob die Vokabeln dieses Vokabulars ausgewählt wurden. */
    public void setSelected(boolean selected) {
        selectionCheckBox.setSelected(selected);
    }

    /** Getter für das Vokabular zu welchem die Bar angezeigt wird. */
    public Vocabulary getVocabulary() {
        return vocabulary;
    }

    /** Getter für die Kategorie zu der das Vokabular gehört. */
    public String getVocabularyCategory() {
        return vocabulary.getCategory();
    }

    /** Getter für die Unterkategorie zu der das Vokabular gehört. */
    public String getVocabularySubCategory() {
        return vocabulary.getSubCategory();
    }

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

    /**
     * Gibt die Vokabelliste als eigene Liste zurück - wenn diese eine eigene Liste ist.
     *
     * Anderenfalls wird eine Ausnahme geworfen.
     */
    public OwnList getVocabularyAsOwnList() {
        if (vocabulary instanceof OwnList ownList) {
            return ownList;
        }
        else {
            throw new RuntimeException("Es wird versucht, ein Vokabular als eigene Liste "
                    + "zurückzugeben, welches keine eigene Liste ist.");
        }
    }

    /** Schaltet die Buttons zum Verschieben nach oben oder unten an oder aus. */
    public void showOrHideIndividualVocabularySortModeMoveButtons() {
        VocabularyBarShownButtonsType switchedType =
                VocabularyBarShownButtonsType.MOVE_UP_AND_DOWN_BUTTONS;
        changeSwitchingContentButtons(switchedType);
    }

    /**
     * Schaltet die Buttons zum Exportieren, Bearbeiten und Löschen von eigene Listen an oder aus.
     */
    public void toggleShowOwnListButtons() {
        VocabularyBarShownButtonsType switchedType =
                VocabularyBarShownButtonsType.OWN_LISTS_EXTRA_BUTTONS;
        changeSwitchingContentButtons(switchedType);
    }

    private void changeSwitchingContentButtons(VocabularyBarShownButtonsType switchedType) {
        if (shownButtonsType == switchedType) {
            showTestViewListSheetButtons();
        }
        else if (switchedType == VocabularyBarShownButtonsType.MOVE_UP_AND_DOWN_BUTTONS) {
            showMoveUpAndDownButtons();
        }
        else if (switchedType == VocabularyBarShownButtonsType.OWN_LISTS_EXTRA_BUTTONS) {
            showOwnListExtraButtons();
        }
        else {
            throw new RuntimeException("Logikfehler.");
        }
    }

    /** Zeigt die normalen Button zum Abfragen und betrachten der Vokabularien an. */
    public void showTestViewListSheetButtons() {
        shownButtonsType = VocabularyBarShownButtonsType.TEST_VIEW_LIST_SHEET_BUTTONS;
        String contentName = TEST_VIEW_LIST_SHEET_BUTTONS_NAME;
        switchCardLayout(contentName);
    }

    private void showMoveUpAndDownButtons() {
        shownButtonsType = VocabularyBarShownButtonsType.MOVE_UP_AND_DOWN_BUTTONS;
        String contentName = MOVE_UP_AND_DOWN_BUTTONS_NAME;
        switchCardLayout(contentName);
    }

    /** Zeigt bei eigenen Listen die Buttons zum Löschen, Bearbeiten und Exportieren an. */
    public void showOwnListExtraButtons() {
        shownButtonsType = VocabularyBarShownButtonsType.OWN_LISTS_EXTRA_BUTTONS;
        String contentName = OWN_LISTS_EXTRA_BUTTONS_NAME;
        switchCardLayout(contentName);
    }

    private void switchCardLayout(String contentName) {
        cardLayout.show(switchingContentButtonPanel, contentName);
        panel.repaint();
    }

    /**
     * Gibt an, ob diese VocabularyBar eine eigene Liste darstellt (wahr), oder (nur) ein normales
     * Vokabular (falsch).
     */
    public boolean isOwnListsBar() {
        return vocabulary instanceof OwnList;
    }

}
