/******************************************************************************
 * 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
 * Nov 24, 2006     hmalphettes         Created
 **/
package org.eclipse.stp.bpmn.figures.connectionanchors.impl;

import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.stp.bpmn.diagram.edit.parts.MessagingEdgeEditPart;
import org.eclipse.stp.bpmn.diagram.edit.parts.SequenceEdgeEditPart;
import org.eclipse.stp.bpmn.figures.connectionanchors.IModelAwareAnchor;
import org.eclipse.stp.bpmn.figures.connectionanchors.IModelAwareAnchorSupport;

/**
 * <p>
 * Computes the anchor's position according to its usage 
 * and the preferences of the diagram (default slideable, position constrained
 * according to the type of connection in the domain model).
 * </p>
 * <p>
 * If the anchor appears to not have that information set, it will behave like 
 * a default slideable anchor.
 * </p>
 * <p>
 * There was another alternative:
 * have the edge edit part update the anchor itself.
 * </p>
 * 
 * @author hmalphettes
 * @author <a href="http://www.intalio.com">&copy; Intalio, Inc.</a>
 */
public class BpmnAwareAnchorSupport implements IModelAwareAnchorSupport {
    
    /**
     * TODO: make it a service if it works well?
     */
    public static final BpmnAwareAnchorSupport INSTANCE = new BpmnAwareAnchorSupport();
    

    /**
     * Method called by the anchor to compute its location.
     * 
     * @param reference The reference point.
     * @return The location to use.
     */
    public Point getLocation(IModelAwareAnchor anchor, Point reference) {
        if (anchor.getConnectionType() == null ||
                anchor.getAnchorPositionningStyle() ==
                    IModelAwareAnchor.POSITIONNING_SLIDEABLE) {
            return returnDefaultLocation(anchor, reference);
        }
        if (anchor.getConnectionType().startsWith(
                String.valueOf(SequenceEdgeEditPart.VISUAL_ID))) {
            //it is a sequence edge
            if (anchor.isSourceAnchor() == IModelAwareAnchor.UNKNOWN_ANCHOR) {
                //it must be on the left-side of the owner's figure.
                //we don't know.
                return returnDefaultLocation(anchor, reference);
            }
            Rectangle thisBox = anchor.getOwner().getBounds().getCopy();
            anchor.getOwner().translateToAbsolute(thisBox);
            Point thisCenter = thisBox.getCenter();
            Point res = new Point();
            if (!anchor.getConnectionType().equals(
                    String.valueOf(SequenceEdgeEditPart.VISUAL_ID))) {
                //this is for an event handler
                //shape on the border of a sub-process.
                res.x = thisCenter.x;
                res.y = thisCenter.y + thisBox.height/2;
            } else {
                if (anchor.isSourceAnchor() == IModelAwareAnchor.SOURCE_ANCHOR) {
                    //it must be on the right-side of the owner's figure.
                    res.x = thisCenter.x + thisBox.width/2;
                } else {//tis target anchor
                    //it must be on the left-side of the owner's figure.
                    res.x = thisCenter.x - thisBox.width/2;
                }
                res.y = thisCenter.y;
            }
            return res;
        }
        if (anchor.getConnectionType().startsWith(
                String.valueOf(MessagingEdgeEditPart.VISUAL_ID))) {
            //it is a messaging edge
            //it is either on the top, either on the bottom depending on
            //where is the other-side of the connection
            if (anchor.getOwner() == null ||
                    anchor.getConnectionOwner() == null ||
                    anchor.isSourceAnchor() == IModelAwareAnchor.UNKNOWN_ANCHOR) {
                return returnDefaultLocation(anchor, reference);
            }
            Connection conn = anchor.getConnectionOwner();
            
            Rectangle thisBox = anchor.getOwner().getBounds().getCopy();
            anchor.getOwner().translateToAbsolute(thisBox);
            Point thisCenter = thisBox.getCenter();
            Point otherCenter = null;
            if (anchor.isSourceAnchor() == IModelAwareAnchor.SOURCE_ANCHOR) {
                if (conn.getTargetAnchor().getOwner() == null) {
                    //this is usually the case for an XYAnchor
                    otherCenter = conn.getTargetAnchor().getReferencePoint();
                } else {
                    otherCenter = conn.getTargetAnchor().getOwner().getBounds().getCenter();
                    conn.getTargetAnchor().getOwner().translateToAbsolute(otherCenter);
                }
            } else {
                if (conn.getSourceAnchor().getOwner() == null) {
                    otherCenter = conn.getSourceAnchor().getReferencePoint();
                } else {
                    otherCenter = conn.getSourceAnchor().getOwner().getBounds().getCenter();
                    conn.getSourceAnchor().getOwner().translateToAbsolute(otherCenter);
                }
            }
//            System.err.println("isSource=" + (anchor.isSourceAnchor() == IModelAwareAnchor.SOURCE_ANCHOR) +
//                    " thisCenter=" + thisCenter + " otherCenter=" + otherCenter);
            Point res = new Point();
            if (thisCenter.y > otherCenter.y) {
                //at the top.
                res.y = thisCenter.y - thisBox.height/2;
            } else {
                //at the bottom.
                res.y = thisCenter.y + thisBox.height/2;
            }
            if (anchor.getCount() > 0 && anchor.getOrderNumber() != -1) {
                int width = thisBox.width;
                res.x = thisCenter.x - width / 2 + 
                    (width / (anchor.getCount() + 1))
                            * (anchor.getOrderNumber() + 1);
            } else {
                res.x = thisCenter.x;
            }
            return res;
        }
        if (reference == null) {
            Point thisCenter = anchor.getOwner().getBounds().getCenter();
            anchor.getOwner().translateToAbsolute(thisCenter);
            return thisCenter;
        }
        return anchor.getDefaultLocation(reference);
    }
    
    /**
     * Helper method to return the default location.
     * @param anchor The anchor
     * @param reference The reference point, might be null.
     * @return
     */
    private Point returnDefaultLocation(IModelAwareAnchor anchor, Point reference) {
        if (reference == null && anchor.getOwner() != null) {
            Point thisCenter = anchor.getOwner().getBounds().getCenter();
            anchor.getOwner().translateToAbsolute(thisCenter);
            return thisCenter;
        }
        return anchor.getDefaultLocation(reference);
    }

    
}
