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.awt.event.KeyListener;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.event.DocumentListener;
import javax.swing.text.Document;

import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.ui.colors.Colorizer;
import de.duehl.swing.ui.dragndrop.EditorUiElementManager;
import de.duehl.swing.ui.text.autocompletion.AutoCompletionTextComponentExtender;

/**
 * Diese Klasse stellt die Auswahl eines Textes (z.B. in einem Optionendialog) dar.
 *
 * @version 1.01     2025-11-29
 * @author Christian Dühl
 */

public class StringSelection implements FontSizeChangable {

    static final int UNDIFINED_STANDARD_FONT_SIZE = -1;

    static final int LEFT_TITLE_DISTANCE = 5;

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

    /** Das Panel mit Überschrift und Eingabefeld. */
    protected JPanel mainPanel;

    /** Das Textfeld mit dem einzugebenden Text. */
    private final JTextField textField;

    /** Die Standard-Schriftgröße des Textfeldes oder -1, wenn nicht gesetzt. */
    private int standardFontSize;

    /** Die 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.
     */
    public StringSelection(String title) {
        titleLabel = new JLabel(title);
        titleLabel.setBorder(BorderFactory.createEmptyBorder(0, LEFT_TITLE_DISTANCE, 0, 0));
        textField = new JTextField();
        GuiTools.setEditFieldColors(textField);
        mainPanel = createPanel();
        standardFontSize = UNDIFINED_STANDARD_FONT_SIZE;
        standardLabelFontSize = UNDIFINED_STANDARD_FONT_SIZE;
        //GuiTools.dispatchMouseScrollEvent(textField); // das tut es schon automatisch.
    }

