package org.jboss.resteasy.client.jaxrs.engines;

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

import org.jboss.resteasy.resteasy_jaxrs.i18n.Messages;

/**
 * Extension of {@link BufferedInputStream} enforcing the contract where reset()
 * always returns to the beginning of the stream, and the internal buffer
 * expands automatically to the total length of content read from the underlying
 * stream.
 *
 * @author ul8b
 */
public class SelfExpandingBufferredInputStream extends BufferedInputStream {
    private static int defaultBufferSize = 8192;

    public SelfExpandingBufferredInputStream(final InputStream in) {
        super(in);
        super.mark(defaultBufferSize);
    }

    public SelfExpandingBufferredInputStream(final InputStream in, final int size) {
        super(in, size);
        super.mark(size);
    }

    /**
     * Not supported. Mark position is always zero.
     */
    @Override
    public synchronized void mark(int readlimit) {
        throw new UnsupportedOperationException(Messages.MESSAGES.alwaysMarkedAtIndex0());
    }

    @Override
    public synchronized int read() throws IOException {
        if (pos == marklimit) {
            expand();
        }
        return super.read();
    }

    @Override
    public synchronized int read(byte[] b, int off, int len) throws IOException {
        while (pos + len > marklimit) {
            expand();
        }
        return super.read(b, off, len);
    }

    @Override
    public synchronized int read(byte[] b) throws IOException {
        while (pos + b.length > marklimit) {
            expand();
        }
        return super.read(b);
    }

    /**
     * Double the current buffer size limit. Reset to zero, then double the
     * buffer size and restore last position in the buffer.
     *
     * @throws IOException
     */
    private void expand() throws IOException {
        int lastPos = pos;
        super.reset();
        super.mark((marklimit < (Integer.MAX_VALUE - 8) / 2) ? (marklimit * 2) : (Integer.MAX_VALUE - 8));
        pos = lastPos;
    }

    /**
     * Return the current maximum size of the internal buffer. This is
     * independent of how much data is actually contained within the buffer.
     *
     * @return internal buffer size
     */
    public int getBufSize() {
        return buf.length;
    }

    public int getCount() {
        return count;
    }

    public int getPos() {
        return pos;
    }

    public int getMarkLimit() {
        return marklimit;
    }

    public int getMarkPos() {
        return markpos;
    }
}
