/*******************************************************************************
 * Copyright (c) 2004, 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.draw2d.graph;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;

/**
 * A vertex representation for the ShortestPathRouting. Vertices are either one
 * of four corners on an <code>Obstacle</code>(Rectangle), or one of the two end
 * points of a <code>Path</code>.
 * 
 * This class is not intended to be subclassed.
 * 
 * @author Whitney Sorenson
 * @since 3.0
 */
class Vertex extends Point {

	// constants for the vertex type
	static final int NOT_SET = 0;
	static final int INNIE = 1;
	static final int OUTIE = 2;

	// for shortest path
	List neighbors;
	boolean isPermanent = false;
	Vertex label;
	double cost = 0;

	// for routing
	int nearestObstacle = 0;
	double offset = 0;
	int type = NOT_SET;
	int count = 0;
	int totalCount = 0;
	Obstacle obs;
	List paths;
	boolean nearestObstacleChecked = false;
	Map cachedCosines;
	int positionOnObstacle = -1;

	private int origX, origY;

	/**
	 * Creates a new Vertex with the given x, y position and on the given
	 * obstacle.
	 * 
	 * @param x
	 *            x point
	 * @param y
	 *            y point
	 * @param obs
	 *            obstacle - can be null
	 */
	Vertex(int x, int y, Obstacle obs) {
		super(x, y);
		origX = x;
		origY = y;
		this.obs = obs;
	}

	/**
	 * Creates a new Vertex with the given point position and on the given
	 * obstacle.
	 * 
	 * @param p
	 *            the point
	 * @param obs
	 *            obstacle - can be null
	 */
	Vertex(Point p, Obstacle obs) {
		this(p.x, p.y, obs);
	}

	/**
	 * Adds a path to this vertex, calculates angle between two segments and
	 * caches it.
	 * 
	 * @param path
	 *            the path
	 * @param start
	 *            the segment to this vertex
	 * @param end
	 *            the segment away from this vertex
	 */
	void addPath(Path path, Segment start, Segment end) {
		if (paths == null) {
			paths = new ArrayList();
			cachedCosines = new HashMap();
		}
		if (!paths.contains(path))
			paths.add(path);
		cachedCosines.put(path, new Double(start.cosine(end)));
	}

	/**
	 * Creates a point that represents this vertex offset by the given amount
	 * times the offset.
	 * 
	 * @param modifier
	 *            the offset
	 * @return a Point that has been bent around this vertex
	 */
	Point bend(int modifier) {
		Point point = new Point(x, y);
		if ((positionOnObstacle & PositionConstants.NORTH) > 0)
			point.y -= modifier * offset;
		else
			point.y += modifier * offset;
		if ((positionOnObstacle & PositionConstants.EAST) > 0)
			point.x += modifier * offset;
		else
			point.x -= modifier * offset;
		return point;
	}

	/**
	 * Resets all fields on this Vertex.
	 */
	void fullReset() {
		totalCount = 0;
		type = NOT_SET;
		count = 0;
		cost = 0;
		offset = getSpacing();
		nearestObstacle = 0;
		label = null;
		nearestObstacleChecked = false;
		isPermanent = false;
		if (neighbors != null)
			neighbors.clear();
		if (cachedCosines != null)
			cachedCosines.clear();
		if (paths != null)
			paths.clear();
	}

	/**
	 * Returns a Rectangle that represents the region around this vertex that
	 * paths will be traveling in.
	 * 
	 * @param extraOffset
	 *            a buffer to add to the region.
	 * @return the rectangle
	 */
	Rectangle getDeformedRectangle(int extraOffset) {
		Rectangle rect = new Rectangle(0, 0, 0, 0);

		if ((positionOnObstacle & PositionConstants.NORTH) > 0) {
			rect.y = y - extraOffset;
			rect.height = origY - y + extraOffset;
		} else {
			rect.y = origY;
			rect.height = y - origY + extraOffset;
		}
		if ((positionOnObstacle & PositionConstants.EAST) > 0) {
			rect.x = origX;
			rect.width = x - origX + extraOffset;
		} else {
			rect.x = x - extraOffset;
			rect.width = origX - x + extraOffset;
		}

		return rect;
	}

	private int getSpacing() {
		if (obs == null)
			return 0;
		return obs.getSpacing();
	}

	/**
	 * Grows this vertex by its offset to its maximum size.
	 */
	void grow() {
		int modifier;

		if (nearestObstacle == 0)
			modifier = totalCount * getSpacing();
		else
			modifier = (nearestObstacle / 2) - 1;

		if ((positionOnObstacle & PositionConstants.NORTH) > 0)
			y -= modifier;
		else
			y += modifier;
		if ((positionOnObstacle & PositionConstants.EAST) > 0)
			x += modifier;
		else
			x -= modifier;
	}

	/**
	 * Shrinks this vertex to its original size.
	 */
	void shrink() {
		x = origX;
		y = origY;
	}

	/**
	 * Updates the offset of this vertex based on its shortest distance.
	 */
	void updateOffset() {
		if (nearestObstacle != 0)
			offset = ((nearestObstacle / 2) - 1) / totalCount;
	}

	/**
	 * @see org.eclipse.draw2d.geometry.Point#toString()
	 */
	public String toString() {
		return "V(" + origX + ", " + origY + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
	}

}
