/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.uima.internal.util.rb_trees;

import org.apache.uima.internal.util.BinaryTree;

/**
 * Red-Black Tree node. Not for public use. Use the interface in RedBlackTree instead. This should
 * probably be an internal class to RedBlackTree, but it's easier to read in a seperate file. See
 * comments in RedBlackTree.
 * 
 */
class RBTNode<T> {

  // The colors.
  private static final boolean RED = true;

  private static final boolean BLACK = false;

  private int key; // The key of the node.

  private boolean color; // The color of the node.

  // A pointer to the parent node. This will be null if this is the
  // root of a tree.
  private RBTNode<T> parent;

  // The following variables are package private so they can be
  // accessed from RedBlackTree.

  RBTNode<T> left; // The left daughter.

  RBTNode<T> right; // The right daughter.

  T element; // The element contained in the node.

  // For the debugging print functions.
  private static final int indentInc = 2;

  /**
   * The real constructor, used only internally.
   */
  private RBTNode(int key, boolean color, RBTNode<T> parent, RBTNode<T> left, RBTNode<T> right,
          T element) {
    this.key = key;
    this.color = color;
    this.parent = parent;
    this.left = left;
    this.right = right;
    this.element = element;
  }

  /**
   * The standard constructor as used by RedBlackTree.
   * 
   * @param key
   *          The key to be inserted.
   * @param element
   *          The value to be inserted.
   */
  RBTNode(int key, T element) {
    // The default color is completely arbitrary.
    this(key, BLACK, null, null, null, element);
  }

  /**
   * Find a node with a certain key. Returns null if no such node exists.
   */
  static final <T> RBTNode<T> find(RBTNode<T> x, int key) {
    while (x != null && x.key != key) {
      if (key < x.key) {
        x = x.left;
      } else {
        x = x.right;
      }
    }
    return x;
  }

  /** 
   * Find the successor node to this. 
   */
  final RBTNode<T> successor() {
    RBTNode<T> x = this;
    // If this has a right daughter, then the successor will be the
    // leftmost daughter of this.right, if it exists, and x.right,
    // else.
    if (x.right != null) {
      x = x.right;
      while (x.left != null) {
        x = x.left;
      }
      return x;
    }
    // If this does not have a right daughter, we need to move up in
    // the tree until we hit a node we're the left daughter of. That
    // will be the successor.
    RBTNode<T> y = x.parent;
    while (y != null && x == y.right) {
      x = y;
      y = x.parent;
    }
    return y;
  }

  /** 
   * Insert a node into a tree. See CLR. 
   */
  static final <T> boolean insert(RedBlackTree<T> tree, RBTNode<T> x) {
    if (!treeInsert(tree, x)) {
      // A node with the same key as x already existed in the tree, so
      // there is nothing left to do.
      return false;
    }
    RBTNode<T> y;
    x.color = RED;
    while (x != tree.root && x.parent.color == RED) {
      if (x.parent == x.parent.parent.left) {
        y = x.parent.parent.right;
        if (colorOf(y) == RED) {
          x.parent.color = BLACK;
          setColor(y, BLACK);
          x.parent.parent.color = RED;
          x = x.parent.parent;
        } else {
          if (x == x.parent.right) {
            x = x.parent;
            x.leftRotate(tree);
          }
          x.parent.color = BLACK;
          x.parent.parent.color = RED;
          x.parent.parent.rightRotate(tree);
        }
      } else {
        y = x.parent.parent.left;
        if (colorOf(y) == RED) {
          x.parent.color = BLACK;
          setColor(y, BLACK);
          x.parent.parent.color = RED;
          x = x.parent.parent;
        } else {
          if (x == x.parent.left) {
            x = x.parent;
            x.rightRotate(tree);
          }
          x.parent.color = BLACK;
          x.parent.parent.color = RED;
          x.parent.parent.leftRotate(tree);
        }
      }
    }
    tree.root.color = BLACK;
    return true;
  }

