/*
 *  ringbuffer.cpp
 *
 *    RingBuffer class implementation. Simple ring buffer functionality
 *    expressed as a c++ class.
 *
 *
 *  Copyright (c) 2005  Arek Korbik
 *
 *  This file is part of XiphQT, the Xiph QuickTime Components.
 *
 *  XiphQT is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  XiphQT 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with XiphQT; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *
 *  Last modified: $Id: ringbuffer.cpp 10575 2005-12-10 19:33:16Z arek $
 *
 */


#include "config.h"
#include "ringbuffer.h"

RingBuffer::RingBuffer() :
mBuffer(NULL),
mBStart(0),
mBEnd(0),
mBSize(0),
mNeedsWrapping(false)
{
}

RingBuffer::~RingBuffer() {
    delete[] mBuffer;
}


void RingBuffer::Initialize(UInt32 inBufferByteSize) {
    mBSize = inBufferByteSize;

    if (mBuffer)
        delete[] mBuffer;

    mBuffer = new Byte[mBSize * 2];

    mBStart = 0;
    mBEnd = 0;
    mNeedsWrapping = false;
}

void RingBuffer::Uninitialize() {
    mBSize = 0;

    if (mBuffer) {
        delete[] mBuffer;
        mBuffer = NULL;
    }

    Reset();

}

void RingBuffer::Reset() {
    mBStart = 0;
    mBEnd = 0;
    mNeedsWrapping = false;
}

UInt32 RingBuffer::GetBufferByteSize() const {
    return mBSize;
}

UInt32 RingBuffer::GetDataAvailable() const {
    UInt32 ret = 0;

    if (mBStart < mBEnd)
        ret = mBEnd - mBStart;
    else if (mBEnd < mBStart)
        ret = mBSize + mBEnd - mBStart;

    return ret;
}

UInt32 RingBuffer::GetSpaceAvailable() const {
    UInt32 ret = mBSize;

    if (mBStart > mBEnd)
        ret =  mBStart - mBEnd;
    else if (mBEnd > mBStart)
        ret = mBSize - mBEnd + mBStart;

    return ret;

}


void RingBuffer::In(const void* data, UInt32& ioBytes) {
    UInt32 copiedBytes = GetSpaceAvailable();
    if (copiedBytes > ioBytes)
        copiedBytes = ioBytes;

    if (mBEnd + copiedBytes <= mBSize) {
        BlockMoveData(data, mBuffer + mBEnd, copiedBytes);
        mBEnd += copiedBytes;
        if (mBEnd < mBStart)
            mNeedsWrapping = true;
    } else {
        UInt32 wrappedBytes = mBSize - mBEnd;
        const Byte* dataSplit = static_cast<const Byte*>(data) + wrappedBytes;
        mBEnd = copiedBytes - wrappedBytes;

        BlockMoveData(data, mBuffer + mBEnd, wrappedBytes);
        BlockMoveData(dataSplit, mBuffer, mBEnd);

        mNeedsWrapping = true;
    }

    ioBytes -= copiedBytes;
}

void RingBuffer::Zap(UInt32 inBytes) {
    if (inBytes >= GetDataAvailable()) {
        mBStart = 0;
        mBEnd = 0;
        mNeedsWrapping = false;
    } else if (mBStart < mBEnd || mBStart + inBytes < mBSize) {
        mBStart += inBytes;
    } else {
        mBStart += inBytes - mBSize;
        mNeedsWrapping = false;
    }
}

Byte* RingBuffer::GetData() {
    if (GetDataAvailable() == 0)
        return mBuffer;
    else {
        if (mNeedsWrapping) {
            BlockMoveData(mBuffer, mBuffer + mBSize, mBEnd);
            mNeedsWrapping = false;
        }
        return mBuffer + mBStart;
    }
}

Byte* RingBuffer::GetDataEnd() {
    UInt32 available = GetDataAvailable();
    if (available == 0)
        return mBuffer;
    else {
        if (mNeedsWrapping) {
            BlockMoveData(mBuffer, mBuffer + mBSize, mBEnd);
            mNeedsWrapping = false;
        }
        return mBuffer + mBStart + available;
    }
}
