#ifndef	TSTACK_H
#define TSTACK_H

/*
 *	Templates for stacks.
 *	Note: Also any LibV client can safely include this header to produce
 *	code for new stacks, I don't recommend to make this header visible:
 *	it can significantly slow down compilation on low-end machines.
 */

/*	======================================================================
 *	Internal macros for "typed stacks"
 */

/*		Call to set all fields to zeros ------------------------------- */
/*		Note: this routine must be called before any other operations.  */
#define	TYPED_STACK_INIT(stack) \
		(stack).n_elem=(stack).next_elem=0; \
		(stack).buf=NULL;

/*		Call to append next element ----------------------------------- */
#define	TYPED_STACK_PUSH(stack,element,type_elem) \
		if((stack).next_elem>=(stack).n_elem) { \
			/* Allocate new storage */ \
			unsigned nextsize; \
			GREATER_POWER_TWO(nextsize,(stack).next_elem,unsigned); \
			(stack).buf=(type_elem*)memRealloc((stack).buf, \
            	nextsize*sizeof(type_elem)); \
			(stack).n_elem=nextsize; \
		} \
		(stack).buf[(stack).next_elem++]=element;

/*		Call to append multiple elements ------------------------------ */
#define	TYPED_STACK_PUSH_BUFFER(stack,buffer,num_elements,type_elem) { \
			unsigned total; \
			total=(stack).next_elem+(num_elements); \
			if(total > (stack).n_elem) { \
				NOT_LESS_POWER_TWO(total,total,unsigned); \
				(stack).buf=(type_elem*)memRealloc( \
                	(stack).buf,total*sizeof(type_elem)); \
				(stack).n_elem=total; \
			} \
			memCopy((stack).buf+(stack).next_elem,(buffer), \
				(num_elements)*sizeof(type_elem)); \
			(stack).next_elem+=(num_elements); \
		}

/*		Call to copy memory from stack into 'dst' in FIFO order ------- */
#define	TYPED_STACK_POP_BUFFER_AS_FIFO(stack,dst,num_elements,type) \
	if((num_elements) > (stack).next_elem) \
		(num_elements)=(stack).next_elem; \
	(stack).next_elem-=(num_elements); \
	memCopy((dst), \
    	(stack).buf+(stack).next_elem,(num_elements)*sizeof(type)); \

/*		Call to set pointer to new position. -------------------------- */
/*		Buffer will be resized to greater size, if necessary.           */
#define	TYPED_STACK_RESIZE(stack,newsize,type_elem) \
		if((newsize) > (stack).n_elem) { \
			/* We must allocate new storage */ \
			unsigned nextlen; \
			NOT_LESS_POWER_TWO(nextlen,(newsize),unsigned); \
			(stack).buf=(type_elem*)memRealloc((stack).buf, \
            	nextlen*sizeof(type_elem)); \
			/* Fill tail with zeros */ \
			memZero((stack).buf+(stack).next_elem, \
				((newsize)-(stack).next_elem)*sizeof(type_elem)); \
			(stack).n_elem=nextlen; \
		} else { \
			/* We fit in current storage */ \
			if((newsize) > (stack).next_elem) \
				memZero((stack).buf+(stack).next_elem, \
					((newsize)-(stack).next_elem)*sizeof(type_elem)); \
		} \
		(stack).next_elem=(newsize);

/*		Call to free unused memory. ----------------------------------- */
#define	TYPED_STACK_FREE_UNUSED_MEMORY(stack,type_elem) \
		if((stack).next_elem) \
			/* We must memRealloc() to lower size */ \
			(stack).buf=(type_elem*)memRealloc((stack).buf, \
            	(stack).next_elem*sizeof(type_elem));\
		else { \
        	/* There are no data - clear stack */ \
			memFree((stack).buf); \
            (stack).buf=NULL; \
		} \
		(stack).n_elem=(stack).next_elem;\

/*	----------------------------------------------------------------------
 *	Templates for "typed stacks"
 */
#define	TYPED_STACK_DEFINE_INIT(type_prefix) \
void	V_GLOBAL \
type_prefix##sInit(type_prefix##_stack_t *stack) \
{ \
	V_ASSERT(stack); \
	TYPED_STACK_INIT(*stack); \
}

#define	TYPED_STACK_DEFINE_CLEAR(type_prefix) \
void	V_GLOBAL \
type_prefix##sClear(type_prefix##_stack_t *stack) \
{ \
	V_ASSERT(stack); \
	if(stack->buf) \
		memFree(stack->buf); \
	TYPED_STACK_INIT(*stack); \
}

#define	TYPED_STACK_DEFINE_PUSH(type_prefix,type) \
void	V_GLOBAL \
type_prefix##sPush(type_prefix##_stack_t *stack,type element) \
{ \
	V_ASSERT(stack); \
	TYPED_STACK_PUSH(*stack,element,type); \
}

#define	TYPED_STACK_DEFINE_POP(type_prefix,type) \
int		V_GLOBAL \
type_prefix##sPop(type_prefix##_stack_t *stack,type *element) \
{ \
	V_ASSERT(stack && element); \
	if(stack->next_elem) { \
		*element=stack->buf[--stack->next_elem]; \
		return 1; \
	} else return 0; \
}

#define	TYPED_STACK_DEFINE_PUSH_BUFFER(type_prefix,type) \
void	V_GLOBAL \
type_prefix##sPushBuffer(type_prefix##_stack_t *stack, \
						type *src,unsigned num_elements) \
{ \
	V_ASSERT(stack && src && num_elements); \
	TYPED_STACK_PUSH_BUFFER(*stack,src,num_elements,type); \
}

