package de.duehl.swing.ui.text.html;

/*
 * Copyright 2021 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.Color;/*
 *
 * 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.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.net.URL;

import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;

import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.ui.colors.Colorizer;

/**
 * Diese Klasse stellt ein Panel zur Verfügung, das Html darstellt und fügt Buttons zum Beenden
 * und zum Umschalten zwischen HTML und dem Quellcode hinzu.
 *
 * Diese Klasse sollte außerhalb NICHT verwendet werden!
 * Statt dessen HtmlAndSourceCodePanel nutzen!
 *
 * @version 1.01     2021-12-09
 * @author Christian Dühl
 */

class HtmlPanel {

    /** Wert für noch nicht gespeicherte Positionen der vertikalen Scrollbar. */
    private static final int NOT_STORED_VERTICAL_SCROLLBAR_VALUE = -1;

    /** EditorPane zur Darstellung des HTML. */
    private final HtmlComponent htmlComponent;

    /** ScrollPane der Pane zur Darstellung des HTML. */
    private final JScrollPane scroll;

    /** Gespeicherte Position der vertikalen Scrollbar. */
    private int storedVerticalScrollbarValue;

    /** Gespeicherte maximale Position der vertikalen Scrollbar. */
    private int storedMaximumVerticalScrollbarValue;

    /** Konstruktor - soll nur aus diesem Paket verwendet werden. */
    HtmlPanel() {
        htmlComponent = new HtmlComponent();
        scroll = new JScrollPane(htmlComponent.getComponent());
        storedVerticalScrollbarValue = NOT_STORED_VERTICAL_SCROLLBAR_VALUE;
        storedMaximumVerticalScrollbarValue = NOT_STORED_VERTICAL_SCROLLBAR_VALUE;
    }

    /**
     * Schreibt den Text der Seite hinter der URL in den Hauptteil des Dialogs und setzt den
     * Textcursor auf den Anfang des Dokuments.
     *
     * @param url
     *            URL, aus der der Inhalt gelesen wird.
     */
    public void showHtml(URL url) {
        htmlComponent.showHtml(url);
    }

    /**
     * Setzt den Text des Editors mit Scrollbalken.
     *
     * @param text
     *            Zu setzender Text.
     */
    public void setText(String html) {
        htmlComponent.setText(html);
    }

    /** Gibt die Länge des Textes zurück. */
    public int getTextLength() {
        return htmlComponent.getTextLength();
    }

    /** Setzt die gewünschte Größe des Text-Elements. */
    public void setPreferredSize(int width, int height) {
        scroll.setPreferredSize(new Dimension(width, height));
    }

    /** Setzt die Farben, falls ein Colorizer übergeben wird. */
    public void setColors(Colorizer colorizer) {
        if (null != colorizer) {
            htmlComponent.setColors(colorizer);
            colorizer.setColors(scroll);
        }
    }

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

    /**
     * Setzt die Position der Eingabemarke im Editor.
     *
     * @param position
     *            Zu setzende Position der Eingabemarke (0 bis Länge des Textes).
     */
    public void setCaretPosition(int position) {
        htmlComponent.setCaretPosition(position);
    }

    /**
     * Returns the position of the text insertion caret for the text component.
     *
     * @return the position of the text insertion caret for the text component &ge; 0
     */
    public int getCaretPosition() {
        return htmlComponent.getCaretPosition();
    }

    /**
     * Returns the <code>Component</code>'s "visible rectangle" - the intersection of this
     * component's visible rectangle, <code>new Rectangle(0, 0, getWidth(), getHeight())</code>,
     * and all of its ancestors' visible rectangles.
     *
     * @return the visible rectangle
     */
    public Rectangle getVisibleRect() {
        return htmlComponent.getVisibleRect();
    }

    /**
     * Forwards the <code>scrollRectToVisible()</code> message to the <code>JComponent</code>'s
     * parent. Components that can service the request, such as <code>JViewport</code>, override
     * this method and perform the scrolling.
     *
     * @param rectangle
     *            the visible <code>Rectangle</code>
     * @see JViewport
     */
    public void scrollRectToVisible(Rectangle rectangle) {
        htmlComponent.scrollRectToVisible(rectangle);
    }

    /**
     * Returns the selected text's start position. Return 0 for an empty document, or the value of
     * dot if no selection.
     *
     * @return the start position &ge; 0
     */
    public int getSelectionStart() {
        return htmlComponent.getSelectionStart();
    }

    /**
     * Returns the selected text's end position. Return 0 if the document is empty, or the value of
     * dot if there is no selection.
     *
     * @return the end position &ge; 0
     */
    public int getSelectionEnd() {
        return htmlComponent.getSelectionEnd();
    }

