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

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.TableRowSorter;

import de.duehl.basics.datetime.date.ImmutualDate;
import de.duehl.basics.text.NumberString;
import de.duehl.basics.text.Text;
import de.duehl.swing.logic.LongTimeProcessInformer;
import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.ui.dialogs.base.NonModalFrameDialogBase;
import de.duehl.swing.ui.tables.ButtonColumn;
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.data.symbol.Kanji;
import de.duehl.vocabulary.japanese.tools.VocableListShuffler;
import de.duehl.vocabulary.japanese.tools.VocabularyTools;
import de.duehl.vocabulary.japanese.ui.components.button.OtherViewButtonPanel;
import de.duehl.vocabulary.japanese.ui.components.button.ViewButtonPressedReactor;
import de.duehl.vocabulary.japanese.ui.data.FumikoUiObjects;
import de.duehl.vocabulary.japanese.ui.dialog.kanji.kanjitest.KanjiTester;
import de.duehl.vocabulary.japanese.ui.dialog.table.vocable.VocableTableColumnModel;
import de.duehl.vocabulary.japanese.ui.dialog.table.vocable.VocableTableModel;
import de.duehl.vocabulary.japanese.ui.dialog.table.vocable.VocableTableRenderer;
import de.duehl.vocabulary.japanese.ui.dialog.vocables.detail.VocableWithInternaViewer;
import de.duehl.vocabulary.japanese.ui.tools.VocabularyTrainerUiTools;

/**
 * Diese Klasse zeigt eine Menge von Vokabeln in Form einer Liste an.
 *
 * @version 1.01     2025-11-22
 * @author Christian Dühl
 */

