import java.io.*;
import java.net.*;
import java.util.concurrent.ConcurrentHashMap;
import java.text.*;
import java.util.Calendar;

class DateUtils {
	public static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss";

	public static String now() {
		Calendar cal = Calendar.getInstance();
		SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_NOW);
		return sdf.format(cal.getTime());

	}
}

public class PropertiesService {

	/**
	 * @param args
	 */
	private static int portNumber;
	private static int maxConnections = 10;

	// constructs a new empty concurrent HashMap with default initial capacity
	// Concurrent HashMap is used to store the (Name,Value) pairs

	private static ConcurrentHashMap<String, String> hashMap = new ConcurrentHashMap<String, String>();

	/*
	 * static class theLock extends Object { }
	 * 
	 * static public theLock lockObject = new theLock();
	 */

	/*
	 * This client thread opens the input and the output streams for a
	 * particular client, ask the client's name, informs all the clients
	 * currently connected to the server about the fact that a new client has
	 * joined the service, and as long as it receive data, echos that data back
	 * to all other clients. When the client leaves the chat room this thread
	 * informs also all the clients about that and terminates.
	 */

	class clientThread extends Thread {
		// input command
		String line;
		// output result
		String outputline;

		// input data stream reader
		BufferedReader is;
		// output data stream writer
		PrintStream os;

		Socket clientSocket = null;
		clientThread t[];

		// constructor of clientThread
		public clientThread(Socket clientSocket, clientThread[] t) {
			this.clientSocket = clientSocket;
			this.t = t;
		}

		@Override
		public void run() {
			// Client Host Name
			String name;

			/*
			 * Open input and output streams
			 */
			try {
				is = new BufferedReader(new InputStreamReader(
						clientSocket.getInputStream()));
				os = new PrintStream(clientSocket.getOutputStream());

				// line = is.readLine();

				// convert client IP address to string and stored in host name
				// variable
				name = this.clientSocket.getInetAddress().toString();

				System.out.println(DateUtils.now() + " :*** A new user " + name
						+ " entered the service !!! ***");

				// based on the request command, output the service properties
				// as documented
				while (true) {
					line = is.readLine();
					if (line.startsWith("/quit"))
						break;
					// Returns a copy of the string, with leading and trailing
					// whitespace omitted
					line = line.trim();
					// Converts all of the characters in this String to lower
					// case
					// using the rules of the default
					line = line.toLowerCase();

					outputline = "";
					// Parsing requests from clients
					// space (' '), tab ('\t'), newline ('\n'), carriage return
					// ('\r'),form feed ('\f')
					char delimitCharacter = ' ';
					String[] parsedArguments = line.split(Character
							.toString(delimitCharacter));
					if (parsedArguments.length != 2) {
						System.out.println(DateUtils.now()
								+ " :Invalid number of command arguments");
						break;
						// this.interrupt();
					}
					String command = parsedArguments[0];
					String commandArgument = parsedArguments[1];
					// System.out.println(command + " " +commandArgument );
					// debug purpose

					if (command.equals("get")) {
						outputline = hashMap.get(commandArgument);
					} else if (command.equals("set")) {
						// check out the position of assignment operator = in
						// the
						// 2nd argument
						int pos = commandArgument.indexOf('=');
						String PropertyName = commandArgument.substring(0, pos);
						String PropertyValue = commandArgument
								.substring(pos + 1);
						outputline = hashMap.put(PropertyName, PropertyValue);

					} else if (command.equals("clear")) {

						outputline = hashMap.get(commandArgument);
						hashMap.put(commandArgument, "");

					} else {

						System.out.println(DateUtils.now()
								+ " :Invalid command");
					}

					this.os.println(outputline);
					System.out.println(DateUtils.now() + name + " :"
							+ outputline);

				}

				System.out.println(DateUtils.now() + " :*** The user " + name
						+ " is leaving the service!!! ***");

				// Clean up:
				// Set to null the current thread variable such that other
				// client could be accepted by the server

				for (int i = 0; i < maxConnections; i++)
					if (t[i] == this)
						t[i] = null;

				// close the output stream
				// close the input stream
				// close the socket

				is.close();
				os.close();
				clientSocket.close();
			} catch (IOException e) {
			}
			;
		}
	}

	// @SuppressWarnings("deprecation")
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// Argument Processing Code

		switch (args.length) {
		case 0: // some code to handle the one and only argument
			setPortNumber(4444);
			break;
		case 1: // some code to handle two parameters
			try {
				setPortNumber(Integer.parseInt(args[0]));
			} catch (NumberFormatException e) {
				System.out.println(DateUtils.now()
						+ " :Port number must be an integer");
				System.exit(1);
			}
			break;
		default:
			System.out.println(DateUtils.now() + " :Invalid Input");
			System.exit(1);
		}
		// instantiate a PropertiesService object

		PropertiesService ps = new PropertiesService();

		// System.out.println("Current Date: " + ft.format(dNow.getTime()));

		// declare a server socket and a client socket for the server as well as
		// input and output stream
		ServerSocket echoServer = null;

		Socket clientSocket = null;

		// This chat server can accept up to 10 clients' connections

		clientThread t[] = new clientThread[maxConnections];

		// Try to open a socket on specified port
		try {
			echoServer = new ServerSocket(getPortNumber());
		} catch (IOException e) {
			System.out.println(DateUtils.now() + e);
		}

		// Try to allocate each socket connection on a single thread, max number
		// is 10 by default
		while (true) {
			try {
				clientSocket = echoServer.accept();
				for (int i = 0; i < maxConnections; i++) {
					if (t[i] == null) {
						// implements client thread that extends Thread class,
						// start() will call run()
						(t[i] = ps.new clientThread(clientSocket, t)).start();
						break;
					}
				}
			} catch (IOException e) {
				System.out.println(DateUtils.now() + e);
			}
		}

	}

	public static void setPortNumber(int portNumber) {
		PropertiesService.portNumber = portNumber;
	}

	public static int getPortNumber() {
		return portNumber;
	}

}
