/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss or reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

#ifndef _DLINK_H
#define	_DLINK_H

/** @addtogroup misc_dll */
/** @{ */

/**
*	Member of a doubly linked list.
*/

typedef struct _blc_dll {

	struct _blc_dll *next;			//!< Pointer forward
	struct _blc_dll *prev;			//!< Pointer backward

} DLLINK;

/**
*	Doubly linked list
*/

typedef struct {

	DLLINK	*head;					//!< Pointer to the head of the list
	DLLINK	*tail;					//!< Pointer to the tail of the list

} DLLIST;

/** @} */

#ifndef BLC_DLINK_INLINE

	void	DL_Init( DLLIST *list );
	void	DL_PutHead( DLLIST *list, DLLINK *link );
	void	DL_PutTail( DLLIST *list, DLLINK *link );
	DLLINK	*DL_GetHead( DLLIST *list );
	DLLINK	*DL_GetTail( DLLIST *list );
	DLLINK	*DL_SeeHead( DLLIST *list );
	DLLINK	*DL_SeeTail( DLLIST *list );
	DLLINK	*DL_Next( DLLINK *link );
	DLLINK	*DL_Prev( DLLINK *link );
	void	DL_Remove( DLLIST *list, DLLINK *link );
	void	DL_InsertBefore( DLLIST *list, DLLINK *refp, DLLINK *link );
	void	DL_InsertAfter( DLLIST *list, DLLINK *refp, DLLINK *link );
	void	DL_Append( DLLIST *list1, DLLIST *list2 );
	void	DL_Cut( DLLIST *list1, DLLIST *list2, DLLINK *refp );

#else

	#if ! defined __GNUC_STDC_INLINE__ && ! defined __GNUC_GNU_INLINE__
		#if defined __STRICT_ANSI__ && __STRICT_ANSI__
			#define __GNUC_STDC_INLINE__ 1
		#else
			#define __GNUC_GNU_INLINE__ 1
		#endif
	#endif
	#if defined __GNUC_STDC_INLINE__ && __GNUC_STDC_INLINE__
		#define _static	static
	#else
		#define	_static extern
	#endif

	_static void	DL_Init( DLLIST *list );
	_static void	DL_PutHead( DLLIST *list, DLLINK	*link );
	_static void	DL_PutTail( DLLIST *list, DLLINK *link );
	_static DLLINK	*DL_GetHead( DLLIST *list );
	_static DLLINK	*DL_GetTail( DLLIST *list );
	_static DLLINK	*DL_SeeHead( DLLIST *list );
	_static DLLINK	*DL_SeeTail( DLLIST *list );
	_static DLLINK	*DL_Next( DLLINK *link );
	_static DLLINK	*DL_Prev( DLLINK *link );
	_static void	DL_Remove( DLLIST *list, DLLINK *link );
	_static void	DL_InsertBefore( DLLIST *list, DLLINK *refp, DLLINK *link );
	_static void	DL_InsertAfter( DLLIST *list, DLLINK *refp, DLLINK *link );
	_static void	DL_Append( DLLIST *list1, DLLIST *list2 );
	_static void	DL_Cut( DLLIST *list1, DLLIST *list2, DLLINK *refp );

	_static __inline__ void	DL_Init( DLLIST *list )
	{
		list->head = (void *) 0;
		list->tail = (void *) 0;
	}

	_static __inline__ void	DL_PutHead( DLLIST *list, DLLINK	*link )
	{
		if ( ( link->next = list->head ) )

			link->next->prev = link;
		else
			list->tail = link;

		link->prev = (void *) 0;
		list->head = link;
	}

	_static __inline__ void	DL_PutTail( DLLIST *list, DLLINK *link )
	{
		if ( ( link->prev = list->tail ) )

			link->prev->next = link;
		else
			list->head = link;

		link->next = (void *) 0;
		list->tail = link;
	}

	_static __inline__ DLLINK	*DL_GetHead( DLLIST *list )
	{
	DLLINK	*link;

		if ( ( link = list->head ) ) {

			if ( ( list->head = link->next ) )

				list->head->prev = (void *) 0;
			else
				list->tail = (void *) 0;
		}

		return( link );
	}

	_static __inline__ DLLINK	*DL_GetTail( DLLIST *list )
	{
	DLLINK	*link;

		if ( ( link = list->tail ) ) {

			if ( ( list->tail = link->prev ) )

				list->tail->next = (void *) 0;
			else
				list->head = (void *) 0;
		}

		return( link );
	}

	_static __inline__ DLLINK	*DL_SeeHead( DLLIST *list )
	{
		return( list->head );
	}

	_static __inline__ DLLINK	*DL_SeeTail( DLLIST *list )
	{
		return( list->tail );
	}

	_static __inline__ DLLINK	*DL_Next( DLLINK *link )
	{
		return( link->next );
	}

	_static __inline__ DLLINK	*DL_Prev( DLLINK *link )
	{
		return( link->prev );
	}

	_static __inline__ void	DL_Remove( DLLIST *list, DLLINK *link )
	{
		if ( link->prev )

			link->prev->next = link->next;
		else
			list->head = link->next;

		if ( link->next )

			link->next->prev = link->prev;
		else
			list->tail = link->prev;
	}

	_static __inline__ void	DL_InsertBefore( DLLIST *list, DLLINK *refp, DLLINK *link )
	{
		if ( ( link->prev = refp->prev ) )

			link->prev->next = link;
		else
			list->head = link;

		link->prev = refp->prev;
		refp->prev = link;
	}

	_static __inline__ void	DL_InsertAfter( DLLIST *list, DLLINK *refp, DLLINK *link )
	{

		if ( ( link->next = refp->next ) )

			link->next->prev = link;
		else
			list->tail = link;

		link->next = refp->next;
		refp->next = link;
	}

	_static __inline__ void	DL_Append( DLLIST *list1, DLLIST *list2 )
	{
		if ( ! list1->head )

			list1 = list2;
		else
			if ( ( list1->tail->next = list2->head ) )

				list2->head->prev = list1->tail;

		list1->tail = list2->tail;
		list2->head = (void *) 0;
		list2->tail = (void *) 0;
	}

	_static __inline__ void	DL_Cut( DLLIST *list1, DLLIST *list2, DLLINK *refp )
	{
		list2->head = refp;
		list2->tail = list1->tail;

		if ( ( list1->tail = link->prev ) )

			list1->tail->next = (void *) 0;
		else
			list1->head = (void *) 0;

		repf->prev = (void *) 0;
	}

	#undef _static

#endif
#endif

