/*
 * Copyright 2002 Silicon Graphics, Inc. All rights reserved.
 */
#include <kl_lib.h>

/* Node height
*/
#define LEFT		1
#define EQUAL		2
#define RIGHT		4

/* Local function prototypes 
 */
static void single_left(btnode_t **);
static void single_right(btnode_t **);
static void balance_right(btnode_t **);
static void balance_left(btnode_t **);
static btnode_t *predecessor(btnode_t *);

/* Balance status during insert/delete
*/
static unsigned char balance = 0;

/*
 * kl_btnode_height()
 */
int
kl_btnode_height(btnode_t *root)
{
	fprintf(stderr, "kl_btnode_height(): not implemented\n");
	return(-1);
}

/*
 * kl_insert_btnode()
 */
int
kl_insert_btnode(btnode_t **root, btnode_t *np, int flags)
{
    int ret, status = 0;

	if (*root == (btnode_t *)NULL) {
		*root = np;
		np->bt_height = EQUAL;
		np->bt_parent = (btnode_t*) 0;
		np->bt_left = (btnode_t*) 0;
		np->bt_right = (btnode_t*) 0;
		balance = 1;
	} else {
		ret = strcmp(np->bt_key, (*root)->bt_key);
		if (ret < 0) {
			status = kl_insert_btnode(&((*root)->bt_left), np, flags);
			if (status == 0) {
				if (np->bt_parent == (btnode_t*) 0) {
					np->bt_parent = *root;
				}
				if (balance == 1) { /* node inserted and grew */
					switch ((*root)->bt_height) {
						case RIGHT:
							(*root)->bt_height = EQUAL;
							balance = 0;
							break;
						case EQUAL:
							(*root)->bt_height = LEFT;
							break;
						case LEFT:
							balance_left(root);
							break;
						default: /* error */
							assert(-1);
					}
				}
			}
		} else if ((ret == 0) && !(flags & DUPLICATES_OK)) {
			status = -1;
		} else {
			status = kl_insert_btnode(&((*root)->bt_right), np, flags);
			if (status == 0) {
				if (np->bt_parent == (btnode_t*) 0) {
					np->bt_parent = *root;
				}
				if (balance == 1) { /* node inserted and grew */
					switch ((*root)->bt_height) {
						case RIGHT:
							balance_right(root);
							break;
						case EQUAL:
							(*root)->bt_height = RIGHT;
							break;
						case LEFT:
							(*root)->bt_height = EQUAL;
							balance = 0;
							break;
						default: /* error */
							assert(-1);
					}
				}
			}
		}
	}
	
	return(status);
}


/*
 * Remove a node from the tree. If `fkt' is non-NULL the node is also freed.
 * Warning: The key will not be freed.
 */
