/*
 * @(#)AppendableByteArrayInputStream.java  1.0  2011-08-28
 * 
 * Copyright (c) 2011 Werner Randelshofer, Immensee, Switzerland.
 * All rights reserved.
 * 
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */
package org.monte.media.io;

import java.io.ByteArrayInputStream;
import java.util.zip.Adler32;

/**
 * A {@code ByteArrayInputStream} which allows to replace the byte buffer underneath.
 *
 * @author Werner Randelshofer
 * @version 1.0 2011-08-28 Created.
 */
public class AppendableByteArrayInputStream extends ByteArrayInputStream {

    public AppendableByteArrayInputStream(byte[] buf, int offset, int length) {
        super(buf, offset, length);
        //  System.out.println("AppendableByteArrayInputStream   pos="+pos+" count="+count);
    }

    public AppendableByteArrayInputStream(byte[] buf) {
        super(buf);
        // System.out.println("AppendableByteArrayInputStream   pos="+pos+" count="+count);
    }

    @Override
    public synchronized int read() {
        int b = super.read();
        /*
        if (b!=-1) {
        String hex="0"+Integer.toHexString(b);
        System.out.println(hex.substring(hex.length()-2));
        }*/
        return b;
    }

    @Override
    public synchronized int read(byte[] b, int off, int len) {
        // System.out.println("AppendableByteArrayInputStream.read...   pos="+pos+" count="+count);
        int count = super.read(b, off, len);
        /*
        if (count>0) {
        for (int i=0;i<count;i++) {
        if (i%16==15) System.out.println();
        String hex="0"+Integer.toHexString(b[i]);
        System.out.print(hex.substring(hex.length()-2));
        }
        System.out.println("   pos="+pos+" count="+this.count);
        }*/
        return count;
    }

    /** Appends new data to the buffer. 
     * 
     * @param buf Data.
     * @param offset Offset in the data.
     * @param length Length of the data.
     * @param discard True if data which has already been read can be discarded.
     */
    public void appendBuffer(byte[] buf, int offset, int length, boolean discard) {
        if (discard) {
            if (this.buf.length >= count - pos + length) {
                // => the buffer has enough space for the existing data and the new data
                System.arraycopy(this.buf, pos, this.buf, 0, count - pos);
                System.arraycopy(buf, offset, this.buf, count - pos, length);
                this.count = count - pos + length;
                this.pos = 0;
                this.mark = 0;
            } else {
                // => the buffer does not have enough space for the new data
                byte[] newBuf = new byte[(count - pos + length + 31) / 32 * 32];
                System.arraycopy(this.buf, pos, newBuf, 0, count - pos);
                System.arraycopy(buf, offset, newBuf, count - pos, length);
                this.buf = newBuf;
                this.count = count - pos + length;
                this.pos = 0;
                this.mark = 0;
            }
        } else {
            if (this.buf.length >= count + length) {
                // => the buffer has enough space for the existing data and the new data
                System.arraycopy(buf, offset, this.buf, count, length);
                this.count = count + length;
            } else {
                // => the buffer does not have enough space for the new data
                byte[] newBuf = new byte[(this.buf.length + length + 31) / 32 * 32];
                System.arraycopy(this.buf, 0, newBuf, 0, count);
                System.arraycopy(buf, offset, newBuf, count, length);
                this.buf = newBuf;
                this.count = count + length;
            }
        }

        //System.out.println("AppendableByteArrayInputStream.appendBuffer   pos="+pos+" count="+count);
    }

    /** Sets the buffer and resets the stream. 
     * This will overwrite the data array in the buffer, if it is large enough.
     * Otherwise it will create a new data array and copy the data into it.
     * 
     * @param buf Data.
     * @param offset Offset in the data.
     * @param length Length of the data.
     */
    public void setBuffer(byte[] buf, int offset, int length) {
        if (this.buf.length >= length) {
            // => the buffer has enough space for the existing data and the new data
            System.arraycopy(buf, offset, this.buf, 0, length);
            this.count = length;
            this.pos = 0;
            this.mark = 0;
        } else {
            // => the buffer may not be overwritten or does not have enough space for the new data
            this.buf = null;
            this.buf = new byte[(length + 31) & ~31];
            System.arraycopy(buf, offset, this.buf, 0, length);
            this.count = length;
            this.pos = 0;
            this.mark = 0;

        }
    }

    public static void main(String[] args) {
        byte[] b = new byte[5];
        int count = 0;
        for (int i = 0; i < b.length; i++) {
            b[i] = (byte) count++;
        }
        AppendableByteArrayInputStream in = new AppendableByteArrayInputStream(b);

        for (int j = 0; j < 3; j++) {
            System.out.println(in.read());
        }

        b = new byte[4];
        for (int i = 0; i < b.length; i++) {
            b[i] = (byte) count++;
        }
        in.appendBuffer(b, 0, b.length, true);
        for (int j = 0; j < 3; j++) {
            System.out.println(in.read());
        }
        b = new byte[6];
        for (int i = 0; i < b.length; i++) {
            b[i] = (byte) count++;
        }
        in.appendBuffer(b, 0, b.length, true);

        for (int d = in.read(); d >= 0; d = in.read()) {
            System.out.println(d);
        }
    }
}
