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.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

import javax.swing.JOptionPane;

/**
 * <pre>
 * NetworkingExamples project: examples.example1_one_client.
 * 
 * A simple example of networking (Sockets) in which:
 *   -- A single Server and Client exchange information, one after the other.
 * 
 * This is the Server's code.
 *  
 * Brevity is king in this example, so it has few comments / instructions after this
 * and is NOT organized in a OO way that permits easy reuse of code.
 * 
 * See the other examples for an OO approach:
 *   -- {@link examples.example2_one_client_OO} does this same example in a more OO way
 *   -- {@link examples.example3_one_client_OO_library} does this same example
 *           using the simple networking library in package {@link simpleNetworking}
 *   -- {@link examples.example4_two_clients_OO_library} also uses the OO networking library {@link simpleNetworking}
 *   -- {@link examples.example5_chat_OO_library} also uses the OO networking library {@link simpleNetworking}
 *  
 * Run this example by:
 *  
 *  1. On one computer, run MainForServer.
 *  
 *     It starts up, nothing else happens.
 *     
 *  2. On another computer (or the same computer, your choice), run MainForClient.
 *  
 *     It asks in the console window/tab for the hostname of the Server.
 *     Enter the name of the computer running MainForServer.
 *     You can enter    localhost    if you are using a single computer for both processes.
 *
 * You should see (in successive dialog boxes):
 *   -- Server got 42 from the Client and will add 1 to it and thus send 43 to the Client
 *   -- Client got 43 from the Server and will add 10 to it and thus send 53 to the Server
 *   -- Server got 53 from the Client and will add 1 to it and thus send 54 to the Client
 *   -- Client got 54 from the Server and will add 10 to it and thus send 64 to the Server
 * etc
 *  
 * Stop the programs by using the dialog boxes (which ask if you want to continue).
 * Stopping the programs in other ways may prevent them from releasing their resources
 * (and especially the PORT being used).  Also, if the Client stops (for whatever reason)
 * without having connected to the Server, the Server is probably still running (without you
 * noticing it).  In this situation, simply run the Client WITHOUT first starting the Server
 * to clear out the lock / running Server.
 * 
 * Before running this program, you may need to tell your Firewall not to
 * block the port that this program uses (4444, chosen arbitrarily).
 * In Windows, do so by:
 *     Control Panel ~ Windows Firewall ~ Exceptions tab ~ Add Port
 * and enter the port number (4444) with any name you like.
 * 
 * Here is what the Server and Client are doing (to illustrate server/client communication):
 *   -- The Server starts with the number 42 and repeatedly:
 *     -- Adds 1 to its current number.
 *     -- Sends that number to the Client.
 *     -- Gets back a number from the Client
 *        and makes that number the Server's current number.
 *      
 *   -- The Client repeatedly:
 *     -- Gets a number from the Server.
 *     -- Adds 10 to that number.
 *     -- Sends that number back to the Server after pausing a few seconds.
 * 
 * The Server's user can ask it to stop and the Client's user can ask it to stop.
 * In either case, the Server/Client sends a STOP message to the Client/Server to ask it to stop too.
 * 
 * Note that most of the code is for handling errors.  There are only 7 Key Statements
 * (see details in the code) that are all-you-need-to-know to do networking in Java:
 * 
 *   -- Key #1:  Server constructs a ServerSocket.
 *   
 *   -- Key #2:  Server uses its ServerSocket to accept a connection to a Client.
 *               Note that accept blocks (waits) until a Client asks for a Connection.
 *               See Key #7 for how the Client does so.
 *               
 *   -- Key #3:  Constructs a BufferedReader that the Server can use to read
 *               from the Client.  The BufferedReader is constructed from the
 *               Socket that the Server got from Key #2 statement.
 *               
 *   -- Key #4:  Constructs a PrintWriter that the Server can use to write
 *               to the Client.  The PrintWriter is constructed from the
 *               Socket that the Server got from Key #2 statement.
 *               
 *   -- Key #5:  Server reads from the Client by using its BufferedReader.
 *   
 *   -- Key #6:  Server writes to the Client by using its PrintWriter.
 *   
 * For the last Key statement, turn to the Client:
 * 
 *   -- Key #7:  Client constructs a Socket for communicating with the Server.
 *   
 * The Client runs its Key #7 statement while the Server is waiting for
 * its Key #2 statement to complete; the Client's Key #7 statement causes
 * the Server's Key #2 statement to complete.
 * 
 * The Client continues from its Key #7 Statement to do as the Server did:
 *   -- Construct a BufferedReader and PrintReader (as per Key #3 and Key #4).
 *   -- Use the BufferedReader and PrintReader to read from and write to the Server
 *      (as per Key #5 and Key #6).
 * 
 * Reading blocks (waits) for input to arrive; however, there are methods
 * available to see if any input has arrived yet.
 * </pre>
 * 
 * @author David Mutchler, based on the Java Tutorials on networking. May, 2009.
 */
