package io.pipe;

/**
 *  Slightly modified from code developed at Tallinn University, Estonia.
 *  Using pipes for inter-thread I/O
 */

import java.io.*;
import java.util.*;

public class PipeDemo {
	public static void main(String[] args) throws Exception {
		System.out.println("Test of Pipe IO\n");
		
		Sender sender =     new Sender();			// Note use of inner class
		Receiver receiver = new Receiver(sender);	// Note use of inner class
		
		sender.start(); 	// Start the sender thread    
		receiver.start();	// Start the receiver thread
		Thread.sleep(8000);	// Take an 8 second break
		sender.interrupt();	// Interrupt the sender thread
	}
}

/** 
 * Class for the sender thread
 * 
 * In Java, you can define multiple top level classes in a single file, 
 * providing that at most one of these is public 
 * 
 */
class Sender extends Thread {
	private Random rand = new Random();
	private PipedWriter out = new PipedWriter();
	
	/**
	 * Accessor method that returns the PipedWriter created
	 * when a Sender object was created (default constructor)
	 * 
	 * @return 		the instance of PipedWriter class to
	 * 				which Sender will be writing characters.
	 * 
	 */
	public PipedWriter getPipedWriter() { 
		return out; 
	}
	
	/**
	 * Invoking start() on a Thread like Sender will cause the 
	 * JVM to invoke this run() method.
	 */
	public void run() {
		while (true) {	
			for (char c = 'A'; c <= 'z'; c++) {
				// Just keep writing the characters from A to z
				try {
					out.write(c);
					System.out.println("Wrote: " + c);
					sleep(rand.nextInt(500));
				} catch (InterruptedException e) {
					System.out.println("* Sending thread has been interrupted and has stopped sending data"); 
					return;  // This will finish the thread, but PipedWriter never closed!
				} catch (IOException e) {
					System.out.println(e.getMessage());
				}
			}
		}
	}
}

/** 
 * Class for the receiver thread
 * 
 * In Java, you can define multiple top level classes in a single file, 
 * providing that at most one of these is public 
 * 
 */
class Receiver extends Thread {
	private PipedReader in;
	
	/**
	 * Constructor for Receiver, accepts a sender thread as a parameter
	 * 
	 * @param sender
	 * @throws IOException
	 */
	public Receiver(Sender sender) throws IOException {
		in = new PipedReader(sender.getPipedWriter());
	}
	
	/**
	 * Invoking start() on a Thread like Receiver will cause the 
	 * JVM to invoke this run() method.
	 */
	public void run() {
		try {
			while (true) {
				// Blocks until characters are there:
				System.out.println("\t\tRead: " + (char)in.read());
			}
		} catch (IOException e) {
			/* We want to catch the fact that the sender did not close 
			 * the PipedWriter after it was interrupted.  This will 
			 * trigger an IO exception for the PipedReader. 
			 */
			System.out.println("\t\t* The sending thread finished without closing their side of the pipe.");
			System.out.println("\t\t  "+ e.getClass().getSimpleName() + " " + e.getMessage());
		}
	}
}

