/*
 * (c) Copyright 1993 by Panagiotis Tsirigotis
 * All rights reserved.  The file named COPYRIGHT specifies the terms 
 * and conditions for redistribution.
 */

static char RCSid[] = "$Id: dll.c,v 3.3 93/09/28 21:07:56 panos Exp $" ;

#include "dllimpl.h"


dict_h dll_create( oo_comp, ko_comp, flags, errnop )
	dict_function	oo_comp ;					/* object-object comparator */
	dict_function	ko_comp ;					/* key-object comparator */
	int				flags ;
	int				*errnop ;
{
	header_s			*hp ;
	char				*id = "dll_create" ;

	if ( ! __dict_args_ok( id,
				flags, errnop, oo_comp, ko_comp, DICT_ORDERED + DICT_UNORDERED ) )
		return( NULL_HANDLE ) ;

	hp = (header_s *) malloc( sizeof( header_s ) ) ;
	if ( hp == NULL )
		return( __dict_create_error( id, flags, errnop, DICT_ENOMEM ) ) ;
	
	/*
	 * Create an allocator
	 */
	hp->alloc = fsm_create( sizeof( node_s ), 0,
				( flags & DICT_RETURN_ERROR ) ? FSM_RETURN_ERROR : FSM_NOFLAGS ) ;
	if ( hp->alloc == NULL )
	{
		free( (char *)hp ) ;
		return( __dict_create_error( id, flags, errnop, DICT_ENOMEM ) ) ;
	}
	
	/*
	 * Allocate and initialize head node
	 */
	hp->head = (node_s *) fsm_alloc( hp->alloc ) ;
	if ( hp->head == NULL )
	{
		fsm_destroy( hp->alloc ) ;
		free( (char *)hp ) ;
		return( __dict_create_error( id, flags, errnop, DICT_ENOMEM ) ) ;
	}

	NEXT( hp->head ) = PREV( hp->head ) = hp->head ;
	OBJ( hp->head ) = NULL ;

	/*
	 * Initialize dictionary header, hints
	 */
	__dict_init_header( DHP( hp ), oo_comp, ko_comp, flags, errnop ) ;
	HINT_CLEAR( hp, last_search ) ;
	HINT_CLEAR( hp, last_successor ) ;
	HINT_CLEAR( hp, last_predecessor ) ;

	return( (dict_h) hp ) ;
}


void dll_destroy( handle )
	dict_h		handle ;
{
	header_s		*hp = LHP( handle ) ;

	fsm_destroy( hp->alloc ) ;
	free( (char *)hp ) ;
}



PRIVATE int dll_do_insert( hp, must_be_uniq, object, objectp )
	register header_s		*hp ;
	bool_int					must_be_uniq ;
	register dict_obj		object ;
	dict_obj					*objectp ;
{
	register dheader_s	*dhp				= DHP( hp ) ;
	register bool_int 	unordered_list = ( dhp->flags & DICT_UNORDERED ) ;
	register node_s		*np ;
	node_s					*new ;
	node_s					*before, *after ;
	char						*id = "dll_do_insert" ;

	if ( object == NULL )
		HANDLE_ERROR( dhp, id, DICT_ENULLOBJECT, DICT_ERR ) ;

	if ( unordered_list && ! must_be_uniq )
		np = NEXT( hp->head ) ;
	else
	{
		/*
		 * Find node n such that key(OBJ(n)) >= key(object)
		 */
		for ( np = NEXT( hp->head ) ; np != hp->head ; np = NEXT( np ) )
		{
			register int v = (*dhp->oo_comp)( OBJ( np ), object ) ;

			if ( v > 0 && ! unordered_list )
				break ;
			if ( v == 0 )
				if ( must_be_uniq )
				{
					if ( objectp != NULL )
						*objectp = OBJ( np ) ;
					ERRNO( dhp ) = DICT_EEXISTS ;
					return( DICT_ERR ) ;
				}
				else
					break ;
		}
	}

	new = (node_s *) fsm_alloc( hp->alloc ) ;
	if ( new == NULL )
		HANDLE_ERROR( dhp, id, DICT_ENOMEM, DICT_ERR ) ;

	/*
	 * The new node is inserted BEFORE np
	 */
	before = PREV( np ) ;
	after = np ;
	NEXT( new ) = after ;
	PREV( new ) = before ;
	NEXT( before ) = new ;
	PREV( after ) = new ;
	OBJ( new ) = object ;
	if ( objectp != NULL )
		*objectp = object ;
	return( DICT_OK ) ;
}


