/*
 * @(#)ColorPicker.java  1.1  2006-03-05
 *
 * Copyright (c) 2005 Werner Randelshofer
 * Staldenmattweg 2, Immensee, CH-6405, Switzerland.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * Werner Randelshofer. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Werner Randelshofer.
 */

package contrib.ch.randelshofer.quaqua.colorchooser;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.security.AccessControlException;

import javax.swing.*;
import javax.swing.colorchooser.AbstractColorChooserPanel;
import javax.swing.plaf.IconUIResource;

import org.jvnet.substance.api.SubstanceColorScheme;
import org.jvnet.substance.utils.SubstanceImageCreator;
import org.jvnet.substance.utils.icon.TransitionAwareIcon;

/**
 * ColorPicker.
 * 
 * @author Werner Randelshofer
 * @version 1.1 2006-03-06 Abort picker when the user presses the Escape-Key. <br>
 *          1.0 December 18, 2005 Created.
 */
public class ColorPicker extends AbstractColorChooserPanel {
	/**
	 * This frame is constantly moved to the current location of the mouse. This
	 * ensures that we can trap mouse clicks while the picker cursor is showing.
	 * Also, by tying the picker cursor to this frame, we ensure that the picker
	 * cursor (the magnifying glass) is shown.
	 */
	private Dialog pickerFrame;

	private Timer pickerTimer;

	/**
	 * Holds the image of the picker cursor.
	 */
	private BufferedImage cursorImage;
	/**
	 * Graphics object used for drawing on the cursorImage.
	 */
	private Graphics2D cursorGraphics;

	/**
	 * The picker cursor.
	 */
	private Cursor pickerCursor;

	/**
	 * The hot spot of the cursor.
	 */
	private Point hotSpot;

	/**
	 * Offset from the hot spot of the pickerCursor to the pixel that we want to
	 * pick. We can't pick the color at the hotSpot of the cursor, because this
	 * point is obscured by the pickerFrame.
	 */
	private Point pickOffset;

	/**
	 * The magnifying glass image.
	 */
	private BufferedImage magnifierImage;

	/**
	 * The robot is used for creating screen captures.
	 */
	private Robot robot;

	private Color previousColor = Color.white;
	private Point previousLoc = new Point();
	private Point pickLoc = new Point();
	private Point captureOffset = new Point();
	private Rectangle captureRect;
	private final static Color transparentColor = new Color(0, true);
	private Rectangle zoomRect;
	private Rectangle glassRect;

	/**
	 * Creates a new instance.
	 */
	public ColorPicker() {
		// Try immediately to create a screen capture in order to fail quickly,
		// when
		// we can't provide a color picker functionality.
		try {
			robot = new Robot();
			robot.createScreenCapture(new Rectangle(0, 0, 1, 1));
		} catch (AWTException e) {
			throw new AccessControlException("Unable to capture screen");
		}

	}

