#ifndef	V_FIFO_H
#define V_FIFO_H

/*
 * 09.02.2003 - complete rewrite of byte FIFO buffer.
 */

/*
 * The structure below represents two similar objects:
 * byte FIFO buffer (with unlimited size) and 'ring buffer'.
 * Routines with 'rbf' prefix keeps FIFO's size; routines
 * with 'u8f' prefix may grow buffer, if necessary.
 */
typedef struct u8_fifo_s
{
	u8_t	*start;
	u8_t	*head, *tail, *end;
	vsize_t	len, space_avail;
}	u8_fifo_t, *u8_fifo_p;

/*
 * 'Block' FIFO. The original idea isn't mine.
 */
typedef struct	fifo_s
{
	vsize_t	element_size;
	vsize_t	size;
	vsize_t	avail;
	u8_t	*buf,*head,*tail,*limit;
} fifo_t, *fifo_p;

#ifdef	__cplusplus
extern "C" {
#endif	/* __cplusplus */
	/* ----------------- byte FIFO methods ----------------------------- */
	/* must be called once at start */
	void	V_GLOBAL	u8fInit(u8_fifo_p f);
	/* frees all memory (but FIFO still remains _usable_) */
	void	V_GLOBAL	u8fClear(u8_fifo_p f);
	/* stores one byte in FIFO; always succeeds (how mush memory U have?) */
	void	V_GLOBAL	u8fPush(u8_fifo_p f,u8_t elem);
	/* retrieves one byte from FIFO; fails if FIFO is empty */
	vboolean V_GLOBAL	u8fPop(u8_fifo_p f,u8_t *elem_ret);
	/* stores 'nb' bytes in FIFO; always succeeds */
	void	V_GLOBAL	u8fPushBuffer(u8_fifo_p f,u8_t *buf,vsize_t nb);
	/* retrieves up to 'nb' bytes; returns number of retrieved bytes */
	vsize_t	V_GLOBAL	u8fPopBuffer(u8_fifo_p f,u8_t *buf,vsize_t nb);
	/* sets FIFO to 'empty' state, but don't free's memory */
	void	V_GLOBAL	u8fPurge(u8_fifo_p f);
	/* ----------------- ring buffer's methods ------------------------- */
	/* Initializes ring buffer to size 'fifo_size'; must be called _once_ */
	void	V_GLOBAL	rbfInit(u8_fifo_p f,vsize_t fifo_size);
	/* frees all memory (ring buffer becomes _unusable_) */
#define	rbfClear		u8fClear
	/* stores one byte in ring buffer; fails if no more space */
	vboolean V_GLOBAL	rbfPush(u8_fifo_p f,u8_t elem);
	/* stores up to 'nb' bytes; returns number of stored bytes */
	vsize_t V_GLOBAL	rbfPushBuffer(u8_fifo_p f,u8_t *buf,vsize_t nb);
	/* retrieves one byte from ring buffer; fails if no more bytes */
#define	rbfPop			u8fPop
	/* retrieves up to 'nb' bytes; returns number of retrieved bytes */
#define	rbfPopBuffer	u8fPopBuffer
	/* sets ring buffer to 'empty' state, but don't free's memory */
#define	rbfPurge		u8fPurge
	/* returns number of bytes in ring buffer */
#define	rbfCount		u8fCount

#ifndef	FIFO_NO_MACRO
#define	u8fCount(fifo)				RBF_LEN(fifo)
#define	fifoCount(fifo)				((fifo)->size - (fifo)->avail)
#else	/* FIFO_NO_MACRO */
	vsize_t	V_GLOBAL	u8fCount(u8_fifo_t *fifo);
	u32_t	V_GLOBAL	fifoCount(fifo_t *fifo);
#endif	/* FIFO_NO_MACRO */
	/*
	 * 'Block' FIFO methods.
	 */
	void	V_GLOBAL	fifoInit(fifo_t *fifo,vsize_t element_size);
	void	V_GLOBAL	fifoPush(fifo_t *fifo,void *element);
	vboolean V_GLOBAL	fifoPop(fifo_t *fifo,void *element);
	void	V_GLOBAL	fifoClear(fifo_t *fifo);
	void	V_GLOBAL	fifoPurge(fifo_t *fifo);
#ifdef	__cplusplus
}
#endif	/* __cplusplus */

/*
 * Name: RBF_LEN(u8_fifo_p f)
 * Purpose: returns number of bytes stored in FIFO.
 */
#define	RBF_LEN(f)					((f)->len - (f)->space_avail)

/*
 * Name: RBF_SPACE_AVAIL(u8_fifo_p f)
 * Purpose: returns amount of available space in FIFO.
 */
#define	RBF_SPACE_AVAIL(f)			((f)->space_avail)