  /** Auxiliary function for insert(). See CLR. */
  private static final <T> boolean treeInsert(RedBlackTree<T> tree, RBTNode<T> z) {
    RBTNode<T> y = null;
    RBTNode<T> x = tree.root;
    while (x != null) {
      y = x;
      if (z.key < x.key) {
        x = x.left;
      } else if (z.key > x.key) {
        x = x.right;
      } else { // z.key == x.key
        x.element = z.element;
        // No node was actually inserted.
        return false;
      }
    }
    z.parent = y;
    if (y == null) {
      tree.root = z;
    } else if (z.key < y.key) {
      y.left = z;
    } else {
      y.right = z;
    }
    return true;
  }

  /** Left rotation, used to keep the tree balanced. See CLR. */
  private final void leftRotate(RedBlackTree<T> tree) {
    RBTNode<T> y = this.right;
    this.right = y.left;
    if (y.left != null) {
      y.left.parent = this;
    }
    y.parent = this.parent;
    if (this.parent == null) {
      tree.root = y;
    } else if (this == this.parent.left) {
      this.parent.left = y;
    } else {
      this.parent.right = y;
    }
    y.left = this;
    this.parent = y;
    return;
  }

  /** Right rotation, used to keep the tree balanced. See CLR. */
  private final void rightRotate(RedBlackTree<T> tree) {
    RBTNode<T> y = this.left;
    this.left = y.right;
    if (y.right != null) {
      y.right.parent = this;
    }
    y.parent = this.parent;
    if (this.parent == null) {
      tree.root = y;
    } else if (this == this.parent.right) {
      this.parent.right = y;
    } else {
      this.parent.left = y;
    }
    y.right = this;
    this.parent = y;
    return;
  }

  /**
   * Delete a given node from the tree. The node must be contained in the tree! Our code is more
   * complicated than CLR because we don't use a NIL sentinel.
   */
  static final <T> void delete(RedBlackTree<T> tree, RBTNode<T> z) {
    RBTNode<T> x;
    RBTNode<T> y;
    RBTNode<T> xParent = null;

    if (z.left == null || z.right == null) {
      y = z;
    } else {
      y = z.successor();
    }
    if (y.left != null) {
      x = y.left;
    } else {
      x = y.right;
    }
    if (x != null) {
      x.parent = y.parent;
    } else {
      xParent = y.parent;
    }
    if (y.parent == null) {
      tree.root = x;
    } else {
      if (y == y.parent.left) {
        y.parent.left = x;
      } else {
        y.parent.right = x;
      }
    }
    if (y != z) {
      z.key = y.key;
      z.element = y.element;
    }
    if (y.color == BLACK) {
      if (x == null) {
        deleteFixupNull(tree, xParent);
      } else {
        deleteFixup(tree, x);
      }
    }
  }

  /** 
   * From CLR. x must not be null. 
   */
  private static final <T> void deleteFixup(RedBlackTree<T> tree, RBTNode<T> x) {
    RBTNode<T> w;
    while (x != tree.root && x.color == BLACK) {
      if (x == x.parent.left) {
        w = x.parent.right;
        if (w.color == RED) {
          w.color = BLACK;
          x.parent.color = RED;
          x.parent.leftRotate(tree);
          w = x.parent.right;
        }
        if (colorOf(leftOf(w)) == BLACK && colorOf(rightOf(w)) == BLACK) {
          w.color = RED;
          x = x.parent;
        } else {
          if (colorOf(rightOf(w)) == BLACK) {
            w.color = RED;
            w.rightRotate(tree);
            w = x.parent.right;
          }
          w.color = x.parent.color;
          x.parent.color = BLACK;
          if (w.right != null) {
            w.right.color = BLACK;
          }
          x.parent.leftRotate(tree);
          x = tree.root;
        }
      } else {
        w = x.parent.left;
        if (w.color == RED) {
          w.color = BLACK;
          x.parent.color = RED;
          x.parent.rightRotate(tree);
          w = x.parent.left;
        }
        if (colorOf(rightOf(w)) == BLACK && colorOf(leftOf(w)) == BLACK) {
          w.color = RED;
          x = x.parent;
        } else {
          if (colorOf(leftOf(w)) == BLACK) {
            w.color = RED;
            w.leftRotate(tree);
            w = x.parent.left;
          }
          w.color = x.parent.color;
          x.parent.color = BLACK;
          if (w.left != null) {
            w.left.color = BLACK;
          }
          x.parent.rightRotate(tree);
          x = tree.root;
        }
      }
    }
    x.color = BLACK;
  }

