#ifndef	V_DEQUE_H
#define	V_DEQUE_H

/*
 * Template for deque implementation with circular doubly-linked list.
 */

#define	DEQUE_STRUCT(prefix,storage_class) \
typedef struct prefix##_deque_s \
{ \
	storage_class	store; \
	struct prefix##_deque_s *next, *prev; \
}	prefix##_deque_t, *prefix##_deque_p;

#define	DECLARE_DEQUE_API(prefix,calling_convention,storage_class) \
\
vsize_t	calling_convention prefix##DeqCount(prefix##_deque_p deq); \
prefix##_deque_p calling_convention prefix##DeqPush(prefix##_deque_p deq, \
	storage_class *store); \
prefix##_deque_p calling_convention prefix##DeqPushBack(prefix##_deque_p deq, \
	storage_class *store); \
prefix##_deque_p calling_convention prefix##DeqPop(prefix##_deque_p deq, \
	storage_class *store_ret); \
prefix##_deque_p calling_convention prefix##DeqPopBack(prefix##_deque_p deq, \
	storage_class *store_ret); \
vboolean	calling_convention prefix##DeqPeek(prefix##_deque_p deq, \
	vsize_t offset_from_last_push,storage_class **store_ptr_ret); \
vboolean	calling_convention prefix##DeqPeekBack(prefix##_deque_p deq, \
	vsize_t offset_from_first_push,storage_class **store_ptr_ret); \
prefix##_deque_p calling_convention prefix##DeqRemove(prefix##_deque_p deq, \
	vsize_t offset_from_last_push); \
prefix##_deque_p calling_convention prefix##DeqRemoveBack(prefix##_deque_p deq, \
	vsize_t offset_from_first_push); \
void	calling_convention prefix##DeqFree(prefix##_deque_p deq);

#define	IMPLEMENT_DEQUE_API(prefix,calling_convention,storage_class) \
\
vsize_t	calling_convention \
prefix##DeqCount(prefix##_deque_p deq) \
{ \
	vsize_t count = 0; \
	prefix##_deque_p ptr = deq; \
	if(deq) \
		for(;;) \
		{ \
			count++; \
			if(ptr->next == deq) \
				break; \
			ptr = ptr->next; \
		} \
	return count; \
} \
\
prefix##_deque_p	calling_convention \
prefix##DeqPush(prefix##_deque_p deq,storage_class *store) \
{ \
	prefix##_deque_p node = \
		(prefix##_deque_p)memAllocZ(sizeof(prefix##_deque_t)); \
	memCopy(&node->store,store,sizeof(storage_class)); \
	if(!deq) \
	{ \
		node->next = node; \
		node->prev = node; \
		return node; \
	} \
	/* insert _before_ que */ \
	node->next = deq; \
	node->prev = deq->prev; \
	deq->prev = node; \
	node->prev->next = node; \
	return node; \
} \
\
prefix##_deque_p	calling_convention \
prefix##DeqPop(prefix##_deque_p deq,storage_class *store_ret) \
{ \
	prefix##_deque_p next, prev; \
	if(!deq) \
		return NULL; \
	if(store_ret) \
		memCopy(store_ret,&deq->store,sizeof(storage_class)); \
	next = deq->next; \
	prev = deq->prev; \
	next->prev = prev; \
	prev->next = next; \
	if(next == deq) \
		next = NULL; \
	memFree(deq); \
	return next; \
} \
\
prefix##_deque_p	calling_convention \
prefix##DeqPushBack(prefix##_deque_p deq,storage_class *store) \
{ \
	prefix##_deque_p new_head = prefix##DeqPush(deq,store); \
	return deq ? deq : new_head; \
} \
\
prefix##_deque_p	calling_convention \
prefix##DeqPopBack(prefix##_deque_p deq,storage_class *store_ret) \
{ \
	return deq ? prefix##DeqPop(deq->prev,store_ret) : NULL; \
} \
 \
vboolean	calling_convention \
prefix##DeqPeek(prefix##_deque_p deq, \
	vsize_t offset_from_last_push,storage_class **store_ptr_ret) \
{ \
	vsize_t count = 0; \
	prefix##_deque_p ptr = deq; \
	if(!ptr) \
		return false; \
	do \
	{ \
		if(count == offset_from_last_push) \
		{ \
			if(store_ptr_ret) \
				*store_ptr_ret = &ptr->store; \
			return true; \
		} \
		count++; \
		ptr = ptr->next; \
	} while(ptr != deq); \
	return false; \
} \
 \
