#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); \
prefix##_deque_p calling_convention \
prefix##DeqScanForward(prefix##_deque_p deq, \
	vboolean (calling_convention *reject)(storage_class*,void_p),void_p arg); \
prefix##_deque_p calling_convention \
prefix##DeqScanBackward(prefix##_deque_p deq, \
	vboolean (calling_convention *reject)(storage_class*,void_p),void_p arg); \
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; \
} \
\
prefix##_deque_p calling_convention \
prefix##DeqScanForward(prefix##_deque_p deq, \
	vboolean (calling_convention *reject)(storage_class*,void_p),void_p arg) \
{ \
	prefix##_deque_p head, ptr, tail; \
	if(!deq) \
		return NULL; \
	deq = deq->prev; \
	head = NULL; \
	tail = deq; \
	do \
	{ \
		ptr = deq->prev; \
		if(reject(&deq->store,arg)) \
			memFree(deq); \
		else \
		{ \
			deq->next = head; \
			head = deq; \
		} \
		deq = ptr; \
	} while(deq != tail); \
	if(!head) \
		return NULL; \
	for(tail = head, ptr = head->next;ptr;ptr = ptr->next) \
	{ \
		ptr->prev = tail; \
		tail = ptr; \
	} \
	head->prev = tail; \
	tail->next = head; \
	return head; \
} \
\
prefix##_deque_p calling_convention \
prefix##DeqScanBackward(prefix##_deque_p deq, \
	vboolean (calling_convention *reject)(storage_class*,void_p),void_p arg) \
{ \
	prefix##_deque_p head, ptr, tail; \
	if(!deq) \
		return NULL; \
	head = deq; \
	tail = NULL; \
	do \
	{ \
		ptr = deq->next; \
		if(reject(&deq->store,arg)) \
			memFree(deq); \
		else \
		{ \
			deq->prev = tail; \
			tail = deq; \
		} \
		deq = ptr; \
	} while(deq != head); \
	if(!tail) \
		return NULL; \
	for(head = tail, ptr = tail->prev;ptr;ptr = ptr->prev) \
	{ \
		ptr->next = head; \
		head = ptr; \
	} \
	head->prev = tail; \
	tail->next = head; \
	return head; \
} \
\
void	calling_convention \
prefix##DeqFree(prefix##_deque_p deq) \
{ \
	prefix##_deque_p head, next; \
	if(!deq) \
		return; \
	head = deq; \
	do \
	{ \
		next = deq->next; \
		memFree(deq); \
		deq = next; \
	} while(deq != head); \
}

#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_SCAN_FORWARD(prefix,deq,reject,arg) \
	((deq) = prefix##DeqScanForward(deq,reject,arg))

#define	DEQ_SCAN_BACKWARD(prefix,deq,reject,arg) \
	((deq) = prefix##DeqScanBackward(deq,reject,arg))

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

#endif	/* V_DEQUE_H */
