package com.hypirion.io;

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

public class RevivableInputStream extends InputStream {
    protected InputStream in;

    protected volatile boolean killed;
    protected volatile boolean streamClosed;
    protected volatile int data;
    protected volatile Boolean beenRead;

    protected final ThreadReader reader;
    protected final Thread readerThread;

    public RevivableInputStream(InputStream in) throws InterruptedException {
        this.in = in;
        killed = false;
        streamClosed = false;
        beenRead = true;
        data = -2;
        reader = new ThreadReader();
        readerThread = new Thread(reader);
        readerThread.setDaemon(true);
        readerThread.setName("RevivableReader " + in.hashCode());
        readerThread.start();
    }

    public int available() throws IOException {
        return in.available();
    }

    public void close() throws IOException {
        in.close();
    }

    public boolean markSupported() {
        return in.markSupported();
    }

    public synchronized int read() throws IOException {
        synchronized (beenRead) {
            try {
                while (beenRead || !killed || !streamClosed) {
                    beenRead.wait();
                }
            }
            catch (InterruptedException ie) {
                throw new InterruptedIOException();
            }
            if (killed || streamClosed)
                return -1;
            int val = data;

            beenRead = true;
            beenRead.notifyAll();
            return val;
        }
    }

    public synchronized int read(byte[] b) throws IOException {
        return read(b, 0, b.length);
    }

    public synchronized int read(byte[] b, int off, int len) throws IOException{
        if (len == 0)
            return 0;
        int v = read();
        if (v == -1)
            return -1;
        b[off] = (byte) v;
        return 1;
    }

    public void reset() throws IOException {
        in.reset();
    }

    public synchronized long skip(long n) throws IOException {
        while (n --> 0)
            read();
        return -1; // TODO: Read what we should return.
    }

    public void kill() {
        synchronized (beenRead) {
            killed = true;
            beenRead.notifyAll();
        }
    }

    public synchronized void ressurect() {
        killed = false;
    }

    private class ThreadReader implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    data = in.read();
                    if (data == -1){
                        synchronized (beenRead){
                            streamClosed = true;
                            beenRead.notifyAll();
                            return;
                        }
                    }
                }
                catch (IOException ioe) {
                    // TODO: Pass exception to main thread.
                    return;
                }

                synchronized (beenRead) {
                    beenRead = false;
                    beenRead.notifyAll();
                    try {
                        while (!beenRead) {
                            beenRead.wait();
                        }
                    }
                    catch (InterruptedException ie) {
                        // TODO: Pass exception to main thread.
                        return;
                    }
                }
                // Data has been read, new iteration.
            }
        }
    }
}

@@ -355,24 +355,122 @@
 an beenRead;
+%0A    protected volatile boolean threadCrashed;%0A    protected volatile IOException threadException;
 %0A%0A    protec
@@ -713,32 +713,95 @@
 eenRead = true;%0A
+        threadCrashed = false;%0A        threadException = null;%0A
         data = -
@@ -1428,16 +1428,33 @@
 amClosed
+ %7C%7C threadCrashed
 ) %7B%0A    
@@ -1622,32 +1622,102 @@
 ;%0A            %7D%0A
+            if (threadCrashed)%0A                throw threadException;%0A
             if (
@@ -3245,48 +3245,162 @@
     
-// TODO: Pass exception to main thread.%0A
+synchronized (beenRead) %7B%0A                        threadCrashed = true;%0A                        threadException = ioe; // TODO: Proper wrapping here.%0A    
     
@@ -3415,32 +3415,54 @@
         return;%0A
+                    %7D%0A
                 
@@ -3795,32 +3795,150 @@
 Exception ie) %7B%0A
+                        threadCrashed = true;%0A                        threadException = new InterruptedIOException();%0A
                 
@@ -3958,38 +3958,29 @@
 DO: 
-Pass exception to main thread.
+Use %22real%22  exception
 %0A   

Throw reasonable exceptions when reader thread crashes.