#include <../source/TAL/objbase.h>

/*
 *	Helper APIs for binary and n-way trees.
 */

V_LOCAL	btree_p
alloc_btree_node(btree_t *parent,int child_index,u32_t extra)
{
	btree_t *n;
	if(parent && parent->children[child_index])
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"alloc_btree_node(): %s child already present.\n",
			child_index ? "right" : "left");
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	n=(btree_t*)memAlloc(sizeof(btree_t)+extra);
	n->parent=parent;
	n->children[0]=n->children[1]=NULL;
	if(parent)
		parent->children[child_index]=n;
	return n;
}

V_LOCAL	void
btDeleteAllNodesFast_r(btree_t *root)
{
	if(!root)
		return;
	btDeleteAllNodesFast_r(root->children[0]);
	btDeleteAllNodesFast_r(root->children[1]);
	memFree(root);
}

V_LOCAL	void
btDeleteAllNodes_r(btree_t *root,void (V_CALL *destructor)(void*))
{
	if(!root)
		return;
	btDeleteAllNodes_r(root->children[0],destructor);
	btDeleteAllNodes_r(root->children[1],destructor);
	destructor(root->data);
	memFree(root);
}

V_LOCAL	ntree_p
alloc_tree_node(u32_t extra,ntree_t *parent)
{
	ntree_t *n=(ntree_t*)memAlloc(sizeof(ntree_t)+extra);
	n->parent=parent;
	vpsInit(&n->children);
	if(parent)
		vpsPush(&parent->children,n);
	return n;
}

V_LOCAL	void
treeDeleteAllNodesFast_r(ntree_t *root)
{
	u32_t ind,count;
	ntree_p *ch;
	if(!root)
		return;
	ch=(ntree_p*)vpsGetStorage(&root->children);
	count=vpsCount(&root->children);
	for(ind=0;ind<count;ind++)
		treeDeleteAllNodesFast_r(ch[ind]);
	vpsClear(&root->children);
	memFree(root);
}

V_LOCAL	void
treeDeleteAllNodes_r(ntree_t *root,void (V_CALL *destructor)(void*))
{
	u32_t ind,count;
	ntree_p *ch;
	if(!root)
		return;
	ch=(ntree_p*)vpsGetStorage(&root->children);
	count=vpsCount(&root->children);
	for(ind=0;ind<count;ind++)
		treeDeleteAllNodes_r(ch[ind],destructor);
	vpsClear(&root->children);
	destructor(root->data);
	memFree(root);
}

/*
 *	Global APIs for binary and n-way trees.
 */

btree_p	V_GLOBAL
btAllocNode(void *data,btree_t *parent,int child_index)
{
	btree_t *n=alloc_btree_node(parent,child_index,0);
	if(n)
		n->data=data;
	return n;
}

btree_p	V_GLOBAL
btStore(void *data,u32_t len,btree_t *parent,int child_index)
{
	btree_t *n;
	if(!data || !len)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"btStore(): nothing to store.\n");
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	n=alloc_btree_node(parent,child_index,len);
	if(n)
	{
		n->data=(void*)(n+1);
		memCopy(n->data,data,len);
	}
	return n;
}

btree_p	V_GLOBAL
btRootNode(btree_t *node)
{
	if(!node)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"btRootNode(): couldn't ascend from NULL.\n");
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	while(node->parent)
		node=node->parent;
	return node;
}

/*
 *	Removes all nodes from leftmost to rightmost.
 */
void	V_GLOBAL
btDeleteAllNodes(btree_t *root,void (V_CALL *destructor)(void*))
{
	if(!root)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"btDeleteAllNodes(): nothing to delete.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	if(destructor)
	{
		btDeleteAllNodes_r(root->children[0],destructor);
		btDeleteAllNodes_r(root->children[1],destructor);
		destructor(root->data);
	}
	else
	{
		btDeleteAllNodesFast_r(root->children[0]);
		btDeleteAllNodesFast_r(root->children[1]);
	}
	memFree(root);
}

ntree_p	V_GLOBAL
treeAllocNode(void *data,ntree_t *parent)
{
	ntree_t *n;
	n=alloc_tree_node(0,parent);
	n->data=data;
	return n;
}

