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

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Point;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.ui.components.selections.StringSelection;
import de.duehl.swing.ui.components.selections.tools.SelectionsHelper;
import de.duehl.swing.ui.dialogs.base.ModalDialogBase;
import de.duehl.swing.ui.layout.VerticalLayout;
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.KanjiSet;
import de.duehl.vocabulary.japanese.data.symbol.Kanji;
import de.duehl.vocabulary.japanese.io.KanjiSetFileRenamer;
import de.duehl.vocabulary.japanese.logic.VocabularyTrainerLogic;
import de.duehl.vocabulary.japanese.ui.components.bars.KanjiBar;
import de.duehl.vocabulary.japanese.ui.data.FumikoUiObjects;
import de.duehl.vocabulary.japanese.ui.dialog.kanji.kanjiset.check.KanjiSetIntegrityChecker;
import de.duehl.vocabulary.japanese.ui.dialog.kanji.kanjiset.filter.KanjiFilterPanel;

/**
 * Diese Klasse lässt den Benutzer eine Kanji-Menge bearbeiten.
 *
 * @version 1.01     2026-03-18
 * @author Christian Dühl
 */

public class KanjiSetEditor extends ModalDialogBase {

    private static final Dimension DIALOG_DIMENSION = new Dimension(1300, 900);

    private static final int NAME_MIN_WIDTH = 450;


    /** Die benutzerdefinierte Kanji-Menge. */
    private final KanjiSet kanjiSet;

    /** Die Kanji der Menge. */
    private final List<Kanji> kanjiSetList;

    /** Die Liste mit den nicht erlaubten Namen für das Umbenennen dieser Kanji-Menge. */
    private final List<String> notAllowedNames;

    /** Die Logik des Vokabeltrainers. */
    private final VocabularyTrainerLogic logic;

    /** 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 Kanji-Mengen. */
    private final KanjiFilterPanel kanjiSetFilter;

    /** Die Darstellung des Namens der Kanji-Menge. */
    private final StringSelection nameSelection;

    /** Die Darstellung der Gruppe der Kanji-Menge. */
    private final StringSelection groupSelection;

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

    /** Der Panel mit den Kanji der Kanji-Menge, die wir hier bearbeiten. */
    private final JPanel kanjiOfSetPanel;

    /** Die ScrollPane um den Panel mit den Kanji der Kanji-Menge, die wir hier bearbeiten. */
    private final JScrollPane kanjiOfSetScroll;

    /** Das Verzeichnis der Kanji-Bars nach den Kanji. */
    private final Map<Kanji, KanjiBar> barByKanji;

    /** Der Button zum Anzeigen oder Verstecken der Buttons zum Bewegen der Bars der Kanji-Menge. */
    private final JButton toggleMoveButtonsButton;

    /** Gibt an, ob die Buttons zum Verschieben auf den Bars der Kanji-Menge angezeigt werden. */
    private boolean showMoveButtonsOnBars;

    /**
     * Konstruktor.
     *
     * @param kanjiSet
     *            Die benutzerdefinierte Kanji-Menge.
     * @param notAllowedNames
     *            Die Liste mit den nicht erlaubten Namen für das Umbenennen dieser Kanji-Menge.
     * @param logic
     *            Die Logik des Vokabeltrainers.
     * @param dataStructures
     *            Die Datenstrukturen des Vokabeltrainers.
     * @param uiObjects
     *            Die häufig verwendeten Funktionen der grafischen Oberfläche des Vokabeltrainers.
     * @param parentLocation
     *            Position des Rahmens der Oberfläche, vor der dieser Dialog erzeugt wird.
     */
    public KanjiSetEditor(KanjiSet kanjiSet, List<String> notAllowedNames,
            VocabularyTrainerLogic logic, FumikoDataStructures dataStructures,
            FumikoUiObjects uiObjects, Point parentLocation) {
        super(parentLocation, uiObjects.getProgramImage(), createTitle(kanjiSet.getName()),
                DIALOG_DIMENSION);
        addClosingWindowListener(() -> quit());

        this.kanjiSet = kanjiSet;
        this.notAllowedNames = notAllowedNames;
        this.logic = logic;
        this.dataStructures = dataStructures;
        this.uiObjects = uiObjects;

        barByKanji = new HashMap<>();

        kanjiSetList = new ArrayList<>();
        kanjiSetList.addAll(kanjiSet.getSet());

        nameSelection = new StringSelection("Name der Kanji-Menge");
        nameSelection.setText(kanjiSet.getName());

        groupSelection = new StringSelection("Gruppe der Kanji-Menge");
        groupSelection.setText(kanjiSet.getGroup());

        kanjiSetFilter = new KanjiFilterPanel(Kanji.getAllKanjiAsList(),
                filteredKanjiList -> reactOnFilteredKanjiList(filteredKanjiList));

        toggleMoveButtonsButton = new JButton();
        showMoveButtonsOnBars = false;

        int numberOfDatasetsPerSide = 20;
        int numberOfColumns = 3;
        pages = new DatasetsOnPages<>(kanjiSetFilter.createFilteredKanjiList(),
                kanji -> createDatasetUi(kanji), numberOfDatasetsPerSide, numberOfColumns);

        kanjiOfSetPanel = new JPanel();
        kanjiOfSetScroll = GuiTools.createScrollPane(kanjiOfSetPanel);

        init();
        filterKanjiList();
        showKanjiInSet();
        fillDialog();
    }