vboolean	calling_convention \
prefix##DeqPeekBack(prefix##_deque_p deq, \
	vsize_t offset_from_first_push, storage_class **store_ptr_ret) \
{ \
	vsize_t count = 0; \
	prefix##_deque_p ptr = deq; \
	if(!ptr) \
		return false; \
	do \
	{ \
		if(count == offset_from_first_push) \
		{ \
			if(store_ptr_ret) \
				*store_ptr_ret = &ptr->prev->store; \
			return true; \
		} \
		count++; \
		ptr = ptr->prev; \
	} while(ptr != deq); \
	return false; \
} \
 \
prefix##_deque_p calling_convention \
prefix##DeqRemove(prefix##_deque_p deq, \
	vsize_t offset_from_last_push) \
{ \
	vsize_t count = 0; \
	prefix##_deque_p ptr = deq; \
	if(!ptr) \
		return NULL; \
	do \
	{ \
		if(count == offset_from_last_push) \
		{ \
			if(ptr == deq) \
				return prefix##DeqPop(ptr,NULL); \
			prefix##DeqPop(ptr,NULL); \
			return deq; \
		} \
		count++; \
		ptr = ptr->next; \
	} while(ptr != deq); \
	return deq; \
} \
 \
prefix##_deque_p calling_convention \
prefix##DeqRemoveBack(prefix##_deque_p deq, \
	vsize_t offset_from_first_push) \
{ \
	vsize_t count = 0; \
	prefix##_deque_p ptr = deq; \
	if(!ptr) \
		return NULL; \
	do \
	{ \
		ptr = ptr->prev; \
		if(count == offset_from_first_push) \
		{ \
			if(ptr == deq) \
				return prefix##DeqPop(ptr,NULL); \
			prefix##DeqPop(ptr,NULL); \
			return deq; \
		} \
		count++; \
	} while(ptr != deq); \
	return deq; \
} \
 \
void	calling_convention \
prefix##DeqFree(prefix##_deque_p deq) \
{ \
	prefix##_deque_p ptr; \
	vsize_t i, n = prefix##DeqCount(deq); \
	for(i=0;i<n;i++) \
	{ \
		ptr = deq->next; \
		memFree(deq); \
		deq = ptr; \
	} \
}

#define	DEQ_COUNT(prefix,deq) \
	prefix##DeqCount(deq)

#define	DEQ_PUSH(prefix,deq,store) \
	((deq) = prefix##DeqPush(deq,store))

#define	DEQ_PUSH_BACK(prefix,deq,store) \
	((deq) = prefix##DeqPushBack(deq,store))

#define	DEQ_POP(prefix,deq,store_ret) \
	((deq) = prefix##DeqPop(deq,store_ret))

#define	DEQ_POP_BACK(prefix,deq,store_ret) \
	((deq) = prefix##DeqPopBack(deq,store_ret))

#define	DEQ_PEEK(prefix,deq,offset_from_last_push,store_ret) \
	prefix##DeqPeek(deq,offset_from_last_push,store_ret)

#define	DEQ_PEEK_BACK(prefix,deq,offset_from_first_push,store_ret) \
	prefix##DeqPeekBack(deq,offset_from_first_push,store_ret)

#define	DEQ_REMOVE(prefix,deq,offset_from_last_push) \
	((deq) = prefix##DeqRemove(deq,offset_from_last_push))

#define	DEQ_REMOVE_BACK(prefix,deq,offset_from_first_push) \
	((deq) = prefix##DeqRemoveBack(deq,offset_from_first_push))

#define	DEQ_FREE(prefix,deq)	prefix##DeqFree(deq)

#endif	/* V_DEQUE_H */
