/**
 * A cursor for node-based binary trees.
 *
 * @author Samuel A. Rebelsky
 * @version 0.5 of April 1999
 */
public class NodeBasedBinaryTreeCursor 
  implements BinaryTreeCursor
{
  // +--------+--------------------------------------------------
  // | Fields |
  // +--------+

  /** The node to which this cursor refers. */
  public BinaryTreeNode node;
  /** The tree which this node belongs to.  Cannot be changed.  */
  protected NodeBasedBinaryTree tree;
  /** 
   * The depth of this cursor. Cannot be changed explicitly, but changed
   * implicitly by up and down. 
   */
  protected int depth;
 
  // +--------------+--------------------------------------------
  // | Constructors |
  // +--------------+

  /**
   * Create a new cursor for a particular node at a particular depth in
   * a particular tree.
   */
  public NodeBasedBinaryTreeCursor(
    BinaryTreeNode node,
    int depth,
    NodeBasedBinaryTree tree)
  {
    this.node = node;
    this.depth = depth;
    this.tree = tree;
  } // NodeBasedBinaryTreeCursor(BinaryTreeNode, int, NodeBasedBinaryTree)

  // +-----------+-----------------------------------------------
  // | Accessors |
  // +-----------+

  /** Get the value associated with the cursor. */
  public Object getValue() {
    return node.getContents();
  } // getValue()

  /** Determine whether there is a left child below the current cursor. */
  public boolean hasLeftChild() {
    return node.getLeft() != null;
  } // hasLeftChild()

  /** Determine whether there is a right child below the current cursor. */
  public boolean hasRightChild() {
    return node.getRight() != null;
  } // hasRightChild()

  /** Determine if the cursor is on a leaf. */
  public boolean onLeaf() {
    return (node.getLeft() == null) && (node.getRight() == null);
  } // onLeaf()

  /** Determine whether the cursor is on the tree. */
  public boolean onTree() {
    // Either the tree or the node are set to null when a cursor is moved
    // off the tree.
    if ((tree == null) || (node == null)) return false;
    // When a node is "deleted" its parent is set to null.  If the node
    // has a null parent and is not the root of the tree, then the cursor
    // is not on the tree.  Because both this class and the tree class are
    // in the same package, we can access its fields directly.  (This is
    // a particular design strategy.)
    if ((node.getParent() == null) && (tree.root != node)) return false;
    // If the tree is empty, the cursor can't be on the tree.
    if (tree.root == null) return false;
    // If none of these conditions holds, then it seems to be on the tree.
    return true;
  } // onTree()

  // +-----------------+-----------------------------------------
  // | Cursor Creation |
  // +-----------------+

  /** Make a copy of the current cursor. */
  public Object clone() {
    return new NodeBasedBinaryTreeCursor(this.node, this.depth, this.tree);
  } // clone()

  /** Create a new cursor at the left child. */
  public BinaryTreeCursor getLeftChild() {
    return new NodeBasedBinaryTreeCursor(this.node.getLeft(), this.depth+1, this.tree);
  } // getLeftChild()

  /** Create a new cursor at the right child. */
  public BinaryTreeCursor getRightChild() {
    return new NodeBasedBinaryTreeCursor(this.node.getRight(), this.depth+1, this.tree);
  } // getRightChild()

  // +-----------------+-----------------------------------------
  // | Cursor Movement |
  // +-----------------+

  /** Move the cursor down to a left child. */
  public void downLeft() {
    node = node.getLeft();
    depth = depth+1;
  } // downLeft()

  /** Move the cursor down to a right child. */
  public void downRight() {
    node = node.getRight();
    depth = depth+1;
  } // downRight()

  /** Move the cursor up one level. */
  public void up() {
    node = node.getParent();
    depth = depth-1;
  } // up()
  
  // +-----------------+-----------------------------------------
  // | Other Modifiers |
  // +-----------------+

  /** Delete the node/subtree referenced by the cursor. */
  public void delete() {
    tree.size -= node.delete();
    tree.recomputeDepth();
    if (node == tree.root) tree.root = null;
    node = null;
    tree = null;
  } // delete()

  /** Set the value in the node associated with this cursor. */
  public void setValue(Object newValue) {
    node.setContents(newValue);
  } // setValue(Object)

  /** Set the left child of the node associated with the cursor. */
  public void setLeft(Object newValue) {
    node.setLeft(new BinaryTreeNode(node, newValue));
    if (depth+1 > tree.depth) tree.depth = depth;
    tree.size +=1;
  } // setLeft(Object)

  /** Set the right child of the node associated with the cursor. */
  public void setRight(Object newValue) {
    node.setRight(new BinaryTreeNode(node, newValue));
    if (depth+1 > tree.depth) tree.depth = depth;
    tree.size += 1;
  } // setRight(Object)

} // class NodeBasedBinaryTreeCursor

