#include "FraBufQ.h"
// Frame buffer queue reference implementation
// Based on "producer - consumer" theory :
//	a producer can only produce more when the queue is not full
//	a consumer can only consume when the queue is not empty
//	and both producing and consuming need some time to complete
//	so for both, 2 steps are used.
//		first,  mark the beginning of produce/consume (GetProduceIndex/ GetConsumeIndex)
//		second, signal that produce/consume is done (ProduceDone/ ConsumeDone)
//	In our case, the image receiving code from camera is the producer
//		and the LCD controller is the consumer

// copy here for easier reference ---- poor memory of old man!
#if 0

typedef struct _DS_LcdFBQueue
{
	VUI32 pdcNdx;	
	VUI32 csmNdx;	
	VUI32 cnt;	
	UI32 cap;		
	US_VersatilePointer ptrs[FRABUF_Q_CAPACITY];
}DS_LcdFBQueue;

#define FBQ_ENTER_CRITICAL()	NVIC_DisableIRQ(LCD_IRQn)
#define FBQ_LEAVE_CRITICAL()	NVIC_EnableIRQ(LCD_IRQn)

#endif

DS_LcdFBQueue g_fbQ;
#define VRAM_SECTION __align(1024) __attribute__((section("SECT_VRAM"), zero_init))
static VRAM_SECTION UI8 s_fbs[FRABUF_Q_CAPACITY][CAM_CAP_W * CAM_CAP_H * 2];

// For all "ERRCODE" return type functions, return values:
//	>= 0 : No error, and may provide more informations
//	< 0 : has error, -1L is generic error code

ERRCODE FBQ_Init(UI32 cap)
{
	UI32 i;	
	if (cap < 1 || cap > FRABUF_Q_CAPACITY)
		return E_GNRC;
	
	g_fbQ.cap = cap;
	g_fbQ.pdcNdx = 0;
	g_fbQ.csmNdx = 0;
	g_fbQ.cnt = 0;
	for (i=0; i<cap; i++)
		g_fbQ.ptrs[i].pu8 = s_fbs[i];

	// init LCD interrupt
	{
		LPC_LCD_Type *pLCD = LPC_LCD;
		// enable base address update interrupt
		BVSET(pLCD->INTMSK, 2);
		// use first framebuffer
		pLCD->UPBASE = (UI32) s_fbs[0];
	}

	
	return E_OK;
}

// ppvFraBuf: 指向新的frame buffer的地址的指针

ERRCODE FBQ_GetProduceIndex(void **ppvFraBuf)
{
	ERRCODE err = E_GENERIC_OVERFLOW;
	// we use the critical section to protect queue control block from being corrupted by IRQ preemption
	FBQ_ENTER_CRITICAL();
	{
		// use "do - while(0)" loop to eliminate the need of scattered "goto" and/or "return",
		// make the code more structural and have only one exit, for safer & easier clean-up and reading
		BEGIN_DO_WHILE_0
		{
			if ((void**)0 == ppvFraBuf)
				break;			

				if (g_fbQ.cnt >= g_fbQ.cap)
				{
					*ppvFraBuf = (void*) 0;
					break;	
				}

			err = g_fbQ.pdcNdx;
			*ppvFraBuf = g_fbQ.ptrs[g_fbQ.pdcNdx].pv;
		}
		END_DO_WHILE_0		
	}
	FBQ_LEAVE_CRITICAL();
	
	return err;
}


ERRCODE FBQ_ProduceDone(void)
{
	ERRCODE err = E_GENERIC_OVERFLOW;
	
	FBQ_ENTER_CRITICAL();
	{
		BEGIN_DO_WHILE_0
		{
			if (g_fbQ.cnt >= g_fbQ.cap)
				break;
			if (++g_fbQ.pdcNdx >= g_fbQ.cap)
				g_fbQ.pdcNdx = 0;			
			err = ++g_fbQ.cnt;
		}
		END_DO_WHILE_0
		
	}
	FBQ_LEAVE_CRITICAL();
	
	return err;
}


ERRCODE FBQ_GetConsumeIndex(void **ppvFraBuf)
{
	ERRCODE err = E_GENERIC_OVERFLOW;
	// we use the critical section to protect queue control block from being corrupted by IRQ preemption
	FBQ_ENTER_CRITICAL();
	{
		// use "do - while(0)" loop to eliminate the need of scattered "goto" and/or "return",
		// make the code more structural and have only one exit, for safer & easier clean-up and reading
		BEGIN_DO_WHILE_0
		{
			if ((void**)0 == ppvFraBuf)
				break;
				if (0 == g_fbQ.cnt)
				{				
					*ppvFraBuf = (void*) 0;
					break;				
				}
				err = g_fbQ.csmNdx;
			*ppvFraBuf = g_fbQ.ptrs[g_fbQ.csmNdx].pv;
		}
		END_DO_WHILE_0		
	}
	FBQ_LEAVE_CRITICAL();
	
	return err;
}

ERRCODE FBQ_ConsumeDone(void)
{
	ERRCODE err = E_GENERIC_OVERFLOW;
	FBQ_ENTER_CRITICAL();
	{
		BEGIN_DO_WHILE_0
		{
			if (0 == g_fbQ.cnt)
				break;
			if (++g_fbQ.csmNdx >= g_fbQ.cap)
				g_fbQ.csmNdx = 0;			
			err = --g_fbQ.cnt;
		}
		END_DO_WHILE_0
		
	}
	FBQ_LEAVE_CRITICAL();
	return err;
}



// Purge the frame buffer queue, seldom used
ERRCODE FBQ_Purge(void)
{
	ERRCODE err = E_GNRC;
	
	FBQ_ENTER_CRITICAL();
	{
		UI32 i;
		g_fbQ.pdcNdx = 0;
		g_fbQ.csmNdx = 0;
		g_fbQ.cnt = 0;
		for (i=0; i<g_fbQ.cap; i++)
			g_fbQ.ptrs[i].addr = 0;

	}
	FBQ_LEAVE_CRITICAL();
	return err;
}

BOOL32 FBQ_IsFull(void)
{
	if (g_fbQ.cnt >= g_fbQ.cap)
		return 1;
	return 0;
}


