/* GrnvsTcpServer.java */

import java.net.*;
import java.io.*;
import java.util.Date;
import java.util.Random;

public class GrnvsTcpServer {
	public static void main(String[] args) {
		int cnt = 0;
		try {
			System.out.println("Warte auf Verbindungen auf Port 2342...");
			ServerSocket grnvsServerSocket = new ServerSocket(2342); // bind + listen
			while (true) {
				Socket socket = grnvsServerSocket.accept();
				/*
				 * Accept blocks until a connection comes in.
				 * According to the state machine we have a "passive open".
				 * As soon as a connection arrives we handle it inside a new thread.
				 * Why? To be able to accept another connection...
				 */
				(new GrnvsServerThread(++cnt, socket)).start();
			}
		} catch (IOException e) {
			System.err.println(e.toString());
			System.exit(1);
		}
	}
}

class GrnvsServerThread extends Thread {
	/*
	 * This thread handles one connection.
	 */
	private int number; // counter of threads since main was started
	private Socket socket; // Our TCP-Socket
	private BufferedReader in; // Communication channel from Client.
	private BufferedWriter out; // Communication Channel to Client.
	private int challenge1, challenge2, challenge3; // We want to have a little challenge...
	private int response; // ..that has to be answered with this response.
	private Long connectionStart; // We only allow Clients to take a certain amount of time.
	private static int maxTime = 5000; // 5 seconds to deliver data...

	public GrnvsServerThread(int number, Socket socket) {
		this.number = number;
		this.socket = socket;
		try {
			this.in = new BufferedReader(new InputStreamReader(socket
					.getInputStream()));
			this.out = new BufferedWriter(new OutputStreamWriter(socket
					.getOutputStream()));
		} catch (IOException e) {
			System.err.println(e.toString());
		}
		// Prepare the challenge
		Random generator = new Random();
		challenge1 = generator.nextInt(9999);
		challenge2 = generator.nextInt(9999);
		challenge3 = generator.nextInt(9999);
		response = challenge1 + challenge2 + challenge3;
	}

	public void run() {
		String msg = "GrnvsTcpServer: Verbindung " + number + " von IP "
				+ socket.getInetAddress() + ":" + socket.getPort();
		System.out.println(msg + " hergestellt" + " (" + challenge1 + '+'
				+ challenge2 + '+' + challenge3 + '=' + response + ")");
		try {
			connectionStart = (new Date()).getTime();
			say(msg); // say (see below) sends data to the Client.
			// We expect the Client to be polite. So he will say HELLO to us...
			if (in.readLine().toUpperCase().compareTo("HELLO") != 0) {
				quit("No greetings?");
				return;
			}
			say("Welcome!");

			// We present the challenge to the Client.
			say("Please answer the following challenge by summing up the numbers:");
			say(Integer.toString(challenge1));
			say(Integer.toString(challenge2));
			say(Integer.toString(challenge3));

			// Did the Client get it?
			if (Integer.parseInt(in.readLine()) != response) {
				quit("Wrong response.");
				return;
			}

			// Client managed the challenge so we ask for further data...
			say("Please send your Teamletter...");
			char teamLetter = in.readLine().toUpperCase().charAt(0);
			if (teamLetter < 'A' || teamLetter > 'N') {
				quit("Group " + teamLetter + " does not exist.");
				return;
			}

			say("Please send your Name formatted as [Surname] [Prename]...");
			String name = in.readLine();

			Date now = new Date();
			// We prepare the log data...
			String logData = teamLetter + "," + name + ","
					+ socket.getInetAddress().toString() + ":"
					+ socket.getPort() + "," + now.toString();
			System.out.println(logData);
			say("Submitted data:");
			say(logData);
			
			// Was the Client fast enough?
		    long timeUsed = now.getTime() - connectionStart;
			if (timeUsed > maxTime) {
				say("Your data was *NOT SAVED* since you answered too slowly... (You took "
						+ timeUsed/1000
						+ " seconds, maximum allowed time is "
						+ maxTime/1000
						+ " seconds.)");
				quit("Pease try again!");
			} else {
				// Client was fast enough, so we save the answer...
				append2log(logData);
				say("Your data was saved.");
				quit("Thank you!");
			}
		} catch (IOException e) {
			System.err.println(e.toString());
		}
	}

	private void say(String msg) {
		/*
		 * We have to do a lot of sending to the Client so we summed this up here...
		 */
		try {
			out.write(msg + "\r\n");
			out.flush(); // we push the data out of our local buffers
		} catch (IOException e) {
			System.err.println(e.toString());
		}
	}

	private void quit(String msg) {
		/*
		 * Since we have many points where the Client might not go along our protocol we possibly have to exit at many steps...
		 * We do that with this method.
		 * So this method cleans up before we end the thread. 
		 */
		try {
			say(msg + "\r\nBye...");
			in.close();
			out.close();
			socket.close();

		} catch (IOException e) {
			System.err.println(e.toString());
		}
	}

	private synchronized void append2log(String text) {
		/*
		 * This method is used to write to the log file.
		 * Since we might have multiple threads and that might cause us trouble while writing we implemented this method synchronized...
		 */
		try {
			FileWriter submissions = new FileWriter("/var/services/GrnvsTcpServer/output/submissions.txt", true);
			submissions.write(text + "\r\n");
			submissions.close();
		} catch (IOException e) {
			System.err.println(e.toString());
		}
	}
}