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

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

import de.duehl.basics.text.Text;
import de.duehl.swing.ui.components.selections.SearchWordStringSelection;
import de.duehl.swing.ui.components.selections.data.SearchWordSelectionInputs;
import de.duehl.swing.ui.components.selections.tools.SelectionsHelper;
import de.duehl.vocabulary.japanese.data.symbol.Kanji;
import de.duehl.vocabulary.japanese.logic.sort.KanjiListSorter;
import de.duehl.vocabulary.japanese.tools.VocabularyTools;

/**
 * Diese Klasse stellt ein Element in Dialogen dar, mit dem eine Menge von Kanji gefiltert werden
 * kann.
 *
 * @version 1.01     2026-01-01
 * @author Christian Dühl
 */

public class KanjiFilterPanel {

    /** Die Liste mit allen Kanji. */
    private final List<Kanji> kanjiList;

    /** Das Objekt das mit den gefilterten Kanji arbeitet (Anzeige, ...). */
    private final FilteredKanjiReactor filteredKanjiReactor;

    /** Der Panel auf dem die Suchkriterien dargestellt werden. */
    private final JPanel panel;

    /** Das Feld zum Eingeben von Suchbegriffen in der deutsche Bedeutung der Kanji. */
    private final SearchWordStringSelection germanMeaningFilterStringSelection;

    /** Das Feld zum Eingeben von Suchbegriffen in der On-Lesung der Kanji. */
    private final SearchWordStringSelection onLesungFilterStringSelection;

    /** Das Feld zum Eingeben von Suchbegriffen in der kun-Lesung der Kanji. */
    private final SearchWordStringSelection kunLesungFilterStringSelection;

    /**
     * Das Feld zum Eingeben von Suchbegriffen in irgendeiner der drei (deutsche Bedeutung,
     * On-Lesung, kun-Lesung) sowie dem Kanjizeichen selbst (z.B. 車).
     */
    private final SearchWordStringSelection searchFilterStringSelection;

    /** Der Button um nach Kanji mit den Suchbegriffen zu suchen. */
    private final JButton searchButton;

    /** Der Button um die Feldinhalte zurückzusetzen. */
    private final JButton resetButton;

    /**
     * Gibt an, ob wirklich gefiltert wird. Wird ausgestellt beim Laden und beim Leeren der Felder.
     */
    private boolean filteringActive;

    /**
     * Konstruktor.
     *
     * @param kanjiList
     *            Die Liste mit allen Kanji.
     * @param filteredKanjiReactor
     *            Das Objekt das mit den gefilterten Kanji arbeitet (Anzeige, ...).
     */
    public KanjiFilterPanel(List<Kanji> kanjiList, FilteredKanjiReactor filteredKanjiReactor) {
        this.kanjiList = kanjiList;
        this.filteredKanjiReactor = filteredKanjiReactor;

        panel = new JPanel();

        germanMeaningFilterStringSelection = new SearchWordStringSelection(
                "Suche in den deutschen Bedeutungen der Kanji");
        onLesungFilterStringSelection = new SearchWordStringSelection(
                "Suche in den On-Lesungen der Kanji");
        kunLesungFilterStringSelection = new SearchWordStringSelection(
                "Suche in den kun-Lesungen der Kanji");
        searchFilterStringSelection = new SearchWordStringSelection(
                "Suche in beliebigen Daten der Kanji oder nach dem Kanjizeichen");
        searchButton = new JButton("filtern");
        resetButton = new JButton("Felder leeren");

        filteringActive = true;

        init();
    }

    private void init() {
        initSearchFields();
        initButtons();
        initPanel();
    }

    private void initSearchFields() {
        initSearchWordStringSelection(germanMeaningFilterStringSelection);
        initSearchWordStringSelection(onLesungFilterStringSelection);
        initSearchWordStringSelection(kunLesungFilterStringSelection);
        initSearchWordStringSelection(searchFilterStringSelection);

        requestFocusInSearchFieldLater();
    }

    private void initSearchWordStringSelection(SearchWordStringSelection stringSelection) {
        stringSelection.addReturnListener(() -> searchButton.doClick());
        stringSelection.biggerText(5);
        SelectionsHelper.initSelectionAsEditor(stringSelection);
        stringSelection.setCheckboxesFocusable(false);
    }

