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

/*
 * Copyright 2024 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.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseWheelListener;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.event.DocumentListener;

import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.ui.area.NotEnteringTabulatorKeyListener;
import de.duehl.swing.ui.colors.Colorizer;
import de.duehl.swing.ui.dragndrop.EditorUiElementManager;

import static de.duehl.swing.ui.components.selections.StringSelection.UNDIFINED_STANDARD_FONT_SIZE;

/**
 * Diese Klasse stellt die Auswahl eines langen Textes dar.
 *
 * @version 1.01     2024-01-29
 * @author Christian Dühl
 */

public class TextAreaSelection implements FontSizeChangable {

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

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

    /** Textarea mit dem einzugebenden Text. */
    private final JTextArea textArea;

    /** Standard-Schriftgröße der Textarea 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.
     */
    public TextAreaSelection(String title) {
        titleLabel = new JLabel(title);
        titleLabel.setBorder(BorderFactory.createEmptyBorder(
                0, StringSelection.LEFT_TITLE_DISTANCE, 0, 0));

        textArea = new JTextArea();
        GuiTools.setEditFieldColors(textArea);
        textArea.setWrapStyleWord(true);
        textArea.setLineWrap(true);

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

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

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

        return panel;
    }

    /** Getter für den (nicht getrimmten) Text. */
    public String getText() {
        String text = textArea.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) {
        textArea.setText(text);
        //textArea.repaint();      // bringt nichts, vermutlich muss man die Scrollpane
                                   // außen hier rein verlegen
    }

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


    /** Getter für das Panel mit Überschrift und Eingabefeld. */
    /*
    public JComponent getPanel() {
        return textArea;
    }
    Auch dann scrollt er nicht!
    */
    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(textArea);
        colorizer.setEditFieldColors(textArea);
    }

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

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

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

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

    /** Setzt den Focus in das Feld. */
    public void requestFocus() {
        textArea.requestFocus();
    }

    /** Ä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(textArea, BorderLayout.CENTER);
    }

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

    /** Fügt einen KeyListener zum Textfeld hinzu, der bei Return die übergebene Aktion ausführt. */
    public void addReturnListener(Runnable runnable) {
        textArea.addKeyListener(new KeyListener() {
            @Override
            public void keyTyped(KeyEvent event) {
            }
            @Override
            public void keyReleased(KeyEvent event) {
            }
            @Override
            public void keyPressed(KeyEvent event) {
                if (event.getKeyCode() == KeyEvent.VK_ENTER && event.getModifiersEx() == 0) {
                    runnable.run();
                }
            }
        });
    }

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

//    /** Fügt einen ActionListener zum Textfeld hinzu. */
//    public void addTextFieldActionListener(ActionListener listener) {
//        textArea.addActionListener(listener);
//    }
    // Das geht bei der TextArea leider nicht.

    /** Setzt die Vordergrundfarbe der Textarea. */
    public void setForegroundColor(Color color) {
        textArea.setForeground(color);
    }

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

    /** Setzt die Anzahl der dargestellten Zeilen.*/
    public void setRows(int rows) {
        textArea.setRows(rows);
    }

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

    /**
     * Fügt einen KeyListener zur TextAreas hinzu, mit dem das Betätigen des Tabulators nicht zu
     * einer Eingabe führt, sondern in der Fokusreihenfolge weitergeht.
     */
    public void addNotEnteringTabulatorKeyListener() {
        textArea.addKeyListener(new NotEnteringTabulatorKeyListener(textArea));
    }

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

    /**
     * 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) {
        textArea.setDragEnabled(enable);
    }

    /** Gibt die Schriftgröße der TextArea zurück. */
    @Override
    public int getFontSize() {
        return textArea.getFont().getSize();
    }

    /** Legt die Schriftgröße der TextArea fest. */
    @Override
    public void setFontSize(int fontSize) {
        GuiTools.setFontSize(textArea, fontSize);
    }

    /** Setzt den Titel über oder neben der TextArea. */
    public void setTitle(String title) {
        titleLabel.setText(title);
    }

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

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

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

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

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

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

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

    /**
     * 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 ignoreUpAndDownInNameField() {
        GuiTools.ignoreUpAndDownInComponent(textArea);
    }

    /**
     * Lässt die Tastenkombinationen Ctrl-Pos1 und Ctrl-End in dem Feld für den Namen an die
     * übergeordnete Komponente weiterreichen.
     */
    public void ignoreCtrlPos1AndCtrlEndInNameField() {
        GuiTools.ignoreCtrlPos1AndCtrlEndInComponent(textArea);
    }

    /** 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 den Font des Textes auf fett. */
    public void switchTextToBold() {
        GuiTools.boldFont(textArea);
    }

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

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

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

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

    public void removeAllMouseWeelListener() {
        for (MouseWheelListener listener : textArea.getMouseWheelListeners()) {
            textArea.removeMouseWheelListener(listener);
        }
    }

    /** Fügt einen DocumentListener hinzu. */
    public void addDocumentListener(DocumentListener documentListener) {
        textArea.getDocument().addDocumentListener(documentListener);
    }

    /** Zeichnet das Textfeld erneut. */
    public void repaintEditArea() {
        textArea.repaint();
    }

}
