#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "hhentry.h"
#include "tree.h"

static TreeNode *
_tree_search_hangul (TreeNode *node, unsigned char *hangul);

static void tree_init (Tree *tree);

Tree *
tree_new ()
{
  Tree *new_tree;
  new_tree = (Tree *) calloc (1, sizeof (Tree));
  new_tree->root = NULL;
  return new_tree;
}

Tree *
tree_n_new (int n)
{
  Tree *trees;
  int i;

  if (!n)
    return NULL;
  
  trees = (Tree *) calloc (n, sizeof (Tree));
  if (!trees){
    fprintf (stderr, "tree_n_new error: memory allocation error\n");
    return NULL;
  }

  for (i = 0 ; i < n; i++){
    trees[i].root = NULL;
  }

  return trees;
}

static void
tree_init (Tree *tree)
{
  assert (tree != NULL);
  if (tree->root != NULL){
    /* This function is meant to be called at the very begining.
       I'll not take care of this case for now...no time.. */
    fprintf (stderr, "I can't initialize non-empty tree");
    exit (-1);
  }

  tree->root = NULL;
}


void
tree_inorder_traverse (TreeNode *node, void *( *func)(void *))
{
  if (node != NULL){
    tree_inorder_traverse (node->left, func);
    func ((void *)node->data);
    tree_inorder_traverse (node->right, func);
  }
}

int
tree_compare_hangul (TreeNode *a, unsigned char *b)
{
  return  strcmp (a->data->hangul, b);
}

TreeNode *
tree_search_hangul (Tree *tree, unsigned char *hangul)
{
  TreeNode *ret_node = NULL;
  ret_node = _tree_search_hangul (tree->root, hangul);
  return ret_node;
}

static TreeNode *
_tree_search_hangul (TreeNode *node, unsigned char *hangul)
{
  assert (hangul != NULL);
  if (hangul == NULL)
    return NULL;

  while (node){
    if (!strcmp (node->data->hangul, hangul))
      return node;
    else if (strcmp (node->data->hangul, hangul) > 0)
      node = node->right;
    else
      node = node->left;
  }
  return NULL;
}

static void
_tree_clear (TreeNode *node)
{
  if (node){
    _tree_clear (node->right);
    _tree_clear (node->left);
    node->right = NULL;
    node->left = NULL; 
    hhitem_free (node->data);
    node->data = NULL;
    free (node);
  }
}

void
tree_clear (Tree *tree)
{
  _tree_clear (tree->root);
}

void
tree_print (Tree *tree, void * (*print_function) (void *))
{
  TreeNode *root = tree->root;
  tree_inorder_traverse (root, print_function);
}



/* I'll do only shallow copy here, under the assumption that
   the tnode passed as an argument is being held by the caller.
   I do this, because this function will be used only create
   dictionary structure, not for general use.
*/
void
tree_shallow_insert (Tree *tree, TreeNode *tnode)
{
  TreeNode *ptr = tree->root ;
  TreeNode *prev;
  
  int comp_return;

  assert (tree != NULL);
  assert (tnode != NULL);

  if (tree == NULL || tnode == NULL){
    fprintf (stderr, "wrong parameter passed\n");
    exit (-1);
  }
  if (!ptr){
    tree->root = tnode;
    return;
  }

  while (ptr){
    prev = ptr;
    comp_return = hhitem_comp (ptr->data, tnode->data);
    if (comp_return < 0)
      ptr = ptr->left;
    else if (comp_return >0)
      ptr = ptr->right;
    else {
      /* no duplication expected, just return */
      return;
    }
  }
  if (hhitem_comp (prev->data, tnode->data) < 0)
    prev->left = tnode;
  else
    prev->right = tnode;
  
}


void
tree_node_copy (TreeNode *dst, TreeNode *src)
{
  assert (dst != NULL);
  assert (src != NULL);

  if (dst == NULL || src == NULL){
    fprintf (stderr, "tree_node_copy error: dst or src is NULL\n");
    return;
  }

  dst->left = src->left;
  dst->right = src->right;
  if (dst->data && src->data)
    hhitem_copy (dst->data, src->data);
}

void
tree_insert (Tree *tree, TreeNode *tnode)
{
  TreeNode *ptr = tree->root ;
  TreeNode *prev;
  int comp_return;

  assert (tree != NULL);
  assert (tnode != NULL);

  if (tree == NULL || tnode == NULL){
    fprintf (stderr, "wrong parameter passed\n");
    exit (-1);
  }
  if (!ptr){
    tree->root = tnode;
    return;
  }

  while (ptr){
    prev = ptr;
    comp_return = hhitem_comp (ptr->data, tnode->data);
    if (comp_return < 0)
      ptr = ptr->left;
    else if (comp_return >0)
      ptr = ptr->right;
    else {
      /* no duplication expected, just return */
      return;
    }
  }
  if (hhitem_comp (prev->data, tnode->data) < 0)
    prev->left = tnode;
  else
    prev->right = tnode;
  
}


/* this function assumes data are sorted already */
void
tree_build_from_array (Tree *tree, TreeNode *data, int first, int last)
{
  int middle;
  if (first <= last){
    middle = (first + last) / 2;
    tree_shallow_insert (tree, data + middle);
    tree_build_from_array (tree, data, first, middle - 1);
    tree_build_from_array (tree, data, middle + 1, last);
  }
    
}


TreeNode *
tree_node_new_skeleton ()
{
  TreeNode *new_node;
  new_node = (TreeNode *)calloc (1, sizeof (TreeNode));
  if (!new_node){
    fprintf (stderr, "tree_node_new_skeleton error: "
	     "memory allocation failure\n");
    return NULL;
  }
  new_node->left = new_node->right = NULL;
  new_node->data = NULL;
  return new_node;
}

TreeNode *
tree_node_new_with_hhitem (HHItem *data)
{
  TreeNode *p_newnode =
    (TreeNode *) calloc (1, sizeof (TreeNode));
  
  p_newnode->left = NULL;
  p_newnode->right = NULL;
  
  p_newnode->data = hhitem_new ();
  hhitem_copy (p_newnode->data, data);

  return p_newnode;
}

void
tree_node_free (TreeNode *tnode)
{
  assert (tnode != NULL);
  if (!tnode)
    return;
  if (tnode->data){
    tnode->left = NULL;
    tnode->right = NULL;
    hhitem_free (tnode->data);
    tnode->data = NULL;
    free (tnode);
  }
}
