package org.limewire.ui.swing.dnd;

import java.awt.AlphaComposite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;

import javax.swing.Icon;
import javax.swing.JPanel;

import org.jdesktop.application.Resource;
import org.limewire.inject.LazySingleton;
import org.limewire.ui.swing.util.GuiUtils;

import com.google.inject.Inject;

/**
 * Panel which gets installed as the glass pane of a Window. 
 * This glass pane creates and displays semi-transparent images
 * over the main application.
 * <p>
 * It is the responsibility of drag listeners to update the positioning,
 * image and visibility of this glass pane.
 */
@LazySingleton
public class GhostDragGlassPane extends JPanel {

    @Resource
    private Icon dragIconAccept;
    @Resource
    private Icon dragIconReject;
    
    private Icon dragIcon;
    
    private float alpha = 0.85f;
    
    private BufferedImage dragged = null;
    private Point location = new Point(0, 0);
    private Point oldLocation = new Point(0, 0);
    
    private int width;
    private int height;
    private Rectangle visibleRect = null;
    
    @Inject
    public GhostDragGlassPane() {
        setOpaque(false);
        
        GuiUtils.assignResources(this);
        this.dragIcon = dragIconAccept != null ? dragIconAccept : dragIconReject;
        updateDragImage();
    }

    private void updateDragImage() {
        if (dragIcon == null) {
            dragged = null;
            width = 0;
            height = 0;
            return;
        }

        width = dragIcon.getIconWidth();
        height = dragIcon.getIconHeight();
        if (width <= 0 || height <= 0) {
            dragged = null;
            width = 0;
            height = 0;
            return;
        }

        dragged = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics g = dragged.getGraphics();
        try {
            dragIcon.paintIcon(this, g, 0, 0);
        } finally {
            g.dispose();
        }
    }
    
    /**
     * Relocates the image to this point. The image is displayed
     * to the right of this location and 50% above/below the 
     * y coordinate of this location.
     */
    public void setPoint(Point location) {
        this.oldLocation = this.location;
        this.location = location;
    }
    
    /**
     * Returns the rectangle of where changes have occurred
     * on the glass pane. This can greatly improve performance
     * by not repainting the entire glass pane.
     */
    public Rectangle getRepaintRect() {
        int x = (int) (location.getX());
        int y = (int) (location.getY() - (height/ 2));
        
        int x2 = (int) (oldLocation.getX());
        int y2 = (int) (oldLocation.getY() - (height/ 2));
        
        return new Rectangle(x, y, width, height).union(new Rectangle(x2, y2, width, height));
    }

    @Override
    protected void paintComponent(Graphics g) {
        if (dragged == null || !isVisible()) {
            return;
        }

        Graphics2D g2 = (Graphics2D) g.create();
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);

        int x = (int) (location.getX());
        int y = (int) (location.getY() - (height/ 2));
        
        if (visibleRect != null) {
            g2.setClip(visibleRect);
        }
        
        g2.drawImage(dragged, x, y, width, height, null);
        g2.dispose();
    }
    
    public void setAccept(boolean accept) {
        Icon oldIcon = dragIcon;
        dragIcon = accept ? dragIconAccept : dragIconReject;
        if(dragIcon != oldIcon) {
            updateDragImage();
            repaint();
        }
    }
}
