/*
*	In this example we will use the user field.
*	We build an associative array. It will pair strings and
*	integers. You can look up a string by the integer and
*	vice versa. For the sake of simplicity we don't care about
*	memory now, we assume that we have malloc() and free().
*
*	We actually build two trees, one for the strings and one for
*	the integers. They are linked through the common node that
*	holds the data for both. We will use the user parameter to
*	distinguish between the two trees.
*
*	We don't detail our own header, it simply contains the prototypes
*	for the API functions. This is the C implementation file.
*/

#include "our_API_protos.h"
#include <string.h>
#include <malloc.h>

/*
*	Define the node type
*/

typedef struct s_node {

	struct s_node	*int_l;		// Integer tree left subtree
	struct s_node	*int_r;		// Integer tree right subtree
	unsigned	 	int_h;		// Integer tree height
	int				int_k;		// Integer tree key

	struct s_node	*str_l;		// String tree left subtree
	struct s_node	*str_r;		// String tree right subtree
	unsigned	 	str_h;		// String tree height
	const char		*str_k;		// String tree key

} *AVL_NODE;

/*
*	The key type is int. When we have to pass the string pointer as
*	key, it should be cast to an int. If the pointer type is wider
*	than the int type, you could do it the other way around, define
*	AVL_KEYT to be a pointer and cast the int to a pointer.
*/

typedef	int		AVL_KEYT;

/*
*	The user type will be an int
*/

typedef int		AVL_USER;

/*
*	Since we use pointers, AVL_NULL is NULL
*/

#define	AVL_NULL	NULL


/*
*	We don't care about memory, so we just define the workspace size to a
*	really high value.
*/

#define	AVL_PLEN	64


/*
*	Now we define the get/set functions and the compare. The user parameter
*	will be used to distinguish between the integer tree and the string tree.
*	If the user parameter is 0, then we use the integer tree, if it is not
*	0, we use the string tree. We don't declare the functions inline, if
*	inlining is prudent, then gcc will inline them anyway.
*/

static AVL_NODE	AVL_GET_L( AVL_NODE node, AVL_USER user )
{
	return user ? node->str_l : node->int_l;
}

static AVL_NODE	AVL_GET_R( AVL_NODE node, AVL_USER user )
{
	return user ? node->str_r : node->int_r;
}

static unsigned	AVL_GET_H( AVL_NODE node, AVL_USER user )
{
	return user ? node->str_h : node->int_h;
}

static void		AVL_SET_L( AVL_NODE node, AVL_NODE tree, AVL_USER user )
{
	if ( user )

		node->str_l = tree;
	else
		node->int_l = tree;
}

static void		AVL_SET_R( AVL_NODE node, AVL_NODE tree, AVL_USER user )
{
	if ( user )

		node->str_r = tree;
	else
		node->int_r = tree;
}

static void		AVL_SET_H( AVL_NODE node, unsigned height, AVL_USER user )
{
	if ( user )

		node->str_h = height;
	else
		node->int_h = height;
}

static int		AVL_CMP( AVL_NODE node, AVL_KEYT key, AVL_USER user )
{
	if ( user ) {

		return strcmp( node->str_k, (char *) key );
	}
	else {

		if ( node->int_k < key ) return -1;
		if ( node->int_k > key ) return +1;
		return 0;
	}
}

/*
*	Get the template and start the implementation
*/

#include <misc/avl.h>

static AVL_NODE	root_int;	// Root for the integer tree
static AVL_NODE	root_str;	// Root for the string tree
static AVL_PATH work;		// Workspace

/*
*	Init
*/

void	AssocInit( void )
{
	root_int = NULL;
	root_str = NULL;
}

/*
*	Look up a string by the int.
*	Returns NULL if the key is not found.
*/

const char *AssocFindByInt( int key )
{
AVL_NODE	node;

	// We pass the integer root and the 0 user parameter
	// will tell the AVL code to use the int_? fields.

	node = AVL_Search( root_int, key, 0 );

	if ( node )

		return node->str_k;
	else
		return NULL;
}

/*
*	Look up an int by a string.
*	If the string is found, then the int will be copied to the location
*	pointed by the 'result' pointer and 1 will be returned, if the string
*	is not found, then 0 is returned and the memory is not modified.
*/

int		AssocFindByString( const char *key, int *result )
{
AVL_NODE	node;

	// We pass the string root and the 1 user parameter will
	// tell the AVL code to use the str_? fields. We have to
	// cast the char pointer to the key type, which is an int.

	node = AVL_Search( root_str, (AVL_KEYT) key, 1 );

	if ( node ) {

		*result = node->int_k;
		return 1;
	}
	else
		return 0;
}

/*
*	Inserts a new node
*	Returns:
*	 0 if succeeded or the same pair was on the list already
*	 1 the int is on the tree with a different string
*	 2 the string is on the tree with a different int
*	 3 both int and string are on the tree with different pairs
*/

int		AssocRemember( const char *key_str, int key_int )
{
AVL_NODE	node;
AVL_NODE	test;

	// Allocate a new node

	node = malloc( sizeof( struct s_node ) );

	// Try to insert it into the integer tree first

	if ( test = AVL_Insert( &root_int, &work, node, key_int, 0 ) ) {

		// This int is already on the tree. We can release the node structure.

		free( node );

		// Let's see if the the node on the tree has the same string key
		// as the one we wanted to insert would have.

		if ( ! strcmp( test->str_k, key_str ) ) {

			// It's the same, so the same pair got registered again,
			// which is fine. Report success.

			return 0;
		}
		else {

			// OK, the int is on the tree with a different string.
			// Our return code depends on whether the string is on
			// the tree or not

			if ( AVL_Search( root_str, (AVL_KEYT) key_str, 1 ) )

				return 3;
			else
				return 1;
		}
	}

	// The int was not on the tree (but is now!).
	// Try to insert to the string tree.

	if ( AVL_Insert( &root_str, &work, node, key_str, 1 ) ) {

		// The string is already on the tree. Remove the new node from
		// the integer tree, release the memory and report error.

		AVL_Delete( &root_int, &work, key_int, 0 );
		free( node );
		return 2;
	}

	// Both insertions succeded, put the keys into the node and
	// report success.

	node->str_k = malloc( strlen( key_str ) + 1 );
	strcpy( node->str_k, key_str );
	node_int_k = key_int;
	return 0 ;
}

/*
*	Delete a node.
*	It receives both an int key and a string key. If the string key is NULL,
*	then it deletes the node identified by the integer key, if the string
*	key is not NULL, then it deletes the node by the string and the integer
*	key will be ignored. If the key is not found, then it does nothing.
*/

void	AssocForget( const char *key_str, int key_int )
{
AVL_NODE	node;

	if ( key_str )

		node = AVL_Delete( &root_str, &work, (AVL_KEYT) key_str, 1 );
	else
		node = AVL_Delete( &root_int, &work, key_int, 0 );

	if ( node ) {

		if ( key_str )

			AVL_Delete( &root_int, &work, node->int_k, 0 );
		else
			AVL_Delete( &root_str, &work, (AVL_KEYT) node->str_k, 1 );

		free( node->key_s );
		free( node );
	}
}

