// ConcurrentBufferInputStream.java -- read bytes from blocking queue
// Copyright (c)2007 Christopher League <league@contrapunctus.net>

// This is free software, but it comes with ABSOLUTELY NO WARRANTY.
// GNU Lesser General Public License 2.1 or Common Public License 1.0

package net.contrapunctus.lzma;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.PrintStream;
import java.util.concurrent.ArrayBlockingQueue;

class ConcurrentBufferInputStream extends InputStream
{
    protected ArrayBlockingQueue<byte[]> q;
    protected byte[] buf = null;
    protected int next = 0;
    protected boolean eof = false;

    private static final PrintStream dbg = System.err;
    private static final boolean DEBUG;

    static {
        String ds = null;
        try { ds = System.getProperty("DEBUG_ConcurrentBuffer"); }
        catch(SecurityException e) { }
        DEBUG = ds != null;
    }

    ConcurrentBufferInputStream( ArrayBlockingQueue<byte[]> q )
    {
        if(DEBUG) dbg.printf("%s << %s%n", this, q);
        this.q = q;
        this.eof = false;
    }

    static InputStream create( ArrayBlockingQueue<byte[]> q )
    {
        InputStream in = new ConcurrentBufferInputStream( q );
        return in;
    }

    protected byte[] guarded_take( ) throws IOException
    {
        try {
            return q.take( );
        }
        catch( InterruptedException exn ) {
            throw new InterruptedIOException( exn.getMessage() );
        }
    }

    protected boolean prepareAndCheckEOF( ) throws IOException
    {
        if( eof ) return true;
        if( buf == null || next >= buf.length )
            {
                buf = guarded_take( );
                next = 0;
                if( buf.length == 0 )
                    {
                        eof = true;
                        return true;
                    }
            }
        return false;
    }

    public int read( ) throws IOException
    {
        if( prepareAndCheckEOF() ) { return -1; }
        int x = buf[next];
        next++;
        return x & 0xff;
    }

    public int read( byte[] b, int off, int len ) throws IOException
    {
        if( prepareAndCheckEOF() ) { return -1; }
        int k = buf.length - next;
        if( len < k ) { k = len; }
        System.arraycopy( buf, next, b, off, k );
        next += k;
        return k;
    }

    public String toString( )
    {
        return String.format("cbIn@%x", hashCode());
    }
}