    private static String createTitle(String name) {
        return "Bearbeiten der Kanji-Menge '" + name + "'";
    }

    private void setTitle() {
        setTitle(createTitle(nameSelection.getTrimmedText()));
    }

    /** Erstellt aus dem übergebenen Datensatz die Anzeige für die Gui im linken Teil. */
    private Component createDatasetUi(Kanji kanji) {
        KanjiBar bar = new KanjiBar(kanji, () -> addKanji(kanji), this, dataStructures, uiObjects,
                () -> getLocation());
        bar.useButtonAsAddKanjiToList();
        if (kanjiSetList.contains(kanji)) {
            bar.disable();
        }
        bar.createGui();
        barByKanji.put(kanji, bar);
        return bar.getPanel();
    }

    private void addKanji(Kanji kanji) {
        if (!kanjiSetList.contains(kanji)) {
            addNotContainedKanji(kanji);
        }
    }

    private void addNotContainedKanji(Kanji kanji) {
        kanjiSetList.add(kanji);
        showKanjiInSet();

        KanjiBar bar = barByKanji.get(kanji);
        bar.disable();
        kanjiSetFilter.requestFocusInSearchFieldLater();
    }

    private void init() {
        initKanjiOfSetPanel();
        initNameSelection();
        initGroupSelection();
        initToggleButton();
        setRightShowAndHideButtonTexts();
        setTitle();
    }

    private void initKanjiOfSetPanel() {
        kanjiOfSetPanel.setLayout(new VerticalLayout(3, VerticalLayout.BOTH));
    }

    private void initNameSelection() {
        SelectionsHelper.initSelectionAsEditor(nameSelection);
    }

    private void initGroupSelection() {
        SelectionsHelper.initSelectionAsEditor(groupSelection);
    }


    private void initToggleButton() {
        toggleMoveButtonsButton.addActionListener(e -> toggleMoveButtons());
    }

    private void toggleMoveButtons() {
        showMoveButtonsOnBars = !showMoveButtonsOnBars;
        setRightShowAndHideButtonTexts();
        showKanjiInSet();
    }

    private void setRightShowAndHideButtonTexts() {
        String show = "einblenden";
        String hide = "ausblenden";
        String moveShowOrHide = showMoveButtonsOnBars ? hide : show;

        /*
        toggleMoveButtonsButton.setText("<html><center>"
                + "Die Buttons zum Verschieben"
                + "<br>"
                + moveShowOrHide
                + "</center></html>");
                */
        toggleMoveButtonsButton.setText("Die Buttons zum Verschieben " + moveShowOrHide);
    }

    private void showKanjiInSet() {
        kanjiOfSetPanel.removeAll();

        for (Kanji kanji : kanjiSetList) {
            KanjiBar bar = new KanjiBar(kanji, () -> deleteFromSet(kanji), this, dataStructures,
                    uiObjects, () -> getLocation());

            bar.useButtonAsDeletion();
            bar.showMoveButtonsOnBars(showMoveButtonsOnBars);
            bar.createGui();
            kanjiOfSetPanel.add(bar.getPanel());
        }

        kanjiOfSetPanel.repaint();
        kanjiOfSetPanel.validate();
        kanjiOfSetPanel.invalidate();

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

    private void deleteFromSet(Kanji kanji) {
        kanjiSetList.remove(kanji);
        showKanjiInSet();

        /*
         * Da das Kanji nicht zwingend auf den momentan bereits vom Benutzer angeschauten Seiten
         * vorhanden sein muss und damit nicht zwingend eine Bar dazu existieren muss, muss
         * abgefragt werden, ob wir die Bar dazu kennen:
         */
        if (barByKanji.containsKey(kanji)) {
            KanjiBar bar = barByKanji.get(kanji);
            bar.enable();
        }
        kanjiSetFilter.requestFocusInSearchFieldLater();
    }

    private void filterKanjiList() {
        List<Kanji> filteredKanjiList = kanjiSetFilter.createFilteredKanjiList();
        reactOnFilteredKanjiList(filteredKanjiList);
    }

    private void reactOnFilteredKanjiList(List<Kanji> filteredKanjiList) {
        if (filteredKanjiList.isEmpty()) {
            GuiTools.informUser(getWindowAsComponent(), "Keine Vokabeln gefunden",
                    "Die Suchkriterien führen zu einer leeren Liste von Vokabeln, daher wird "
                    + "diese nicht angezeigt.");
        }
        else {
            pages.setOtherDatasets(filteredKanjiList);
        }
    }

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

        pages.addOwnExtension(createPagesExtensionPart());
    }

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

