/*
 *  21.04.2004 Original verion. davagin@udm.ru.
 *-----------------------------------------------------------------------
 *  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, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *----------------------------------------------------------------------
 */
package davaguine.jmac.tools;


/**
 * Author: Dmitry Vaguine
 * Date: 06.05.2004
 * Time: 21:05:48
 */
public class CircleBuffer {
    // construction / destruction
    public CircleBuffer() {
        m_pBuffer = null;
        m_nTotal = 0;
        m_nHead = 0;
        m_nTail = 0;
        m_nEndCap = 0;
        m_nMaxDirectWriteBytes = 0;
    }

    // create the buffer
    public void CreateBuffer(int nBytes, int nMaxDirectWriteBytes) {
        m_nMaxDirectWriteBytes = nMaxDirectWriteBytes;
        m_nTotal = nBytes + 1 + nMaxDirectWriteBytes;
        m_pBuffer = new byte[m_nTotal];
        byteBuffer = new ByteBuffer();
        m_nHead = 0;
        m_nTail = 0;
        m_nEndCap = m_nTotal;
    }

    // query
    public int MaxAdd() {
        int nMaxAdd = (m_nTail >= m_nHead) ? (m_nTotal - 1 - m_nMaxDirectWriteBytes) - (m_nTail - m_nHead) : m_nHead - m_nTail - 1;
        return nMaxAdd;
    }

    public int MaxGet() {
        return (m_nTail >= m_nHead) ? m_nTail - m_nHead : (m_nEndCap - m_nHead) + m_nTail;
    }

    // direct writing
    public ByteBuffer GetDirectWritePointer() {
        // return a pointer to the tail -- note that it will always be safe to write
        // at least m_nMaxDirectWriteBytes since we use an end cap region
        byteBuffer.reset(m_pBuffer, m_nTail);
        return byteBuffer;
    }

    public void UpdateAfterDirectWrite(int nBytes) {
        // update the tail
        m_nTail += nBytes;

        // if the tail enters the "end cap" area, set the end cap and loop around
        if (m_nTail >= (m_nTotal - m_nMaxDirectWriteBytes)) {
            m_nEndCap = m_nTail;
            m_nTail = 0;
        }
    }

    // get data
    public int Get(byte[] pBuffer, int index, int nBytes) {
        int nTotalGetBytes = 0;

        if (pBuffer != null && nBytes > 0) {
            int nHeadBytes = Math.min(m_nEndCap - m_nHead, nBytes);
            int nFrontBytes = nBytes - nHeadBytes;

            System.arraycopy(m_pBuffer, m_nHead, pBuffer, index, nHeadBytes);
            nTotalGetBytes = nHeadBytes;

            if (nFrontBytes > 0) {
                System.arraycopy(m_pBuffer, 0, pBuffer, index + nHeadBytes, nFrontBytes);
                nTotalGetBytes += nFrontBytes;
            }

            RemoveHead(nBytes);
        }

        return nTotalGetBytes;
    }

    // remove / empty
    public void Empty() {
        m_nHead = 0;
        m_nTail = 0;
        m_nEndCap = m_nTotal;
    }

    public int RemoveHead(int nBytes) {
        nBytes = Math.min(MaxGet(), nBytes);
        m_nHead += nBytes;
        if (m_nHead >= m_nEndCap)
            m_nHead -= m_nEndCap;
        return nBytes;
    }

    public int RemoveTail(int nBytes) {
        nBytes = Math.min(MaxGet(), nBytes);
        m_nTail -= nBytes;
        if (m_nTail < 0)
            m_nTail += m_nEndCap;
        return nBytes;
    }

    private int m_nTotal;
    private int m_nMaxDirectWriteBytes;
    private int m_nEndCap;
    private int m_nHead;
    private int m_nTail;
    private byte[] m_pBuffer;
    private ByteBuffer byteBuffer;
}
