/******************************************************************************
 * Copyright (c) 2006, Intalio Inc.
 * 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:
 *     Intalio Inc. - initial API and implementation
 *******************************************************************************/

/** 
 * Date          	Author             Changes 
 * 11 Aug 2006   	BIlchyshyn         Created 
 **/

package org.eclipse.stp.bpmn.tools;

import java.util.List;

import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PrecisionDimension;
import org.eclipse.draw2d.geometry.PrecisionPoint;
import org.eclipse.draw2d.geometry.PrecisionRectangle;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.RootEditPart;
import org.eclipse.gef.SnapToHelper;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.stp.bpmn.diagram.edit.parts.PoolEditPart;
import org.eclipse.stp.bpmn.diagram.edit.parts.PoolPoolCompartmentEditPart;
import org.eclipse.swt.SWT;

/**
 * TweakedResizeTracker
 * 
 * @author mpeleshchyshyn
 * @author <a href="http://www.intalio.com">&copy; Intalio, Inc.</a>
 */
public class PoolResizeTracker extends ActivityResizeTracker {

    /**
     * Constructs a resize tracker that resizes in the specified direction. The
     * direction is specified using {@link PositionConstants#NORTH},
     * {@link PositionConstants#NORTH_EAST}, etc.
     * 
     * @param owner
     *            of the resize handle which returned this tracker
     * @param direction
     *            the direction
     */
    public PoolResizeTracker(GraphicalEditPart owner, int direction) {
        super(owner, direction);
    }