        panel.add(createKanjiSelectionPart(), BorderLayout.CENTER);
        panel.add(createKanjiSetPart(), BorderLayout.EAST);

        return panel;
    }

    private Component createKanjiSelectionPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        GuiTools.createTitle("Auswahl der Kanji", panel);

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

        return panel;
    }

    private Component createKanjiSetPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        GuiTools.createTitle("Die Kanji-Menge", panel);

        panel.add(createKanjiSetUpperPart(), BorderLayout.NORTH);
        panel.add(kanjiOfSetScroll, BorderLayout.CENTER);

        return panel;
    }

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

        panel.add(createNameAndGroupPart(), BorderLayout.CENTER);
        panel.add(toggleMoveButtonsButton, BorderLayout.SOUTH);

        return panel;
    }

    private Component createNameAndGroupPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new VerticalLayout(0, VerticalLayout.BOTH));

        panel.add(createDummyWidthLabel());
        panel.add(nameSelection.getPanel());
        panel.add(groupSelection.getPanel()); // Möchte man die bekannten auswählen können, siehe Listen-Editor.

        return panel;
    }

    private Component createDummyWidthLabel() {
        JLabel label = new JLabel("");
        label.setPreferredSize(new Dimension(NAME_MIN_WIDTH, 0));
        return label;
    }

