/*-------------------------------- Arctic Core ------------------------------
 * Copyright (C) 2013, ArcCore AB, Sweden, www.arccore.com.
 * Contact: <contact@arccore.com>
 * 
 * You may ONLY use this file:
 * 1)if you have a valid commercial ArcCore license and then in accordance with  
 * the terms contained in the written license agreement between you and ArcCore, 
 * or alternatively
 * 2)if you follow the terms found in GNU General Public License version 2 as 
 * published by the Free Software Foundation and appearing in the file 
 * LICENSE.GPL included in the packaging of this file or here 
 * <http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>
 *-------------------------------- Arctic Core -----------------------------*/
/*
 * DESCRIPTION
 *  A circular buffer implementation.
 *
 *  This file implements the following versions:
 *  1. CirqBuffDynXXXX
 *     - The size of the data is not known of compile time
 *       (semidynamic, since there is no free() function )
 *  2. CirqBuffXXX
 *     - A static implementation, data is known at compile time.
 *       (implemented in the header file?)
 *
 * Implementation note:
 * - CirqBuffDynXXXX, valgrind: OK
 * - coverage: Not run
 *
 */
//#define _TEST_CIRQ_BUFFER_DYN_
/*lint -w1 */
/*lint -w2  -e526 -e628 */

/**
@file cirq_buffer.c
@brief A circular buffer implementation.
*/
/********************************************************************************************
Debug Switch Definition Section
********************************************************************************************/


/********************************************************************************************
Include File Section
********************************************************************************************/
#include <stdlib.h>
#include <string.h>
#include "cirq_buffer.h"
#include "log.h"
//#include "Os.h"
/********************************************************************************************
Macro Definition Section
********************************************************************************************/
#define MEMCPY(_x,_y,_z)	memcpy((_x),(_y),(_z))


/********************************************************************************************
Prototype Declaration Section
********************************************************************************************/
void vCirqBuffInit(CirqBufferType *cirqbuffer, void *buffer, uint32_t maxCnt, uint32_t dataSize);
int32_t lCirqBuffPush( CirqBufferType *cPtr, void *dataPtr );
int32_t lCirqBuffPop(CirqBufferType *cPtr, void *dataPtr );
void * pvCirqBuffPushLock( CirqBufferType *cPtr);
void * pvCirqBuffPopLock(CirqBufferType *cPtr );

/********************************************************************************************
Const Global Variable Definition Section
********************************************************************************************/


/********************************************************************************************
Global Variable Definition Section
********************************************************************************************/

/********************************************************************************************
Static Variable Definition Section
********************************************************************************************/


/********************************************************************************************
Structure Type Definition Section
********************************************************************************************/

/********************************************************************************************
Function Definition Section
********************************************************************************************/
static inline uint8_t CirqBuff_Empty(CirqBufferType *cPtr ) {
    return (cPtr->currCnt == 0);
}

static inline uint8_t CirqBuff_Full(CirqBufferType *cPtr ) {
    return (cPtr->currCnt == cPtr->maxCnt);
}

static inline int32_t CirqBuff_Size( CirqBufferType *cPtr ){
    return cPtr->currCnt;
}

static inline void CirqBuff_PushRelease( CirqBufferType *cPtr) {
    ++cPtr->currCnt;
}

static inline void CirqBuff_PopRelease( CirqBufferType *cPtr) {
    --cPtr->currCnt;
}

void vCirqBuffInit(CirqBufferType *cirqbuffer, void *buffer, uint32_t maxCnt, uint32_t dataSize)
{

    cirqbuffer->bufStart = buffer;
    cirqbuffer->maxCnt = maxCnt;
    cirqbuffer->bufEnd = (char *)cirqbuffer->bufStart + dataSize*maxCnt;
    cirqbuffer->head = cirqbuffer->bufStart;
    cirqbuffer->tail = cirqbuffer->bufStart;
    cirqbuffer->dataSize = dataSize;
    cirqbuffer->currCnt = 0;
}


