// Copyright 2001-2009, FreeHEP.
package com.cherry.lib.doc.office.thirdpart.emf.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
 */
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;
	}
}
