package simpleNetworking;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * A class for simple reading (via readLine and readLineIfReady)
 * and writing (via writeLine), using a socket (i.e. through the network).
 *  
 * @author David Mutchler, using examples.example1_one_client from the Java Tutorials.  May, 2009.
 */
public class ReaderWriter {
	private Socket socket;
	private BufferedReader in;
	private PrintWriter out;

	/**
	 * Constructs and stores a reader and writer for the given socket.
	 *
	 * @param socket Socket from which to construct a reader and writer.
	 * @throws IOException if the reader and/or writer cannot be constructed.
	 */
	public ReaderWriter(Socket socket) throws IOException {
		try {
			this.socket = socket;
			
		    this.in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			this.out = new PrintWriter(socket.getOutputStream(), true);
			
		} catch (IOException exception) {
			this.close();
		}
	}
	
	/**
	 * Returns the next line available from the socket associated with this connection,
	 * stripping the terminating newline.
	 * Blocks (waits) if no line is available yet.
	 *
	 * @return the next line available from the socket associated with this connection,
	 *         but with the terminating newline stripped.
	 * @throws IOException if an IO error occurs while reading.
	 */
	public String readLine() throws IOException {
		// Blocks until a line is sent, the buffer is filled, or an IO exception occurs.
		return this.in.readLine();
	}

	/**
	 * Returns the next line available from the socket associated with this connection,
	 * stripping the terminating newline.
	 * Returns immediately (returning null) if no line is available yet.
	 *
	 * @return the next line available from the socket associated with this connection,
	 *         but with the terminating newline stripped,
	 *         or null if no line is available yet.
	 * @throws IOException if an IO error occurs while reading.
	 */
	public String readLineIfReady() throws IOException {
		if (this.in.ready()) {
			return this.in.readLine();
		} else {
			return null;
		}
	}
	
	/**
	 * Sends the given String, appending a newline to it,
	 * to the socket associated with this connection.
	 * 
	 * @param stringToWrite String to send (with a newline appended)
	 *                      to the socket associated with this connection.
	 * @throws IOException if an error occurs while writing.
	 */
	public void writeLine(String stringToWrite) throws IOException {
		this.out.println(stringToWrite);
		
		if (this.out.checkError()) {
			throw new IOException("Error writing " + stringToWrite);
		}
	}
	
	/**
	 * Closes all resources that this ReaderWriter uses: writer, reader, socket.
	 */
	void close() {
		Closeable[] resourcesToClose = {this.out, this.in};

		for (Closeable resource : resourcesToClose) {
			try {
				resource.close();

			} catch (Exception exception) {
				// Ignore; we made our best effort at closing the resource.
			}
		}

		try {
			this.socket.close();

		} catch (Exception exception) {
			// Ignore; we made our best effort at closing the resource.
		}
	}
}