#include <stdio.h>
#include <time.h>
#include <limits.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <math.h>

#define UCHAR unsigned char
#define ULONG unsigned long
#define USHRT unsigned short
#define dbl   double

/* Queue structure */
#define QUEUE_FULL_FLAG 1
#define QUEUE_EMPTY_FLAG -1
#define QUEUE_OK 0
//  
#define BITS_ELE_KNT    12  // 4.096 elements numbered 0-4095

typedef struct  {
    UCHAR   *pBuffer;   //  pointer to storage, 16 to 4096 elements
    ULONG Tail  :BITS_ELE_KNT;  //  # elements, with range of 0-4095
    ULONG Head  :BITS_ELE_KNT;  //  # elements, with range of 0-4095
    ULONG EleBytes  :8;     //  sizeof(elements) with range of 0-256 bytes
    USHRT EleKnt    :BITS_ELE_KNT +1;// 1 extra bit for # elements (1-4096)
    USHRT   IsFull  :1;     // queue is full
    USHRT   IsEmpty :1;     // queue is empty
    USHRT   Unused  :1;     // 16th bit of USHRT
}   QUEUE_DESC;

//  ---------------------------------------------------------------------------
//  Function prototypes
QUEUE_DESC *Q_Init(QUEUE_DESC *Q, int BitsForEleKnt, int DataTypeSz);
int Q_Put(QUEUE_DESC *Q, UCHAR *pNew);
int Q_Get(UCHAR *pOld, QUEUE_DESC *Q);

//  ---------------------------------------------------------------------------
QUEUE_DESC *Q_Init(QUEUE_DESC *Q, int BitsForEleKnt, int DataTypeSz)    {
    memset((void *)Q, 0, sizeof(QUEUE_DESC));//init flags and bit integers to zero
    Q->EleKnt   =   (USHRT)pow(2.0, BitsForEleKnt);
    Q->EleBytes =   DataTypeSz;
    Q->Head = Q->Tail = 0;
		
	if(NULL == (Q->pBuffer = (UCHAR *)calloc(Q->EleKnt, Q->EleBytes)))  {
        return NULL;
    }   else    {
        return Q;
    }
}

//  ---------------------------------------------------------------------------
int Q_Put(QUEUE_DESC *Q, UCHAR *pNew)   
{
    memcpy(Q->pBuffer + (Q->Tail * Q->EleBytes), pNew, Q->EleBytes);

	if(Q->Tail == (Q->Head + Q->EleKnt)) {
        Q->Tail += 1;   
        return QUEUE_FULL_FLAG; //  queue is full
    }

    Q->Tail += 1;   
    return QUEUE_OK; 
}

//  ---------------------------------------------------------------------------
int Q_Get(UCHAR *pOld, QUEUE_DESC *Q)   
{
    memcpy(pOld, Q->pBuffer + (Q->Head * Q->EleBytes), Q->EleBytes);
    Q->Head += 1;   

    if(Q->Head == Q->Tail)      {
        return QUEUE_EMPTY_FLAG; // queue Empty - nothing to get
    }

    return QUEUE_OK; // No errors
}

//
//  ---------------------------------------------------------------------------
int main(void)    {

    int i = 0;
	QUEUE_DESC Queue, *Q;

    if(NULL == (Q = Q_Init(&Queue, BITS_ELE_KNT, sizeof(int)))) {
        printf("\nProgram failed to initialize. Aborting.\n\n");
        return 0;
    }

	printf("Let's try to put 10.000 elements in a circular buffer of 4.096 elements.\n");
	printf("So, before start we have:\n");
	printf("  Q->Head = %i, Q->Tail = %i\n", Q->Head, Q->Tail);
	printf("  Q->EleKnt = %i\n", Q->EleKnt);
	
    for (i = 0; i < 10000; i++)    
	{
        int dummy = i * i;
        
		if(QUEUE_FULL_FLAG == Q_Put(Q, (UCHAR *)&dummy))    {
            
			printf("Oh Crap. Queue became full at %i.\n", i);
            break;
        }
    }

	if (i >= 10000)
		printf("WTF. Queue is still not full? BUT I JUST ADDED 10.000 ELEMENTS!? \n");
    
	printf("After the loop we have the following values:\n");
	printf("  Q->Head = %i, Q->Tail = %i\n", Q->Head, Q->Tail);

	printf("Note that 10000 mod 4096 = %i, and you overflowed the buffer %i times.\n", 10000 % 4096, 10000 / 4096);
	
    return 0;
}