package de.duehl.swing.ui.elements;

/*
 * Copyright 2020 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;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionAdapter;

import javax.swing.JComponent;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;

import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.ui.elements.watch.StopWatchLabel;

/**
 * Diese Klasse stellt eine einfache Implementation einer GlassPane dar, die alle Events fängt und
 * ignoriert und dem JFrame einen "disabled"-Aussehen gibt.
 *
 * Die Hintergrundfarbe der GlassPane sollte eine Farbe mit einem Alhpa-Wert sein, um den
 * Disabled-Look zu erzeugen.
 *
 * Quelle: http://tips4java.wordpress.com/2008/11/07/disabled-glass-pane/
 *
 * @version 1.01            2020-02-07
 * @author Rob Camick     - ursprüngliche Idee
 * @author Christian Dühl - Erweiterungen und Anpassungen, Stoppuhr
 */

public class DisabledGlassPane extends JComponent {

    private static final long serialVersionUID = 1L;

    private static final Color FALLBACK_COLOR = new Color(184, 207, 229);

    /** Das Label, das angezeigt wird. */
    private final StopWatchLabel stopWatchLabel;

    /** Mit der GlassPane verbundene Component (z.B. JFrame oder JDialog). */
    private Component connectedComponent;

    /** Konstruktor. */
    public DisabledGlassPane() {
        setOpaque(false);

        stopWatchLabel = new StopWatchLabel();

        SwingUtilities.invokeLater(() -> stopWatchLabel.stop());
        // ohne das invokeLater() kommt der Start NACH dem Stopp!

        setOurBackground();
        setLayout(new GridBagLayout());

        disableMouseKeyboaurdAndFocusEvents();
    }

    private void disableMouseKeyboaurdAndFocusEvents() {
        addMouseListener(new MouseAdapter() { });
        addMouseMotionListener(new MouseMotionAdapter() { });
        addKeyListener(createConsumingKeyListener());
        setFocusTraversalKeysEnabled(false);
    }

    /**
     * Implementiert den KeyListener so, dass die Events konsumiert werden (und damit nicht an die
     * eigentliche Oberfläche durchkommen).
     */
    private KeyListener createConsumingKeyListener() {
        return new KeyListener() {
            @Override
            public void keyTyped(KeyEvent event) {
                event.consume();
            }
            @Override
            public void keyReleased(KeyEvent event) {
                event.consume();
            }
            @Override
            public void keyPressed(KeyEvent event) {
                event.consume();
            }
        };
    }

    /**
     * Setzt die Hintergrundfarbe, dabei wird ein Alphawert von 128 der normalen Farbe hinzugefügt.
     */
    private void setOurBackground() {
        Color base = getBaseColor();
        Color background = new Color(base.getRed(), base.getGreen(), base.getBlue(), 128);
        setBackground(background);
    }

    /**
     * Ermittelt die Basisfarbe. Manche UI-Layouts geben für "inactiveCaptionBorder" leider null
     * zurück, daher wird in dem Fall ein Defaultwert verwendet.
     */
    private Color getBaseColor() {
        Color base = UIManager.getColor("inactiveCaptionBorder");
        if (null == base) {
            base = FALLBACK_COLOR;
        }
        return base;
    }

    /**
     * Die GlassPane ist durchsichtig, aber wir wollen den Hintergrund einzeichnen, um ihr den
     * Disabled-Look zu verschaffen.
     *
     * @param graphics
     *            Grafik-Objekt.
     */
    @Override
    protected void paintComponent(Graphics graphics) {
        graphics.setColor(getBackground());
        graphics.fillRect(0, 0, getSize().width, getSize().height);
    }

    /**
     * Für die Hintergrundfarbe des Nachrichten-Labels (stopWatchLabel) wird die gleiche Farbe wie
     * für den Hintergrund der GlassPane verwendet, nur ohne deren Alphawert.
     *
     * @param background
     *            Zu verwendende Hintergrundfarbe.
     */
    @Override
    public void setBackground(Color background) {
        super.setBackground(background);

        Color messageBackground = new Color(background.getRGB());
        stopWatchLabel.setBackground(messageBackground);
    }

    /**
     * Macht die GlassPane sichtbar und setzt den Mauscursor aus den Wait-Cursor.
     *
     * @param text
     *            Diese Nachricht wird in der Mitte der GlassPane angezeigt, darunter läuft eine
     *            Stoppuhr um anzuzeigen, wie lange die GlassPane nun schon aktiv ist.
     */
    public void activate(String text) {
        if (text != null && !text.isEmpty()) {
            stopWatchLabel.stop();

            removeAll();

            stopWatchLabel.setStartText("<html><center>" + text + "<br/>");
            stopWatchLabel.setEndText("</center></html>");
            stopWatchLabel.start();
            //stopWatchLabel = new StopWatchLabel("<html><center>" + text + "<br/>",
            //        "</center></html>");
            stopWatchLabel.setOpaque(true);
            stopWatchLabel.setBorder(new EmptyBorder(10, 10, 10, 10));
            add(stopWatchLabel, new GridBagConstraints());

            setOurBackground();
            stopWatchLabel.setForeground(getForeground());
            stopWatchLabel.setVisible(true);
        }
        else {
            if (null != stopWatchLabel) {
                stopWatchLabel.setVisible(false);
            }
        }

        setVisible(true);
        waiCursorOn();
        requestFocusInWindow();
    }

    private void waiCursorOn() {
        GuiTools.waitcursorOn(this);
        checkConnectedComponentSet();
        GuiTools.waitcursorOn(connectedComponent);
    }

    /** Versteckt die GlassPane wieder und beendet die Stoppuhr. */
    public void deactivate() {
        stopWatchLabel.stop();
        waitCursorOff();
        setVisible(false);
    }

    private void waitCursorOff() {
        GuiTools.waitcursorOff(this);
        checkConnectedComponentSet();
        GuiTools.waitcursorOff(connectedComponent);
    }

    private void checkConnectedComponentSet() {
        if (connectedComponent == null) {
            throw new RuntimeException(
                    "Es wurde vergessen, DisabledGlassPane#connectTo(Component) aufzurufen!");
        }
    }

    /** Verbindet die GlassPane mit der RootPane einer Component (z.B. JFrame oder JDialog). */
    public void connectTo(Component component) {
        JRootPane rootPane = SwingUtilities.getRootPane(component);
        rootPane.setGlassPane(this);
        connectedComponent = component;
    }

}
