#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "/u1/h0/jkinne/public_html/cs202-f2019/code/data_structures/binary_tree/bst.h"

// find where word is in the tree and return its parent
// if not in the tree then return what would be parent if
// we inserted
tree_t *bst_lookup_parent(tree_t *root, void *data, int data_size) {
  if (root == NULL) return NULL;

  // look for word in the tree.  keep track of current
  // and its parent as we go.
  tree_t *current=root, *parent = NULL;
  while (current  != NULL) {
    int result = current->compare(data, data_size, current->data, current->data_size);
    if (result == 0) return parent; // found the word in the tree
    
    parent = current;
    
    if (result < 0)
      current = current->left;
    else
      current = current->right;
  }

  return parent;
}

// make a new node - allocate memory, set right/left = NULL,
//   put in word and number
tree_t *bst_make_node(void *data, int data_size,
		      int (*compare)(void *, int, void *, int),
		      void (*print)(void *, int, int),
		      void (*free)(void *, int)) {
  // create a new node 
  tree_t *t = (tree_t *) malloc(sizeof(tree_t));
  assert(t != NULL);
  t->left = t->right = NULL;

  t->data = malloc(data_size);
  memcpy(t->data, data, data_size);
  t->data_size = data_size;
  
  t->compare = compare;
  t->print = print;
  t->free = free;

  return t;
}


// free memory from the node
void bst_free_node(tree_t *t) {
  if (t == NULL) return;
  if (t->data != NULL) t->free(t->data, t->data_size);
  free(t);
}

// HW42 - if allow_duplicate is non-zero, still do the insert if duplicate, and insert duplicates on left
tree_t *bst_insert(tree_t *root,
		   void *data, int data_size, unsigned int flags,
		   int (*compare)(void *, int, void *, int),
		   void (*print)(void *, int, int),
		   void (*free)(void *, int)) {
  if (data == NULL) return root; // sanity check
      
  tree_t *t = bst_make_node(data, data_size, compare, print, free);

  // NULL root is empty tree, so t is now the root, return it
  if (root == NULL) return t;

  // find where should be inserted (lookup)
  tree_t *parent = bst_lookup_parent(root, data, data_size);
  if (parent == NULL) {
    // root != NULL but parent==NULL means s is in the root
    bst_free_node(t);
    return root;
  }

  // there is a parent, let's see...
  int result = parent->compare(data, data_size, parent->data, parent->data_size);
  if (result < 0 && parent->left == NULL) // put on the left
    parent->left = t;
  else if (result > 0 && parent->right == NULL) // put on the right
    parent->right = t;
  else // already in the tree
    bst_free_node(t);

  // HW42 - check whether node is balanced or not (need max_depth set
  //  properly in order to do this), and if not balanced o
  //  AVL rotation(s).
  //  See https://www.geeksforgeeks.org/avl-tree-set-1-insertion/

  /*
    note: t has 0 children, it's "balance factor" is 0
    loop with z=t, then current = z->parent, as long as z != NULL
    for z - 
      update value for z->max_height
      compute z's balance factor: z->left->max_height - z->right->max_height
        (careful about if z->left == NULL or z->right == NULL
      if (balance_factor(z) == 2 && balance_factor(z->left) == 1) {
        tree_t * y = z->left; tree_t *x = y->left;
        tree_t * T1, T2, T3, T4 - set them...;
        tree_t * z_parent = z->parent;
	update everything like on the board;
      }
   */
  
  return root;
}


tree_t *bst_lookup(tree_t *root, void *data, int data_size) {
  tree_t *current=root;
  while (current  != NULL) {
    int result = current->compare(data, data_size, current->data, current->data_size);
    if (result == 0) break;
    
    if (result < 0)
      current = current->left;
    else
      current = current->right;
  }
  return current;
}  

tree_t *bst_delete(tree_t *root, tree_t *p, unsigned int flags) {
  // HW42 - needs to be completed
  /*
    Let p be the node to delete.
    Let p_parent be p's parent.
    Cases 
    1) p is a leaf
       p_parent->(left or right) = NULL
    2) p->left != NULL
       p_largest = largest in p->left subtree
       put p_largest in p
       p_largest->right = p->right
       p_largest->left = p->left
       somewhere in there, delete(p_largest) recursively
    3) p->left == NULL
       p_parent->(left or right) = p->right
    After that, free p
   */
  return root;
}

// helper function for bst_print, prints the tree
//  with the # spaces indicating depth
void bst_print__(tree_t *root, int depth) {
  if (root == NULL) return;

  bst_print__(root->left, depth+1);
  root->print(root->data, root->data_size, depth);
  bst_print__(root->right, depth+1);
}

// HW42 - modify bst_print so it uses parameters as described in bst.h
void bst_print(tree_t *root, int limit, unsigned int flags) {
  bst_print__(root, 0);
}

// HW42 - complete function as described in bst.h
void bst_map(tree_t *root, void (*fun)(tree_t *, void *), void *param) {
}