//==================================================== file = circQueue.c =====
//=  An implementation of a circular queue for messages                       =
//=============================================================================
//=  Notes:                                                                   =
//=    1) Works not just for strings, but any kind of byte data               =
//=---------------------------------------------------------------------------=
//=  Example execution:                                                       =
//=                                                                           =
//=   retcode = 0                                                             =
//=   retcode = 0                                                             =
//=   retcode = 11  buff = String one                                         =
//=   retcode = 11  buff = String two                                         =
//=   retcode = -3  buff =                                                    =
//=   retcode = 0                                                             =
//=   retcode = 0                                                             =
//=   retcode = 13  buff = String three                                       =
//=   retcode = 12  buff = String four                                        =
//=   retcode = -3  buff =                                                    =
//=---------------------------------------------------------------------------=
//=  Build: bcc32 circQueue.c                                                 =
//=---------------------------------------------------------------------------=
//=  Execute: circQueue                                                       =
//=---------------------------------------------------------------------------=
//=  Author: Ken Christensen                                                  =
//=          University of South Florida                                      =
//=          WWW: http://www.csee.usf.edu/~christen                           =
//=          Email: christen@csee.usf.edu                                     =
//=---------------------------------------------------------------------------=
//=  History: KJC (10/10/08) - Genesis                                        =
//=============================================================================
//----- Include files ---------------------------------------------------------
#include <stdio.h>              // Needed for printf()
#include <stdlib.h>             // Needed for malloc()
#include <string.h>             // Needed for memcpy() and strcpy()

//----- Defines for function cQueue() -----------------------------------------
#define          MAX      4     // Number of slots in circular buffer
#define      DEQUEUE      1     // Flag value for dequeue operation
#define      ENQUEUE      2     // Flag value for enqueue operation
#define           OK      0     // Return code for success
#define   QUEUE_FULL     -1     // Return code for queue full
#define    NO_MEMORY     -2     // Return code for malloc failed
#define  QUEUE_EMPTY     -3     // Return code for queue empty
#define     BAD_FLAG     -4     // Return code for bad flag

//----- Function prototype ----------------------------------------------------
int cQueue(int qFlag, char *buff, int buffLen);

//=============================================================================
//=  Main program                                                             =
//=============================================================================
void main(void)
{
  char    sBuff[256];           // Send buffer
  char    rBuff[256];           // Receive buffer
  int     buffLen;              // Buffer length
  int     retcode;              // Return code
  int     i;                    // Loop counter

  // Enqueue four buffers
  strcpy(sBuff, "String one");
  buffLen = strlen(sBuff) + 1;
  retcode = cQueue(ENQUEUE, sBuff, buffLen);
  printf("retcode = %d \n", retcode);
  strcpy(sBuff, "String two");
  buffLen = strlen(sBuff) + 1;
  retcode = cQueue(ENQUEUE, sBuff, buffLen);
  printf("retcode = %d \n", retcode);

  // Dequeue three buffers
  retcode = cQueue(DEQUEUE, rBuff, 0);
  printf("retcode = %d  buff = ", retcode);
  for (i=0; i<retcode; i++)
    printf("%c", rBuff[i]);
  printf("\n");
  retcode = cQueue(DEQUEUE, rBuff, 0);
  printf("retcode = %d  buff = ", retcode);
  for (i=0; i<retcode; i++)
    printf("%c", rBuff[i]);
  printf("\n");
  retcode = cQueue(DEQUEUE, rBuff, 0);
  printf("retcode = %d  buff = ", retcode);
  for (i=0; i<retcode; i++)
    printf("%c", rBuff[i]);
  printf("\n");

  // Enqueue two buffers
  strcpy(sBuff, "String three");
  buffLen = strlen(sBuff) + 1;
  retcode = cQueue(ENQUEUE, sBuff, buffLen);
  printf("retcode = %d \n", retcode);
  strcpy(sBuff, "String four");
  buffLen = strlen(sBuff) + 1;
  retcode = cQueue(ENQUEUE, sBuff, buffLen);
  printf("retcode = %d \n", retcode);

  // Dequeue three buffers
  retcode = cQueue(DEQUEUE, rBuff, 0);
  printf("retcode = %d  buff = ", retcode);
  for (i=0; i<retcode; i++)
    printf("%c", rBuff[i]);
  printf("\n");
  retcode = cQueue(DEQUEUE, rBuff, 0);
  printf("retcode = %d  buff = ", retcode);
  for (i=0; i<retcode; i++)
    printf("%c", rBuff[i]);
  printf("\n");
  retcode = cQueue(DEQUEUE, rBuff, 0);
  printf("retcode = %d  buff = ", retcode);
  for (i=0; i<retcode; i++)
    printf("%c", rBuff[i]);
  printf("\n");
}

