// Copyright 2014, Kevin Ko <kevin@faveset.com>

package com.faveset.khttp;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;

import java.nio.channels.Selector;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

class HttpServer {
    public interface Handler {
        void onRequest(HttpRequest req, HttpResponseWriter writer);
    }

    private interface SelectableChannelHandler {
        void onReady(SelectableChannel chan, Object attachment) throws IOException;
    }

    private Map<String, Handler> mHttpHandlerMap = new HashMap<String, Handler>();

    private Map<SelectionKey, SelectableChannelHandler> mSelectableChannelHandlerMap = new HashMap<SelectionKey, SelectableChannelHandler>();

    private ServerSocketChannel mListenChan;
    private Selector mSelector;

    public HttpServer() {
        mHttpHandlerMap = new HashMap<String, Handler>();
    }

    public void close() throws IOException {
        mListenChan.close();

        mSelector.close();
    }

    private void handleAccept(SelectableChannel chanArg, Object attachment) throws IOException {
        ServerSocketChannel chan = (ServerSocketChannel) chanArg;

        SocketChannel newChan = chan.accept();
        newChan.configureBlocking(false);
    }

    public void listenAndServe(String listenAddr, int port) throws IllegalArgumentException, IOException {
        InetSocketAddress sa = new InetSocketAddress(listenAddr, port);

        mListenChan = ServerSocketChannel.open();
        mListenChan.configureBlocking(false);

        ServerSocket sock = mListenChan.socket();
        sock.bind(sa);

        mSelector = Selector.open();
        SelectionKey listenKey = mListenChan.register(mSelector, SelectionKey.OP_ACCEPT);
        registerSelectableChannelHandler(listenKey, new SelectableChannelHandler() {
            public void onReady(SelectableChannel chan, Object attach) throws IOException {
                handleAccept(chan, attach);
            }
        });

        while (true) {
            int readyCount = mSelector.select();
            if (readyCount == 0) {
                // We were interrupted.
                break;
            }

            Set<SelectionKey> readyKeys = mSelector.selectedKeys();
            for (SelectionKey key : readyKeys) {
                if (!key.isValid()) {
                    continue;
                }

                SelectableChannelHandler handler = mSelectableChannelHandlerMap.get(key);
                handler.onReady(key.channel(), key.attachment());
            }
        }

        mSelector.close();
    }

    public void registerGet(String url, Handler handler) {
        mHttpHandlerMap.put(url, handler);
    }

    private void registerSelectableChannelHandler(SelectionKey key, SelectableChannelHandler handler) {
        mSelectableChannelHandlerMap.put(key, handler);
    }

    private void unregisterSelectableChannelHandler(SelectionKey key) {
        mSelectableChannelHandlerMap.remove(key);
    }
};

@@ -588,35 +588,26 @@
 rface Select
-ableChannel
+or
 Handler %7B%0A  
@@ -635,43 +635,18 @@
 lect
-ableChannel chan, Object attachment
+ionKey key
 ) th
@@ -786,35 +786,26 @@
 nKey, Select
-ableChannel
+or
 Handler%3E mSe
@@ -804,35 +804,26 @@
 ler%3E mSelect
-ableChannel
+or
 HandlerMap =
@@ -851,35 +851,26 @@
 nKey, Select
-ableChannel
+or
 Handler%3E();%0A
@@ -1207,27 +1207,8 @@
 nArg
-, Object attachment
 ) th
@@ -1381,16 +1381,364 @@
 (false);
+%0A%0A        // Prepare to read in the request.%0A        SelectionKey key = newChan.register(mSelector, SelectionKey.OP_READ);%0A        registerSelectorHandler(key, new SelectorHandler() %7B%0A            public void onReady(SelectionKey key) throws IOException %7B%0A                // TODO%0A                //handleHttp(chan, attach);%0A            %7D%0A        %7D);
 %0A    %7D%0A%0A
@@ -2233,35 +2233,26 @@
 gisterSelect
-ableChannel
+or
 Handler(list
@@ -2264,35 +2264,26 @@
 , new Select
-ableChannel
+or
 Handler() %7B%0A
@@ -2324,39 +2324,18 @@
 lect
-ableChannel chan, Object attach
+ionKey key
 ) th
@@ -2382,28 +2382,29 @@
 eAccept(
-chan, attach
+key.channel()
 );%0A     
@@ -2570,16 +2570,47 @@
 errupted
+, or the selector was cancelled
 .%0A      
@@ -2863,35 +2863,26 @@
       Select
-ableChannel
+or
 Handler hand
@@ -2890,35 +2890,26 @@
 er = mSelect
-ableChannel
+or
 HandlerMap.g
@@ -2956,36 +2956,8 @@
 (key
-.channel(), key.attachment()
 );%0A 
@@ -3150,35 +3150,26 @@
 gisterSelect
-ableChannel
+or
 Handler(Sele
@@ -3184,35 +3184,26 @@
  key, Select
-ableChannel
+or
 Handler hand
@@ -3220,35 +3220,26 @@
      mSelect
-ableChannel
+or
 HandlerMap.p
@@ -3292,35 +3292,26 @@
 gisterSelect
-ableChannel
+or
 Handler(Sele
@@ -3341,27 +3341,18 @@
  mSelect
-ableChannel
+or
 HandlerM

Rename to SelectorHandler instead of SelectionChannelHandler and simplify interface.