import java.net.*;
import java.io.*;

/** Simple server socket that listens for a connection on port 1501.  When
 * it gets one, it spawns a new Thread to handle the client.  It listens
 * for a hello message, logs the message and then sends a reply.
 * @author R. B. Evans
 * @version 1.0
 */
public class SockTestServer
{
    // port number we are listening on
    final int portNumber = 1501;
    // the ServerSocket
    ServerSocket serverSocket;
    // incoming Socket
    Socket theConnection;
    // Thread which handles incoming connections
    HandleConnection palmConnect;

    // Reader for our log file
    BufferedReader reader;
    // output stream
    OutputLog writer;
    // number of log entries in the SockTestServerLog.txt file
    int logEntries = 0;
    // let's us turn on debugging
    boolean debug = false;

    /** Listener for Socket Connections from SockTest.  Read in the
     *logfile, and start up a listener on port 1501 */
    public SockTestServer() {
	if (debug) {
	    System.out.println("Reading History File");
	}
	// read in the log file and count the entries
	try {
	    reader = new BufferedReader(new FileReader("SockTestServerLog.txt"));
	} catch (IOException ioe) {
	    System.err.println("Couldn't open SockTestServerLog.txt");
	}
	String line = "";
	try {
	    for (logEntries=0; (logEntries < 10000 && (line != null)); logEntries++) {
		line = reader.readLine();
	    }
	    reader.close();
	    logEntries -= 1;
	} catch (IOException ioe) {
	    System.err.println("Couldn't read a line in SockTestServerLog.txt");
	}
	if (debug) {
	    System.out.println("History file had " + logEntries + " entries...");
	    System.out.println("Opening log file for writing...");
	}
	// open the output log
	try {
	    writer = new OutputLog(new FileWriter("SockTestServerLog.txt", true));
	} catch (IOException ioe) {
	    System.err.println("Couldn't open SockTestServerLog for writing");
	}

	if (debug) {
	    System.out.println("Starting SockTestServer on port " + portNumber + "...");
	}

	// Create our Server socket
	try {
	    serverSocket = new ServerSocket(portNumber);
	} catch (IOException ioe) {
	    System.err.println("Couldn't open server socket");
	}

	if (debug) {
	    System.out.println("Server socket made");
	    System.out.println("CommsTest(): waiting for a Connect");
	}

	// listen for connections
	while (true) {
	    try {
		theConnection = serverSocket.accept();
	    } catch (IOException ioe) {
		System.err.println("Error in accepting socket connection");
	    }
	    // pass the connection off to the HandleConnection object
	    palmConnect = new HandleConnection(theConnection);
	    // and start it's thread
	    palmConnect.start();
	}
    }
    
    public static void main(String[] argv) 
    {
  	SockTestServer sts = new SockTestServer();
    }


    /** Output Writer.  Basically a BufferedWriter with a synchronized
     * write method to let the multiple threads in SockTestServer use it.
     */
    protected class OutputLog extends BufferedWriter {

	/** Create an OutputLog from a Writer object */
	public OutputLog(Writer out) {
	    super(out);
	}

	/** Write the character array to the output stream.
	 * @param buff the character array to write
	 * @param off the offset from the beginning of the array
	 * @param len the length on the data to write from the array offset
	 */
	public synchronized void write(char[] buff, int off, int len) {
	    try {
		super.write(buff, off, len);
		newLine();
		flush();
	    } catch (IOException ioe) {
		System.err.println("Unable to write to SockTestServerLog.txt");
	    }
	}
    }

    /** class to process hello message and to send back response */
    protected class HandleConnection extends Thread {

	// the input and output streams
	InputStream is;
	InputStreamReader isr;
	BufferedReader br; 
	OutputStream out;
	// number of bytes read
	int bytesRead = 0;
	// buffer for incoming data
	char[] incomingData=new char[200];

	/** Class to process a hello message on a socket and to send a
	 *reply.
	 * @param theConnection a socket connection to the client
	 */
	public HandleConnection(Socket theConnection) {
  	    try {
		is = theConnection.getInputStream();
  		isr = new InputStreamReader(is);
  		br = new java.io.BufferedReader(isr);
		out  =  theConnection.getOutputStream();
  	    } catch (IOException ioe) {
  		System.err.println("Couldn't get io streams on new socket connection");
  	    }
	}

	/** process the incoming message */
	public void run() {
	    try {
		// read the data
		int charsRead = br.read(incomingData, 0, 200);
		if (debug) {
		    System.out.println("Logging data...");
		}
		// write it to the log
		writer.write(incomingData, 0, charsRead);
		logEntries++;

		System.out.println(new String(incomingData, 0, charsRead));
		if (debug) {
		    System.out.println("Sending message back to palm...");
		}
		// send out the response message
		out.write(("Congratulations! You have successfully connected to the server on webdev.apl.jhu.edu.\n" +  logEntries + " people have connected since June 16th, 2000").getBytes());
		out.flush();
		// close the sockets
		is.close();
		out.close(); 
	    } catch (IOException ioe) {
		System.err.println("Error in SockTestServer " +
				   ioe);
		System.err.println("Error in SockTestServer...Closing sockets");
		try {
		    is.close();
		    out.close();
		} catch (Exception e) {}
	    }
	}
    }
}