int 
kl_delete_btnode(btnode_t **root, btnode_t *dp, void(*fkt)(void*), int f)
{
	int ret; 
	int status = 0;
	
	if (*root == (btnode_t*) 0) {
		return -1;
	}
	
	ret = strcmp((*root)->bt_key, dp->bt_key);
	if (ret > 0) {
		status = kl_delete_btnode(&(*root)->bt_left, dp, fkt, f);
		if (status == 0 && balance == 1) {
			switch ((*root)->bt_height) {
				case LEFT:
					(*root)->bt_height = EQUAL;
					break;
				case EQUAL:
					(*root)->bt_height = RIGHT;
					balance = 0;
					break;
				case RIGHT:
					balance_right(root);
					balance = 0;
					break;
				default:
					assert(-1);
			}
		}
	}
	else if (ret == 0) { /* found key */
		/* the node might have two children
		*/
		if ((*root)->bt_left != (btnode_t*) 0 
				&& (*root)->bt_right != (btnode_t*) 0) {
			/* replace current node with its predecessor
			*/
			
			/* note that `root' may change while deleting, so save it
			*/
			btnode_t *node = *root;
			btnode_t *pre = predecessor(*root);
			
			/* delete predecessor without freeing memory
			*/
			status = kl_delete_btnode(root, pre, 0, f);
			assert(status == 0);
			
			pre->bt_parent = node->bt_parent;
			pre->bt_left = node->bt_left;
			pre->bt_right = node->bt_right;
			
			/* inform children about their new parents
			*/
			if (pre->bt_left != (btnode_t*) 0) {
				pre->bt_left->bt_parent = pre;
			}
			if (pre->bt_right != (btnode_t*) 0) {
				pre->bt_right->bt_parent = pre;
			}
			
			/* inform parent about its new child
			*/
			if (node->bt_parent != (btnode_t*) 0) {
				if (node->bt_parent->bt_left == node) {
					node->bt_parent->bt_left = pre;
				}
				else {
					node->bt_parent->bt_right = pre;
				}
			}
			
			/* free node
			*/
			if (fkt != (void(*)(void*)) 0) {
				fkt(node);
			}
		}
		else { /* node has one child at most */
			btnode_t* child = (btnode_t*) 0;
			
			if ((*root)->bt_left != (btnode_t*) 0) {
				child = (*root)->bt_left;
				child->bt_parent = (*root)->bt_parent;
			}
			else if ((*root)->bt_right != (btnode_t*) 0) {
				child = (*root)->bt_right;
				child->bt_parent = (*root)->bt_parent;
			}
			
			if (fkt != (void(*)(void*)) 0) {
				fkt(*root);
			}
			
			*root = child;
			
			balance = 1;
			status = 0;
		}
	}
	else {
		status = kl_delete_btnode(&(*root)->bt_right, dp, fkt, f);
		if (status == 0 && balance == 1) {
			switch ((*root)->bt_height) {
				case LEFT:
					balance_left(root);
					balance = 0;
					break;
				case EQUAL:
					(*root)->bt_height = LEFT;
					balance = 0;
					break;
				case RIGHT:
					(*root)->bt_height = EQUAL;
					break;
				default:
					assert(-1);
			}
		}
	}
		
	return status;
}

static btnode_t*
predecessor(btnode_t *node)
{
	btnode_t *pre = node->bt_left;
	
	while (pre->bt_right != (btnode_t*) 0) {
		pre = pre->bt_right;
	}
	
	return pre;
}

/*
 * balance_right()
 */
static void 
balance_right(btnode_t **root)
{
	btnode_t *child_right = (*root)->bt_right;
	btnode_t *child_left = child_right->bt_left;
	
	switch (child_right->bt_height) {
		case RIGHT:
			(*root)->bt_height = EQUAL;
			child_right->bt_height = EQUAL;
			single_left(root);
			balance = 0;
			break;
		case EQUAL:
			child_right->bt_height = LEFT;
			single_left(root);
			balance = 0;
			break;
		case LEFT:
			switch (child_left->bt_height) {
				case RIGHT:
					(*root)->bt_height = LEFT;
					child_right->bt_height = EQUAL;
					break;
				case EQUAL:
					(*root)->bt_height = EQUAL;
					child_right->bt_height = EQUAL;
					break;
				case LEFT:
					(*root)->bt_height = EQUAL;
					child_right->bt_height = RIGHT;
					break;
				default:
					assert(-1);
			}
			child_left->bt_height = EQUAL;
			single_right(&(*root)->bt_right);
			single_left(root);
			balance = 0;
			break;
		default: /* error */
			assert(-1);
	}
}

/*
 * balance_left()
 */
static void balance_left(btnode_t **root)
{
	btnode_t *child_left = (*root)->bt_left;
	btnode_t *child_right = child_left->bt_right;
	
	switch (child_left->bt_height) {
		case RIGHT:
			switch (child_right->bt_height) {
				case RIGHT:
					(*root)->bt_height = EQUAL;
					child_left->bt_height = LEFT;
					break;
				case EQUAL:
					(*root)->bt_height = EQUAL;
					child_left->bt_height = EQUAL;
					break;
				case LEFT:
					(*root)->bt_height = RIGHT;
					child_left->bt_height = EQUAL;
					break;
				default:
					assert(-1);
			}
			child_right->bt_height = EQUAL;
			single_left(&(*root)->bt_left);
			single_right(root);
			balance = 0;
			break;
		case EQUAL:
			child_left->bt_height = RIGHT;
			single_right(root);
			balance = 0;
			break;
		case LEFT:
			(*root)->bt_height = EQUAL;
			child_left->bt_height = EQUAL;
			single_right(root);
			balance = 0;
			break;
		default:
			assert(-1);
	}
}

/*
 * single_left()
 */