    /**
     * Sets the selection start to the specified position. The new starting point is constrained to
     * be before or at the current selection end. <p>
     *
     * This is available for backward compatibility to code that called this method on
     * <code>java.awt.TextComponent</code>. This is implemented to forward to the
     * <code>Caret</code> implementation which is where the actual selection is maintained.
     *
     * @param start
     *            the start position of the text &ge; 0
     */
    public void setSelectionStart(int start) {
        htmlComponent.setSelectionStart(start);
    }

    /**
     * Sets the selection end to the specified position. The new end point is constrained to be at
     * or after the current selection start. <p>
     *
     * This is available for backward compatibility to code that called this method on
     * <code>java.awt.TextComponent</code>. This is implemented to forward to the
     * <code>Caret</code> implementation which is where the actual selection is maintained.
     *
     * @param end
     *            the end position of the text &ge; 0
     */
    public void setSelectionEnd(int end) {
        htmlComponent.setSelectionEnd(end);
    }

    /**
     * Gibt den in der text-Komponente selektierten Text zurück.
     *
     * Falls dieser nicht ermittelt werden kann, wird der leere String zurückgegeben.
     */
    public String getSelectedText() {
        return htmlComponent.getSelectedText();
    }

    /** Löscht die Selektion. */
    public void clearSelection() {
        htmlComponent.clearSelection();
    }

    /** Getter für die einzufügende Komponente. */
    public Component getComponent() {
        return scroll;
    }

    /**
     * Setzt eine Scrollleiste auf den Minimalwert. Passiert im gleichen Thread.
     *
     * @param scroll
     *            Zu bearbeitende Scrollleiste.
     */
    public void scrollScrollbarToMinimumLater() {
        GuiTools.scrollScrollbarToMinimumLater(scroll);
    }

    /**
     * Setzt eine Scrollleiste auf den Maximalwert. Passiert im gleichen Thread.
     *
     * @param scroll
     *            Zu bearbeitende Scrollleiste.
     */
    public void scrollScrollbarToMaximumLater() {
        GuiTools.scrollScrollbarToMaximumLater(scroll);
    }

    /**
     * Zeigt in einer Scrollleiste den vorherigen Abschnitt an (etwa für PAGE-UP).
     *
     * Passiert im gleichen Thread.
     *
     * @param scroll
     *            Zu bearbeitende Scrollleiste.
     */
   public void scrollScrollbarToPreviousSectionLater() {
        GuiTools.scrollScrollbarToPreviousSectionLater(scroll);
    }

   /**
    * Zeigt in einer Scrollleiste den nächsten Abschnitt an (etwa für PAGE-DOWN).
    *
    * Passiert im gleichen Thread.
    *
    * @param scroll
    *            Zu bearbeitende Scrollleiste.
    */
    public void scrollScrollbarToNextSectionLater() {
        GuiTools.scrollScrollbarToNextSectionLater(scroll);
    }

    /** Zeichnet den Editor neu. */
    public void repaint() {
        htmlComponent.repaint();
        scroll.validate();
    }

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

    /** Setzt die Hintergrundfarben für die Darstellung. */
    public void setBackground(Color backgroundColor) {
        htmlComponent.setBackground(backgroundColor);
    }

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

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

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

    /** Zeigt den Cursor an (rot und nicht blinkend). */
    public void showCursor() {
        htmlComponent.showCursor();
    }

    /** Speichert die vertikale Position des Textes. */
    public void storeVerticalPosition() {
        JScrollBar verticalScrollBar = scroll.getVerticalScrollBar();
        storedVerticalScrollbarValue = verticalScrollBar.getValue();
        storedMaximumVerticalScrollbarValue = verticalScrollBar.getMaximum();
    }

    /** Stellt die gespeicherte vertikale Position des Textes wieder her. */
    public void restoreVerticalPosition() {
        if (storedVerticalScrollbarValue != NOT_STORED_VERTICAL_SCROLLBAR_VALUE
                && storedMaximumVerticalScrollbarValue != NOT_STORED_VERTICAL_SCROLLBAR_VALUE) {
            JScrollBar verticalScrollBar = scroll.getVerticalScrollBar();
            int actualMaximumVerticalScrollbarValue = verticalScrollBar.getMaximum();
            int value = storedVerticalScrollbarValue;
            if (value > actualMaximumVerticalScrollbarValue) {
                value = actualMaximumVerticalScrollbarValue;
            }
            restoreVerticalPositionLater(value);
        }
    }

    private void restoreVerticalPositionLater(int value) {
        JScrollBar verticalScrollBar = scroll.getVerticalScrollBar();
        SwingUtilities.invokeLater(() -> verticalScrollBar.setValue(value));
    }

    /** Gibt den HTML-Text zurück. */
    public String getHtmlText() {
        return htmlComponent.getHtmlText();
    }

}