public class VocabularyListerDialog extends NonModalFrameDialogBase
        implements ViewButtonPressedReactor {

    private static final Dimension DIALOG_DIMENSION = new Dimension(1500, 1000);

    private static final Color TABLE_FOREGROUND = new Color(0, 0, 255);
    private static final Color TABLE_BACKGROUND = new Color(240, 240, 255);

    private static final float DEFAULT_FONT_SIZE = 15f;


    /** 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;

    // /** Die Oberfläche, welche diese Klasse aufruft, auf der man eine GlassPane anzeigen kann. */
    // private final LongTimeProcessInformer longTimeProcessInformer;
    // TODO: Prüfen, wie und wo ich das zu verwenden habe! Reicht die eine Stelle?

    /** Die Tabelle. */
    private final JTable table;

    /** Das Modell der anzuzeigenden Tabelle. */
    private final VocableTableModel tableModel;

    /** Der Renderer der Tabelle. */
    private final VocableTableRenderer tableRenderer;

    /** Die ScrollPane um die Vokabeln. */
    private JScrollPane scrollPane;

    /** Button um die Schriftgröße der Tabelle zu erhöhen. */
    private final JButton increaseFontSizeButton;

    /** Button um die Schriftgröße der Tabelle zu verringern. */
    private final JButton standardFontSizeButton;

    /** Button um die Schriftgröße der Tabelle zu verringern. */
    private final JButton decreaseFontSizeButton;

    /**
     * Zeigt drei Buttons an, mit denen man eine Menge von Vokabeln in Form einer
     * Einzeldarstellung, in Form einer Liste und in Form eines Vokabelblattes anzeigen lassen
     * kann.
     */
    private final OtherViewButtonPanel otherViewPanel;

    /**
     * Konstruktor.
     *
     * @param vocables
     *            Die Liste mit den abzufragenden Vokabeln.
     * @param description
     *            Die Beschreibung der Menge von Vokabeln.
     * @param dataStructures
     *            Die Datenstrukturen des Vokabeltrainers.
     * @param uiObjects
     *            Die häufig verwendeten Funktionen der grafischen Oberfläche des Vokabeltrainers.
     * @param location
     *            Die Position des Rahmens der Oberfläche, die den Panel anzeigt, welcher diese
     *            Klasse verwendet.
     * @param longTimeProcessInformer
     *            Die Oberfläche, welche diese Klasse aufruft, auf der man eine GlassPane anzeigen
     *            kann.
     */
    public VocabularyListerDialog(List<Vocable> vocables, String description,
            FumikoDataStructures dataStructures, FumikoUiObjects uiObjects, Point location,
            LongTimeProcessInformer longTimeProcessInformer) {
        super(location, uiObjects.getProgramImage(),
                createDialogTitle(vocables, description, dataStructures), DIALOG_DIMENSION);
        addEscapeBehaviour();

        this.dataStructures = dataStructures;
        this.uiObjects = uiObjects;
        //this.longTimeProcessInformer = longTimeProcessInformer;

        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;
        }

        table = new JTable();
        tableModel = new VocableTableModel(this.vocables, dataStructures);
        tableRenderer = new VocableTableRenderer(TABLE_FOREGROUND, TABLE_BACKGROUND);
        increaseFontSizeButton = new JButton();
        standardFontSizeButton = new JButton();
        decreaseFontSizeButton = new JButton();
        otherViewPanel = new OtherViewButtonPanel(vocables, (ViewButtonPressedReactor) this,
                description, dataStructures, uiObjects, longTimeProcessInformer);
        otherViewPanel.disableListenDarstellungButton();

        init();
        fillDialog();
    }

    private static String createDialogTitle(List<Vocable> vocables, String description,
            FumikoDataStructures dataStructures) {
        String frontTitle = "Liste mit den " + NumberString.taupu(vocables.size())
                + " Vokabeln aus " + description;
        return VocabularyTrainerUiTools.generateTitleWithVocabularyTestSuccesss(vocables,
                frontTitle, dataStructures);
    }

    private void init() {
        initTable();
        initScrollPane();
    }

    private void initTable() {
        initTableModel();
        initTableColumnModel();
        initTableRenderer();
        setTableSelectionMode();
        setTableRowHight();
        switchReorderingOfTableColumnsOff();
        initTableButtonEditorAndRenderer();
        initTableSorter();
    }

    private void initTableModel() {
        table.setModel(tableModel);
    }

    private void initTableColumnModel() {
        Options options = dataStructures.getOptions();
        table.setColumnModel(new VocableTableColumnModel(options));
    }

    private void initTableRenderer() {
        tableRenderer.setFontSize(DEFAULT_FONT_SIZE);
        table.setDefaultRenderer(Object.class, tableRenderer);
    }

    private void setTableSelectionMode() {
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        //table.getSelectionModel().addListSelectionListener(createSelectionListener());
    }

    private void setTableRowHight() {
        tableRenderer.initRowHeightOfTable(table);
    }

    private void switchReorderingOfTableColumnsOff() {
        table.getTableHeader().setReorderingAllowed(false); // verschieben der Spalten verhindern
    }

    private void initTableButtonEditorAndRenderer() {
        new ButtonColumn(table, row -> reactOnPlayMp3ButtonClick(row), 6);
        new ButtonColumn(table, row -> reactOnShowInterna(row), 17);
    }

    private void reactOnPlayMp3ButtonClick(int row) {
        Vocable vocable = vocables.get(row);
        String mp3 = vocable.getMp3();
        VocabularyTools.playMp3(mp3);
    }

    private void reactOnShowInterna(int row) {
        Vocable vocable = vocables.get(row);

        VocableWithInternaViewer viewer = new VocableWithInternaViewer(vocable, dataStructures,
                uiObjects, getLocation());
        viewer.setVisible(true);
    }

    private void initTableSorter() {
        TableRowSorter<VocableTableModel> sorter = new TableRowSorter<>(tableModel);

        /*
         * Comparator<Object> dateSorter = createDateSorter();
         * sorter.setComparator(13, dateSorter); // first seen date
         * sorter.setComparator(14, dateSorter); // last test date
         * sorter.setComparator(15, dateSorter); // last correct test date
         *
         * Das brauche ich nicht mehr wegen getColumnClass() im Modell. Da hatten sich nur die
         * Spaltenindices vertauscht, als ich die Wortarten und Suchbegriffe zugefügt hatte.
         */

        /*
         * Die letzten zehn Ergebnisse werden leider alphanumerisch sortiert, das ist nicht so
         * schön. Daher mache ich es so:
         */
        Comparator<String> lastTenTestResultsSorter = createLastTenTestResultsSorter();
        sorter.setComparator(16, lastTenTestResultsSorter);

        table.setRowSorter(sorter);


        /*
         * Das scheint es gar nicht zu brauchen!
         * Vermutlich auch wegen getColumnClass() im Modell.
         *
         * List<RowSorter.SortKey> sortKeys = new ArrayList<>(25);
         * sortKeys.add(new RowSorter.SortKey(0, SortOrder.UNSORTED));
         * sortKeys.add(new RowSorter.SortKey(1, SortOrder.ASCENDING));
         * sortKeys.add(new RowSorter.SortKey(2, SortOrder.ASCENDING));
         * sortKeys.add(new RowSorter.SortKey(3, SortOrder.ASCENDING));
         * sortKeys.add(new RowSorter.SortKey(4, SortOrder.ASCENDING));
         * sortKeys.add(new RowSorter.SortKey(13, SortOrder.UNSORTED));
         * sorter.setSortKeys(sortKeys);
         *
         * Siehe https://docs.oracle.com/javase%2Ftutorial%2Fuiswing%2F%2F/components/table.html
        */
    }

    /**
     * Erzeugt einen Comparator für Tabellenspalten, die die letzten zehn Abfrageergebnisse als
     * Strings in Form von Plus- und Minuszeichen darstellen.
     */
    public static Comparator<String> createLastTenTestResultsSorter() {
        return new Comparator<String>() {
            @Override
            public int compare(String results1, String results2) {
                int numberOfPlus1 = Text.countPartInString(results1, "+");
                int numberOfPlus2 = Text.countPartInString(results2, "+");
                if (numberOfPlus1 != numberOfPlus2) {
                    return numberOfPlus2 - numberOfPlus1; // je mehr desto besser
                }

                int numberOfMinus1 = Text.countPartInString(results1, "-");
                int numberOfMinus2 = Text.countPartInString(results2, "-");
                if (numberOfMinus1 != numberOfMinus2) {
                    return numberOfMinus1 - numberOfMinus2; // je weniger desto besser
                }

                int lastMinusIndex1 = results1.lastIndexOf("-");
                int lastMinusIndex2 = results2.lastIndexOf("-");
                return lastMinusIndex1 - lastMinusIndex2; // je kleiner desto besser
            }
        };
    }

    @SuppressWarnings("unused")
    private Comparator<Object> createDateSorter() {
        return new Comparator<Object>() {
            @Override
            public int compare(Object o1, Object o2) {
                String s1 = o1.toString();
                String s2 = o2.toString();
                ImmutualDate date1 = new ImmutualDate(s1);
                ImmutualDate date2 = new ImmutualDate(s2);
                return date1.compareTo(date2);
            }
        };
    }

    private void initScrollPane() {
        scrollPane = GuiTools.createScrollPane(table);
    }

    /** Baut die Gui auf. */
    @Override
    protected void populateDialog() {
        initElements();

        add(createCenterPart(), BorderLayout.CENTER);
        add(createButtonsPart(),  BorderLayout.SOUTH);

        GuiTools.scrollScrollbarToMinimumLater(scrollPane);
    }

    private void initElements() {
        initIncreaseFontSizeButton();
        initStandardFontSizeButton();
        initDecreaseFontSizeButton();
    }

    private void initIncreaseFontSizeButton() {
        increaseFontSizeButton.setText("+");
        increaseFontSizeButton.addActionListener(e -> increaseFontSize());
    }

    private void increaseFontSize() {
        tableRenderer.increaseFontSize();
        tableRenderer.initRowHeightOfTable(table);
        table.repaint();
    }

    private void initStandardFontSizeButton() {
        String text = "default size (" + NumberString.twoDecimalPlaces(DEFAULT_FONT_SIZE) + ")";
        standardFontSizeButton.setText(text);
        standardFontSizeButton.addActionListener(e -> standardFontSize());
    }

    private void standardFontSize() {
        tableRenderer.setFontSize(DEFAULT_FONT_SIZE);
        tableRenderer.initRowHeightOfTable(table);
        table.repaint();
    }

    private void initDecreaseFontSizeButton() {
        decreaseFontSizeButton.setText("-");
        decreaseFontSizeButton.addActionListener(e -> decreaseFontSize());
    }

    private void decreaseFontSize() {
        tableRenderer.decreaseFontSize();
        tableRenderer.initRowHeightOfTable(table);
        table.repaint();
    }

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

        panel.add(scrollPane, BorderLayout.CENTER);

        return panel;
    }

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

        panel.add(createFontSizeColorButtonsAndKanjiTestPart(), BorderLayout.WEST);
        panel.add(GuiTools.centerHorizontal(otherViewPanel.getPanel()), BorderLayout.CENTER);
        panel.add(createQuitButton(), BorderLayout.EAST);

        return panel;
    }

    private Component createFontSizeColorButtonsAndKanjiTestPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout(FlowLayout.LEFT, 3, 0));

        panel.add(createFontSizeButtonsPart());
        panel.add(createColorButtonsPart());
        panel.add(GuiTools.addLeftSpace(createKanjiTestButton(), 50));

        return panel;
    }

    private Component createFontSizeButtonsPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout(FlowLayout.LEFT, 3, 0));

        panel.add(decreaseFontSizeButton);
        panel.add(standardFontSizeButton);
        panel.add(increaseFontSizeButton);

        return panel;
    }

    private Component createColorButtonsPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout(FlowLayout.LEFT, 3, 0));

        panel.add(createSwitchForegroundColorButton());
        panel.add(createSwitchBackgroundColorButton());

        return panel;
    }

    private Component createSwitchForegroundColorButton() {
        JButton button = new JButton("toggle Vordergrundfarbe");
        button.addActionListener(e -> toggleColorForegroundDependingOnLastSuccess());
        return button;
    }

    private void toggleColorForegroundDependingOnLastSuccess() {
        tableRenderer.toggleColorForegroundDependingOnLastSuccess();
        table.repaint();
    }

    private Component createSwitchBackgroundColorButton() {
        JButton button = new JButton("toggle Hintergrundfarbe");
        button.addActionListener(e -> toggleColorBackgroundDependingOnLastSuccess());
        return button;
    }

    private void toggleColorBackgroundDependingOnLastSuccess() {
        tableRenderer.toggleColorBackgroundDependingOnLastSuccess();
        table.repaint();
    }

    private Component createKanjiTestButton() {
        JButton button = new JButton("Bekannte Kanji abfragen");
        button.addActionListener(e -> testKnownKanji());
        return button;
    }

    private void testKnownKanji() {
        List<Kanji> knownKanjiInVocables = extractKnownKanjiFromVocables();
        if (knownKanjiInVocables.isEmpty()) {
            String title = "Keine bekannten Kanji gefunden";
            String message = "In den Vokabeln dieser Liste wurden keine bekannten Kanji gefunden.";
            GuiTools.informUser(getWindowAsComponent(), title, message);
        }
        else {
            startKanjiTest(knownKanjiInVocables);
        }
    }

    private List<Kanji> extractKnownKanjiFromVocables() {
        List<Kanji> knownKanjiInVocables = new ArrayList<>();

        for (Vocable vocable : vocables) {
            List<Kanji> knownKanjiInVocable = extractKnownKanjiFromVocable(vocable);
            for (Kanji kanji : knownKanjiInVocable) {
                if (!knownKanjiInVocables.contains(kanji)) {
                    knownKanjiInVocables.add(kanji);
                }
            }
        }

        return knownKanjiInVocables;
    }

    private List<Kanji> extractKnownKanjiFromVocable(Vocable vocable) {
        List<Kanji> knownKanjiInVocable = new ArrayList<>();
        String kanjiString = vocable.getKanji();

        for (Kanji kanji : Kanji.getAllKanjiAsList()) {
            String kanjiAsString = kanji.getCharacter();
            if (kanjiString.contains(kanjiAsString) && !knownKanjiInVocable.contains(kanji)) {
                knownKanjiInVocable.add(kanji);
            }
        }

        return knownKanjiInVocable;
    }

    private void startKanjiTest(List<Kanji> knownKanjiInVocables) {
        boolean germanMeaningCaseSensitivity = false;
        boolean onLesungCaseSensitivity = false;
        boolean kunLesungCaseSensitivity = false;
        KanjiTester tester = new KanjiTester(knownKanjiInVocables,
                "Kanji die in einer Vokabelliste vorkommen", germanMeaningCaseSensitivity,
                onLesungCaseSensitivity, kunLesungCaseSensitivity, dataStructures, uiObjects,
                getLocation());
        tester.setVisible(true);
    }

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

    private void quit() {
        closeDialog();
    }

    /** Wird aufgerufen, wenn die Vokabeln in einer anderen Ansicht angezeigt werden. */
    @Override
    public void otherViewOpened() {
        closeDialog();
    }

}
