//

package edu.cmu.cs.vbc.prog.zipme;

/**
 * This class is general purpose class for writing data to a buffer.
 * It allows you to write bits as well as bytes
 * Based on DeflaterPending.java
 *
 * @author Jochen Hoenicke
 * @date Jan 5, 2000
 */

/**
 * This class is general purpose class for writing data to a buffer.
 * It allows you to write bits as well as bytes
 * Based on DeflaterPending.java
 *
 * @author Jochen Hoenicke
 * @date Jan 5, 2000
 */
class PendingBuffer {

    protected byte[] buf;

    int start;

    int end;

    int bits;

    int bitCount;

    public PendingBuffer() {
        this(4096);
    }

    public PendingBuffer(int bufsize) {
        buf = new byte[bufsize];
    }

    public final void reset() {
        start = end = bitCount = 0;
    }

    public final void writeByte(int b) {
        if (DeflaterConstants.DEBUGGING && start != 0)
            throw new IllegalStateException();
        buf[end++] = (byte) b;
    }

    public final void writeShort(int s) {
        if (DeflaterConstants.DEBUGGING && start != 0)
            throw new IllegalStateException();
        buf[end++] = (byte) s;
        buf[end++] = (byte) (s >> 8);
    }

    public final void writeInt(int s) {
        if (DeflaterConstants.DEBUGGING && start != 0)
            throw new IllegalStateException();
        buf[end++] = (byte) s;
        buf[end++] = (byte) (s >> 8);
        buf[end++] = (byte) (s >> 16);
        buf[end++] = (byte) (s >> 24);
    }

    public final void writeBlock(byte[] block, int offset, int len) {
        if (DeflaterConstants.DEBUGGING && start != 0)
            throw new IllegalStateException();
        System.arraycopy(block, offset, buf, end, len);
        end += len;
    }

    public final int getBitCount() {
        return bitCount;
    }

    public final void alignToByte() {
        if (DeflaterConstants.DEBUGGING && start != 0)
            throw new IllegalStateException();
        if (bitCount > 0) {
            buf[end++] = (byte) bits;
            if (bitCount > 8)
                buf[end++] = (byte) (bits >>> 8);
        }
        bits = 0;
        bitCount = 0;
    }

    public final void writeBits(int b, int count) {
        if (DeflaterConstants.DEBUGGING && start != 0)
            throw new IllegalStateException();
        if (DeflaterConstants.DEBUGGING)
            System.err.println("writeBits(" + Integer.toHexString(b) + "," + count + ")");
        bits |= b << bitCount;
        bitCount += count;
        if (bitCount >= 16) {
            buf[end++] = (byte) bits;
            buf[end++] = (byte) (bits >>> 8);
            bits >>>= 16;
            bitCount -= 16;
        }
    }

    public final void writeShortMSB(int s) {
        if (DeflaterConstants.DEBUGGING && start != 0)
            throw new IllegalStateException();
        buf[end++] = (byte) (s >> 8);
        buf[end++] = (byte) s;
    }

    public final boolean isFlushed() {
        return end == 0;
    }

    /**
     * Flushes the pending buffer into the given output array. If the
     * output array is to small, only a partial flush is done.
     *
     * @param output the output array;
     * @param offset the offset into output array;
     * @param length the maximum number of bytes to store;
     * @throws IndexOutOfBoundsException if offset or length are
     *                                   invalid.
     */
    public final int flush(byte[] output, int offset, int length) {
        if (bitCount >= 8) {
            buf[end++] = (byte) bits;
            bits >>>= 8;
            bitCount -= 8;
        }
        if (length > end - start) {
            length = end - start;
            System.arraycopy(buf, start, output, offset, length);
            start = 0;
            end = 0;
        } else {
            System.arraycopy(buf, start, output, offset, length);
            start += length;
        }
        return length;
    }

    /**
     * Flushes the pending buffer and returns that data in a new array
     *
     * @return the output stream
     */
    public final byte[] toByteArray() {
        byte[] ret = new byte[end - start];
        System.arraycopy(buf, start, ret, 0, ret.length);
        start = 0;
        end = 0;
        return ret;
    }

}