ntree_p	V_GLOBAL
treeStore(void *data,u32_t len,ntree_t *parent)
{
	ntree_t *n;
	if(!data || !len)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"treeStore(): nothing to store.\n");
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	n=alloc_tree_node(len,parent);
	n->data=(void*)(n+1);
	memCopy(n->data,data,len);
	return n;
}

ntree_p	V_GLOBAL
treeRootNode(ntree_t *node)
{
	if(!node)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"treeRootNode(): couldn't ascend from NULL.\n");
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	while(node->parent)
		node=node->parent;
	return node;
}

void	V_GLOBAL
treeDeleteAllNodes(ntree_t *root,void (V_CALL *destructor)(void*))
{
	if(!root)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"treeDeleteAllNodes(): nothing to delete.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	if(destructor)
		treeDeleteAllNodes_r(root,destructor);
	else
		treeDeleteAllNodesFast_r(root);
}

/*
**	Implementation of abstract type 'SET' using binary trees.
*/

/*
**	Allocates node for unical 'data' and inserts in tree.
**	Original copyright (C) Caolan.McNamara@ul.ie
*/
btree_p	V_GLOBAL
btInsertNode(btree_p root,void *data,
	int (V_CALL *compar)(void *,void *,void *),void *arg)
{
	int child;
	btree_t *new_node, *node, *parent;
	if(!compar)
		return NULL;
	node = root;
	parent = NULL;
	while(node)
	{
		if(!compar(data,node->data,arg))
			return NULL;
		parent = node;
		node = compar(data,node->data,arg) < 0 ?
			node->children[0] : node->children[1];
	}
	if(parent)
	{
		child = compar(data,parent->data,arg) < 0 ? 0 : 1;
		new_node = btAllocNode(data,parent,child);
	}
	else
		new_node = btAllocNode(data,NULL,0);
	return new_node;
}

/*
**	Finds node containing 'data'.
**	Original copyright (C) Caolan.McNamara@ul.ie
*/
btree_p	V_GLOBAL
btFindNode(btree_p root,void *data,
	int (V_CALL *compar)(void *,void *,void *),void *arg)
{
	int comp;
	if(!compar)
		return NULL;
	while(root)
	{
		comp = compar(data,root->data,arg);
		if(comp == 0)
			break;
		else if(comp < 0)
			root = root->children[0];
		else
			root = root->children[1];
	}
	return root;
}

/*
**	Removes node containing 'data'.
**	Original copyright (C) Caolan.McNamara@ul.ie
*/
vboolean	V_GLOBAL
btDeleteNode(btree_p *root,void *data,
	int (V_CALL *compar)(void *,void *,void *),void *arg,
	void (V_CALL *destructor)(void*))
{
	btree_p X, Y, node;
	if(!root || !*root || !compar)
		return false;
	node=btFindNode(*root,data,compar,arg);
	if(!node)
		return false;
	if(!node->children[0] || !node->children[1])
		Y = node;
	else
	{
		Y = node->children[1];
		while(Y->children[0])
			Y = Y->children[0];
	}
	/* X is Y's only child */
	if(Y->children[0])
		X = Y->children[0];
	else
		X = Y->children[1];
	/* remove Y from the parent chain */
	if(X)
		X->parent = Y->parent;
	if(Y->parent)
		if(Y == Y->parent->children[0])
			Y->parent->children[0] = X;
		else
			Y->parent->children[1] = X;
	else
		*root = X;
	/* Y is the node we're removing */
	/* 'node' is the data we're removing (?) */
	/* if node and Y are not the same, replace node with Y. */
	if (Y != node)
	{
		Y->children[0] = node->children[0];
		if (Y->children[0])
			Y->children[0]->parent = Y;
		Y->children[1] = node->children[1];
		if (Y->children[1])
			Y->children[1]->parent = Y;
		Y->parent = node->parent;
		if (node->parent)
			if (node == node->parent->children[0])
				node->parent->children[0] = Y;
			else
				node->parent->children[1] = Y;
		else
			*root = Y;
	}
	else
		node=Y;
	if(destructor)
		destructor(node->data);
	memFree(node);
	return true;
}

