#include "CCycleBuffer.h"
#include <string.h>
//#include "cameraLog.h"
#include "kylinlogger_cpp.h"

CCycleBuffer::CCycleBuffer(uint32_t size)
{
    m_nBufSize = size;
    m_nReadPos = 0;
    m_nWritePos = 0;
    m_pBuf = new char[m_nBufSize];
    m_bEmpty = true;
    m_bFull = false;

    lock = new QReadWriteLock;
}

CCycleBuffer::~CCycleBuffer()
{
    delete[] m_pBuf;
    delete  lock;
}

uint32_t CCycleBuffer::Write(uint8_t * buf, uint32_t count)
{
    if(count <= 0)
        return 0;

    lock->lockForWrite();

    m_bEmpty = false;

    //buffer is full
    if(m_bFull)
    {
        lock->unlock();
        return 0;
    }
    else if(m_nReadPos == m_nWritePos) // buffer is empty
    {
        /*                          == mem model ==
        (empty)            m_nReadPos                (empty)
        |----------------------------------|-----------------------------------------|
        m_nWritePos									m_nBufSize
        */
        uint32_t leftcount = m_nBufSize - m_nWritePos;
        if(leftcount >= count)
        {
            memcpy(m_pBuf + m_nWritePos, buf, count);
            m_nWritePos += count;

            if(m_nWritePos == m_nReadPos + m_nBufSize)
                m_nWritePos = m_nReadPos = 0;

            m_bFull = (m_nWritePos == m_nReadPos);

            if( 0 == count)
            {
                m_bFull = false;
                m_bEmpty = true;
            }
            lock->unlock();
            return count;
        }
        else
        {
            memcpy(m_pBuf + m_nWritePos, buf, leftcount);
            m_nWritePos = (m_nReadPos >= count - leftcount) ? count - leftcount : m_nWritePos;
            memcpy(m_pBuf, buf + leftcount, m_nWritePos);

            m_bFull = (m_nWritePos == m_nReadPos);

            if( m_nWritePos == m_nBufSize)
            {
                m_bFull = false;
                m_bEmpty = true;
                Q_ASSERT(0 == count);
            }

            Q_ASSERT(m_nReadPos <= m_nBufSize);
            Q_ASSERT(m_nWritePos <= m_nBufSize);
            lock->unlock();
            return leftcount + m_nWritePos;
        }
    }
    else if(m_nReadPos < m_nWritePos)
    {
        /*                           == mem model ==
        (empty)                 (data)                     (empty)
        |-------------------|----------------------------|---------------------------|
        m_nReadPos                m_nWritePos       (leftcount)
        */
        uint32_t leftcount = m_nBufSize - m_nWritePos;
        if(leftcount >= count)
        {
            memcpy(m_pBuf + m_nWritePos, buf, count);
            m_nWritePos += count;

            if(m_nWritePos == m_nReadPos + m_nBufSize)
                m_nWritePos = m_nReadPos = 0;

            m_bFull = (m_nReadPos == m_nWritePos);

            Q_ASSERT(m_nReadPos < m_nBufSize);
            Q_ASSERT(m_nWritePos <= m_nBufSize);
            lock->unlock();
            return count;
        }
        else
        {
            memcpy(m_pBuf + m_nWritePos, buf, leftcount);
            /*int*/ m_nWritePos = (m_nReadPos >= count - leftcount) ? count - leftcount : m_nReadPos;
            memcpy(m_pBuf, buf + leftcount, m_nWritePos);
            m_bFull = (m_nReadPos == m_nWritePos);
            Q_ASSERT(m_nReadPos <= m_nBufSize);
            Q_ASSERT(m_nWritePos <= m_nBufSize);
            lock->unlock();
            return leftcount + m_nWritePos;
        }
    }
    else
    {
        /*                          == mem model ==
        (data)                 (empty)                     (data)
        |-------------------|----------------------------|---------------------------|
        m_nWritePos    (leftcount)    m_nReadPos
        */
        uint32_t leftcount = m_nReadPos - m_nWritePos;
        if(leftcount >= count)
        {
            memcpy(m_pBuf + m_nWritePos, buf, count);
            m_nWritePos += count;
            m_bFull = (m_nReadPos == m_nWritePos);
            Q_ASSERT(m_nReadPos <= m_nBufSize);
            Q_ASSERT(m_nWritePos <= m_nBufSize);
            lock->unlock();
            return count;
        }
        else
        {
            memcpy(m_pBuf + m_nWritePos, buf, leftcount);
            m_nWritePos += leftcount;
            m_bFull = (m_nReadPos == m_nWritePos);
            Q_ASSERT(m_bFull);
            Q_ASSERT(m_nReadPos <= m_nBufSize);
            Q_ASSERT(m_nWritePos <= m_nBufSize);
            lock->unlock();
            return leftcount;
        }
    }
}

