package examples.example1_one_client;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.BindException;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.NoRouteToHostException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

import javax.swing.JOptionPane;

/**
 * <pre>
 * NetworkingExamples project: examples.example1_one_client.
 *   -- A single Server and Client exchange information, one after the other.
 * 
 * This is the Client's code.
 * 
 * See the comment at the beginning of {@link MainForServer}
 * for a detailed explanation of this program, including instructions for running it.
 * </pre>
 * 
 * @author David Mutchler, based on the Java Tutorials on networking. May, 2009.
 */
public class MainForClient {
	private static Socket socketToCommunicateWithServer;
	private static BufferedReader in;
	private static PrintWriter out;

	/**
	 * Constructs a Client and starts it running.
	 * 
	 * @param commandLineArguments Ignored here.
	 */
	public static void main(String[] commandLineArguments) {
		String hostName = "";

		try {
			hostName = JOptionPane.showInputDialog(
					"OK, you are the Client and have started.\n\n"
					+ "What is the HostName (e.g. mutchler-3.rose-hulman.edu)\n"
					+ "or IP address (e.g. 137.112.40.1) of the Server?\n"
					+ "(Enter   localhost   or just press OK for this local machine.)\n\n");
			
			//-------------------------------------------------------------------------------------
			// KEY #7: Connect to the given Server, getting a Socket for communicating with it.
			MainForClient.socketToCommunicateWithServer =
				new Socket(hostName, MainForServer.PORT);
			//-------------------------------------------------------------------------------------

			//-------------------------------------------------------------------------------------
			// From the Socket, construct a BufferedReader for reading from the Client.
			MainForClient.in = new BufferedReader(new InputStreamReader(
					MainForClient.socketToCommunicateWithServer.getInputStream()));
			//-------------------------------------------------------------------------------------

			//-------------------------------------------------------------------------------------
			// From the Socket, construct a PrintWriter for writing to the Client.
			MainForClient.out = new PrintWriter(
					MainForClient.socketToCommunicateWithServer.getOutputStream(), true);
			//-------------------------------------------------------------------------------------

			//-------------------------------------------------------------------------------------
			// Now start communicating with the Server.
			//-------------------------------------------------------------------------------------

			int number;
			String dataFromServer;
			int dataFromUser;

			JOptionPane.showMessageDialog(null,
					"This Client is running on   "
					+ InetAddress.getLocalHost().getCanonicalHostName() + "\n"
					+ "which is IP address   "
					+ InetAddress.getLocalHost().getHostAddress() + "\n\n");

			number = (int) (Math.random() * 100);
			JOptionPane.showMessageDialog(null,
					"The Client starts the ball rolling with the random number " + number);

			//-----------------------------------------------------------------------------
			// Write to the Server.
			MainForClient.out.println(number);
			//-----------------------------------------------------------------------------

			while (true) {

				//-----------------------------------------------------------------------------
				// Read from the Server.
				dataFromServer = MainForClient.in.readLine();
				//-----------------------------------------------------------------------------

				if (dataFromServer.equals("STOP")) {	// Server says to stop.
					JOptionPane.showMessageDialog(null,
							"Client ended because the Server asked it to do so");
					break;	
				}

				number = (int) (Math.random() * 10);	// What the Client does in this example.

				dataFromUser = JOptionPane.showConfirmDialog(
						null,
						"The Client got " + dataFromServer + " from the Server.\n\n"
						+ "The Client chooses to add " + number + " to the above.\n\n"
						+ "Thus the Client will send "
						+ (number + Integer.parseInt(dataFromServer))
						+ " to the Server after a few seconds pause.\n\n"
						+ "Press   OK   to continue or   Cancel   to quit this program.\n\n",
						"Continue?",
						JOptionPane.OK_CANCEL_OPTION);

				if (dataFromUser == JOptionPane.CANCEL_OPTION) {
					// User selected   Cancel   in input dialog.
					JOptionPane.showMessageDialog(null,
							"Client ended because the user asked it to do so.\n\n"
							+ "Tell the Server to stop too.\n\n");
					MainForClient.out.println("STOP");	// Tell the Server to stop
					break;								// and stop this Client.
				}

				Thread.sleep((long) (Math.random() * 5000));

				//-----------------------------------------------------------------------------
				// Write to the Server.
				MainForClient.out.println(number + Integer.parseInt(dataFromServer));
				//-----------------------------------------------------------------------------
			}

		} catch (UnknownHostException exception) {
			System.out.println(exception);
			JOptionPane.showMessageDialog(null,
					"Client could not connect to   " + hostName
					+ "    because that is an unknown host.\n\n"
					+ "The Server may still running; fix that by running a Client\n"
					+ "(without starting a Server).\n\n");

		} catch (ConnectException exception) {
			System.out.println(exception);
			JOptionPane.showMessageDialog(null,
					"Client could not connect to   " + hostName
					+ "   either because:\n"
					+ " -- the Server is not yet started, or \n"
					+ " -- the host does not accept connections on port " + MainForServer.PORT + ".\n\n"
					+ "The Server may still running; fix that by running a Client\n"
					+ "(without starting a Server).\n\n");

		} catch (NoRouteToHostException exception) {
			System.out.println(exception);
			JOptionPane.showMessageDialog(null,
					"Client could not connect to   " + hostName
					+ "   perhaps because\n"
					+ "the hostname   " + hostName
					+ "   does not allow connections on port   " + MainForServer.PORT + ".\n\n"
					+ "The Server may still running; fix that by running a Client\n"
					+ "(without starting a Server).\n\n");

		} catch (BindException exception) {
			System.out.println(exception);
			JOptionPane.showMessageDialog(null,
					"Client could not connect to   " + hostName
					+ "   perhaps because\n"
					+ "port   " + MainForServer.PORT + "   is in use.\n"
					+ "The Server is probably still running; stop it by getting a Client to run.\n\n"
					+ "The Server may still running; fix that by running a Client\n"
					+ "(without starting a Server).\n\n");

		} catch (SocketException exception) {
			System.out.println(exception);
			JOptionPane.showMessageDialog(null,
					"Client ended, either because it could not start\n"
					+ "or (more likely) because the user closed the Server.\n\n");

		} catch (IOException exception) {
			System.out.println(exception);
			JOptionPane.showMessageDialog(null,
					"Client ended, probably because the user closed the Server.\n\n");
		}

		catch (Throwable exception) {
			System.out.println(exception);
			JOptionPane.showMessageDialog(null, "Client ended abnormally.  See stack trace.\n\n");
			JOptionPane.showMessageDialog(null, exception.getStackTrace());
		}

		MainForClient.close();
	}

	/**
	 * Closes all resources that this Client uses: writer, reader, socket.
	 */
	private static void close() {
		Closeable[] resourcesToClose = {MainForClient.out, MainForClient.in};

		JOptionPane.showMessageDialog(null, "Closing the Client's resources.");

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

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

		try {
			MainForClient.socketToCommunicateWithServer.close();

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