    /**
     * @see org.eclipse.gef.tools.SimpleDragTracker#updateSourceRequest()
     */
    protected void updateSourceRequest() {
        ChangeBoundsRequest request = (ChangeBoundsRequest) getSourceRequest();
        Dimension d = getDragMoveDelta();

        Point location = new Point(getLocation());
        Point moveDelta = new Point(0, 0);
        Dimension resizeDelta = new Dimension(0, 0);
        int direction = getResizeDirection();
        List part = getOperationSet();
        List siblings = null;

        request.setConstrainedResize(false);
        request.setCenteredResize(getCurrentInput().isModKeyDown(SWT.MOD1));

        GraphicalEditPart owner = getOwner();
        PrecisionRectangle sourceRect = getSourceRectangle();
        SnapToHelper snapToHelper = getSnapToHelper();

        /*
         * When the length of a pool is modified, all pools, are modified to
         * have the same length.
         */
        if (hasPoolEditPart(part)
                && ((direction == PositionConstants.EAST)
                        || (direction == PositionConstants.WEST)
                        || (direction == PositionConstants.NORTH_EAST)
                        || (direction == PositionConstants.NORTH_WEST)
                        || (direction == PositionConstants.SOUTH_EAST) || (direction == PositionConstants.SOUTH_EAST))) {
            for (int k = 0; k < part.size(); k++) {
                if (part.get(k) instanceof PoolEditPart) {
                    siblings = ((PoolEditPart) part.get(k)).getParent()
                            .getChildren();
                }
            }

            for (int i = 0; i < siblings.size(); i++) {
                if (!(part.contains(siblings.get(i)))) {
                    part.add(siblings.get(i));
                }
            }
        }

        Dimension minSize = getMinPoolSize(part);
        Dimension siblingsMinSize = getMinPoolSize(siblings);
        Dimension figureSize = owner.getFigure().getSize();
        RootEditPart rootEditPart = owner.getRoot();
        if (rootEditPart instanceof ScalableFreeformRootEditPart) {
            double zoom = ((ScalableFreeformRootEditPart) rootEditPart)
                    .getZoomManager().getZoom();
            minSize.scale(zoom);
            siblingsMinSize.scale(zoom);
            figureSize.scale(zoom);
        }

        if (((direction & PositionConstants.NORTH) != 0) && (part.size() < 2)) {
            // prevents from children shapes clipping

            if (getCurrentInput().isControlKeyDown()) {
                if (minSize.height > (figureSize.height - 2 * d.height)) {
                    d.setSize(new Dimension(d.width,
                            (figureSize.height - minSize.height) / 2));
                }
                resizeDelta.height -= d.height;
            } else if (minSize.height > (figureSize.height - d.height)) {
                d.setSize(new Dimension(d.width, figureSize.height
                        - minSize.height));
            }
            moveDelta.y += d.height;
            resizeDelta.height -= d.height;
        }
        if (((direction & PositionConstants.SOUTH) != 0) && (part.size() < 2)) {
            if (getCurrentInput().isControlKeyDown()) {
                if (minSize.height > (figureSize.height + 2 * d.height)) {
                    d.setSize(new Dimension(d.width,
                            -(figureSize.height - minSize.height) / 2));
                }
                moveDelta.y -= d.height;
                resizeDelta.height += d.height;
            } else if (minSize.height > (figureSize.height + d.height)) {
                d.setSize(new Dimension(d.width,
                        -(figureSize.height - minSize.height)));
            }
            resizeDelta.height += d.height;
        }
        if ((direction & PositionConstants.WEST) != 0) {
            if (siblingsMinSize.width > (figureSize.width - d.width)) {
                d.setSize(new Dimension(figureSize.width
                        - siblingsMinSize.width, d.height));
            }
            moveDelta.x += d.width;
            resizeDelta.width -= d.width;
        }
        if ((direction & PositionConstants.EAST) != 0) {
            if (siblingsMinSize.width > (figureSize.width + d.width)) {
                d.setSize(new Dimension(
                        -(figureSize.width - siblingsMinSize.width), d.height));
            }
            resizeDelta.width += d.width;
        }

        request.setMoveDelta(moveDelta);
        request.setSizeDelta(resizeDelta);
        request.setLocation(location);
        request.setEditParts(part);
        request.getExtendedData().clear();

        if (!getCurrentInput().isAltKeyDown() && snapToHelper != null) {
            PrecisionRectangle rect = sourceRect.getPreciseCopy();
            rect.translate(moveDelta);
            rect.resize(resizeDelta);
            PrecisionRectangle result = new PrecisionRectangle();

            snapToHelper.snapRectangle(request, request.getResizeDirection(),
                    rect, result);
            if (request.isCenteredResize()) {
                if (result.preciseX != 0.0)
                    result.preciseWidth += -result.preciseX;
                else if (result.preciseWidth != 0.0) {
                    result.preciseX = -result.preciseWidth;
                    result.preciseWidth *= 2.0;
                }

                if (result.preciseY != 0.0)
                    result.preciseHeight += -result.preciseY;
                else if (result.preciseHeight != 0.0) {
                    result.preciseY = -result.preciseHeight;
                    result.preciseHeight *= 2.0;
                }
                result.updateInts();
            }

            PrecisionPoint preciseMove = new PrecisionPoint(result.x
                    + moveDelta.x, result.y + moveDelta.y);

            PrecisionDimension preciseResize = new PrecisionDimension(
                    result.width + resizeDelta.width, result.height
                            + resizeDelta.height);

            request.setMoveDelta(preciseMove);
            request.setSizeDelta(preciseResize);
        }
    }

    private boolean hasPoolEditPart(List parts) {
        for (int i = 0; i < parts.size(); i++) {
            if (parts.get(i) instanceof PoolEditPart)
                return true;
        }

        return false;
    }

    private Dimension getMinPoolSize(List parts) {
        Dimension result = new Dimension(150, 50);

        if (parts == null) {
            return result;
        }

        for (int i = 0; i < parts.size(); i++) {
            if (parts.get(i) instanceof PoolEditPart) {
                PoolEditPart pool = (PoolEditPart) parts.get(i);
                IGraphicalEditPart poolCompartment = pool
                        .getChildBySemanticHint(Integer
                                .toString(PoolPoolCompartmentEditPart.VISUAL_ID));
                int nameWidth = poolCompartment.getFigure().getBounds().x + 1;
                Dimension rect = pool.getPoolContainRectangle().getSize();
                rect.expand(PoolPoolCompartmentEditPart.INSETS.getWidth(),
                        PoolPoolCompartmentEditPart.INSETS.getHeight() + 2);

                if (rect.height > result.height) {
                    result.setSize(new Dimension(result.width, rect.height));
                }
                if (rect.width + nameWidth > result.width) {
                    result.setSize(new Dimension(rect.width + nameWidth,
                            result.height));
                }
            }
        }

        return result;
    }
}
