/*******************************************************************************
 * Copyright (c) 2005 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.bpel.ui.editparts.policies;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.bpel.common.ui.figures.InsetResizeHandle;
import org.eclipse.bpel.ui.BPELUIPlugin;
import org.eclipse.bpel.ui.IBPELUIConstants;
import org.eclipse.bpel.ui.util.BPELDragEditPartsTracker;
import org.eclipse.draw2d.Cursors;
import org.eclipse.draw2d.FigureUtilities;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.RectangleFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Handle;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editpolicies.NonResizableEditPolicy;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.tools.ResizeTracker;
import org.eclipse.jface.resource.ColorRegistry;


/**
 * Add handles to the selected edit part. We use this instead of
 * NonResizableEditPolicy because we don't want a focus rectangle.
 * 
 * TODO: can this policy be installed higher up in the editpart hierarchy?
 * (search refs to see what I mean)
 */
public class BPELSelectionEditPolicy extends NonResizableEditPolicy {
	
	protected boolean fResizable;
	protected boolean fMovable;
	
	/**
	 * Brand new BPELSelectionEditPolicy ... 
	 * @param resizable
	 * @param movable
	 */
	
	public BPELSelectionEditPolicy(boolean resizable, boolean movable) {
		this.fResizable = resizable;
		this.fMovable = movable;
	}
	
	@Override
	protected List<Handle> createSelectionHandles() {
								
		if (fResizable) {
			return createResizableHandles ( (GraphicalEditPart)getHost() );			
		} 
		return createCornerHandles ( (GraphicalEditPart)getHost() );
	}
	
	/**
	 * Dispatches erase requests to more specific methods.
	 * @see org.eclipse.gef.EditPolicy#eraseSourceFeedback(org.eclipse.gef.Request)
	 */
	
	@Override
	public void eraseSourceFeedback(Request request) {
		if (fResizable && REQ_RESIZE.equals(request.getType()))
			eraseChangeBoundsFeedback((ChangeBoundsRequest)request);
		else
			super.eraseSourceFeedback(request);
	}

	/**
	 * @see org.eclipse.gef.EditPolicy#getCommand(org.eclipse.gef.Request)
	 */
	@Override
	public Command getCommand(Request request) {
		if (fResizable && REQ_RESIZE.equals(request.getType()))
			return getResizeCommand((ChangeBoundsRequest)request);

		return super.getCommand(request);
	}

	/**
	 * Returns the command contribution for the given resize request. By default, the request
	 * is redispatched to the host's parent as a {@link
	 * org.eclipse.gef.RequestConstants#REQ_RESIZE_CHILDREN}.  The parent's editpolicies
	 * determine how to perform the resize based on the layout manager in use.
	 * @param request the resize request
	 * @return the command contribution obtained from the parent
	 */
	protected Command getResizeCommand(ChangeBoundsRequest request) {
		ChangeBoundsRequest req = new ChangeBoundsRequest(REQ_RESIZE_CHILDREN);
		req.setEditParts(getHost());
	
		req.setMoveDelta(request.getMoveDelta());
		req.setSizeDelta(request.getSizeDelta());
		req.setLocation(request.getLocation());
		req.setResizeDirection(request.getResizeDirection());
		return getHost().getParent().getCommand(req);
	}

	/**
	 * @see org.eclipse.gef.EditPolicy#showSourceFeedback(org.eclipse.gef.Request)
	 */
	@Override
	public void showSourceFeedback(Request request) {
		if (fResizable && REQ_RESIZE.equals(request.getType()))
			showChangeBoundsFeedback((ChangeBoundsRequest)request);
		else
			super.showSourceFeedback(request);
	}

	/**
	 * @see org.eclipse.gef.EditPolicy#understandsRequest(org.eclipse.gef.Request)
	 */
	@Override
	public boolean understandsRequest(Request request) {
		if (fResizable && REQ_RESIZE.equals(request.getType()))
			return true;
// TODO: This code is more correct than the above code. Change it in the future.
//		if (REQ_RESIZE.equals(request.getType())) return resizable;
		if (REQ_MOVE.equals(request.getType())) return fMovable;
		return super.understandsRequest(request);
	}
	
	
	List<Handle> createCornerHandles(GraphicalEditPart part ) {
		
		List<Handle> handleList = new ArrayList<Handle>(4);
		
		handleList.add(createHandle(part, PositionConstants.SOUTH_EAST, getSouthInset(), getEastInset()));
		handleList.add(createHandle(part, PositionConstants.SOUTH_WEST, getSouthInset(), getWestInset()));
		handleList.add(createHandle(part, PositionConstants.NORTH_WEST, getNorthInset(), getWestInset()));
		handleList.add(createHandle(part, PositionConstants.NORTH_EAST, getNorthInset(), getEastInset()));
		
		return handleList ;
	}
	