/*
 * Name: RBF_CHAIN_LENS(u8_fifo_p f,vsize_t &len1,vsize_t &len2)
 * Purpose: stores lengths of the FIFO's data chains in len1 and len2.
 * Pointers to data can be obtainded by RBF_CHAIN[1|2] macros.
 */
#define RBF_CHAIN_LENS(f,len1,len2) { \
		vsize_t nb = (f)->len - (f)->space_avail; \
		(len1) = (f)->end - (f)->tail; \
		if((len1) > nb) \
			(len1) = nb; \
		(len2) = nb - (len1); \
	}

#define RBF_CHAIN1_LEN(f,clen) { \
		vsize_t nb = (f)->len - (f)->space_avail; \
		(clen) = (f)->end - (f)->tail; \
		if((clen) > nb) \
			(clen) = nb; \
	}

#define RBF_CHAIN2_LEN(f,clen) { \
		vsize_t len1, nb = (f)->len - (f)->space_avail; \
		len1 = (f)->end - (f)->tail; \
		if(len1 > nb) \
			len1 = nb; \
		clen = nb - len1; \
	}

/*
 * Name: RBF_CHAIN[1|2](u8_fifo_p f)
 * Purpose: returns pointer to 1st/2nd FIFO's data chain.
 */
#define RBF_CHAIN1(f) ((f)->tail)
#define RBF_CHAIN2(f) ((f)->start)

/*
 * Name: RBF_FREE_CHAIN_LENS(u8_fifo_p f,vsize_t &len1,vsize_t &len2)
 * Purpose: stores lengths of the FIFO's free chains in len1 and len2.
 * Pointers to data can be obtainded by RBF_FREE_CHAIN[1|2] macros.
 */
#define RBF_FREE_CHAIN_LENS(f,len1,len2) \
	(len1) = (f)->end - (f)->head; \
	if((len1) > (f)->space_avail) \
		(len1) = (f)->space_avail; \
	(len2) = (f)->space_avail - (len1); \

/*
 * Name: RBF_FREE_CHAIN[1|2](u8_fifo_p f)
 * Purpose: returns pointer to 1st/2nd FIFO's free chain.
 */
#define RBF_FREE_CHAIN1(f) ((f)->head)
#define RBF_FREE_CHAIN2(f) ((f)->start)

/*
 * Name: RBF_PUSH_NOCHECK(u8_fifo_p f,u8_t elem)
 * Purpose: stores one byte in FIFO without any checks.
 */
#define	RBF_PUSH_NOCHECK(f,elem) \
	*(f)->head = (elem); \
	(f)->space_avail--; \
	if(++(f)->head == (f)->end) \
		(f)->head = (f)->start; \

/*
 * Name: RBF_POP_NOCHECK(u8_fifo_p f,u8_t *ret_ptr)
 * Purpose: retrieves one byte from FIFO without any checks.
 */
#define	RBF_POP_NOCHECK(f,ret_ptr) \
	(f)->space_avail++; \
	*(ret_ptr) = *(f)->tail; \
	if(++(f)->tail == (f)->end) \
		(f)->tail = (f)->start; \

/*
 * Name: RBF_MOVE_TAIL(u8_fifo_p f,vsize_t delta)
 * Purpose: moves FIFO's tail, effectively decreasing FIFO's lenght.
 */
#define RBF_MOVE_TAIL(f,delta) \
	(f)->tail += (delta); \
	if((f)->tail > (f)->end) \
		(f)->tail -= (f)->len; \
	(f)->space_avail += (delta);

/*
 * Name: RBF_SET_EMPTY(u8_fifo_p f)
 * Purpose: zeroes FIFO's lenght.
 * Note: equivalent of RBF_MOVE_TAIL(f,rbfCount(f))
 */
#define RBF_SET_EMPTY(f) \
	(f)->tail = (f)->head; \
	(f)->space_avail = (f)->len; \

/*
 * Name: RBF_MOVE_HEAD(u8_fifo_p f,vsize_t delta)
 * Purpose: moves FIFO's head, effectively increasing FIFO's lenght.
 */
#define RBF_MOVE_HEAD(f,delta) \
	(f)->head += (delta); \
	if((f)->head > (f)->end) \
		(f)->head -= (f)->len; \
	(f)->space_avail -= (delta);

/*
 * Name: RBF_SET_FULL(u8_fifo_p f)
 * Purpose: zeroes FIFO's available space.
 * Note: equivalent of RBF_MOVE_HEAD(f,f->len - rbfCount(f))
 */
#define RBF_SET_FULL(f) \
	(f)->head = (f)->tail; \
	(f)->space_avail = 0; \

#endif	/* V_FIFO_H */