/**
 * Push an entry to the buffer.
 *
 * @param cPtr    Pointer to the queue created with CirqBuffStatCreate, etc.
 * @param dataPtr
 * @return 0 - if successfully pushed.
 *         1- nothing pushed (it was full)
 */
int32_t lCirqBuffPush( CirqBufferType *cPtr, void *dataPtr ) 
{
    int32_t ret = 0;
    
    if( (cPtr==SAF_NULL) || (cPtr->currCnt == cPtr->maxCnt)) 
    {
        ret = 1;	/* No more room */
    }
    else
    {
        /*lint -save -e632 STANDARDIZED INTERFACE */
        MEMCPY(cPtr->head,dataPtr,cPtr->dataSize);
        /*lint -restore */
        cPtr->head = (char *)cPtr->head + cPtr->dataSize;
        if( cPtr->head == cPtr->bufEnd) 
        {
            cPtr->head = cPtr->bufStart;
        }
        ++cPtr->currCnt;
    }
    return ret;
}



/**
 * Pop an entry from the buffer.
 *
 * @param cPtr    Pointer to the queue created with CirqBuffStatCreate, etc.
 * @param dataPtr
 * @return 0 - if successfully popped.
 *         1- nothing popped (it was empty)
 */
int32_t lCirqBuffPop(CirqBufferType *cPtr, void *dataPtr )
{
    int32_t ret = 0;

    if((cPtr==SAF_NULL) || (cPtr->currCnt == 0) )
    {
        ret = 1;
    }
    else
    {
        /*lint -save -e632 STANDARDIZED INTERFACE */
        MEMCPY(dataPtr,cPtr->tail,cPtr->dataSize);
        /*lint -restore */
        cPtr->tail = (char *)cPtr->tail + cPtr->dataSize;
        if( cPtr->tail == cPtr->bufEnd) 
        {
            cPtr->tail = cPtr->bufStart;
        } 
        --cPtr->currCnt;
    }
    return ret;
}


/**
 * PushLock an entry to the buffer.
 * NOTE: This must be used in combination with CirqBuff_PushRelease when the
 * application has filled the buffer with data. Lock and release must be done
 * in consecutive order i.e lock/release/lock/release. lock/lock/release/release is not
 * allowed.
 *
 * @param cPtr    Pointer to the queue created with CirqBuffStatCreate, etc.
 * @return ptr to buffer - if successfully pushed.
 *         SAF_NULL - nothing pushed (it was full)
 */
void *pvCirqBuffPushLock( CirqBufferType *cPtr) 
{
    void *dataPtr;
    if((cPtr==SAF_NULL) || (cPtr->currCnt == cPtr->maxCnt)) 
    {
        dataPtr = SAF_NULL;	/* No more room */
    }
    else
    {
        dataPtr = cPtr->head;
        cPtr->head = (char *)cPtr->head + cPtr->dataSize;
        if( cPtr->head == cPtr->bufEnd) 
        {
            cPtr->head = cPtr->bufStart;
        }
    }
    return dataPtr;
}

/**
 * PopLock an entry from the buffer.
 * NOTE: This must be used in combination with CirqBuff_PopRelease when the
 * application has used or copied the buffer data. Lock and release must be done
 * in consecutive order i.e lock/release/lock/release. lock/lock/release/release is not
 * allowed.
 *
 * @param cPtr    Pointer to the queue created with CirqBuffStatCreate, etc.
 * @return ptr to buffer - if successfully pushed.
 *         SAF_NULL - nothing pushed (it was full)
 */
void * pvCirqBuffPopLock(CirqBufferType *cPtr ) 
{
    void *dataPtr;
    if((cPtr==SAF_NULL) || (cPtr->currCnt == 0)) 
    {
        dataPtr = SAF_NULL;
    }
    else
    {
        dataPtr = cPtr->tail;
        cPtr->tail = (char *)cPtr->tail + cPtr->dataSize;
        if( cPtr->tail == cPtr->bufEnd) 
        {
            cPtr->tail = cPtr->bufStart;
        }
    }
    return dataPtr;
}