static void
single_left(btnode_t **root)
{
	btnode_t* tmp = (*root)->bt_right;
	(*root)->bt_right = tmp->bt_left;
	tmp->bt_left = *root;
	*root = tmp;
	
	/* assign parents */
	(*root)->bt_parent = (*root)->bt_left->bt_parent;
	(*root)->bt_left->bt_parent = *root;
	if ((*root)->bt_left->bt_right != (btnode_t*) 0) {
		(*root)->bt_left->bt_right->bt_parent = (*root)->bt_left;
	}
}

/*
 * single_right()
 */
static void
single_right(btnode_t **root)
{
	btnode_t *tmp = (*root)->bt_left;
	(*root)->bt_left = tmp->bt_right;
	tmp->bt_right = *root;
	*root = tmp;
	
	/* assign parents */
	(*root)->bt_parent = (*root)->bt_right->bt_parent;
	(*root)->bt_right->bt_parent = *root;
	if ((*root)->bt_right->bt_left != (btnode_t*) 0) {
		(*root)->bt_right->bt_left->bt_parent = (*root)->bt_right;
	}
}

/*
 * kl_find_btnode()
 */
btnode_t *
_kl_find_btnode(btnode_t *np, char *key, int *max_depth, size_t len)
{
	int ret;
	btnode_t *found = NULL;

	if (!np) {
		return((btnode_t *)NULL);
	}

	/* Bump the counter (if a pointer for one was passed in)
	 * to calculate total nodes searched before finding/not
	 * finding a key.
	 */
	if (max_depth) {
		(*max_depth)++;
	}

	/* See if this node is the one we are looking for
	 */
	if(len) {
		ret = strncmp(key, np->bt_key, len);
	} else {
		ret = strcmp(key, np->bt_key);
	}
	if (ret == 0) {
		return(np);
	} else if (ret < 0) {
		if ((found = _kl_find_btnode(np->bt_left, key,
					     max_depth, len))) {
			return(found);
		}
	} else if (ret > 0) {
		if ((found = _kl_find_btnode(np->bt_right, key,
					     max_depth, len))) {
			return(found);
		}
	}
	return((btnode_t *)NULL);
}

/*
 * kl_first_btnode()
 */
btnode_t *
kl_first_btnode(btnode_t *np)
{
	if (!np) {
		return((btnode_t *)NULL);
	}

	/* Walk down the left side 'til the end...
	 */
	while (np->bt_left) {
		np = np->bt_left;
	}
	return(np);
}

/*
 * kl_next_btnode()
 */
btnode_t *
kl_next_btnode(btnode_t *np)
{
	int ret;
	btnode_t *parent;

	if (np) {
		if (np->bt_right) {
			return(kl_first_btnode(np->bt_right));
		} else {
			parent = np->bt_parent;
			while (parent) {
				ret = strcmp(np->bt_key, parent->bt_key);
				if (ret < 0) {
					return(parent);
				}
				parent = parent->bt_parent;
			}
		}
	}
	return((btnode_t *)NULL);
}

/*
 * kl_prev_btnode()
 */
btnode_t *
kl_prev_btnode(btnode_t *np)
{
	int ret;
	btnode_t *next, *parent;

	if (np) {
		if ((next = np->bt_left)) {
			while (next->bt_right) {
				next = next->bt_right;
			}
			return(next);
		}
		parent = np->bt_parent;
		while (parent) {
			ret = strcmp(parent->bt_key, np->bt_key);
			if (ret < 0) {
				return(parent);
			}
			parent = parent->bt_parent;
		}
	}
	return((btnode_t *)NULL);
}

/* for debugging
void lprint(btnode_t*);

void lprint(btnode_t *r)
{	
	char *key_l=0, *key_r=0, *key_p=0;
	
	if (r == 0) {
		return;
	}
	
	if (r->bt_left != 0) {
		key_l = r->bt_left->bt_key;
	}
	else key_l = 0;
	if (r->bt_right != 0) {
		key_r = r->bt_right->bt_key;
	}
	else key_r = 0;
	if (r->bt_parent != 0) {
		key_p = r->bt_parent->bt_key;
	}
	else key_p = 0;
	
	printf("Node: %s (%p) Left: %s (%p) Right: %s (%p) Parent: %s (%p)\n"
		, r->bt_key, r
		, key_l, r->bt_left
		, key_r, r->bt_right
		, key_p, r->bt_parent
	);
	
	lprint(r->bt_left);
	lprint(r->bt_right);
}
*/