//    private Component createToggelButtonPart() {
//        JPanel panel = new JPanel();
//        panel.setLayout(new BorderLayout());
//
//        panel.add(toggleMoveButtonsButton, BorderLayout.EAST);
//
//        return panel;
//    }

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

        panel.add(createQuitButton(), BorderLayout.WEST);
        panel.add(createOkButton(), BorderLayout.EAST);

        return panel;
    }

    private Component createQuitButton() {
        JButton button = new JButton("Abbrechen");
        button.addActionListener(e -> quit());
        return button;
    }

    private void quit() {
        closeDialog();
    }

    private Component createOkButton() {
        JButton button = new JButton(" Ok ");
        GuiTools.boldFont(button);
        GuiTools.biggerFont(button, 5);
        button.addActionListener(e -> apply());
        return button;
    }

    private void apply() {
        String oldName = kanjiSet.getName();
        String newName = nameSelection.getTrimmedText();
        String group = groupSelection.getTrimmedText();
        if (!newName.equals(oldName)) {
            if (newName.isBlank()
                    || KanjiSet.determineBareFilename(newName).isBlank()
                    || notAllowedNames.contains(newName)) {
                String title = "Die Kanji-Menge wurde nicht umbenannt";
                String message = "Der eingegebene Name '" + newName + "' ist bereits vorhanden oder "
                        + "resultiert in einem leeren Dateinamen.\n"
                        + "Die Kanji-Menge wurde daher nicht umbenannt. "
                        + "Die restlichen Änderungen werden aber übernommen.";
                GuiTools.informUser(getWindowAsComponent(), title, message);
                newName = oldName;
            }
            else if (!newName.equals(oldName)) {
                KanjiSetFileRenamer.renameListFile(oldName, newName);
                kanjiSet.setName(newName);
            }
        }

        kanjiSet.setGroup(group);

        List<Kanji> kanjiListOfSet = kanjiSet.getSet();
        kanjiListOfSet.clear();
        kanjiListOfSet.addAll(kanjiSetList);

        saveKanjiLists();
        perhapsCheckKanjiSetIntegrity();
        quit();
    }

    /** Speichert die Kanji-Mengen ab. */
    private void saveKanjiLists() {
        logic.saveKanjiSets();
    }

    private void perhapsCheckKanjiSetIntegrity() {
        Options options = dataStructures.getOptions();
        if (options.isCheckKanjiSetIntegrityAfterEditingKanjiSets()) {
            checkKanjiSetIntegrity();
        }
    }

    private void checkKanjiSetIntegrity() {
        List<KanjiSet> kanjiSets = dataStructures.getKanjiSets();
        KanjiSetIntegrityChecker.checkKanjiSetsIntegrity(kanjiSets, getWindowAsComponent(),
                getLocation());
    }

    private Component createPagesExtensionPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout(FlowLayout.CENTER, 3, 3));

        panel.add(createSelectAllButton());
        panel.add(createDeselectAllButton());
        panel.add(createAddSelectedButton());
        panel.add(createAddAllButton());

        return panel;
    }

    private Component createSelectAllButton() {
        JButton button = new JButton("select all");
        button.addActionListener(e -> selectAll());
        return button;
    }

    private void selectAll() {
        boolean selected = true;
        setBarsOnActualPageSelected(selected);
    }

    private Component createDeselectAllButton() {
        JButton button = new JButton("deselect all");
        button.addActionListener(e -> deselectAll());
        return button;
    }

    private void deselectAll() {
        boolean selected = false;
        setBarsOnActualPageSelected(selected);
    }

    private void setBarsOnActualPageSelected(boolean selected) {
        for (Kanji kanji : pages.getDatasetsFromActualPage()) {
            KanjiBar bar = barByKanji.get(kanji);
            bar.setSelected(selected);
        }
    }

    private Component createAddSelectedButton() {
        JButton button = new JButton("add selected");
        button.addActionListener(e -> addSelected());
        return button;
    }

    private void addSelected() {
        List<KanjiBar> addedBars = new ArrayList<>();

        for (Kanji kanji : pages.getDatasetsFromActualPage()) {
            KanjiBar bar = barByKanji.get(kanji);
            if (bar.isSelected()) {
                addKanji(kanji);
                addedBars.add(bar);
            }
        }

        disableAddedBars(addedBars);
    }

    private Component createAddAllButton() {
        JButton button = new JButton("add all from active Page");
        button.addActionListener(e -> addAll());
        return button;
    }

    private void addAll() {
        List<KanjiBar> addedBars = new ArrayList<>();

        for (Kanji kanji : pages.getDatasetsFromActualPage()) {
            addKanji(kanji);
            KanjiBar bar = barByKanji.get(kanji);
            addedBars.add(bar);
        }

        disableAddedBars(addedBars);
    }

    private void disableAddedBars(List<KanjiBar> addedBars) {
        for (KanjiBar bar : addedBars) {
            bar.setSelected(false);
            bar.disable();
        }
    }

    /** Verschiebt die übergebene Bar eines Kanji an die erste Stelle. */
    public void moveBarToFirst(Kanji kanji) {
        int index = kanjiSetList.indexOf(kanji);
        kanjiSetList.remove(index);
        kanjiSetList.add(0, kanji);
        showKanjiInSet();
    }

    /** Verschiebt die übergebene Bar eines Kanji nach oben. */
    public void moveBarUp(Kanji kanji) {
        int index = kanjiSetList.indexOf(kanji);
        kanjiSetList.remove(index);
        kanjiSetList.add(index - 1, kanji);
        showKanjiInSet();
    }

    /** Verschiebt die übergebene Bar eines Kanji nach unten. */
    public void moveBarDown(Kanji kanji) {
        int index = kanjiSetList.indexOf(kanji);
        kanjiSetList.remove(index);
        kanjiSetList.add(index + 1, kanji);
        showKanjiInSet();
    }

    /** Verschiebt die übergebene Bar eines Kanji an die letzte Stelle. */
    public void moveBarToLast(Kanji kanji) {
        int index = kanjiSetList.indexOf(kanji);
        kanjiSetList.remove(index);
        kanjiSetList.add(kanji);
        showKanjiInSet();
    }

    /** Gibt an, ob die übergebene Bar eines Kanji nach oben bewegt werden kann. */
    public boolean canBarMoveUp(Kanji kanji) {
        int index = kanjiSetList.indexOf(kanji);
        return index > 0;
    }

    /** Gibt an, ob die übergebene Bar eines Kanji nach unten bewegt werden kann. */
    public boolean canBarMoveDown(Kanji kanji) {
        int index = kanjiSetList.indexOf(kanji);
        return index < kanjiSetList.size() - 1;
    }

}