  /**
   * Like deleteFixup(), only that the node we should be working on is null, and we actually hand in
   * the node's mother. Special case because we don't use sentinels.
   */
  private static final <T> void deleteFixupNull(RedBlackTree<T> tree, RBTNode<T> x) {
    // if x == null, we just deleted the only node in the tree
    if (x == null) {
      return;
    }

    RBTNode<T> w;

    if (x.left == null) {
      w = x.right;
      if (w.color == RED) {
        w.color = BLACK;
        x.color = RED;
        x.leftRotate(tree);
        w = x.right;
      }
      if (colorOf(leftOf(w)) == BLACK && colorOf(rightOf(w)) == BLACK) {
        w.color = RED;
      } else {
        if (colorOf(rightOf(w)) == BLACK) {
          w.color = RED;
          w.rightRotate(tree);
          w = x.right;
        }
        w.color = x.color;
        x.color = BLACK;
        if (w.right != null) {
          w.right.color = BLACK;
        }
        x.leftRotate(tree);
        x = tree.root;
      }
    } else {
      w = x.left;
      if (w.color == RED) {
        w.color = BLACK;
        x.color = RED;
        x.rightRotate(tree);
        w = x.left;
      }
      if (colorOf(rightOf(w)) == BLACK && colorOf(leftOf(w)) == BLACK) {
        w.color = RED;
      } else {
        if (colorOf(leftOf(w)) == BLACK) {
          w.color = RED;
          w.leftRotate(tree);
          w = x.left;
        }
        w.color = x.color;
        x.color = BLACK;
        if (w.left != null) {
          w.left.color = BLACK;
        }
        x.rightRotate(tree);
        x = tree.root;
      }
    }
    while (x != tree.root && x.color == BLACK) {
      if (x == x.parent.left) {
        w = x.parent.right;
        if (w.color == RED) {
          w.color = BLACK;
          x.parent.color = RED;
          x.parent.leftRotate(tree);
          w = x.parent.right;
        }
        if (colorOf(leftOf(w)) == BLACK && colorOf(rightOf(w)) == BLACK) {
          w.color = RED;
          x = x.parent;
        } else {
          if (colorOf(rightOf(w)) == BLACK) {
            w.color = RED;
            w.rightRotate(tree);
            w = x.parent.right;
          }
          w.color = x.parent.color;
          x.parent.color = BLACK;
          if (w.right != null) {
            w.right.color = BLACK;
          }
          x.parent.leftRotate(tree);
          x = tree.root;
        }
      } else {
        w = x.parent.left;
        if (w.color == RED) {
          w.color = BLACK;
          x.parent.color = RED;
          x.parent.rightRotate(tree);
          w = x.parent.left;
        }
        if (colorOf(rightOf(w)) == BLACK && colorOf(leftOf(w)) == BLACK) {
          w.color = RED;
          x = x.parent;
        } else {
          if (colorOf(leftOf(w)) == BLACK) {
            w.color = RED;
            w.leftRotate(tree);
            w = x.parent.left;
          }
          w.color = x.parent.color;
          x.parent.color = BLACK;
          if (w.left != null) {
            w.left.color = BLACK;
          }
          x.parent.rightRotate(tree);
          x = tree.root;
        }
      }
    }
    x.color = BLACK;
  }

