package de.duehl.swing.ui.components.selections;

/*
 * Copyright 2025 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionListener;
import java.awt.event.FocusListener;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.Border;

import de.duehl.basics.collections.CollectionsHelper;
import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.ui.colors.Colorizer;
import de.duehl.swing.ui.components.selections.tools.BackgroundColorChangingComboBoxEditor;
import de.duehl.swing.ui.dragndrop.EditorUiElementManager;

/**
 * Diese Klasse stellt die Auswahl einer Reihe von Strings in Form einer ComboBox (z.B. in einem
 * Optionendialog) dar.
 *
 * @version 1.01     2025-08-04
 * @author Christian Dühl
 */

public class ComboBoxSelection implements FontSizeChangable {

    static final int UNDIFINED_STANDARD_FONT_SIZE = -1;

    static final int LEFT_TITLE_DISTANCE = 5;

    /** Liste mit den disjunkten Werten der ComboBox. */
    private final List<String> values;

    /** Überschrift für die Wahl dieser Datei oder dieses Verzeichnisses. */
    private final JLabel titleLabel;

    /** Panel mit Überschrift und ComboBox. */
    private final JPanel mainPanel;

    /** Die ComboBox mit der Auswahl an Werten. */
    private final JComboBox<String> comboBox;

    /** Der Editor der Combobox. */
    private final BackgroundColorChangingComboBoxEditor comboBoxEditor;

    /** Standard-Schriftgröße der ComboBox oder -1, wenn nicht gesetzt. */
    private int standardFontSize;

    /** Standard-Schriftgröße der Überschrift oder -1, wenn nicht gesetzt. */
    private int standardLabelFontSize;

    /**
     * Konstruktor.
     *
     * @param title
     *            Überschrift für die Wahl dieser Datei oder dieses Verzeichnisses.
     * @param values
     *            Liste mit den anzuzeigenden, disjunkten Werten der ComboBox.
     */
    public ComboBoxSelection(String title, List<String> values) {
        if (!CollectionsHelper.isDisjunct(values)) {
            throw new IllegalArgumentException("Die übergebenen Werte für die ComboBox sind nicht "
                    + "disjunkt:\n"
                    + CollectionsHelper.listListNice(values));
        }
        this.values = values;

        titleLabel = new JLabel(title);
        titleLabel.setBorder(BorderFactory.createEmptyBorder(0, LEFT_TITLE_DISTANCE, 0, 0));

        comboBox = new JComboBox<>();
        for (String value : values) {
            comboBox.addItem(value);
        }
        comboBoxEditor = new BackgroundColorChangingComboBoxEditor();
        comboBox.setEditor(comboBoxEditor);

        mainPanel = createMainPanel();
        standardFontSize = UNDIFINED_STANDARD_FONT_SIZE;
        standardLabelFontSize = UNDIFINED_STANDARD_FONT_SIZE;
    }

