/*
 * tree.c
 * A package for managing generic binary trees.
 */
#include "tree.h"
#include <stdlib.h>

/* Prototypes of static functions: */
static void tree_del_node (Tree *p_tree, Node *p_node);
static void tree_reset_sub_tree (Node *p_node, int is_owner);

/* ------------------------------------------------------------------------
 * Function: tree_init
 */
Tree *tree_init (Comp_func f_comp, int is_owner)
{
  Tree    *p_tree;

  /* Sanity check: */
  if (f_comp == NULL)
    return (NULL);

  /* Allocate the Tree structure. */
  p_tree = (Tree *) malloc (sizeof(Tree));

  /* Set its fields (the tree is now empty). */
  p_tree->p_root = NULL;
  p_tree->n_objects = 0;
  p_tree->f_comp = f_comp;
  p_tree->is_owner = is_owner;

  return (p_tree);
}

/* ------------------------------------------------------------------------
 * Function: tree_insert
 */
void tree_insert (Tree *p_tree, void *p_obj)
{
  Node    *p_insert;      /* The new node we insert. */
  Node    *p_curr;        /* The current node we scan. */

  /* Sanity check: */
  if (p_tree == NULL || p_obj == NULL)
    return;

  /* Create a new node pointing to the new object. */
  p_insert = (Node *) malloc (sizeof(Node));

  p_insert->p_parent = NULL;
  p_insert->p_object = p_obj;
  p_insert->p_right = NULL;
  p_insert->p_left = NULL;

  /* If the tree is empty, set the new node as its root. */
  if (p_tree->p_root == NULL)
  {
    p_tree->p_root = p_insert;
    p_tree->n_objects = 1;

    return;
  }

  /* In case the tree is not empty, start scanning it from its root. */
  p_curr = p_tree->p_root;
  while (1)
  {
    /* Compare the inserted object with the object stored at the current
       node. */
    if (p_tree->f_comp (p_curr->p_object, p_obj) < 0)
    {
      /* The new object should be inserted at the left sub-tree of p_curr. */
      if (p_curr->p_left == NULL)
      {
        /* No left child - insert p_insert as p_curr's left child. */
        p_curr->p_left = p_insert;
        p_insert->p_parent = p_curr;
        break;
      }

      /* Proceed to the left sub-tree of p_curr. */
      p_curr = p_curr->p_left;
    }
    else
    {
      /* The new object should be inserted at the right sub-tree of p_curr. */
      if (p_curr->p_right == NULL)
      {
        /* No right child - insert p_insert as p_curr's right child. */
        p_curr->p_right = p_insert;
        p_insert->p_parent = p_curr;
        break;
      }

      /* Proceed to the right sub-tree of p_curr. */
      p_curr = p_curr->p_right;
    }
  }

  /* Update the number of objects in the tree. */
  p_tree->n_objects++;

  return;
}

/* ------------------------------------------------------------------------
 * Function: tree_find
 */
const Node *tree_find (const Tree *p_tree, void *p_obj)
{
  const Node  *p_curr;        /* The current node we scan. */
  int         res;            /* Current comparison result. */

  /* Sanity check: */
  if (p_tree == NULL || p_obj == NULL)
    return (NULL);

  /* Start scanning the tree it from its root. */
  p_curr = p_tree->p_root;

  while (p_curr != NULL)
  {
    /* Compare the queried object with the object stored at the current
       node. */
    res = p_tree->f_comp (p_curr->p_object, p_obj);

    if (res == 0)
    {
      /* The object was found at p_curr. */
      return (p_curr);
    }
    else if (res < 0)
    {
      /* Continue scanning the left sub-tree of p_curr. */
      p_curr = p_curr->p_left;
    }
    else
    {
      /* Continue scanning the right sub-tree of p_curr. */
      p_curr = p_curr->p_right;
    }
  }

  /* If we reached here, the queried object was not found. */
  return (NULL);
}

/* ------------------------------------------------------------------------
 * Function: tree_delete
 */
int tree_delete (Tree *p_tree, void *p_obj)
{
  Node  *p_del;             /* Points to the node to be deleted. */

  /* Sanity check: */
  if (p_tree == NULL || p_obj == NULL)
    return (0);

  /* Find the object in the tree. */
  p_del = (Node *) tree_find (p_tree, p_obj);

  if (p_del == NULL)
    /* The object is not found in the tree. */
    return (0);

  /* Delete the node containing p_obj from the tree: */
  tree_del_node (p_tree, p_del);

  return (1);
}

/* ------------------------------------------------------------------------
 * Function: tree_delete
 */
void tree_reset (Tree *p_tree)
{
  /* Sanity check: */
  if (p_tree == NULL)
    return;

  /* If the tree is not empty, delete the sub-tree rooted at the
     tree root (i.e. the entire tree). */
  if (p_tree->p_root != NULL)
    tree_reset_sub_tree (p_tree->p_root, p_tree->is_owner);

  /* Mark that the tree is now empty. */
  p_tree->p_root = NULL;
  p_tree->n_objects = 0;

  return;
}

/* ------------------------------------------------------------------------
 * Function: tree_min
 */
const Node *tree_min (const Tree *p_tree)
{
  const Node  *p_min;

  /* Sanity check: */
  if (p_tree == NULL)
    return (NULL);

  /* In case of an empty tree: */
  if (p_tree->p_root == NULL)
    return (NULL);

  /* Find the leftmost node in the tree and return it. */
  p_min = p_tree->p_root;

  while (p_min->p_left != NULL)
    p_min = p_min->p_left;

  return (p_min);
}