  /**
   * Fill an array with the keys contained in the tree. The array must at least have the size of the
   * tree! Returns the size of the tree, for internal reasons.
   */
  int keys(int pos, int[] keys) {
    int cur = pos;
    if (this.left != null) {
      cur = this.left.keys(cur, keys);
    }
    keys[cur] = this.key;
    ++cur;
    if (this.right != null) {
      cur = this.right.keys(cur, keys);
    }
    return cur;
  }

  // Use as accessor when node might be null.
  private static final <T> boolean colorOf(RBTNode<T> x) {
    return (x == null) ? BLACK : x.color;
  }

  // Below some accessor functions to be used when the node might be
  // null.

  // Use when node might be null.
  private static final <T> void setColor(RBTNode<T> x, boolean c) {
    if (x != null) {
      x.color = c;
    }
  }

  // Use when node might be null.
  private static final <T> RBTNode<T> leftOf(RBTNode<T> x) {
    return (x == null) ? null : x.left;
  }

  // Use when node might be null.
  private static final <T> RBTNode<T> rightOf(RBTNode<T> x) {
    return (x == null) ? null : x.right;
  }

  // Debugging aid.
  public void printKeys(int indent) {
    for (int i = 0; i < indent; i++) {
      System.out.print(' ');
    }
    System.out.print(this.key);
    System.out.print(":");
    if (this.color == RED) {
      System.out.println("red");
    } else {
      System.out.println("black");
    }
    indent += indentInc;
    if (this.left != null) {
      this.left.printKeys(indent);
    }
    if (this.right != null) {
      this.right.printKeys(indent);
    }
    return;
  }

  // Debugging aid.
  public void printElements(int indent) {
    for (int i = 0; i < indent; i++) {
      System.out.print(' ');
    }
    System.out.println(this.element.toString());
    indent += indentInc;
    if (this.left != null) {
      this.left.printElements(indent);
    }
    if (this.right != null) {
      this.right.printElements(indent);
    }
    return;
  }

  void getBinaryTree(BinaryTree tree) {
    tree.setValue(new RBTKeyValuePair(this.key, this.element));
    if (this.left != null) {
      BinaryTree newLeft = tree.newLeftDtr();
      this.left.getBinaryTree(newLeft);
    }
    if (this.right != null) {
      BinaryTree newRight = tree.newRightDtr();
      this.right.getBinaryTree(newRight);
    }
  }

  // public class PreorderIterator implements Iterator {

  // private RBTNode current;

  // private PreorderIterator() {
  // this.current = null;
  // }

  // PreorderIterator(RBTNode node) {
  // this.current = node;
  // }

  // public boolean hasNext() {
  // return (this.current == null);
  // }

  // public Object next() {
  // if (this.current == null) {
  // throw new java.util.NoSuchElementException();
  // }
  // RBTNode node = this.current;
  // // Return this at the end.
  // final RBTKeyValuePair rv = new RBTKeyValuePair(node.key, node.element);
  // // Find the next value.
  // if (node.left != null) {
  // // Go down left branch if it exists.
  // node = node.left;
  // } else if (node.right != null) {
  // // Else, go down right branch, if it exists.
  // node = node.right;
  // } else {
  // // Else, back up the tree.
  // while (node.parent != null) {
  // if (node == node.parent.right || node.parent.right == null) {
  // // This is a right dtr or an only child, continue backing
  // // up the tree.
  // node = node.parent;
  // } else {
  // // This is a left dtr and a right dtr exists, so this is
  // // our next candidate.
  // node = node.parent.right;
  // break;
  // }
  // }
  // if (node.parent == null) {
  // this.current = null;
  // } else {
  // this.current = node;
  // }
  // }
  // return rv;
  // }

  // public void remove() {
  // throw new java.lang.UnsupportedOperationException();
  // }
  // }

  // java.util.Iterator preorderIterator() {
  // return new PreorderIterator(this);
  // }

}
