/*******************************************************************************
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC,
 * Canada. 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: The Chisel Group, University of Victoria
 *******************************************************************************/
package org.eclipse.zest.core.widgets.internal;

import org.eclipse.draw2d.AbstractLocator;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;

/**
 * A locator that finds the middle of a connection based on the bendpoints.
 * @author Del Myers
 *
 */
//@tag zest(bug(154391-ArcEnds(fix))) : use this locator to ensure that labels will be in the middle of connections.
//@tag zest.bug.160368-ConnectionAlign : replaces MidBenpointLocator in order to have finer control over alignment.
public class AligningBendpointLocator extends AbstractLocator {
	/**
	 * "Vertical" alignment contstant. Figures should be placed in the middle
	 * of the line.
	 */
	public static final int MIDDLE = 0;
	/**
	 * "Vertical" alignment constant. Figures should be placed above the line.
	 */
	public static final int ABOVE = 1;
	/**
	 * "Vertical" alignment constant. Figures should be placed below the line.
	 */
	public static final int BELOW = 2;

	/**
	 * "Horizontal" alignment constant. Figures should be placed in the center
	 * of the points on the line.
	 */
	public static final int CENTER = 0;

	/**
	 * "Horizontal" alignment constant. Figures should be placed at the beginning
	 * of the line. Figures will be anchored so that they have one end at the
	 * beginning of the connection, not so that they are centered at the start
	 * point. Which end of the figure is placed at that point will depend
	 * on the direction of the first two points.
	 */
	public static final int BEGINNING = 1;

	/**
	 * "Horizontal" alignment constant. Figures should be placed at the end of
	 * the line. Figures will be anchored so that they have one end at the
	 * beginning of the connection, not so that they are centered at the end
	 * point. Which end of the figure is placed at that point will depend
	 * on the direction of the last two points.
	 */
	public static final int END = 2;

	/**
	 * "Horizontal" alignment constant. Figures should be centered between the
	 * first two points on the connection. For connections with only two points, 
	 * this will behave the same as CENTER.
	 */
	public static final int CENTER_BEGINNING = 3;

	/**
	 * "Horizontal" alignment constant. Figures should be centered between the
	 * last two points on the connection. For connections with only two points,
	 * this will behave the same as CENTER.
	 */
	public static final int CENTER_END = 4;
	private int horizontal;
	private int vertical;
	private Connection connection;

	/**
	 * @param connection
	 */
	public AligningBendpointLocator(Connection connection) {
		this(connection, CENTER, MIDDLE);
	}

	public AligningBendpointLocator(Connection connection, int horizontal, int vertical) {
		this.connection = connection;
		this.horizontal = horizontal;
		this.vertical = vertical;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.draw2d.ConnectionLocator#getReferencePoint()
	 */
	protected Point getReferencePoint() {
		PointList points = getConnection().getPoints();
		Point p = points.getMidpoint().getCopy();
		PointList tempPoints = new PointList();
		switch (horizontal) {
		case BEGINNING:
			p = points.getFirstPoint().getCopy();
			break;
		case END:
			p = points.getLastPoint().getCopy();
			break;
		case CENTER_BEGINNING:
			tempPoints.addPoint(points.getFirstPoint().getCopy());
			tempPoints.addPoint(points.getPoint(1).getCopy());
			p = tempPoints.getMidpoint().getCopy();
			break;
		case CENTER_END:
			tempPoints = new PointList();
			int s = points.size();
			tempPoints.addPoint(points.getLastPoint().getCopy());
			tempPoints.addPoint(points.getPoint(s - 2).getCopy());
			p = tempPoints.getMidpoint().getCopy();
		case CENTER:
		default:
			p = points.getMidpoint().getCopy();
		}
		return p;
	}

	/**
	 * Recalculates the position of the figure and returns the updated bounds.
	 * @param target The figure to relocate
	 */
	public void relocate(IFigure target) {
		Dimension prefSize = target.getPreferredSize();
		Point center = getReferencePoint();
		calculatePosition();
		//@tag zest(bug(GEFProblem)) : there seems to be a bug in GEF that if the following is done, then labels get printed in the wrong location
		//target.translateToRelative(center);
		target.setBounds(getNewBounds(prefSize, center));
	}

	/**
	 * Translates the center point depending on the horizontal and veritical
	 * alignments based on the given bounds.
	 * @param center
	 */
	private void calculatePosition() {
		PointList points = getConnection().getPoints();
		int position = 0;
		switch (horizontal) {
		case BEGINNING:
			Point first = points.getFirstPoint();
			Point next = points.getPoint(1);
			if (first.x <= next.x)
				position |= PositionConstants.EAST;
			else
				position |= PositionConstants.WEST;
			break;
		case END:
			Point last = points.getLastPoint();
			int s = points.size();
			Point before = points.getPoint(s - 1);
			if (last.x <= before.x)
				position |= PositionConstants.EAST;
			else
				position |= PositionConstants.WEST;
			break;
		}
		if (position == 0)
			position = PositionConstants.CENTER;
		switch (vertical) {
		case ABOVE:
			position |= PositionConstants.NORTH;
			break;
		case BELOW:
			position |= PositionConstants.SOUTH;
			break;
		case MIDDLE:
			position |= PositionConstants.MIDDLE;
		}
		setRelativePosition(position);

	}

	/**
	 * @return the horizontal alignment.
	 */
	public int getHorizontalAlignment() {
		return horizontal;
	}

	/**
	 * @param horizontal the horizontal alignment to set. One of CENTER,
	 * BEGINNING, END, CENTER_BEGINNING, or CENTER_END.
	 */
	public void setHorizontalAlignment(int horizontal) {
		this.horizontal = horizontal;
	}

	/**
	 * @param vertical the vertical alignment to set. One of ABOVE, MIDDLE, or
	 * BELOW.
	 */
	public void setVerticalAlginment(int vertical) {
		this.vertical = vertical;
	}

	/**
	 * @return the vertical alginment.
	 */
	public int getVerticalAlignment() {
		return vertical;
	}

	/**
	 * @return the connection
	 */
	public Connection getConnection() {
		return connection;
	}

}
