// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.swingspy;

import javax.swing.JPanel;
import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;
import java.awt.AWTEvent;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseEvent;

/**
 * Glass pane for some spy effects.
 */
public class SwingSpyGlassPane extends JPanel implements AWTEventListener {

    private final RootPaneContainer rootPaneContainer;
    private final Color fontColor;
    private final Color highlightColor;
    private final AlphaComposite alpha;
    private final Font font;
    private Point point;
    private Rectangle highcmp;

    public SwingSpyGlassPane(RootPaneContainer rootPaneContainer) {
        super(null);
	    setName("SwingSpyGlass");
        this.rootPaneContainer = rootPaneContainer;
        this.fontColor = Color.RED.darker();
        this.highlightColor = new Color(0xFFDDDD);
        this.font = new Font("sansserif", Font.BOLD, 14);
        this.alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
        setOpaque(false);
    }

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setComposite(alpha);
        if (highcmp != null) {
            g2.setColor(highlightColor);
            g2.fillRect((int)highcmp.getMinX(), (int)highcmp.getMinY(), (int) highcmp.getWidth(), (int) highcmp.getHeight());
        }
        if (point != null) {
            g2.setColor(fontColor);
            g2.setFont(font);
            g2.drawString("Spy", point.x + 15, point.y + 20);
        }
        g2.dispose();
    }

    public void eventDispatched(AWTEvent event) {
        if (event instanceof MouseEvent) {
            MouseEvent me = (MouseEvent) event;
            Component mecmp = me.getComponent();

            if (!SwingUtilities.isDescendingFrom(mecmp, (Component) rootPaneContainer)) {
                return;
            }
            if ((me.getID() == MouseEvent.MOUSE_EXITED) && (mecmp == rootPaneContainer)) {
                highcmp = null;
                point = null;
            } else {
                MouseEvent converted = SwingUtilities.convertMouseEvent(mecmp, me, this);
                point = converted.getPoint();
                Component parent = mecmp;
                Rectangle rect = new Rectangle();
                rect.width = mecmp.getWidth();
                rect.height = mecmp.getHeight();
                Rectangle parentBounds = new Rectangle();
                while ((parent != null) && (parent != this.getRootPane()) && (parent != rootPaneContainer)) {
                    parent.getBounds(parentBounds);
                    rect.x += parentBounds.x;
                    rect.y += parentBounds.y;
                    parent = parent.getParent();
                }
                highcmp = rect;
            }
            repaint();
        }
    }

    /**
     * If someone adds a mouseListener to the GlassPane or set a new cursor
     * we expect that he knows what he is doing and return the super.contains(x, y) otherwise
     * we return false to respect the cursors for the underneath components.
     */
    @Override
    public boolean contains(int x, int y) {
        if (getMouseListeners().length == 0 && getMouseMotionListeners().length == 0
                && getMouseWheelListeners().length == 0
                && getCursor() == Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)) {
            return false;
        }
        return super.contains(x, y);
    }
}
