import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.HashMap;

public class simplekv {
	final Charset charset = Charset.forName("UTF-8");
	
	public static void main(String[] args) throws Throwable {
		if (args.length != 1) {
			System.err.println("Wrong arg count");
			return;
		}
		short port = Short.parseShort(args[0]);
		simplekv kvServer = new simplekv(port);
		System.out.println("Server started");
		kvServer.loop();
	}
	
	HashMap<String, String> kv = new HashMap<String, String>();

	Selector sel = Selector.open();
	
	public simplekv(short port) throws Throwable {
		ServerSocketChannel ssc = ServerSocketChannel.open();
		ssc.bind(new InetSocketAddress(port));
		ssc.configureBlocking(false);
		ssc.register(sel, SelectionKey.OP_ACCEPT);
	}
	
	private void loop() throws Throwable {
		while(true){
			sel.select();
			for(SelectionKey key : sel.selectedKeys()){
				if(key.isAcceptable()){
					ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
					acceptNewClient(ssc);
				} else if (key.isReadable()){
					SocketChannel sc = (SocketChannel) key.channel();
					readData(sc, key);
				}
			}
			sel.selectedKeys().clear();
		}
	}

	private void acceptNewClient(ServerSocketChannel ssc) throws Throwable {
		SocketChannel newClientChannel = ssc.accept();
		newClientChannel.configureBlocking(false);
		newClientChannel.register(sel, SelectionKey.OP_READ);
	}
	
	private void readData(SocketChannel sc, SelectionKey key) throws Throwable {
		ByteBuffer bb = ByteBuffer.allocate(255);
		if(sc.read(bb) == -1){
			sc.close();
			return;
		}
		bb.flip();
		String string = charset.decode(bb).toString();
		assert(string.endsWith("\n"));
		
		executeLine(sc, key, string.trim());
	}

	private void executeLine(SocketChannel sc, SelectionKey selKey, String line) throws Throwable {
		String tokens[] = line.trim().split(" ", 2);
		String kvKey = tokens[0];
		String response;
		if(tokens.length==1){
			String value = kv.get(kvKey);
			response = "[KV] " + kvKey + (value == null ? " EMPTY" : " => " + value);
		} else {
			String newVal = tokens[1];
			String oldVal = kv.put(kvKey, newVal);
			response = "[KV] " + kvKey + " IS " + newVal + " (WAS " + (oldVal == null ? "EMPTY" : oldVal) + ")";
		} 
		send(sc, selKey, response);
	}

	private void send(SocketChannel sc, SelectionKey selKey, String response) throws Throwable {
		ByteBuffer bb = charset.encode(response+"\n");
		sc.write(bb);
		assert(!bb.hasRemaining());
	}
}
