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

package org.jline.utils;

import java.io.InterruptedIOException;
import java.io.IOException;
import java.io.InputStream;

public class NonBlockingInputStreamImpl extends NonBlockingInputStream
{
    private InputStream in;
    private int b;
    private String name;
    private boolean threadIsReading;
    private IOException exception;
    private long threadDelay;
    private Thread thread;
    
    public NonBlockingInputStreamImpl(final String name, final InputStream in) {
        this.b = -2;
        this.threadIsReading = false;
        this.exception = null;
        this.threadDelay = 60000L;
        this.in = in;
        this.name = name;
    }
    
    private synchronized void startReadingThreadIfNeeded() {
        if (this.thread == null) {
            (this.thread = new Thread(this::run)).setName(this.name + " non blocking reader thread");
            this.thread.setDaemon(true);
            this.thread.start();
        }
    }
    
    @Override
    public synchronized void shutdown() {
        if (this.thread != null) {
            this.notify();
        }
    }
    
    @Override
    public void close() throws IOException {
        this.in.close();
        this.shutdown();
    }
    
    @Override
    public synchronized int read(final long timeout, final boolean isPeek) throws IOException {
        if (this.exception == null) {
            if (this.b >= -1) {
                assert this.exception == null;
            }
            else if (!isPeek && timeout <= 0L && !this.threadIsReading) {
                this.b = this.in.read();
            }
            else {
                if (!this.threadIsReading) {
                    this.threadIsReading = true;
                    this.startReadingThreadIfNeeded();
                    this.notifyAll();
                }
                final Timeout t = new Timeout(timeout);
                while (!t.elapsed()) {
                    try {
                        if (Thread.interrupted()) {
                            throw new InterruptedException();
                        }
                        this.wait(t.timeout());
                    }
                    catch (final InterruptedException e) {
                        this.exception = (IOException)new InterruptedIOException().initCause(e);
                    }
                    if (this.exception != null) {
                        assert this.b == -2;
                        final IOException toBeThrown = this.exception;
                        if (!isPeek) {
                            this.exception = null;
                        }
                        throw toBeThrown;
                    }
                    else {
                        if (this.b < -1) {
                            continue;
                        }
                        assert this.exception == null;
                        break;
                    }
                }
            }
            final int ret = this.b;
            if (!isPeek) {
                this.b = -2;
            }
            return ret;
        }
        assert this.b == -2;
        final IOException toBeThrown2 = this.exception;
        if (!isPeek) {
            this.exception = null;
        }
        throw toBeThrown2;
    }
    
    private void run() {
        Log.debug("NonBlockingInputStream start");
        try {
            int byteRead;
            do {
                synchronized (this) {
                    boolean needToRead = this.threadIsReading;
                    try {
                        if (!needToRead) {
                            this.wait(this.threadDelay);
                        }
                    }
                    catch (final InterruptedException ex) {}
                    needToRead = this.threadIsReading;
                    if (!needToRead) {
                        return;
                    }
                }
                byteRead = -2;
                IOException failure = null;
                try {
                    byteRead = this.in.read();
                }
                catch (final IOException e) {
                    failure = e;
                }
                synchronized (this) {
                    this.exception = failure;
                    this.b = byteRead;
                    this.threadIsReading = false;
                    this.notify();
                }
            } while (byteRead >= 0);
        }
        catch (final Throwable t) {
            Log.warn("Error in NonBlockingInputStream thread", t);
            Log.debug("NonBlockingInputStream shutdown");
            synchronized (this) {
                this.thread = null;
                this.threadIsReading = false;
            }
        }
        finally {
            Log.debug("NonBlockingInputStream shutdown");
            synchronized (this) {
                this.thread = null;
                this.threadIsReading = false;
            }
        }
    }
}