    private void initButtons() {
        searchButton.addActionListener(e -> filterKanjiList());
        resetButton.addActionListener(e -> reset());
    }

    private void filterKanjiList() {
        if (filteringActive) {
            List<Kanji> filteredKanjiList = createFilteredKanjiList();
            sort(filteredKanjiList);
            filteredKanjiReactor.reactOnFilteredKanjiList(filteredKanjiList);
        }
    }

    /** Erzeugt eine Liste mit den Vokabeln, die zu den Suchkriterien passen. */
    public List<Kanji> createFilteredKanjiList() {
        List<Kanji> filteredKanjiList = new ArrayList<>();

        SearchWordSelectionInputs germanMeaningInputs = germanMeaningFilterStringSelection
                .getInputs();
        SearchWordSelectionInputs onLesungInputs = onLesungFilterStringSelection.getInputs();
        SearchWordSelectionInputs kunLesungInputs = kunLesungFilterStringSelection.getInputs();
        SearchWordSelectionInputs searchInputs = searchFilterStringSelection.getInputs();

        for (Kanji kanji : kanjiList) {
            List<String> germanMeanings = kanji.getGermanMeanings();
            List<String> onLesungen = kanji.getOnLesungen();
            List<String> kunLesungen = kanji.getKunLesungen();

            List<String> lowerdedGermanMeanings = Text.toLowerCase(germanMeanings);
            List<String> lowerdedOnLesungen = Text.toLowerCase(onLesungen);
            List<String> lowerdedKunLesungen = Text.toLowerCase(kunLesungen);
            String kanjiCharacter = kanji.getCharacter();

            boolean germanMeaningOk = germanMeaningInputs.isTextEmpty()
                    || listWordContains(germanMeaningInputs, germanMeanings, lowerdedGermanMeanings);
            boolean onLesungOk = onLesungInputs.isTextEmpty()
                    || listWordContains(onLesungInputs, onLesungen, lowerdedOnLesungen);
            boolean kunLesungOk = kunLesungInputs.isTextEmpty()
                    || listWordContains(kunLesungInputs, kunLesungen, lowerdedKunLesungen);

            boolean searchOk = searchInputs.isTextEmpty()
                    || listWordContains(searchInputs, germanMeanings, lowerdedGermanMeanings)
                    || listWordContains(searchInputs, onLesungen, lowerdedOnLesungen)
                    || listWordContains(searchInputs, kunLesungen, lowerdedKunLesungen)
                    || kanjiCharacter.equals(searchInputs.getText());

            if (germanMeaningOk && onLesungOk && kunLesungOk && searchOk) {
                filteredKanjiList.add(kanji);
            }
        }

        return filteredKanjiList;
    }