	Handle createHandle(GraphicalEditPart owner, int direction, int verticalInset, int horizontalInset) {
		InsetResizeHandle handle = new InsetResizeHandle(owner, direction, verticalInset, horizontalInset);		
		handle.setCursor(Cursors.SIZEALL);
		handle.setDragTracker(new BPELDragEditPartsTracker(owner));
		return handle;
	}
	
	List<Handle> createResizableHandles ( GraphicalEditPart part ) {
		
		List<Handle> handlesList = new ArrayList<Handle>(8);
		
		handlesList.add(createResizableHandle(part, PositionConstants.EAST, 0, getEastInset()));
		handlesList.add(createResizableHandle(part, PositionConstants.SOUTH_EAST, getSouthInset(), getEastInset()));
		handlesList.add(createResizableHandle(part, PositionConstants.SOUTH, getSouthInset(), 0));
		handlesList.add(createResizableHandle(part, PositionConstants.SOUTH_WEST, getSouthInset(), getWestInset()));
		handlesList.add(createResizableHandle(part, PositionConstants.WEST, 0, getWestInset()));
		handlesList.add(createResizableHandle(part, PositionConstants.NORTH_WEST, getNorthInset(), getWestInset()));
		handlesList.add(createResizableHandle(part, PositionConstants.NORTH, getNorthInset(), 0));
		handlesList.add(createResizableHandle(part, PositionConstants.NORTH_EAST, getNorthInset(), getEastInset()));
		
		return handlesList;
	}
	
	Handle createResizableHandle(GraphicalEditPart owner, int direction, int verticalInset, int horizontalInset) {
		// ResizeHandle handle = new ResizeHandle(owner, direction);
		InsetResizeHandle handle = new InsetResizeHandle(owner, direction, verticalInset, horizontalInset);
		handle.setDragTracker(new ResizeTracker(owner, direction));
		return handle;
	}
	
	@Override
	protected void eraseChangeBoundsFeedback(ChangeBoundsRequest request) {
		if (REQ_MOVE.equals(request.getType()) && !fMovable)
			return;
		super.eraseChangeBoundsFeedback(request);
	}

	
	@Override
	protected void showChangeBoundsFeedback(ChangeBoundsRequest request) {
		if (REQ_MOVE.equals(request.getType()) && !fMovable)
			return;
		
		// Take the super implementation to account for the drawer width on the ghost figure.
		IFigure p = getDragSourceFeedbackFigure();
		
		Rectangle r = getHostFigure().getBounds().getCopy();
		getHostFigure().translateToAbsolute(r);
		r.translate(request.getMoveDelta());
		Dimension resize = request.getSizeDelta();
		r.width += resize.width;
		r.height += resize.height;

		// Adjust for drawer width
		r.x += getDrawerInset();
		r.width -= getDrawerInset() * 2;
		p.translateToRelative(r);
		p.setBounds(r);
	}

	/**
	 * Override method from superclass to adjust for drawer width on ghost figure
	 */
	@Override
	protected IFigure createDragSourceFeedbackFigure() {
		// Use a ghost rectangle for feedback
		ColorRegistry registry = BPELUIPlugin.INSTANCE.getColorRegistry();
		RectangleFigure r = new RectangleFigure();
		FigureUtilities.makeGhostShape(r);
		r.setLineStyle(Graphics.LINE_DASHDOT);
		r.setForegroundColor(registry.get(IBPELUIConstants.COLOR_BLACK));
		Rectangle bounds = ((GraphicalEditPart)getHost()).getFigure().getBounds().getCopy();
		// Adjust for drawer width if necessary
		bounds.x += getDrawerInset();
		bounds.width -= getDrawerInset() * 2;
		r.setBounds(bounds);
		addFeedback(r);
		return r;
	}
	
	// Subclasses may override these methods to locate feedback and handles.
	protected int getDrawerInset() {
		return 0;
	}
	protected int getNorthInset() {
		return 0;
	}
	protected int getSouthInset() {
		return 0;
	}
	protected int getEastInset() {
		return 0;
	}
	protected int getWestInset() {
		return 0;
	}
}
