/*
 * Video On Demand Samples
 *
 * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * You may also obtain this software under a propriety license from Microchip.
 * Please contact Microchip for further information.
 *
 */

/*----------------------------------------------------------*/
/*! \file
 *  \brief  This file contains the CRingBuffer class.
 */
/*----------------------------------------------------------*/

#ifndef CRINGBUFFER_H
#define CRINGBUFFER_H

#include "Console.h"


/*----------------------------------------------------------*/
/*! \brief Class providing a ring buffer. One thread can
*        write to the buffer while another thread is reading
*        from it. This class has highest performance for
*        synchronization, since it is not using mutexes.
*
*        LIMITATIONS:
*        - only one thread can read
*        - only one thread can write
*        - the ring buffer size needs to be aligned to
*          read, write block size
*
*        Violating any limitation will lead to unpredictable
*        results.
*/
/*----------------------------------------------------------*/
class CRingBuffer
{
    uint8_t *m_pBuf;
    uint32_t m_nWriteTot;                                   // total bytes written to ring buffer
    uint32_t m_nReadTot;                                    // total bytes read from ring buffer
    uint32_t m_nWritePos;                                   // memory write position
    uint32_t m_nReadPos;                                    // memory read position
    uint32_t m_nReadLen;                                    // bytes per read
    uint32_t m_nWriteLen;                                   // bytes per write
    uint32_t m_nBufLen;                                     // bytes of buffer

public:
    /*----------------------------------------------------------*/
    /*! \brief constructs a ring buffer instance
     *  \param nReadLen - read block size in bytes
     *  \param nWriteLen - write block size in bytes
     *  \param nWriteBlocks - total buffer size counted in write
     *         blocks
     */
    /*----------------------------------------------------------*/
    CRingBuffer(uint32_t nReadLen, uint32_t nWriteLen, uint32_t nWriteBlocks)
        : m_nWriteTot(0)
        , m_nReadTot(0)
        , m_nWritePos(0)
        , m_nReadPos(0)
        , m_nReadLen(nReadLen)
        , m_nWriteLen(nWriteLen)
	, m_nBufLen(nWriteBlocks*nWriteLen)
    {
        if ( 0 != (m_nBufLen % m_nReadLen) ) {
            ConsolePrintf( PRIO_ERROR, RED"! ring size needs to be dividable by read block size"RESETCOLOR);
            throw (-10);
        }
        m_pBuf = new uint8_t[m_nBufLen];

        if (NULL == m_pBuf) {
            ConsolePrintf( PRIO_ERROR, RED"! out of memory"RESETCOLOR);
            throw (-10);
        }
    }



    /*----------------------------------------------------------*/
    /*! \brief destructs ring buffer instance
     */
    /*----------------------------------------------------------*/
    ~CRingBuffer()
    {
        delete m_pBuf;
    }



    /*----------------------------------------------------------*/
    /*! \brief checks if there is enough memory available to
     *        write a chunk of data to the ring buffer
     * \return true, if can be written
     */
    /*----------------------------------------------------------*/
    bool GetCanWrite()
    {
        uint32_t nFilled = m_nWriteTot - m_nReadTot;            
        return nFilled < m_nBufLen - m_nWriteLen;
    }



    /*----------------------------------------------------------*/
    /*! \brief checks if there is enough memory available to
     *        read a chunk of data from the ring buffer
     * \return true, if can be read
     */
    /*----------------------------------------------------------*/
    bool GetCanRead()
    {
        uint32_t nFilled = m_nWriteTot - m_nReadTot;            
        return nFilled >= m_nReadLen;  
    }



    /*----------------------------------------------------------*/
    /*! \brief gets pointer to current read position
     * \return pointer to current read position
     */
    /*----------------------------------------------------------*/
    uint8_t *GetReadPos()
    {
        return m_pBuf + m_nReadPos;                             
    }



    /*----------------------------------------------------------*/
    /*! \brief gets pointer to current write position
     * \return pointer to current write position
     */
    /*----------------------------------------------------------*/
    uint8_t *GetWritePos()
    {
        return m_pBuf + m_nWritePos;                            
    }



    /*----------------------------------------------------------*/
    /*! \brief called after a chunk of data has been written
     *        to the ring buffer
     */
    /*----------------------------------------------------------*/
    void WriteDone()
    {
        m_nWriteTot += m_nWriteLen;
        m_nWritePos += m_nWriteLen;
        if (m_nBufLen == m_nWritePos)
            m_nWritePos = 0;
    }



    /*----------------------------------------------------------*/
    /*! \brief called after a chunk of data has been read
     *        from the ring buffer
     */
    /*----------------------------------------------------------*/
    void ReadDone()
    {
        m_nReadTot += m_nReadLen;
        m_nReadPos += m_nReadLen;
        if (m_nBufLen == m_nReadPos)
            m_nReadPos = 0;
    }
};

#endif