package de.duehl.swing.ui.dragndrop;

/*
 * Copyright 2023 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 javax.swing.JComboBox;
import javax.swing.JTextArea;
/*
 *
 * 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 javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import de.duehl.basics.logic.ErrorHandler;
import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.ui.timer.WaitingUserInputTimerOperator;

/**
 * Diese Klasse fügt einen Listener für Änderungen bei der Texteingabe sowie für die
 * Nachbearbeitung bei Drag 'n' Drop zu einem Textfeld hinzu.
 *
 * @version 1.01     2023-02-24
 * @author Christian Dühl
 */

public class EditorUiElementManager extends WaitingUserInputTimerOperator {

    private static final int MILLISECONDS_BEFORE_REFRESH = 250;

    /** Objekt zum Anzeigen von Fehlern. */
    protected final ErrorHandler error;

    /** Objekt das über Änderungen an den Daten informiert wird. */
    protected final ChangeInformer changeInformer;

    /**
     * Konstruktor.
     *
     * @param error
     *            Objekt zum Anzeigen von Fehlern.
     * @param changeInformer
     *            Objekt das über Änderungen an den Daten informiert wird.
     */
    public EditorUiElementManager(ErrorHandler error, ChangeInformer changeInformer) {
        this(error, changeInformer, MILLISECONDS_BEFORE_REFRESH);
    }

    /**
     * Konstruktor.
     *
     * @param error
     *            Objekt zum Anzeigen von Fehlern.
     * @param changeInformer
     *            Objekt das über Änderungen an den Daten informiert wird.
     * @param millisecondsBeforeRefresh
     *            Anzahl der Millisekunden vor Reaktion auf die Änderung durch den Benutzer.
     */
    public EditorUiElementManager(ErrorHandler error, ChangeInformer changeInformer,
            int millisecondsBeforeRefresh) {
        super(millisecondsBeforeRefresh);
        this.error = error;
        this.changeInformer = changeInformer;
    }

    /**
     * Fügt einen Listener für Änderungen bei der Texteingabe sowie zur Bearbeitung von
     * Drag 'n' Drop bei Textfeldern hinzu.
     *
     * @param textField
     *            Zu behandelndes Textfeld.
     */
    public void addChangeListenerAndDragNDropCorrectorToTextField(JTextField textField) {
        addDragNDropCorrectorToTextField(textField);
        addChangeListenerToTextField(textField);
    }

    /**
     * Fügt einen Listener für Änderungen bei der Texteingabe sowie zur Bearbeitung von
     * Drag 'n' Drop bei mehrzeiligen Textfeldern hinzu.
     *
     * @param textField
     *            Zu behandelndes mehrzeiliges Textfeld.
     */
    public void addChangeListenerAndDragNDropCorrectorToTextArea(JTextArea textArea) {
        addDragNDropCorrectorToTextArea(textArea);
        addChangeListenerToTextArea(textArea);
    }

    /**
     * Fügt einen Listener für Änderungen bei der Texteingabe sowie zur Bearbeitung von
     * Drag 'n' Drop bei bearbeitbaren ComboBoxen hinzu.
     *
     * @param comboBox
     *            Zu behandelnde bearbeitbare ComboBox.
     */
    public void addChangeListenerAndDragNDropCorrectorToComboBox(JComboBox<String> comboBox) {
        addDragNDropCorrectorToComboBox(comboBox);
        addChangeListenerToComboBox(comboBox);
    }

    /**
     * Fügt einen Listener für die Nachbearbeitung bei Drag 'n' Drop zu einem Textfeld hinzu.
     *
     * @param textField
     *            Zu behandelndes Textfeld.
     */
    private void addDragNDropCorrectorToTextField(JTextField textField) {
        GuiTools.addDropTargetListenerToTextField(textField, error,
                new StandardTextComponentDropTargetListener(textField));
    }

    /**
     * Fügt einen Listener für die Nachbearbeitung bei Drag 'n' Drop zu einem mehrzeiligen Textfeld
     * hinzu.
     *
     * @param textArea
     *            Zu behandelndes mehrzeiliges Textfeld.
     */
    private void addDragNDropCorrectorToTextArea(JTextArea textArea) {
        GuiTools.addDropTargetListenerToTextArea(textArea, error,
                new StandardTextComponentDropTargetListener(textArea));
    }

    /**
     * Fügt einen Listener für die Nachbearbeitung bei Drag 'n' Drop zu einer bearbeitbaren
     * ComboBox hinzu.
     *
     * @param comboBox
     *            Zu behandelnde bearbeitbare ComboBox.
     */
    private void addDragNDropCorrectorToComboBox(JComboBox<String> comboBox) {
        JTextField textfield = GuiTools.getTextFieldInComboBox(comboBox);
        addDragNDropCorrectorToTextField(textfield);
    }

    /**
     * Fügt einen Listener für Änderungen bei der Texteingabe sowie zu einem Textfeld hinzu.
     *
     * @param textField
     *            Zu behandelndes Textfeld.
     */
    protected final void addChangeListenerToTextField(JTextField textField) {
        textField.getDocument().addDocumentListener(createChangeDocumentListener());
    }

    /**
     * Fügt einen Listener für Änderungen bei der Texteingabe sowie zu einem mehrzeiligen Textfeld
     * hinzu.
     *
     * @param textArea
     *            Zu behandelndes mehrzeiliges Textfeld.
     */
    protected final void addChangeListenerToTextArea(JTextArea textArea) {
        textArea.getDocument().addDocumentListener(createChangeDocumentListener());
    }

    /**
     * Fügt einen Listener für Änderungen bei der Texteingabe sowie zu einer bearbeitbaren
     * ComboBox hinzu.
     *
     * @param comboBox
     *            Zu behandelnde bearbeitbare ComboBox.
     */
    private void addChangeListenerToComboBox(JComboBox<String> comboBox) {
        JTextField textfield = GuiTools.getTextFieldInComboBox(comboBox);
        addChangeListenerToTextField(textfield);
        /*
         *  ((JTextField)comboBox.getEditor().getEditorComponent()).getDocument().addDocumentListener(
         *       createChangeDocumentListener());
         * https://stackoverflow.com/questions/27129155/adding-a-documentlistener-to-a-jcombobox
         *
         */
    }

    private DocumentListener createChangeDocumentListener() {
        return new DocumentListener() {
            @Override
            public void changedUpdate(DocumentEvent e) {
                warnAboutUserInput();
            }
            @Override
            public void removeUpdate(DocumentEvent e) {
                warnAboutUserInput();
            }
            @Override
            public void insertUpdate(DocumentEvent e) {
                warnAboutUserInput();
            }
            private void warnAboutUserInput() {
                // Beim ChangeListener wird gewartet, bevor Änderungen wirklich ausgeführt werden:
                startTimer();
            }
        };
    }

    /** Erzeugt für den ChangeListener ein Runnable, das im EDT über die Änderungen informiert. */
    @Override
    protected final Runnable createRunnable() {
        return new Runnable() {
            @Override
            public void run() {
                SwingUtilities.invokeLater(() -> changeInformer.informAboutChangingData());
            }
        };
    }

}