    private boolean listWordContains(SearchWordSelectionInputs inputs,
            List<String> originalValues, List<String> compareValues) {
        if (originalValues.size() != compareValues.size()) {
            throw new RuntimeException("Die Listen sind unterschiedlich groß!\n"
                    + "\t" + "originalValues.size() = " + originalValues.size() + "\n"
                    + "\t" + "compareValues.size()  = " + compareValues.size() + "\n");
        }

        for (int index = 0; index < originalValues.size(); ++index) {
            String originalValue = originalValues.get(index);
            String compareValue = compareValues.get(index);

            if (singleWordContains(inputs, originalValue, compareValue)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Sucht nach den Suchkriterien in dem Inhalt eines Feldes.
     *
     * @param inputs
     *            Die Suchkriterien.
     * @param originalValue
     *            Der originale Inhalt des Feldes.
     * @param compareValue
     *            Der Vergleichs-Inhalt des Feldes.
     * @return Wahrheitswert.
     */
    private boolean singleWordContains(SearchWordSelectionInputs inputs, String originalValue,
            String compareValue) {
        String search;
        String value;
        if (inputs.isWholeWordSearch()) {
            if (inputs.isCaseSensitiveSearch()) {
                search = inputs.getText();
                value = originalValue;
            }
            else {
                search = Text.toLowerCase(inputs.getText());
                value = Text.toLowerCase(originalValue);
                /*
                 * Falls das irgendwann zu langsam wird, kann man die nur in Kleinschreibweise
                 * geänderten Ausprägungen ebenfalls in der Vokabel mit abspeichern und hier
                 * entsprechend übergeben. Aber im Moment ist es auch so schnell genug.
                 */
            }
        }
        else if (inputs.isCaseSensitiveSearch()) {
            search = VocabularyTools.removePuncuationMarksAndSpaces(inputs.getText());
            value = VocabularyTools.removePuncuationMarksAndSpaces(originalValue);
            /*
             * Falls das irgendwann zu langsam wird, kann man die Ausprägungen ohne Satzzeichen
             * ebenfalls in der Vokabel mit abspeichern und hier entsprechend übergeben. Aber im
             * Moment ist es auch so schnell genug.
             */
        }
        else {
            search = VocabularyTools.createCompareTranslation(inputs.getText());
            value = VocabularyTools.createCompareTranslation(compareValue);
        }

        if (inputs.isWholeWordSearch()) {
            if (value.contains(search)) { // möglichst wenig Regex-Prüfungen!
                Pattern pattern = Pattern.compile("\\b" + Pattern.quote(search) + "\\b");
                Matcher matcher = pattern.matcher(value);
                return (matcher.find());
            }
            else {
                return false;
            }
        }
        else {
            return value.contains(search);
        }
    }

    private void sort(List<Kanji> filteredKanjiList) {
        String search = getRelevantSearchWord();
        if (!search.isBlank()) {
            KanjiListSorter sorter = new KanjiListSorter(filteredKanjiList, search);
            sorter.sort();
        }
    }

    private String getRelevantSearchWord() {
        SearchWordSelectionInputs germanMeaningInputs =
                germanMeaningFilterStringSelection.getInputs();
        SearchWordSelectionInputs onLesungInputs = onLesungFilterStringSelection.getInputs();
        SearchWordSelectionInputs kunLesungInputs = kunLesungFilterStringSelection.getInputs();
        SearchWordSelectionInputs searchInputs = searchFilterStringSelection.getInputs();

        if (!germanMeaningInputs.isTextEmpty()) {
            return germanMeaningInputs.getText();
        }
        if (!onLesungInputs.isTextEmpty()) {
            return onLesungInputs.getText();
        }
        if (!kunLesungInputs.isTextEmpty()) {
            return kunLesungInputs.getText();
        }
        if (!searchInputs.isTextEmpty()) {
            return searchInputs.getText();
        }

        return "";
    }

    private void reset() {
        filteringActive = false;

        SearchWordSelectionInputs emptyInputs = new SearchWordSelectionInputs();

        germanMeaningFilterStringSelection.loadInputs(emptyInputs);
        onLesungFilterStringSelection.loadInputs(emptyInputs);
        kunLesungFilterStringSelection.loadInputs(emptyInputs);
        searchFilterStringSelection.loadInputs(emptyInputs);

        filteringActive = true;

        searchButton.doClick();
        requestFocusInSearchFieldLater();
    }

    /** Arrangiert die Elemente passend für den Dialog zum Bearbeiten von Kanji-Mengen. */
    private void initPanel() {
        panel.setLayout(new BorderLayout());

        panel.add(createSearchFilterSelections(), BorderLayout.CENTER);
        panel.add(createButtonPart(), BorderLayout.SOUTH);
    }

    private Component createSearchFilterSelections() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(0, 2, 2, 2));

        /* Zeile 1: */
        panel.add(onLesungFilterStringSelection.getPanel());
        panel.add(kunLesungFilterStringSelection.getPanel());

        /* Zeile 2: */
        panel.add(germanMeaningFilterStringSelection.getPanel());
        panel.add(searchFilterStringSelection.getPanel());

        return panel;
    }

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

        panel.add(resetButton);
        panel.add(searchButton);

        return panel;
    }

    /** Getter für den Panel auf dem die Suchkriterien dargestellt werden. */
    public JPanel getPanel() {
        return panel;
    }

    /** Setzt später den Focus auf das Eingabefeld für den Suchbegriff. */
    public void requestFocusInSearchFieldLater() {
        SwingUtilities.invokeLater(() -> searchFilterStringSelection.requestFocus());
    }

}
