#include <stdlib.h>
#include <errno.h>
#include <stdio.h>

#include "bintree.h"

#if 0
static int NIL_VALUE = 1;
#endif
static void* NIL_NODE = &NIL_NODE;

static struct bintree_node *new_nil_node(struct bintree_node *parent);

void bintree_new(struct bintree *t, bintree_cmp_func cmp, void *ctx)
{
	t->root = new_nil_node(NULL);
	t->cmp = cmp;
	t->ctx = ctx;
}

static struct bintree_node *new_nil_node(struct bintree_node *parent)
{
   struct bintree_node *node = (struct bintree_node*)calloc(1, sizeof(struct bintree_node));
   if (!node)
      return NULL;
   node->value = NIL_NODE;
   node->parent = parent;
   node->left = NULL;
   node->right = NULL;
   return node;
}

static inline int is_nil(const struct bintree_node *node)
{
	return node == NULL || node->value == NIL_NODE;
}

static int insert(struct bintree *t, struct bintree_node *root, void *value)
{
   int cmp_res = 0;
   if (is_nil(root))
   {
      root->left = new_nil_node(root);
      root->right = new_nil_node(root);
      if (!root->left || !root->right)
      {
         if (root->left)
         {
            free(root->left);
            root->left = NULL;
         }
         if (root->right)
         {
            free(root->right);
            root->right = NULL;
         }
         return -ENOMEM;
      }
      root->value = value;
   }
   else
   {
      cmp_res = t->cmp(root->value, value, t->ctx);
      if (cmp_res > 0)
         return insert(t, root->left, value);
      else if (cmp_res < 0)
         return insert(t, root->right, value);
      else
         return -EINVAL;
   }
   return 0;
}

int bintree_insert(struct bintree *t, void *value)
{
	return insert(t, t->root, value);
}

static int _bintree_iterate(struct bintree_node *n, bintree_iter_cb cb, void *ctx)
{
   int rv;
   if (is_nil(n))
      return 0;
   else
   {
      if((rv = _bintree_iterate(n->left, cb, ctx)) != 0)
         return rv;
      if ((rv = cb(n->value, ctx)) != 0)
         return rv;
      if((rv = _bintree_iterate(n->right, cb, ctx)) != 0)
         return rv;
   }

   return 0;
}

int bintree_iterate(const struct bintree *t, bintree_iter_cb cb, void *ctx)
{
	return _bintree_iterate(t->root, cb, ctx);
}

static void bintree_free_node(struct bintree_node *n)
{
   if (n == NULL)
      return;

   if (n->value == NIL_NODE)
   {
      free(n);
      return;
   }
   n->value = NULL;
   bintree_free_node(n->left);
   bintree_free_node(n->right);
   free(n);
}

void bintree_free(struct bintree *t)
{
	bintree_free_node(t->root);
}