#define	TYPED_STACK_DEFINE_POP_BUFFER(type_prefix,type) \
int		V_GLOBAL \
type_prefix##sPopBuffer(type_prefix##_stack_t *stack, \
						type *dst,unsigned num_elements) \
{ \
	V_ASSERT(stack && dst && num_elements); \
    TYPED_STACK_POP_BUFFER_AS_FIFO(*stack,dst,num_elements,type); \
	return num_elements; \
}

#define	TYPED_STACK_DEFINE_RESIZE(type_prefix,type) \
void	V_GLOBAL \
type_prefix##sResize(type_prefix##_stack_t *stack,unsigned newsize) \
{ \
	V_ASSERT(stack); \
	TYPED_STACK_RESIZE(*stack,newsize,type); \
}

#define	TYPED_STACK_DEFINE_ADJUST(type_prefix,type) \
void	V_GLOBAL \
type_prefix##sTrim(type_prefix##_stack_t *stack) \
{ \
	V_ASSERT(stack); \
	TYPED_STACK_FREE_UNUSED_MEMORY(*stack,type); \
}

#define	TYPED_STACK_DEFINE_COUNT(type_prefix) \
unsigned	V_GLOBAL \
type_prefix##sCount(type_prefix##_stack_t *stack) \
{ \
	V_ASSERT(stack); \
	return stack->next_elem; \
}

#define	TYPED_STACK_DEFINE_GET(type_prefix,type) \
type	V_GLOBAL \
type_prefix##sGet(type_prefix##_stack_t *stack,unsigned pos) \
{ \
	V_ASSERT(stack && stack->n_elem > pos); \
	return stack->buf[pos]; \
}

#define	TYPED_STACK_DEFINE_GET_STORAGE(type_prefix,type_ptr) \
type_ptr	V_GLOBAL \
type_prefix##sGetStorage(type_prefix##_stack_t *stack) \
{ \
	V_ASSERT(stack); \
	return stack->buf; \
}

#define	TYPED_STACK_DEFINE_SET(type_prefix,type) \
void	V_GLOBAL \
type_prefix##sSet(type_prefix##_stack_t *stack,unsigned pos,type element) \
{ \
	V_ASSERT(stack); \
	if(pos >= stack->next_elem) \
	{ \
		if(pos >= stack->n_elem) \
		{ \
			TYPED_STACK_RESIZE(*stack,pos+1,type); \
		} \
		stack->next_elem = pos+1; \
	} \
	stack->buf[pos]=element; \
}

#define	TYPED_STACK_DEFINE_CONCAT(type_prefix,type) \
void	V_GLOBAL \
type_prefix##sConcat(type_prefix##_stack_t *dst, \
					 type_prefix##_stack_t *src) \
{ \
	V_ASSERT(src && dst); \
	TYPED_STACK_PUSH_BUFFER(*dst,src->buf,src->next_elem,type); \
}

/*	Now collect all together ------------------------------------------ */
/*  To define new stack: invoke 'TYPED_STACK_IMPLEMENT_CLASS' in global
 *	scope, passing:
 *		- 'method_prefix' - letter(s) for prefix of routines;
 *		- 'element_type' - type of element (must be scalar or any other
 *			type with '=' operation defined);
 *		- 'pointer_type' - pointer to 'element_type'.
 */
#ifdef	STACK_NO_MACRO 

#define	TYPED_STACK_IMPLEMENT_CLASS(method_prefix, \
			element_type,pointer_type) \
		TYPED_STACK_DEFINE_INIT(method_prefix) \
		TYPED_STACK_DEFINE_CLEAR(method_prefix) \
		TYPED_STACK_DEFINE_PUSH(method_prefix,element_type) \
		TYPED_STACK_DEFINE_PUSH_BUFFER(method_prefix,element_type) \
		TYPED_STACK_DEFINE_POP(method_prefix,element_type) \
		TYPED_STACK_DEFINE_POP_BUFFER(method_prefix,element_type) \
		TYPED_STACK_DEFINE_RESIZE(method_prefix,element_type) \
		TYPED_STACK_DEFINE_ADJUST(method_prefix,element_type) \
		TYPED_STACK_DEFINE_CONCAT(method_prefix,element_type) \
		TYPED_STACK_DEFINE_SET(method_prefix,element_type) \
		TYPED_STACK_DEFINE_COUNT(method_prefix) \
		TYPED_STACK_DEFINE_GET(method_prefix,element_type) \
		TYPED_STACK_DEFINE_GET_STORAGE(method_prefix,pointer_type) 

#else	/* !STACK_NO_MACRO */

#define	TYPED_STACK_IMPLEMENT_CLASS(method_prefix, \
			element_type,pointer_type) \
		TYPED_STACK_DEFINE_INIT(method_prefix) \
		TYPED_STACK_DEFINE_CLEAR(method_prefix) \
		TYPED_STACK_DEFINE_PUSH(method_prefix,element_type) \
		TYPED_STACK_DEFINE_PUSH_BUFFER(method_prefix,element_type) \
		TYPED_STACK_DEFINE_POP(method_prefix,element_type) \
		TYPED_STACK_DEFINE_POP_BUFFER(method_prefix,element_type) \
		TYPED_STACK_DEFINE_RESIZE(method_prefix,element_type) \
		TYPED_STACK_DEFINE_ADJUST(method_prefix,element_type) \
		TYPED_STACK_DEFINE_CONCAT(method_prefix,element_type) \
		TYPED_STACK_DEFINE_SET(method_prefix,element_type) 

#endif	/* !STACK_NO_MACRO */

#endif	/* TSTACK_H */