    /** Erstellt den Panel mit der Überschrift und dem Eingabefeld in vertikaler Anordnung. */
    protected JPanel createPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());

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

        //GuiTools.dispatchMouseScrollEvent(panel);

        return panel;
    }

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

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

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

    /** Setter für die Text als Zahl. */
    public void setText(int numberText) {
        setText(Integer.toString(numberText));
    }

    /** Getter für das Panel mit Überschrift und Eingabefeld. */
    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(textField);
        colorizer.setEditFieldColors(textField);
    }

    /** Setzt die Farbe für die Überschrift. */
    public void setTitleColor(Color color) {
        titleLabel.setForeground(color);
    }

    /** Setzt die Farbe für den Text. */
    public void setTextColor(Color color) {
        textField.setForeground(color);
    }

    /** Setter für die gewünschte Größe des Textfeldes. */
    public void setTextFieldPreferredSize(Dimension preferredSize) {
        textField.setPreferredSize(preferredSize);
    }

    /** Setter für die kleinste Größe des Textfeldes. */
    public void setTextFieldMinimumSize(Dimension minimumSize) {
        textField.setMinimumSize(minimumSize);
    }

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

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

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

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

    /** Setzt den Fokus in das Textfeld etwas später und im EDT. */
    public void requestFocusLater() {
        SwingUtilities.invokeLater(() -> requestFocus());
    }

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

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

    /** Fügt einen KeyListener zum Textfeld hinzu. */
    public void addKeyListener(KeyListener keyListener) {
        textField.addKeyListener(keyListener);
    }

    /** Fügt einen KeyListener zum Textfeld hinzu, der bei Return die übergebene Aktion ausführt. */
    public void addReturnListener(Runnable runnable) {
        GuiTools.addReturnListener(textField, runnable);
    }

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

    /** Fügt einen ActionListener zum Textfeld hinzu. */
    public void addTextFieldActionListener(ActionListener listener) {
        textField.addActionListener(listener);
    }

    /** Setzt die Hintergrundfarbe des Textfeldes. */
    public void setBackgroundColor(Color color) {
        textField.setBackground(color);
    }

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

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

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

    /**
     * Wechselt die Schriftgröße des Textfeldes 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 = textField.getFont();
        return font.getSize();
    }

    /** Setzt die Schriftgröße. */
    @Override
    public void setFontSize(int fontSize) {
        GuiTools.setFontSize(textField, 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 des Textfeldes 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 Farbe des Carets. */
    public void setCaretColor(Color caretColor) {
        textField.setCaretColor(caretColor);
    }

    /** Setzt die Farbe des Carets auf rot. */
    public void setRedCaretColor() {
        textField.setCaretColor(Color.RED);
    }

    /** Setzt den Carets ganz nach links. */
    public void setCaretToLeft() {
        textField.setCaretPosition(0);
    }

    /** Setzt die Schriftart des Textfeldes auf Monospaced und 14 Punkt. */
    public void setMonospacedFont() {
        GuiTools.setMonospacedFont(textField);
    }

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

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

    /**
     * Turns on or off automatic drag handling. In order to enable automatic drag handling, this
     * property should be set to {@code true}, and the component's {@code TransferHandler} needs to
     * be {@code non-null}. The default value of the {@code dragEnabled} property is {@code false}.
     * <p>
     * The job of honoring this property, and recognizing a user drag gesture, lies with the look
     * and feel implementation, and in particular, the component's {@code TextUI}. When automatic
     * drag handling is enabled, most look and feels (including those that subclass
     * {@code BasicLookAndFeel}) begin a drag and drop operation whenever the user presses the
     * mouse button over a selection and then moves the mouse a few pixels. Setting this property
     * to {@code true} can therefore have a subtle effect on how selections behave.
     * <p>
     * If a look and feel is used that ignores this property, you can still begin a drag and drop
     * operation by calling {@code exportAsDrag} on the component's {@code TransferHandler}.
     *
     * @param enable
     *            whether or not to enable automatic drag handling
     */
    public void setDragEnabled(boolean enable) {
        textField.setDragEnabled(enable);
    }

    /** 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(textField);
    }

    /** Setzt den Font des Labels auf normal. */
    public void switchLabelToNormal() {
        GuiTools.normalFont(titleLabel);
    }

    /** Setzt den Font des Textes auf normal. */
    public void switchTextToNormal() {
        GuiTools.normalFont(textField);
    }

    /** Setzt die Ausrichtung des Textfeldes auf rechtsbündig. */
    public void rightTextfieldAlignment() {
        textField.setHorizontalAlignment(JTextField.RIGHT);
    }

    /** Gibt die Anzahl der darstellbaren Zeichen an oder -1, wenn nicht ermittelbar. */
    public int getNumberOfDisplayableCharakters() {
        textField.setHorizontalAlignment(JTextField.RIGHT);
        int width = textField.getWidth();
        //System.out.println("width = " + width);
        Font font = textField.getFont();
        String family = font.getFamily();
        if ("Monospaced".equals(family)) {
            int size = font.getSize();
            //System.out.println("size = " + size);
            int numberOfChars = (int) ((1.58 * width) / size);
            //System.out.println("numberOfChars = " + numberOfChars);
            return numberOfChars;
        }
        else {
            return -1;
        }
    }

    /** Ergänzt das Textfeld um Autovervollständigung für die übergebenen Begriffe. */
    public void addAutoCompletion(List<String> autoCompletionWords) {
        AutoCompletionTextComponentExtender extender = new AutoCompletionTextComponentExtender(
                textField, autoCompletionWords);
        extender.changeActivationKeyToSpace();
        extender.doNotInsertSpaceBehindCompletion();
        extender.setMinimalLengthForAutoCompletionStart(1);
        extender.extendAutoCompletion();
    }

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

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

    /** Setzt die horizontale Ausrichtung des Textfeldes. */
    public void setHorizontalAlignment(int alignment) {
        textField.setHorizontalAlignment(alignment);
    }

    /** Fügt zum Dokument des Textfeldes einen Listener hinzu. */
    public void addTextFieldDocumentListener(DocumentListener documentListener) {
        Document document = textField.getDocument();
        document.addDocumentListener(documentListener);
    }

    /**
     * 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) {
        textField.addMouseListener(GuiTools.createDoubleClickMouseAdapter(runnable));
    }

    /** Getter für die Textkomponente. */
    /*
    public JTextComponent getTextComponent() {
        return textField;
    }
    */

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

    /** Zeichnet das Textfeld erneut. */
    public void repaintEditField() {
        textField.repaint();
    }

    /** Gibt die Überschrift zurück. */
    public String getTitle() {
        return titleLabel.getText();
    }

}
