/*******************************************************************************
 * Copyright (c) 2003, 2010 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.gef.editpolicies;

import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.widgets.Display;

import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.FigureUtilities;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.PrecisionPoint;
import org.eclipse.draw2d.geometry.Rectangle;

import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.SnapToGeometry;
import org.eclipse.gef.SnapToGuides;

/**
 * An EditPolicy that is used to show snap feedback for guides (red lines) and
 * geometry (blue lines). This policy is not required for Grids.
 * 
 * @author Randy Hudson
 * @author Pratik Shah
 * @since 3.0
 */
public class SnapFeedbackPolicy extends GraphicalEditPolicy {

	IFigure guide[] = new IFigure[6];
	Integer location[] = new Integer[6];

	/**
	 * @see org.eclipse.gef.EditPolicy#eraseTargetFeedback(org.eclipse.gef.Request)
	 */
	public void eraseTargetFeedback(Request request) {
		for (int i = 0; i < guide.length; i++) {
			if (guide[i] != null)
				removeFeedback(guide[i]);
			guide[i] = null;
			location[i] = null;
		}
	}

	static class FadeIn extends Figure {
		int opacity = 0;
		static final int FRAMES = 6;
		Image image;
		static int count;

		FadeIn(Color bg) {
			setBackgroundColor(bg);
			super.setOpaque(true);
		}

		private Color createMixedColor() {
			return FigureUtilities
					.mixColors(getLocalBackgroundColor(), getParent()
							.getBackgroundColor(), (double) opacity / FRAMES);
		}

		/**
		 * @see org.eclipse.draw2d.Figure#paintFigure(org.eclipse.draw2d.Graphics)
		 */
		protected void paintFigure(Graphics graphics) {
			if (opacity != FRAMES) {
				if (image != null) {
					image.dispose();
					count--;
					image = null;
				}
				if (opacity != FRAMES - 1) {
					Display display = Display.getCurrent();
					PaletteData pData = new PaletteData(0xFF, 0xFF00, 0xFF0000);
					Color localBackgroundColor = createMixedColor();
					int fillColor = pData.getPixel(localBackgroundColor
							.getRGB());
					localBackgroundColor.dispose();
					ImageData iData = new ImageData(1, 1, 24, pData);
					iData.setPixel(0, 0, fillColor);
					iData.setAlpha(0, 0, 255 * opacity / FRAMES);
					image = new Image(display, iData);
					count++;
				}
				Display.getCurrent().timerExec(100, new Runnable() {
					public void run() {
						opacity = Math.min(FRAMES, opacity + 1);
						repaint();
					}
				});
			}
			Rectangle r = getBounds();
			if (image != null)
				graphics.drawImage(image, 0, 0, 1, 1, r.x, r.y, r.width,
						r.height);
			else
				super.paintFigure(graphics);
		}

		/**
		 * @see org.eclipse.draw2d.Figure#removeNotify()
		 */
		public void removeNotify() {
			if (image != null) {
				image.dispose();
				count--;
				image = null;
			}
		}
	}

	// Even offset indicates a vertical feedback line; odd, horizontal.
	void highlightGuide(Integer pos, Color color, int offset) {
		if (pos == null) {
			if (guide[offset] != null) {
				removeFeedback(guide[offset]);
				guide[offset] = null;
			}
			location[offset] = pos;
			return;
		}

		// pos is an integer relative to target's client area.
		// translate pos to absolute, and then make it relative to fig.
		int position = pos.intValue();
		PrecisionPoint loc = new PrecisionPoint(position, position);
		IFigure contentPane = ((GraphicalEditPart) getHost()).getContentPane();
		contentPane.translateToParent(loc);
		contentPane.translateToAbsolute(loc);

		if (location[offset] == null || !location[offset].equals(pos)) {
			location[offset] = pos;
			if (guide[offset] != null) {
				removeFeedback(guide[offset]);
				guide[offset] = null;
			}

			IFigure fig = new FadeIn(color);
			guide[offset] = fig;
			addFeedback(fig);
			fig.translateToRelative(loc);
			position = offset % 2 == 0 ? (int) Math.round(loc.preciseX())
					: (int) Math.round(loc.preciseY());
			Rectangle figBounds = getFeedbackLayer().getBounds().getCopy();
			if ((offset % 2) == 1) {
				figBounds.height = 1;
				figBounds.y = position;
			} else {
				figBounds.x = position;
				figBounds.width = 1;
			}
			fig.setBounds(figBounds);
		} else {
			// The feedback layer could have grown (if auto-scrolling), so
			// resize the fade-in
			// line.
			IFigure fig = guide[offset];
			Rectangle figBounds = fig.getBounds().getCopy();
			Rectangle feedbackBounds = getFeedbackLayer().getBounds();
			if ((offset % 2) == 1) {
				figBounds.x = feedbackBounds.x;
				figBounds.width = feedbackBounds.width;
			} else {
				figBounds.y = feedbackBounds.y;
				figBounds.height = feedbackBounds.height;
			}
			fig.setBounds(figBounds);
		}
	}

	/**
	 * @see org.eclipse.gef.EditPolicy#showTargetFeedback(org.eclipse.gef.Request)
	 */
	public void showTargetFeedback(Request req) {
		if (req.getType().equals(REQ_MOVE) || req.getType().equals(REQ_RESIZE)
				|| req.getType().equals(REQ_CLONE)
				|| req.getType().equals(REQ_ADD)
				|| req.getType().equals(REQ_CREATE)) {

			Integer value;
			value = (Integer) req.getExtendedData().get(
					SnapToGeometry.KEY_WEST_ANCHOR);
			highlightGuide(value, ColorConstants.blue, 0);

			value = (Integer) req.getExtendedData().get(
					SnapToGeometry.KEY_NORTH_ANCHOR);
			highlightGuide(value, ColorConstants.blue, 1);

			value = (Integer) req.getExtendedData().get(
					SnapToGeometry.KEY_EAST_ANCHOR);
			highlightGuide(value, ColorConstants.blue, 2);

			value = (Integer) req.getExtendedData().get(
					SnapToGeometry.KEY_SOUTH_ANCHOR);
			highlightGuide(value, ColorConstants.blue, 3);

			value = (Integer) req.getExtendedData().get(
					SnapToGuides.KEY_VERTICAL_GUIDE);
			highlightGuide(value, ColorConstants.red, 4);

			value = (Integer) req.getExtendedData().get(
					SnapToGuides.KEY_HORIZONTAL_GUIDE);
			highlightGuide(value, ColorConstants.red, 5);
		}
	}

}