package fr.upem.nio;

import java.io.IOException;
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.util.Set;

public class NioTCPServer {

	private final int BUFFER_SIZE = 1024;
	private final ServerSocketChannel serverSocketChannel;
	private Selector selector;

	public NioTCPServer(int port) throws IOException {
		serverSocketChannel=ServerSocketChannel.open();
		serverSocketChannel.bind(new InetSocketAddress(port));
		selector=Selector.open();
	}

	public void launch() throws IOException {
		serverSocketChannel.configureBlocking(false);
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		Set<SelectionKey> selectedKeys = selector.selectedKeys();
		try {
			while(!Thread.interrupted()) {
				selector.select();
				for(SelectionKey key : selectedKeys) {
					if(key.isValid() && key.isAcceptable()) {
						doAccept(key);
					}
					try {
						if(key.isValid() && key.isWritable()) {
							ChannelHandlerEcho hand = (ChannelHandler) key.attachment();
							hand.doWrite();
						}
						if(key.isValid() && key.isReadable()) {
							ChannelHandlerEcho hand = (ChannelHandler) key.attachment();
							hand.doRead();
						} 
					} catch (IOException e) {
						try {key.channel().close();} catch (IOException e2) {}
					}
				}
				selectedKeys.clear();
			}
		} finally {
			selector.close();
		}
	}

	

	private void doAccept(SelectionKey key) throws IOException {
		SocketChannel socket=serverSocketChannel.accept();
		socket.configureBlocking(false);
		SelectionKey channelKey = socket.register(selector,SelectionKey.OP_READ);
		channelKey.attach(new ChannelHandler(channelKey,BUFFER_SIZE));
	}

	public static void main(String[] args) throws NumberFormatException, IOException {
		new NioTCPServerEcho(Integer.parseInt(args[0])).launch();
	}
	
}



