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

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;

/**
 * A node in a DirectedGraph. A node has 0 or more incoming and outgoing {@link Edge}s. A
 * node is given a width and height by the client. When a layout places the node in the
 * graph, it will determine the node's x and y location.  It may also modify the node's
 * height.
 * 
 * A node represents both the <EM>input</EM> and the <EM>output</EM> for a layout
 * algorithm. The following fields are used as input to a graph layout:
 * <UL>
 *   <LI>{@link #width} - the node's width.
 *   <LI>{@link #height} - the node's height.
 *   <LI>{@link #outgoing} - the node's outgoing edges.
 *   <LI>{@link #incoming} - the node's incoming edges.
 *   <LI>padding - the amount of space to be left around the outside of the node.
 *   <LI>{@link #incomingOffset} - the default attachment point for incoming edges.
 *   <LI>{@link #outgoingOffset} - the default attachment point for outgoing edges.
 *   <LI>parent - the parent subgraph containing this node.
 * </UL>
 * <P>
 * The following fields are calculated by a graph layout and comprise the <EM>output</EM>:
 * <UL>
 *   <LI>{@link #x} - the node's x location
 *   <LI>{@link #y} - the node's y location
 *   <LI>{@link #height} - the node's height may be stretched to match the height of other
 *   nodes
 * </UL>
 * 
 * @author Randy Hudson
 * @since 2.1.2
 */
public class Node {
	
Node left, right;

Object workingData[] = new Object[3];
int workingInts[] = new int[4];

/**
 * Clients may use this field to mark the Node with an arbitrary data object.
 */
public Object data;

//used by various graph visitors
boolean flag;

/**
 * The height of this node. This value should be set prior to laying out the directed
 * graph.  Depending on the layout rules, a node's height may be expanded to match the
 * height of other nodes around it.
 */
public int height = 40;

/**
 * @deprecated use {@link #setRowConstraint(int)} and {@link #getRowConstraint()}
 */
public int rowOrder = -1;

/**
 * The edges for which this node is the target.
 */
public EdgeList incoming = new EdgeList();

/**
 * The default attachment point for incoming edges.  <code>-1</code> indicates that the
 * node's horizontal center should be used.
 */
public int incomingOffset = -1;

// A non-decreasing number given to consecutive nodes in a Rank.
int index;

//Used in Compound graphs to quickly determine whether a node is inside a subgraph.
int nestingIndex = -1;

/**
 * The edges for which this node is the source.
 */
public EdgeList outgoing = new EdgeList();

Insets padding;
private Subgraph parent;
int rank;

/**
 * @deprecated for internal use only
 */
public double sortValue;

/**
 * The node's outgoing offset attachment point.
 */
public int outgoingOffset = -1;

/**
 * The node's width. The default value is 50.
 */
public int width = 50;

/**
 * The node's x coordinate.
 */
public int x;
/**
 * The node's y coordinate.
 */
public int y;

/**
 * Constructs a new node.
 */
public Node() { }

/**
 * Constructs a node with the given data object
 * @param data an arbitrary data object
 */
public Node(Object data) {
	this(data, null);
}

/**
 * Constructs a node inside the given subgraph.
 * @param parent the parent subgraph
 */
public Node(Subgraph parent) {
	this(null, parent);
}

/**
 * Constructs a node with the given data object and parent subgraph. This node is added to
 * the set of members for the parent subgraph
 * @param data an arbitrary data object
 * @param parent the parent subgraph or <code>null</code>
 */
public Node(Object data, Subgraph parent) {
	this.data = data;
	this.parent = parent;
	if (parent != null)
		parent.addMember(this);
}

/**
 * Returns the incoming attachment point.  This is the distance from the left edge to the
 * default incoming attachment point for edges.  Each incoming edge may have it's own
 * attachment setting which takes priority over this default one.
 * @return the incoming offset
 */
public int getOffsetIncoming() {
	if (incomingOffset == -1)
		return width / 2;
	return incomingOffset;
}

/**
 * Returns the outgoing attachment point.  This is the distance from the left edge to the
 * default outgoing attachment point for edges.  Each outgoing edge may have it's own
 * attachment setting which takes priority over this default one.
 * @return the outgoing offset
 */
public int getOffsetOutgoing() {
	if (outgoingOffset == -1)
		return width / 2;
	return outgoingOffset;
}

/**
 * Returns the padding for this node or <code>null</code> if the default padding for the
 * graph should be used.
 * @return the padding or <code>null</code>
 */
public Insets getPadding() {
	return padding;
}

/**
 * Returns the parent Subgraph or <code>null</code> if there is no parent. Subgraphs are
 * only for use in {@link CompoundDirectedGraphLayout}.
 * @return the parent or <code>null</code>
 */
public Subgraph getParent() {
	return parent;
}

/**
 * For internal use only. Returns <code>true</code> if the given node is equal to this
 * node.  This method is implemented for consitency with Subgraph.
 * @param node the node in question
 * @return <code>true</code> if nested
 */
boolean isNested(Node node) {
	return node == this;
}

/**
 * Sets the padding. <code>null</code> indicates that the default padding should be used.
 * @param padding an insets or <code>null</code>
 */
public void setPadding(Insets padding) {
	this.padding = padding;
}

/**
 * Sets the parent subgraph.  This method should not be called directly.  The constructor
 * will set the parent accordingly.
 * @param parent the parent
 */
public void setParent(Subgraph parent) {
	this.parent = parent;
}

/**
 * Sets the row sorting constraint for this node. By default, a node's constraint is
 * <code>-1</code>. If two nodes have different values both >= 0, the node with the
 * smaller constraint will be placed to the left of the other node. In all other cases no
 * relative placement is guaranteed.
 * @param value the row constraint
 * @since 3.2
 */
public void setRowConstraint(int value) {
    this.rowOrder = value;
}

/**
 * Returns the row constraint for this node.
 * @return the row constraint
 * @since 3.2
 */
public int getRowConstraint() {
	return rowOrder;
}

/**
 * Sets the size of this node to the given dimension.
 * @param size the new size
 * @since 3.2
 */
public void setSize(Dimension size) {
	width = size.width;
	height = size.height;
}

/**
 * @see Object#toString()
 */
public String toString() {
	return "N(" + data + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}

Iterator iteratorNeighbors() {
	return new Iterator() {
		int offset;
		EdgeList list = outgoing;
		public Object next() {
			Edge edge = list.getEdge(offset++);
			if (offset < list.size())
				return edge.opposite(Node.this);
			if (list == outgoing) {
				list = incoming;
				offset = 0;
			} else
				list = null;
			return edge.opposite(Node.this);
		}
	
		public boolean hasNext() {
			if (list == null)
				return false;
			if (offset < list.size())
				return true;
			if (list == outgoing) {
				list = incoming;
				offset = 0;
			}
			return offset < list.size();
		}
	
		public void remove() {
			throw new RuntimeException("Remove not supported"); //$NON-NLS-1$
		}
	};
}

/**
 * Returns a reference to a node located left from this one
 * @return <code>Node</code> on the left from this one
 * @since 3.4
 */
public Node getLeft() {
	return left;
}

/**
 * Returns a reference to a node located right from this one
 * @return <code>Node</code> on the right from this one
 * @since 3.4
 */
public Node getRight() {
	return right;
}

}