    /** Erstellt den Panel mit der Überschrift und der ComboBox in vertikaler Anordnung. */
    private JPanel createMainPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());

        panel.add(titleLabel, BorderLayout.NORTH);
        panel.add(comboBox, BorderLayout.CENTER);

        return panel;
    }

    /** Getter für den in der ComboBox ausgewählten Text. */
    public String getSelectedItem() {
        String selected = (String) comboBox.getSelectedItem();
        return selected;
    }

    /** Setter für den in der ComboBox ausgewählten Text. */
    public void setSelectedItem(String text) {
        if (values.contains(text)) {
            comboBox.setSelectedItem(text);
        }
        else {
            throw new IllegalArgumentException(
                    "Der übergebene Text '" + text + "' wird in der ComboBox nicht angezeigt.");
        }
    }

    /** Setter für den Index des in der ComboBox ausgewählten Text. */
    public void setSelectedIndex(int index) {
        if (index >= 0 && index < values.size()) {
            comboBox.setSelectedIndex(index);
        }
        else {
            throw new IllegalArgumentException("Der übergebene Index '" + index
                    + "' liegt nicht im erlaubten Bereich [0, " + values.size() + ").");
        }
    }

    /** Getter für das Panel mit Überschrift und ComboBox. */
    public JPanel getPanel() {
        return mainPanel;
    }

    /** Färbt alle Komponenten mit dem übergebenen Colorizer ein. */
    public void colorize(Colorizer colorizer) {
        colorizer.setColors(mainPanel);
        colorizer.setColors(titleLabel);
        colorizer.setColors(comboBox);
    }

    /** Setter für die gewünschte Größe der ComboBox. */
    public void setPreferredSize(Dimension preferredSize) {
        comboBox.setPreferredSize(preferredSize);
    }

    /** Setter für die kleinste Größe der ComboBox. */
    public void setMinimumSize(Dimension minimumSize) {
        comboBox.setMinimumSize(minimumSize);
    }

    /** Fügt einen FocusListener zum ComboBox hinzu. */
    public void addTextFieldFocuslistener(FocusListener focusListener) {
        comboBox.addFocusListener(focusListener);
    }

    /** Legt fest, ob das ComboBox editierbar ist. */
    public void setEditable(boolean editable) {
        comboBox.setEditable(editable);
    }

    /** Legt fest, ob das ComboBox fokussierbar ist. */
    public void setFocusable(boolean focusable) {
        comboBox.setFocusable(focusable);
    }

    /** Setzt den Fokus in das ComboBox. */
    public void requestFocus() {
        comboBox.requestFocus();
    }

    /** Gibt an, ob das ComboBox den Fokus hat. */
    public boolean hasFocus() {
        return comboBox.hasFocus();
    }

    /** Ändert die Anordnung von Überschrift und ComboBox in horizontal, Überschrift links. */
    public void makeHorizontal() {
        titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
        mainPanel.removeAll();
        mainPanel.add(titleLabel, BorderLayout.WEST);
        mainPanel.add(comboBox, BorderLayout.CENTER);
    }

    /** Setzt den Tooltip. */
    public void setToolTipText(String tooltip) {
        titleLabel.setToolTipText(tooltip);
        mainPanel.setToolTipText(tooltip);
        comboBox.setToolTipText(tooltip);
    }

    /** Fügt einen ActionListener zum ComboBox hinzu. */
    public void addComboBoxActionListener(ActionListener listener) {
        comboBox.addActionListener(listener);
    }

    /** Setzt die Hintergrundfarbe der ComboBox. */
    public void setBackgroundColor(Color color) {
        comboBoxEditor.setBackground(color);
    }

    /** Vergrößert die Schriftart. */
    @Override
    public void biggerText(int addSize) {
        GuiTools.biggerFont(comboBox, addSize);
    }

    /** Speichert die aktuelle Schriftgröße der ComboBox als Standard. */
    @Override
    public void storeAsStandardFontSize() {
        Font font = comboBox.getFont();
        standardFontSize = font.getSize();
    }

    /**
     * Wechselt die Schriftgröße der ComboBox auf den Standard, falls dieser mittels
     * storeAsStandardFontSize() zuvor gesetzt wurde.
     */
    @Override
    public void setToStandardFontSize() {
        if (standardFontSize != UNDIFINED_STANDARD_FONT_SIZE) {
            setFontSize(standardFontSize);
        }
    }

    /** Gibt die Schriftgröße zurück. */
    @Override
    public int getFontSize() {
        Font font = comboBox.getFont();
        return font.getSize();
    }

    /** Setzt die Schriftgröße. */
    @Override
    public void setFontSize(int fontSize) {
        GuiTools.setFontSize(comboBox, fontSize);
    }

    /** Vergrößert die Schriftart der Überschrift des Textfeldes. */
    @Override
    public void biggerLabelText(int addSize) {
        GuiTools.biggerFont(titleLabel, addSize);
    }

    /** Speichert die aktuelle Schriftgröße der Überschrift der ComboBox als Standard. */
    @Override
    public void storeAsStandardLabelFontSize() {
        Font font = titleLabel.getFont();
        standardLabelFontSize = font.getSize();
    }

    /**
     * Wechselt die Schriftgröße der Überschrift des Textfeldes auf den Standard, falls dieser
     * mittels storeAsStandardLabelFontSize() zuvor gesetzt wurde.
     */
    @Override
    public void setToStandardLabelFontSize() {
        if (standardLabelFontSize != UNDIFINED_STANDARD_FONT_SIZE) {
            setLabelFontSize(standardLabelFontSize);
        }
    }

    /** Gibt die Schriftgröße der Überschrift zurück. */
    @Override
    public int getLabelFontSize() {
        Font font = titleLabel.getFont();
        return font.getSize();
    }

    /** Setzt die Schriftgröße der Überschrift. */
    @Override
    public void setLabelFontSize(int fontSize) {
        GuiTools.setFontSize(titleLabel, fontSize);
    }

    /** Setzt die Schriftart der ComboBox auf Monospaced und 14 Punkt. */
    public void setMonospacedFont() {
        GuiTools.setMonospacedFont(comboBox);
    }

    /** Setzt die Schriftart der ComboBox auf Monospaced und die angegebene Schriftgröße. */
    public void setMonospacedFont(int fontSize) {
        GuiTools.setMonospacedFont(comboBox, fontSize);
    }

    /** Setzt den Font des Labels auf fett. */
    public void switchLabelToBold() {
        GuiTools.boldFont(titleLabel);
    }

    /** Setzt den Font des Textes auf fett. */
    public void switchTextToBold() {
        GuiTools.boldFont(comboBox);
    }

    /** Legt fest, ob die Textkomponente enabled ist oder nicht. */
    public void setEnabled(boolean enabled) {
        comboBox.setEnabled(enabled);
    }

    /** Legt einen Rahmen um das ganze Element fest. */
    public void setBorder(Border border) {
        mainPanel.setBorder(border);
    }

    /**
     * Lässt die Tastenkombinationen Page-Up und Page-Down an die übergeordnete Komponente
     * weiterreichen.
     */
    public void ignorePageUpAndPageDown() {
        GuiTools.ignorePageUpAndPageDownInComponent(comboBox);
    }

    /**
     * Lässt die Tastenkombinationen Pfeiltaste nach oben und Pfeiltaste nach unten in dem Feld für
     * den Namen an die übergeordnete Komponente weiterreichen.
     */
    public void ignoreUpAndDown() {
        GuiTools.ignoreUpAndDownInComponent(comboBox);
    }

    /**
     * Fügt einen Listener für Änderungen bei der Texteingabe sowie zur Bearbeitung vonDrag 'n'
     * Drop bei Textfeldern hinzu.
     */
    public void addChangeListenerAndDragNDropCorrectorToComboBox(
            EditorUiElementManager elementManager) {
        throwExceptionIfNotEditable();

        JTextField textField = comboBoxEditor.getTextField();
        elementManager.addChangeListenerAndDragNDropCorrectorToTextField(textField);
        //elementManager.addChangeListenerAndDragNDropCorrectorToComboBox(comboBox);
    }

    /**
     * Hinterlegt, wie auf Doppelklick in das Textfeld reagiert werden soll. Standardverhalten:
     * Doppelklick markiert das getroffene Wort, Dreifachklick den ganzen Text.
     */
    public void reactOnDoubleClick(Runnable runnable) {
        throwExceptionIfNotEditable();

        JTextField textField = comboBoxEditor.getTextField();
        GuiTools.createDoubleClickAndDispatchMouseScrollEventMouseAdapter(textField, runnable);
    }

    /** Setter für den Text. */
    public void setText(String text) {
        throwExceptionIfNotEditable();

        if (values.contains(text)) {
            comboBox.setSelectedItem(text);
        }

        /*
         * Beides, da sonst nicht die Überprüfung des Textes angestoßen wird!
         */
        JTextField textfield = comboBoxEditor.getTextField();
        textfield.setText(text);
    }

    private void throwExceptionIfNotEditable() {
        if (!comboBox.isEditable()) {
            throw new RuntimeException("Unzulässig, wenn nicht editierbar!");
        }
    }

    /** Reicht das Scrollen an die Eltern-Komponente des Textfeldes weiter. */
    public void dispatchMouseScrollEvent() {
        GuiTools.dispatchMouseScrollEvent(titleLabel);
        GuiTools.dispatchMouseScrollEvent(comboBox);
        GuiTools.dispatchMouseScrollEvent(mainPanel);
        GuiTools.dispatchMouseScrollEvent(comboBoxEditor.getTextField());
    }

    /** Getter für den (getrimmten) Text. */
    public String getTrimmedText() {
        String text = getText();
        text = text.trim();
        return text;
    }

    /** Getter für den (nicht getrimmten) Text. */
    public String getText() {
        String text;
        if (comboBox.isEditable()) {
            JTextField textField = comboBoxEditor.getTextField();
            text = textField.getText();
        }
        else {
            text = getSelectedItem();
        }
        return text;
    }

    /** Setzt die Farbe des Carets auf rot. */
    public void setRedCaretColor() {
        if (comboBox.isEditable()) {
            JTextField textField = comboBoxEditor.getTextField();
            textField.setCaretColor(Color.RED);
        }
    }

    /** Setzt die Vordergrundfarbe des Textfeldes. */
    public void setForegroundColor(Color color) {
        JTextField textField = comboBoxEditor.getTextField();
        textField.setForeground(color);
    }

    /** Setzt die Combobox zentriert. */
    public void setComboBoxCentered() {
        comboBox.setAlignmentX(JLabel.CENTER);
        comboBox.setAlignmentY(JLabel.CENTER);
        ((JLabel) comboBox.getRenderer())
                .setHorizontalAlignment(SwingConstants.CENTER);
    }

    /*
     * Wenn man mal Tab irgendwie verwenden möchte:
     *
     * https://stackoverflow.com/questions/24336767/
     *     how-to-accept-a-value-in-a-swing-jcombobox-with-the-tab-key
     *
     */

}
