package com.colloquial.arithcode.ppm;

/** <P>Stores a queue of bytes in a buffer with a maximum size.  New
 * bytes are added to the tail of the queue, and if the size exceeds
 * the maximum, bytes are removed from the front of the queue.  Used
 * to model a sliding window of a fixed width over a stream of bytes
 * presented a byte at a time.  The bytes in the current window are
 * accessed through an array of bytes, an offset and a length.  
 * 
 * <P>For instance, with a maximum length of 2, beginning with an
 * empty buffer and adding bytes 1, 2, 3, and 4 in that order leads to
 * queues <code>{1}</code>, <code>{1,2}</code>, <code>{2,3}</code> and
 * <code>{3,4}</code>.
 *
 * @author <a href="http://www.colloquial.com/carp/">Bob Carpenter</a>
 * @version 1.0
 * @since 1.0
 */ 
public final class ByteBuffer {

    /** Construct a context buffer of given maximum size.
     * @param maxWidth Maximum number of bytes in a context.
     */
    public ByteBuffer(int maxWidth) {
        _maxWidth = maxWidth;
        _bytes = new byte[BUFFER_SIZE_MULTIPLIER * maxWidth];
    }

    /** Current array of bytes backing this byte buffer.  The returned
     * bytes are not a copy and should not be modified.
     * @return Array of bytes backing this buffer.
     */
    public byte[] bytes() {
        return _bytes;
    }

    /** Current offset of this buffer into the byte array.
     * @return Offset of the buffer into the byte array.
     */
    public int offset() {
        return _offset;
    }

    /** Current length of this buffer.  
     * @return Length of this buffer.
     */
    public int length() {
        return _length;
    }

    /** Add a byte to the end of the context, removing first element if
     * necessary.
     * @param b Byte to push onto the tail of the context.
     */
    public void buffer(byte b) {
        if (nextFreeIndex() > maxIndex()) tampDown();
        _bytes[nextFreeIndex()] = b;
        if (_length < _maxWidth) ++_length;
        else ++_offset;
    }

    /** Return a string representation of this context using
     * the current localization to convert bytes to characters.
     * @return String representation of this context.
     */
    public String toString() {
        return new String(_bytes,_offset,_length);
    }

    /** Array of bytes used to buffer incoming bytes.
     */
    final byte[] _bytes;

    /** Maximum number of bytes in queue before adding pushes one off.
     */
    private final int _maxWidth;

    /** Offset of first byte of current context in buffer.
     */
    int _offset = 0;

    /** Number of bytes in the context.  Maximum will be given during construction.
     */
    int _length = 0;

    /** Index in the buffer for next element. May point beyond the
     * maximum index if there is no more space.
     * @return Index for next element.
     */
    private int nextFreeIndex() {
        return _offset+_length;
    }

    /** The maximum index in the buffer.
     * @param Index of last element in the buffer.
     */
    private int maxIndex() {
        return _bytes.length-1;
    }

    /** Moves bytes in context down to start of buffer.
     */
    private void tampDown() {
        for (int i = 0; i < _length-1; ++i) { 
            _bytes[i] = _bytes[_offset+i+1]; 
        }
        _offset = 0;
    }

    /** Number of contexts that fit in the buffer without shifting.
     */
    private static final int BUFFER_SIZE_MULTIPLIER = 32;

}
