// Copyright 2001, FreeHEP.
package org.freehep.util.io;

import java.io.IOException;
import java.io.InputStream;

/**
 * The input buffer can be limited to less than the number of bytes of the
 * underlying buffer. Only one real input stream exists, which is where the
 * reads take place. A buffer is limited by some length. If more is read, -1 is
 * returned. Multiple limits can be set by calling pushBuffer. If bytes are left
 * in the buffer when popBuffer is called, they are returned in an array.
 * Otherwise null is returned.
 * 
 * @author Mark Donszelmann
 * @author Charles Loomis
 * @version $Id: ByteCountInputStream.java 8584 2006-08-10 23:06:37Z duns $
 */
public class ByteCountInputStream extends ByteOrderInputStream {

    private int index;

    private int[] size;

    private long len;

    /**
     * Create a Byte Count input stream from given stream
     * 
     * @param in stream to read from
     * @param littleEndian true if stream should be little endian
     * @param stackDepth maximum number of buffers used while reading
     */
    public ByteCountInputStream(InputStream in, boolean littleEndian,
            int stackDepth) {
        super(in, littleEndian);
        size = new int[stackDepth];
        index = -1;
        len = 0;
    }

    public int read() throws IOException {
        // original stream
        if (index == -1) {
            len++;
            return super.read();
        }

        // end of buffer
        if (size[index] <= 0)
            return -1;

        // decrease counter
        size[index]--;

        len++;
        return super.read();
    }

    /**
     * Push the current buffer to the stack
     * 
     * @param len number of bytes that can be read from the current buffer
     */
    public void pushBuffer(int len) {
        if (index >= size.length - 1) {
            System.err
                    .println("ByteCountInputStream: trying to push more buffers than stackDepth: "
                            + size.length);
            return;
        }

        if (index >= 0) {
            if (size[index] < len) {
                System.err
                        .println("ByteCountInputStream: trying to set a length: "
                                + len
                                + ", longer than the underlying buffer: "
                                + size[index]);
                return;
            }
            size[index] -= len;
        }
        index++;
        size[index] = len;
    }

    /**
     * Pops the buffer from the stack and returns leftover bytes in a byte array
     * 
     * @return null if buffer was completely read. Otherwise rest of buffer is
     *         read and returned.
     * @throws IOException if read fails
     */
    public byte[] popBuffer() throws IOException {
        if (index >= 0) {
            int len = size[index];
            if (len > 0) {
                return readByte(len);
            } else if (len < 0) {
                System.err.println("ByteCountInputStream: Internal Error");
            }
            index--;
        }
        return null;
    }

    /**
     * @return number of bytes that can be read from the current buffer
     */
    public long getLength() {
        return (index >= 0) ? size[index] : len;
    }
}