	/**
	 * Gets the picker frame. If the frame does not yet exist, it is created
	 * along with all the other objects that are needed to make the picker work.
	 */
	private Dialog getPickerFrame() {
		if (pickerFrame == null) {
			Window owner = SwingUtilities.getWindowAncestor(this);
			if (owner instanceof Dialog) {
				pickerFrame = new Dialog((Dialog) owner);
			} else if (owner instanceof Frame) {
				pickerFrame = new Dialog((Frame) owner);
			} else {
				pickerFrame = new Dialog(new JFrame());
			}

			pickerFrame.addMouseListener(new MouseAdapter() {
				@Override
				public void mousePressed(MouseEvent evt) {
					pickFinish();
				}

				@Override
				public void mouseExited(MouseEvent evt) {
					updatePicker();
				}
			});

			pickerFrame.addMouseMotionListener(new MouseMotionAdapter() {
				@Override
				public void mouseMoved(MouseEvent evt) {
					updatePicker();
				}
			});
			pickerFrame.setSize(3, 3);
			pickerFrame.setUndecorated(true);
			pickerFrame.setAlwaysOnTop(true);

			pickerFrame.addKeyListener(new KeyAdapter() {
				@Override
				public void keyPressed(KeyEvent e) {
					switch (e.getKeyCode()) {
					case KeyEvent.VK_ESCAPE:
						pickCancel();
						break;
					case KeyEvent.VK_ENTER:
						pickFinish();
						break;
					}
				}
			});

			magnifierImage = (BufferedImage) UIManager
					.get("ColorChooser.colorPickerMagnifier");
			glassRect = (Rectangle) UIManager
					.get("ColorChooser.colorPickerGlassRect");
			zoomRect = (Rectangle) UIManager
					.get("ColorChooser.colorPickerZoomRect");
			hotSpot = (Point) UIManager.get("ColorChooser.colorPickerHotSpot");// new
			// Point(29,
			// 29);
			captureRect = new Rectangle((Rectangle) UIManager
					.get("ColorChooser.colorPickerCaptureRect"));
			pickOffset = (Point) UIManager
					.get("ColorChooser.colorPickerPickOffset");
			captureOffset = new Point(captureRect.x, captureRect.y);
			cursorImage = getGraphicsConfiguration().createCompatibleImage(
					magnifierImage.getWidth(), magnifierImage.getHeight(),
					Transparency.TRANSLUCENT);
			cursorGraphics = cursorImage.createGraphics();
			cursorGraphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
					RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);

			pickerTimer = new Timer(5, new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					updatePicker();
				}
			});
		}
		return pickerFrame;
	}

	/**
	 * Updates the color picker.
	 */
	protected void updatePicker() {
		if (pickerFrame != null && pickerFrame.isShowing()) {
			PointerInfo info = MouseInfo.getPointerInfo();
			Point mouseLoc = info.getLocation();
			pickerFrame.setLocation(mouseLoc.x - pickerFrame.getWidth() / 2,
					mouseLoc.y - pickerFrame.getHeight() / 2);

			pickLoc.x = mouseLoc.x + pickOffset.x;
			pickLoc.y = mouseLoc.y + pickOffset.y;

			if (pickLoc.x >= 0 && pickLoc.y >= 0) {
				Color c = robot.getPixelColor(pickLoc.x, pickLoc.y);
				if (!c.equals(previousColor) || !mouseLoc.equals(previousLoc)) {
					previousColor = c;
					previousLoc = mouseLoc;

					captureRect.setLocation(mouseLoc.x + captureOffset.x,
							mouseLoc.y + captureOffset.y);
					if (captureRect.x >= 0 && captureRect.y >= 0) {
						BufferedImage capture = robot
								.createScreenCapture(captureRect);

						// Clear the cursor graphics
						cursorGraphics.setComposite(AlphaComposite.Src);
						cursorGraphics.setColor(transparentColor);
						cursorGraphics.fillRect(0, 0, cursorImage.getWidth(),
								cursorImage.getHeight());

						// Fill the area for the zoomed screen capture with a
						// non-transparent color (any non-transparent color does
						// the job).
						cursorGraphics.setColor(Color.red);
						cursorGraphics.fillOval(glassRect.x, glassRect.y,
								glassRect.width, glassRect.height);

						// Paint the screen capture with a zoom factor of 5
						cursorGraphics.setComposite(AlphaComposite.SrcIn);
						cursorGraphics.drawImage(capture, zoomRect.x,
								zoomRect.y, zoomRect.width, zoomRect.height,
								this);

						// Draw the magnifying glass image
						cursorGraphics.setComposite(AlphaComposite.SrcOver);
						cursorGraphics.drawImage(magnifierImage, 0, 0, this);

						// We need to create a new subImage. This forces that
						// the color picker uses the new imagery.
						BufferedImage subImage = cursorImage
								.getSubimage(0, 0, cursorImage.getWidth(),
										cursorImage.getHeight());
						pickerFrame.setCursor(getToolkit().createCustomCursor(
								cursorImage, hotSpot, "ColorPicker"));
					}
				}
			}
		}
	}

	/**
	 * This method is called from within the constructor to initialize the form.
	 * WARNING: Do NOT modify this code. The content of this method is always
	 * regenerated by the Form Editor.
	 */
	private void initComponents() {// GEN-BEGIN:initComponents
		pickerButton = new javax.swing.JButton();

		setLayout(new java.awt.BorderLayout());

		pickerButton.setBorderPainted(false);
		pickerButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
		pickerButton.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				pickBegin(evt);
			}
		});

		add(pickerButton, java.awt.BorderLayout.CENTER);

	}// GEN-END:initComponents

	private void pickBegin(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_pickBegin
		getPickerFrame();
		pickerTimer.start();
		getPickerFrame().setVisible(true);
	}// GEN-LAST:event_pickBegin

	protected void pickFinish() {
		pickerTimer.stop();
		pickerFrame.setVisible(false);
		PointerInfo info = MouseInfo.getPointerInfo();
		Point loc = info.getLocation();
		Color c = robot.getPixelColor(loc.x + pickOffset.x, loc.y
				+ pickOffset.y);
		getColorSelectionModel().setSelectedColor(c);
	}

	protected void pickCancel() {
		pickerTimer.stop();
		pickerFrame.setVisible(false);
	}

	@Override
	protected void buildChooser() {
		initComponents();
		pickerButton.setIcon(new TransitionAwareIcon(pickerButton,
				new TransitionAwareIcon.Delegate() {
					@Override
					public Icon getColorSchemeIcon(SubstanceColorScheme scheme) {
						return new IconUIResource(SubstanceImageCreator
								.getSearchIcon(15, scheme, pickerButton
										.getComponentOrientation()
										.isLeftToRight()));
					}
				}, "ColorChooser.colorPickerIcon"));
	}

	@Override
	public String getDisplayName() {
		return "Color Picker";
	}

	@Override
	public Icon getLargeDisplayIcon() {
		return UIManager.getIcon("ColorChooser.colorPickerIcon");
	}

	@Override
	public Icon getSmallDisplayIcon() {
		return getLargeDisplayIcon();
	}

	@Override
	public void updateChooser() {
	}

	// Variables declaration - do not modify//GEN-BEGIN:variables
	private javax.swing.JButton pickerButton;
	// End of variables declaration//GEN-END:variables

}