uint32_t CCycleBuffer::Read(uint8_t * buf, uint32_t count)
{
    if(count <= 0) return 0;

    lock->lockForRead();
    m_bFull = false;

    if(m_bEmpty)
    {
        lock->unlock();
        return 0;
    }
    else if(m_nReadPos == m_nWritePos)
    {
        /*                          == mem model ==
        (data)						m_nReadPos                (data)
        |--------------------------------|--------------------------------------------|
        m_nWritePos								  m_nBufSize
        */
        uint32_t leftcount = m_nBufSize - m_nReadPos;
        if(leftcount >= count)
        {
            memcpy(buf, m_pBuf + m_nReadPos, count);
            m_nReadPos += count;
            m_bEmpty = (m_nReadPos == m_nWritePos);
            lock->unlock();
            return count;
        }
        else
        {
            memcpy(buf, m_pBuf + m_nReadPos, leftcount);
            m_nReadPos = (m_nWritePos >= count - leftcount) ? count - leftcount : m_nWritePos;
            memcpy(buf + leftcount, m_pBuf, m_nReadPos);
            m_bEmpty = (m_nReadPos == m_nWritePos);
            lock->unlock();
            return leftcount + m_nReadPos;
        }
    }
    else if(m_nReadPos < m_nWritePos)
    {
        /*                          == mem model ==
        (read)                 (unread)                      (read)
        |-------------------|----------------------------|---------------------------|
        m_nReadPos                m_nWritePos                     m_nBufSize
        */
        uint32_t leftcount = m_nWritePos - m_nReadPos;
        int c = (leftcount >= count) ? count : leftcount;
        memcpy(buf, m_pBuf + m_nReadPos, c);
        m_nReadPos += c;
        m_bEmpty = (m_nReadPos == m_nWritePos);
        Q_ASSERT(m_nReadPos <= m_nBufSize);
        Q_ASSERT(m_nWritePos <= m_nBufSize);
        lock->unlock();
        return c;
    }
    else
    {
        /*                          == mem model ==
        (unread)                (read)                      (unread)
        |-------------------|----------------------------|---------------------------|
        m_nWritePos                  m_nReadPos                  m_nBufSize
        */
        uint32_t leftcount = m_nBufSize - m_nReadPos;
        if(leftcount > count)
        {
            memcpy(buf, m_pBuf + m_nReadPos, count);
            m_nReadPos += count;
            m_bEmpty = (m_nReadPos == m_nWritePos);
            Q_ASSERT(m_nReadPos <= m_nBufSize);
            Q_ASSERT(m_nWritePos <= m_nBufSize);
            lock->unlock();
            return count;
        }
        else
        {
            memcpy(buf, m_pBuf + m_nReadPos, leftcount);
            m_nReadPos = (m_nWritePos >= count - leftcount) ? count - leftcount : m_nWritePos;
            memcpy(buf + leftcount, m_pBuf, m_nReadPos);			//midify bug by zason
            m_bEmpty = (m_nReadPos == m_nWritePos);
            Q_ASSERT(m_nReadPos <= m_nBufSize);
            Q_ASSERT(m_nWritePos <= m_nBufSize);
            lock->unlock();
            return leftcount + m_nReadPos;
        }
    }
}

uint32_t CCycleBuffer::GetLength()
{
    lock->lockForRead();
    if(m_bEmpty)
    {
        lock->unlock();
        return 0;
    }
    else if(m_bFull)
    {
        lock->unlock();
        return m_nBufSize;
    }
    else if(m_nReadPos < m_nWritePos)
    {
        lock->unlock();
        return m_nWritePos - m_nReadPos;
    }
    else
    {
        lock->unlock();
        return m_nBufSize - m_nReadPos + m_nWritePos;
    }
}

uint32_t CCycleBuffer::GetFreeLength()
{
    lock->lockForRead();
    if(m_bEmpty)
    {
        lock->unlock();
        return m_nBufSize;
    }
    else if(m_bFull)
    {
        lock->unlock();
        return 0;
    }
    else if(m_nReadPos < m_nWritePos)
    {
        lock->unlock();
        return m_nBufSize - m_nReadPos + m_nWritePos;
    }
    else
    {
        lock->unlock();
        return m_nWritePos - m_nReadPos;
    }
}

void CCycleBuffer::Empty()
{
    lock->lockForWrite();
    m_nReadPos = 0;
    m_nWritePos = 0;
    m_bEmpty = true;
    m_bFull = false;
    lock->unlock();
}

bool CCycleBuffer::isFull()
{
    return m_bFull;
}

bool CCycleBuffer::isEmpty()
{
    return m_bEmpty;
}