int dll_insert( handle, object )
	dict_h		handle ;
	dict_obj		object ;
{
	header_s		*hp = LHP( handle ) ;

	return( dll_do_insert( hp, hp->dh.flags & DICT_UNIQUE_KEYS,
					object, (dict_obj *)NULL ) ) ;
}



int dll_insert_uniq( handle, object, objectp )
	dict_h		handle ;
	dict_obj		object ;
	dict_obj		*objectp ;
{
	header_s		*hp	= LHP( handle ) ;
	dheader_s	*dhp	= DHP( hp ) ;

	if ( dhp->oo_comp == NULL_FUNC )
		HANDLE_ERROR( dhp, "dll_insert_uniq", DICT_ENOOOCOMP, DICT_ERR ) ;
	return( dll_do_insert( hp, TRUE, object, objectp ) ) ;
}


int dll_delete( handle, object )
	dict_h				handle ;
	register dict_obj object ;
{
	register header_s *hp	= LHP( handle ) ;
	dheader_s			*dhp	= DHP( hp ) ;
	register node_s	*np ;
	node_s				*after, *before ;

	if ( object == NULL )
		HANDLE_ERROR( dhp, "dll_delete", DICT_ENULLOBJECT, DICT_ERR ) ;

	if ( OBJ( hp->hint.last_search ) == object )
		np = hp->hint.last_search ;
	else
		for ( np = NEXT( hp->head ) ;; np = NEXT( np ) )
			if ( np == hp->head )
			{
				ERRNO( dhp ) = DICT_ENOTFOUND ;
				return( DICT_ERR ) ;
			}
			else if ( object == OBJ( np ) )
				break ;

	/*
	 * First disconnect, then release
	 */
	after = NEXT( np ) ;
	before = PREV( np ) ;
	NEXT( before ) = after ;
	PREV( after ) = before ;
	OBJ( np ) = NULL ;
	fsm_free( hp->alloc, (char *)np ) ;

	/*
	 * Clear all hints
	 */
	HINT_CLEAR( hp, last_search ) ;
	HINT_CLEAR( hp, last_successor ) ;
	HINT_CLEAR( hp, last_predecessor ) ;

	return( DICT_OK ) ;
}


dict_obj dll_search( handle, key )
	dict_h					handle ;
	register dict_key		key ;
{
	register header_s		*hp				= LHP( handle ) ;
	register dheader_s	*dhp				= DHP( hp ) ;
	register bool_int		unordered_list	= ( dhp->flags & DICT_UNORDERED ) ;
	register node_s		*np ;

	for ( np = NEXT( hp->head ) ; np != hp->head ; np = NEXT( np ) )
	{
		register int v = (*dhp->ko_comp)( key, OBJ( np ) ) ;

		if ( v == 0 )
		{
			hp->hint.last_search = np ;		/* update search hint */
			return( OBJ( np ) ) ;
		}
		else if ( v < 0 && ! unordered_list )
			break ;
	}
	return( NULL_OBJ ) ;
}


/*
 * Returns a pointer to the object with the smallest key value or
 * NULL if the list is empty.
 *
 * NOTE: here we depend on the fact that OBJ( head ) == NULL
 */
dict_obj dll_minimum( handle )
	dict_h		handle ;
{
	header_s		*hp = LHP( handle ) ;
	node_s		*np = NEXT( hp->head ) ;

	hp->hint.last_successor = np ;			/* update hint */
	return( OBJ( np ) ) ;
}