/* ------------------------------------------------------------------------
 * Function: tree_next
 */
const Node *tree_next (const Node *p_node)
{
  const Node  *p_succ;

  /* Sanity check: */
  if (p_node == NULL)
    return (NULL);

  /* Check if p_node has a right child and act accordingly. */
  if (p_node->p_right != NULL)
  {
    /* If the node has a right child, its successor is the leftmost
       child in the right sub-tree. */
    p_succ = p_node->p_right;

    while (p_succ->p_left != NULL)
      p_succ = p_succ->p_left;
  }
  else
  {
    /* Go up the tree until reaching an ancestor node from the left.
       This ancestor node is the successor of p_node in the tree. */
    p_succ = p_node;
    while (p_succ->p_parent != NULL &&
           p_succ == p_succ->p_parent->p_right)
    {
      p_succ = p_succ->p_parent;
    }

    p_succ = p_succ->p_parent;
  }

  return (p_succ);
}

/* ------------------------------------------------------------------------
 * Function: tree_max
 */
const Node *tree_max (const Tree *p_tree)
{
  const Node  *p_max;

  /* Sanity check: */
  if (p_tree == NULL)
    return (NULL);

  /* In case of an empty tree: */
  if (p_tree->p_root == NULL)
    return (NULL);

  /* Find the rightmost node in the tree and return it. */
  p_max = p_tree->p_root;

  while (p_max->p_right != NULL)
    p_max = p_max->p_right;

  return (p_max);

}

/* ------------------------------------------------------------------------
 * Function: tree_prev
 */
const Node *tree_prev (const Node *p_node)
{
  const Node  *p_pred;

  /* Sanity check: */
  if (p_node == NULL)
    return (NULL);

  /* Check if p_node has a left child and act accordingly. */
  if (p_node->p_left != NULL)
  {
    /* If the node has a left child, its predecessor is the rightmost
       child in the left sub-tree. */
    p_pred = p_node->p_left;

    while (p_pred->p_right != NULL)
      p_pred = p_pred->p_right;
  }
  else
  {
    /* Go up the tree until reaching an ancestor node from the right.
       This ancestor node is the predecessor of p_node in the tree. */
    p_pred = p_node;
    while (p_pred->p_parent != NULL &&
           p_pred == p_pred->p_parent->p_left)
    {
      p_pred = p_pred->p_parent;
    }

    p_pred = p_pred->p_parent;
  }

  return (p_pred);
}

/* ------------------------------------------------------------------------
 * Function: tree_del_node
 * Purpose:  Delete a node from a binary tree.
 * Input:    p_tree - The binary tree.
 *           p_node - A pointer to the node to be deleted.
 * Returns:  Nothing.
 */
static void tree_del_node (Tree *p_tree, Node *p_node)
{
  /* Check how many children p_node has and act accordingly. */
  if (p_node->p_left == NULL || p_node->p_right == NULL)
  {
    /* If one of the children is not NULL (but the other must be), keep
       a pointer to it. */
    Node  *p_child = NULL;

    if (p_node->p_left != NULL)
      p_child = p_node->p_left;
    else if (p_node->p_right != NULL)
      p_child = p_node->p_right;

    /* If necessary, free the object stored at p_node. */
    if (p_tree->is_owner)
      free (p_node->p_object);

    /* Link p_child directly to p_node's parent. */
    if (p_node->p_parent != NULL)
    {
      /* Check if p_node is a left or a right child, and link p_child
         instead of it. */
      if (p_node == p_node->p_parent->p_left)
        p_node->p_parent->p_left = p_child;
      else
        p_node->p_parent->p_right = p_child;
    }
    else
    {
      /* Make p_child the new tree root. */
      p_tree->p_root = p_child;
    }

    if (p_child != NULL)
      p_child->p_parent = p_node->p_parent;

    /* Free the node itself. */
    free (p_node);
  }
  else
  {
    /* The node to be deleted has two children: Find its successor, which
       is the leftmost node in its right sub-tree. */
    Node  *p_succ;
    void  *temp;

    p_succ = p_node->p_right;
    while (p_succ->p_left != NULL)
      p_succ = p_succ->p_left;

    /* Swap the objects pointed by p_node and its successor, so p_succ will
       store the object that we wish to delete. */
    temp = p_node->p_object;
    p_node->p_object = p_succ->p_object;
    p_succ->p_object = temp;

    /* Now we can simply delete p_succ recursively. Notice that it has one
       child at most, so the recursion will stop there. */
    tree_del_node (p_tree, p_succ);
  }

  return;
}

/* ------------------------------------------------------------------------
 * Function: tree_reset_sub_tree
 * Purpose:  Delete the entire sub-tree rooted at a given root.
 * Input:    p_node   - A pointer to the sub-tree root.
 *           is_owner - Does the tree own its objects (and should free them).
 * Returns:  Nothing.
 */
static void tree_reset_sub_tree (Node *p_node, int is_owner)
{
  /* If necessary, free the object. */
  if (is_owner)
    free (p_node->p_object);

  /* Free the left and right sub-trees recursively. */
  if (p_node->p_left != NULL)
    tree_reset_sub_tree (p_node->p_left, is_owner);

  if (p_node->p_right != NULL)
    tree_reset_sub_tree (p_node->p_right, is_owner);

  /* Free the node itself. */
  free (p_node);

  return;
}

