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

import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;

/**
 * A directed Edge joining a source and target Node.  Edges indicate the dependencies
 * between nodes.  An Edge provides the information needed to perform a graph layout, and
 * it stores the result of the layout in its various field. Therefore, it functions
 * both as input and output.  The input data consists of:
 * <UL>
 *   <LI>{@link #source} - the source Node
 *   <LI>{@link #target} - the target Node
 *   <LI>{@link #delta} - the minimum number of rows the edge should span
 *   <LI>{@link #weight} - a hint indicating this edge's importance
 *   <LI>{@link #width} - the edge's width
 *   <LI>{@link #padding} - the amount of space to leave on either side of the edge
 *   <LI>[{@link #offsetSource}] - the edge's attachment point at the source node
 *   <LI>[{@link #offsetTarget}] - the edge's attachment point at the target node
 * </UL>
 * <P>The output of a layout consists of bending longer edges, and potentially inverting
 * edges to remove cycles in the graph.  The output consists of:
 * <UL>
 *   <LI>{@link #vNodes} - the virtual nodes (if any) which make up the bendpoints
 *   <LI>{@link #isFeedback} - <code>true</code> if the edge points backwards
 * </UL>
 * 
 * @author hudsonr
 * @since 2.1.2
 */
public class Edge {

int cut;

/**
 * An arbitrary data field for use by clients.
 */
public Object data;

/**
 * The minimum rank separation between the source and target nodes. The default value is
 * 1.
 * @deprecated use accessors instead
 */
public int delta = 1;

/**
 * The ending point.
 * @deprecated use {@link #getPoints()}
 */
public Point end;

boolean flag;

/**
 * @deprecated INTERNAL field, use accessor method
 * Indicates an edge was inverted during the layout
 */
public boolean isFeedback = false;

/**
 * The edge's attachment point at the <em>source</em> node. The default value is -1, which
 * indicates that the edge should use the node's default {@link Node#getOffsetOutgoing()
 * outgoing} attachment point.
 * @deprecated use accessors instead
 */
public int offsetSource = -1;

/**
 * The edge's attachment point at the <em>target</em> node. The default value is -1, which
 * indicates that the edge should use the node's default {@link Node#getOffsetIncoming()
 * incoming} attachment point.
 * @deprecated use accessors instead
 */
public int offsetTarget = -1;

/**
 * The minimum amount of space to leave on both the left and right sides of the edge.
 * @deprecated use accessors instead
 */
public int padding = 10;

private PointList points;

/**
 * The source Node.
 */
public Node source;
/**
 * The starting point.
 * @deprecated use {@link #getPoints()}
 */
public Point start;

/**
 * The target Node.
 */
public Node target;

boolean tree;

/**
 * The virtual nodes used to bend edges which go across one or more ranks.  Each virtual
 * node is just a regular node which occupies some small amount of space on a row. It's
 * width is equivalent to the edge's width.  Clients can use each virtual node's location
 * (x, y, width, and height) as the way to position an edge which spans multiple rows.
 */
public NodeList vNodes;
/**
 * A hint indicating how straight and short the edge should be relative to
 * other edges in the graph. The default value is <code>1</code>.
 */
public int weight = 1;

/**
 * @deprecated use accessors instead
 */
public int width = 1;

/**
 * Constructs a new edge with the given source and target nodes.  All other fields will
 * have their default values.
 * @param source the source Node
 * @param target the target Node
 */
public Edge(Node source, Node target) {
	this(null, source, target);
}

/**
 * Constructs a new edge with the given source, target, delta, and weight.
 * @param source the source Node
 * @param target the target Node
 * @param delta the minimum edge span
 * @param weight the weight hint
 */
public Edge(Node source, Node target, int delta, int weight) {
	this(source, target);
	this.delta = delta;
	this.weight = weight;
}

/**
 * Constructs a new edge with the given data object, source, and target node.
 * @param data an arbitrary data object
 * @param source the source node
 * @param target the target node
 */
public Edge(Object data, Node source, Node target) {
	this.data = data;
	this.source = source;
	this.target = target;
	source.outgoing.add(this);
	target.incoming.add(this);
}

/**
 * Returns the delta value. The delta is the minimum rank separation for the edge's source
 * and target nodes.
 * @return the delta.
 * @since 3.2
 */
public int getDelta() {
	return delta;
}

/**
 * For internal use only. Returns the index of the {@link Node} (or {@link VirtualNode})
 * on this edge at the given rank.  If this edge doesn't have a node at the given rank, -1
 * is returned.
 * @param rank the rank
 * @return the edges index at the given rank
 */
int getIndexForRank(int rank) {
	if (source.rank == rank)
		return source.index;
	if (target.rank == rank)
		return target.index;
	if (vNodes != null)
		return vNodes.getNode(rank - source.rank - 1).index;
	return -1;
}

/**
 * For internal use only. Returns the target node's row minus the source node's row.
 * @return the distance from the source to target ranks
 */
public int getLength() {
	return (target.rank - source.rank);
}

public int getPadding() {
	return padding;
}

/**
 * Returns the path connecting the edge's source and target.
 * @return a point list
 * @since 3.2
 */
public PointList getPoints() {
	return points;
}

int getSlack() {
	return (target.rank - source.rank) - delta;
}

/**
 * Returns the effective source offset for this edge.  The effective source offset is
 * either the {@link #offsetSource} field, or the source node's default outgoing offset if
 * that field's value is -1.
 * @return the source offset
 */
public int getSourceOffset() {
	if (offsetSource != -1)
		return offsetSource;
	return source.getOffsetOutgoing();
}

/**
 * Returns the effective target offset for this edge.  The effective target offset is
 * either the {@link #offsetTarget} field, or the target node's default incoming offset if
 * that field's value is -1.
 * @return the target offset
 */
public int getTargetOffset() {
	if (offsetTarget != -1)
		return offsetTarget;
	return target.getOffsetIncoming();
}

public int getWidth() {
	return width;
}

/**
 * Swaps the source and target nodes. If any positional data has been calculated, it is
 * inverted as well to reflect the new direction.
 * @since 2.1.2
 */
public void invert() {
	source.outgoing.remove(this);
	target.incoming.remove(this);
	
	Node oldTarget = target;
	target = source;
	source = oldTarget;

	int temp = offsetSource;
	offsetSource = offsetTarget;
	offsetTarget = temp;
	
	target.incoming.add(this);
	source.outgoing.add(this);
	
	if (points != null)
		points.reverse();
	
	if (vNodes != null) {
		NodeList newVNodes = new NodeList();
		for (int j = vNodes.size() - 1; j >= 0; j--) {
			newVNodes.add(vNodes.getNode(j));
		}
		vNodes = newVNodes;
	}

	if (start != null) {
		Point pt = start;
		start = end;
		end = pt;
	}
}

/**
 * Returns <code>true</code> if the edge was a feedback edge. The layout algorithm may
 * invert one or more edges to remove all cycles from the input. The set of edges that are
 * inverted are referred to as the "feedback" set.
 * @return <code>true</code> if the edge is feedback
 * @since 3.2
 */
public boolean isFeedback() {
	return isFeedback;
}

/**
 * For internal use only.  Returns the node opposite the given node on this edge.
 * @param end one end
 * @return the other end
 */
public Node opposite(Node end) {
	if (source == end)
		return target;
	return source;
}

/**
 * Sets the delta value.
 * @param delta the new delta value
 * @since 3.2
 */
public void setDelta(int delta) {
	this.delta = delta;
}

/**
 * Sets the padding for this edge.
 * @param padding the padding
 * @since 3.2
 */
public void setPadding(int padding) {
	this.padding = padding;
}

void setPoints(PointList points) {
	this.points = points;
	start = points.getFirstPoint();
	end = points.getLastPoint();
}

/**
 * Sets the source node and adds this edge to the new source's outgoing edges. If the
 * source node is previously set, removes this edge from the old source's outgoing edges.
 * @param node the new source
 * @since 3.2
 */
public void setSource(Node node) {
	if (source == node)
		return;
	if (source != null)
		source.outgoing.remove(this);
	source = node;
	if (source != null)
		source.outgoing.add(this);
}

public void setSourceOffset(int offset) {
	this.offsetSource = offset;
}

/**
 * Sets the target node and adds this edge to the new target's incoming edges. If the
 * target node is previously set, removes this edge from the old target's incoming edges.
 * @param node the new target
 * @since 3.2
 */
public void setTarget(Node node) {
	if (target == node)
		return;
	if (target != null)
		target.incoming.remove(this);
	target = node;
	if (target != null)
		target.incoming.add(this);
}

public void setTargetOffset(int offset) {
	this.offsetTarget = offset;
}

/**
 * Sets the width of the edge.
 * @param width the new width
 * @since 3.2
 */
public void setWidth(int width) {
	this.width = width;
}

}