/*
 * Returns a pointer to the object with the greatest key value or
 * NULL if the list is empty.
 *
 * NOTE: here we depend on the fact that OBJ( head ) == NULL
 */
dict_obj dll_maximum( handle )
	dict_h		handle ;
{
	header_s		*hp = LHP( handle ) ;
	node_s		*np = PREV( hp->head ) ;

	hp->hint.last_predecessor = np ;			/* update hint */
	return( OBJ( np ) ) ;
}


/*
 * Returns a pointer to the object with the next >= key value or
 * NULL if the list is empty or the given object is the last one on the
 * list.
 *
 * NOTE: here we depend on the fact that OBJ( head ) == NULL
 */
dict_obj dll_successor( handle, object )
	dict_h				handle ;
	register dict_obj object ;
{
	register header_s *hp	= LHP( handle ) ;
	dheader_s			*dhp	= DHP( hp ) ;
	register node_s	*np ;
	node_s				*successor ;
	char					*id = "dll_successor" ;

	if ( object == NULL )
		HANDLE_ERROR( dhp, id, DICT_ENULLOBJECT, NULL_OBJ ) ;

	if ( OBJ( hp->hint.last_successor ) == object )
		successor = NEXT( hp->hint.last_successor ) ;
	else
	{
		ERRNO( dhp ) = DICT_ENOERROR ;
		for ( np = NEXT( hp->head ) ; np != hp->head ; np = NEXT( np ) )
			if ( OBJ( np ) == object )
				break ;
		if ( np == hp->head )
			HANDLE_ERROR( dhp, id, DICT_EBADOBJECT, NULL_OBJ ) ;
		successor = NEXT( np ) ;
	}
	hp->hint.last_successor = successor ;
	return( OBJ( successor ) ) ;
}



/*
 * Returns a pointer to the object with the next <= key value or
 * NULL if the list is empty or the given object is the first one on the
 * list.
 *
 * NOTE: here we depend on the fact that OBJ( head ) == NULL
 */
dict_obj dll_predecessor( handle, object )
	dict_h				handle ;
	register dict_obj object ;
{
	register header_s *hp	= LHP( handle ) ;
	dheader_s			*dhp	= DHP( hp ) ;
	node_s				*predecessor ;
	register node_s	*np ;
	char					*id = "dll_predecessor" ;

	if ( object == NULL )
		HANDLE_ERROR( dhp, id, DICT_ENULLOBJECT, NULL_OBJ ) ;

	if ( OBJ( hp->hint.last_predecessor ) == object )
		predecessor = PREV( hp->hint.last_predecessor ) ;
	else
	{
		ERRNO( dhp ) = DICT_ENOERROR ;
		for ( np = PREV( hp->head ) ; np != hp->head ; np = PREV( np ) )
			if ( OBJ( np ) == object )
				break ;
		if ( np == hp->head )
			HANDLE_ERROR( dhp, id, DICT_EBADOBJECT, NULL_OBJ ) ;
		predecessor = PREV( np ) ;
	}
	hp->hint.last_predecessor = predecessor ;
	return( OBJ( predecessor ) ) ;
}


void dll_iterate( handle, direction )
	dict_h					handle ;
	enum dict_direction	direction ;
{
	register header_s		*hp	= LHP( handle ) ;
	dheader_s				*dhp	= DHP( hp ) ;
	struct dll_iterator	*dip	= &hp->iter ;

	if ( dhp->flags & DICT_UNORDERED )
		dip->direction = DICT_FROM_START ;
	else
		dip->direction = direction ;

	if ( dip->direction == DICT_FROM_START )
		dip->next = NEXT( hp->head ) ;
	else
		dip->next = PREV( hp->head ) ;
}


dict_obj dll_nextobj( handle )
	dict_h		handle ;
{
	struct dll_iterator	*dip		= &LHP( handle )->iter ;
	node_s					*current = dip->next ;

	if ( dip->direction == DICT_FROM_START )
		dip->next = NEXT( current ) ;
	else
		dip->next = PREV( current ) ;
	return( OBJ( current ) ) ;
}