//=============================================================================
//=  Function to enqueue or dequeue a buffer                                  =
//=---------------------------------------------------------------------------=
//=  If qFlag is ENQUEUE then                                                 =
//=    - buff is input buffer of bytes                                        =
//=    - buffLen is length of buffer in bytes                                 =
//=    - return value is OK for success, QUEUE_FULL for queue full, and       =
//=      NO_MEMORY for malloc failed                                          =
//=  If qFlag is DEQUEUE then                                                 =
//=    - buff is returned buffer of bytes                                     =
//=    - return value is buffer length (> 0) for success, QUEUE_EMPTY for     =
//=      queue empty                                                          =
//=  If qFlag is not ENQUEUE or DEQUEUE                                       =
//=    - return value is BAD_FLAG                                             =
//=---------------------------------------------------------------------------=
//=  Notes:                                                                   =
//=    1) MAX is the number of slots in the buffer and is defined in main()   =
//=    2) The flags ENQUEUE, DEQUEUE, OK, QUEUE_FULL, NO_MEMORY, QUEUE_EMPTY, =
//=       and BAD_FLAG are defined in main()                                 =
//=============================================================================
int cQueue(int qFlag, char *buff, int buffLen)
{
  struct Buffer
  {
    char *buffer;
    int  len;
  };                                    // Stucture for a buffer
  static struct Buffer CBuffer[MAX+1];  // The circular buffer
  static int headPtr = 0;               // Index of next empty buffer
  static int tailPtr = 0;               // Index of last non-empty buffer

  // Enqueue or dequeue a buff
  if (qFlag == ENQUEUE)
  {
    if (((headPtr + 1) % (MAX + 1)) != tailPtr)
    {
      // Allocate buffer space for next slot in CBuffer
      CBuffer[headPtr].buffer = (char *) malloc(sizeof(char) * buffLen);
      if (CBuffer[headPtr].buffer == NULL)
        return(NO_MEMORY);

      // Copy buff and buffLen into CBuffer
      memcpy(CBuffer[headPtr].buffer, buff, buffLen);
      CBuffer[headPtr].len = buffLen;

      // Increment headPtr
      headPtr = (headPtr + 1) % (MAX + 1);

      // Return with success
      return(OK);
    }
    else
      return(QUEUE_FULL);   // Return with queue full
  }
  else if (qFlag == DEQUEUE)
  {
    if (tailPtr != headPtr)
    {
      // Copy Cbuffer into buff and buflen
      memcpy(buff, CBuffer[tailPtr].buffer, CBuffer[tailPtr].len);
      buffLen = CBuffer[tailPtr].len;

      // Free previously allocated memory for buffer and zero-out len
      free(CBuffer[tailPtr].buffer);
      CBuffer[tailPtr].len = 0;

      // Increment tailPtr
      tailPtr = (tailPtr + 1) % (MAX + 1);

      // Return with success (which is the length of dequeued buffer)
      return(buffLen);
    }
    else
      return(QUEUE_EMPTY);  // Return with queue empty
  }
  else
    return(BAD_FLAG);       // Retunr with bad flag
}