public class MainForServer {
	/**
	 * Port: Both Server and Client must use the same one, and it must be open.
	 */
	static int PORT = 5444;

	private static Socket socketToCommunicateWithClient;
	private static BufferedReader in;
	private static PrintWriter out;

	/**
	 * Constructs a Server and starts it running.
	 * 
	 * @param commandLineArguments Ignored here.
	 */
	public static void main(String[] commandLineArguments) {
		try {
			//-------------------------------------------------------------------------------------
			// KEY #1: Construct a ServerSocket that accepts connections from Clients.
			ServerSocket serverSocket = new ServerSocket(MainForServer.PORT);
			//-------------------------------------------------------------------------------------

			JOptionPane.showMessageDialog(null,
					"The Server has started and will wait for a connection.\n\n"
					+ "Start the Client AFTER pressing OK in this dialog.\n\n");

			//-------------------------------------------------------------------------------------
			// KEY #2: Block (wait) until a Client connects;
			//         get a Socket for communicating with that Client.
			MainForServer.socketToCommunicateWithClient = serverSocket.accept();
			//-------------------------------------------------------------------------------------

			// Done with the ServerSocket, since we wanted to accept only a single Client in this example.
			serverSocket.close();

			//-------------------------------------------------------------------------------------
			// KEY #3: From the Socket, construct a BufferedReader for reading from the Client.
			MainForServer.in = new BufferedReader(new InputStreamReader(
					MainForServer.socketToCommunicateWithClient.getInputStream()));
			//-------------------------------------------------------------------------------------

			//-------------------------------------------------------------------------------------
			// KEY #4: From the Socket, construct a PrintWriter for writing to the Client.
			MainForServer.out = new PrintWriter(
					MainForServer.socketToCommunicateWithClient.getOutputStream(), true);
			//-------------------------------------------------------------------------------------

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

			String dataFromClient, dataFromUser;
			int numberFromUser;

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

			while (true) {

				//-----------------------------------------------------------------------------
				// KEY #5: Read from the Client.
				dataFromClient = MainForServer.in.readLine();
				//-----------------------------------------------------------------------------

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

				dataFromUser = JOptionPane.showInputDialog(
						"The number from the Client is " + dataFromClient + ".\n\n"
						+ "What do you want to add to it?\n\n"
						+ "(Or press     Cancel     to quit this program.)\n\n");

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

				try {
					numberFromUser = Integer.parseInt(dataFromUser);
				} catch (NumberFormatException exception) {
					JOptionPane.showMessageDialog(null,
							"You entered   " + dataFromUser + "\n"
							+ "which is not a number!  I will use   0   instead.\n\n");
					numberFromUser = 0;
				}

				//-----------------------------------------------------------------------------
				// KEY #6: Write to the Client.
				MainForServer.out.println(Integer.parseInt(dataFromClient) + numberFromUser);
				//-----------------------------------------------------------------------------
			}

		} catch (BindException exception) {
			System.out.println(exception);
			JOptionPane.showMessageDialog(null,
					"Server could not connect to port   " + MainForServer.PORT + "\n"
					+ "perhaps because that port is in use.\n\n");

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

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

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

		MainForServer.close();
	}

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

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

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

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

		try {
			MainForServer.socketToCommunicateWithClient.close();

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