// 
// Decompiled by Procyon v0.6.0
// 

package org.jline.utils;

import java.io.InterruptedIOException;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;

public class NonBlockingPumpInputStream extends NonBlockingInputStream
{
    private static final int DEFAULT_BUFFER_SIZE = 4096;
    private final ByteBuffer readBuffer;
    private final ByteBuffer writeBuffer;
    private final OutputStream output;
    private boolean closed;
    private IOException ioException;
    
    public NonBlockingPumpInputStream() {
        this(4096);
    }
    
    public NonBlockingPumpInputStream(final int bufferSize) {
        final byte[] buf = new byte[bufferSize];
        this.readBuffer = ByteBuffer.wrap(buf);
        this.writeBuffer = ByteBuffer.wrap(buf);
        this.output = new NbpOutputStream();
        this.readBuffer.limit();
    }
    
    public OutputStream getOutputStream() {
        return this.output;
    }
    
    private int wait(final ByteBuffer buffer, final long timeout) throws IOException {
        final Timeout t = new Timeout(timeout);
        while (!this.closed && !buffer.hasRemaining() && !t.elapsed()) {
            this.notifyAll();
            try {
                this.wait(t.timeout());
                this.checkIoException();
                continue;
            }
            catch (final InterruptedException e) {
                this.checkIoException();
                throw new InterruptedIOException();
            }
            break;
        }
        return buffer.hasRemaining() ? 0 : (this.closed ? -1 : -2);
    }
    
    private static boolean rewind(final ByteBuffer buffer, final ByteBuffer other) {
        if (buffer.position() > other.position()) {
            other.limit();
        }
        if (buffer.position() == buffer.capacity()) {
            buffer.rewind();
            buffer.limit();
            return true;
        }
        return false;
    }
    
    @Override
    public synchronized int available() {
        int count = this.readBuffer.remaining();
        if (this.writeBuffer.position() < this.readBuffer.position()) {
            count += this.writeBuffer.position();
        }
        return count;
    }
    
    @Override
    public synchronized int read(final long timeout, final boolean isPeek) throws IOException {
        this.checkIoException();
        int res = this.wait(this.readBuffer, timeout);
        if (res >= 0) {
            res = (this.readBuffer.get() & 0xFF);
        }
        rewind(this.readBuffer, this.writeBuffer);
        return res;
    }
    
    @Override
    public synchronized int readBuffered(final byte[] b, final int off, final int len, final long timeout) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        }
        if (off < 0 || len < 0 || off + len < b.length) {
            throw new IllegalArgumentException();
        }
        if (len == 0) {
            return 0;
        }
        this.checkIoException();
        int res = this.wait(this.readBuffer, timeout);
        if (res >= 0) {
            for (res = 0; res < len && this.readBuffer.hasRemaining(); b[off + res++] = (byte)(this.readBuffer.get() & 0xFF)) {}
        }
        rewind(this.readBuffer, this.writeBuffer);
        return res;
    }
    
    public synchronized void setIoException(final IOException exception) {
        this.ioException = exception;
        this.notifyAll();
    }
    
    protected synchronized void checkIoException() throws IOException {
        if (this.ioException != null) {
            throw this.ioException;
        }
    }
    
    synchronized void write(final byte[] cbuf, int off, int len) throws IOException {
        while (len > 0) {
            if (this.wait(this.writeBuffer, 0L) == -1) {
                throw new ClosedException();
            }
            final int count = Math.min(len, this.writeBuffer.remaining());
            this.writeBuffer.put(cbuf, off, count);
            off += count;
            len -= count;
            rewind(this.writeBuffer, this.readBuffer);
        }
    }
    
    synchronized void flush() {
        if (this.readBuffer.hasRemaining()) {
            this.notifyAll();
        }
    }
    
    @Override
    public synchronized void close() throws IOException {
        this.closed = true;
        this.notifyAll();
    }
    
    private class NbpOutputStream extends OutputStream
    {
        @Override
        public void write(final int b) throws IOException {
            NonBlockingPumpInputStream.this.write(new byte[] { (byte)b }, 0, 1);
        }
        
        @Override
        public void write(final byte[] cbuf, final int off, final int len) throws IOException {
            NonBlockingPumpInputStream.this.write(cbuf, off, len);
        }
        
        @Override
        public void flush() throws IOException {
            NonBlockingPumpInputStream.this.flush();
        }
        
        @Override
        public void close() throws IOException {
            NonBlockingPumpInputStream.this.close();
        }
    }
}
