package org.rakam.server.http;


import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.ConcurrentSet;

import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;


public class HttpServerHandler extends ChannelInboundHandlerAdapter {
    protected RakamHttpRequest request;
    RouteMatcher routes;
    private StringBuilder body = new StringBuilder(2 << 15);
    private static String EMPTY_BODY = "";

    public HttpServerHandler(RouteMatcher routes) {
        this.routes = routes;
    }

    RakamHttpRequest createRequest(ChannelHandlerContext ctx) {
        return new RakamHttpRequest(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof io.netty.handler.codec.http.HttpRequest) {
            this.request = createRequest(ctx);
            this.request.setRequest((io.netty.handler.codec.http.HttpRequest) msg);
            if (HttpHeaders.is100ContinueExpected(request)) {
                ctx.writeAndFlush(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));
            } else {
                routes.handle(request);
            }
        }else
        if (msg instanceof LastHttpContent) {
            HttpContent chunk = (HttpContent) msg;
            if (chunk.content().isReadable()) {
                String s = chunk.content().toString(CharsetUtil.UTF_8);
                if (body == null || body.length() == 0) {
                    request.handleBody(s);
                } else {
                    body.append(s);
                    request.handleBody(body.toString());
                }
                body.delete(0, body.length());
                chunk.release();
            } else {
                // even if body content is empty, call request.handleBody method.
                if (request.getBodyHandler()!=null) {
                    request.handleBody(EMPTY_BODY);
                }
            }
        } else if (msg instanceof HttpContent) {
            HttpContent chunk = (HttpContent) msg;
            if (chunk.content().isReadable()) {
                String s = chunk.content().toString(CharsetUtil.UTF_8);
                if (body == null) {
                    body = new StringBuilder(s);
                } else {
                    body.append(s);
                }
                chunk.release();
            }

        } else if (msg instanceof WebSocketFrame) {
            routes.handle(ctx, (WebSocketFrame) msg);
        } else {
//            super.channelRead(ctx, msg);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

    protected static class DebugHttpServerHandler extends ChannelInboundHandlerAdapter {
        private final ConcurrentSet<ChannelHandlerContext> activeChannels;
        private final HttpServerHandler serverHandler;
        final static AttributeKey<Integer> START_TIME = AttributeKey.valueOf("/start_time");

        public DebugHttpServerHandler(ConcurrentSet<ChannelHandlerContext> activeChannels, HttpServerHandler handler) {
            this.activeChannels = activeChannels;
            this.serverHandler = handler;
        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            activeChannels.add(ctx);
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            activeChannels.remove(ctx);
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            serverHandler.channelRead(ctx, msg);

            if (msg instanceof io.netty.handler.codec.http.HttpRequest) {
                ctx.attr(RouteMatcher.PATH).set(serverHandler.request.path());
                ctx.attr(START_TIME).set((int) (System.currentTimeMillis()/1000));
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
            ctx.close();
        }
    }
}



@@ -1716,32 +1716,54 @@
 tpContent) msg;%0A
+            try %7B%0A    
             if (
@@ -1786,32 +1786,36 @@
 isReadable()) %7B%0A
+    
                 
@@ -1878,32 +1878,36 @@
                 
+   
+ 
 if (body == null
@@ -1944,32 +1944,36 @@
                 
+    
 request.handleBo
@@ -1987,32 +1987,36 @@
                 
+  
+  
 %7D else %7B%0A       
@@ -2000,32 +2000,36 @@
        %7D else %7B%0A
+    
                 
@@ -2060,32 +2060,36 @@
                 
+ 
+   
 request.handleBo
@@ -2105,24 +2105,28 @@
 oString());%0A
+    
             
@@ -2143,24 +2143,28 @@
             
+    
 body.delete(
@@ -2190,32 +2190,36 @@
                 
+   
+ 
 chunk.release();
@@ -2223,32 +2223,36 @@
 ();%0A            
+    
 %7D else %7B%0A       
@@ -2236,32 +2236,36 @@
        %7D else %7B%0A
+    
                 
@@ -2342,24 +2342,28 @@
             
+    
 if (request.
@@ -2400,32 +2400,36 @@
                 
+  
+  
 request.handleBo
@@ -2452,33 +2452,186 @@
                 
-%7D
+    %7D%0A                %7D%0A            %7D catch (HttpRequestException e) %7B%0A                HttpServer.returnError(request, e.getMessage(), e.getStatusCode());
 %0A            %7D%0A 
@@ -3171,68 +3171,8 @@
 g);%0A
-        %7D else %7B%0A//            super.channelRead(ctx, msg);%0A
     
@@ -4591,181 +4591,8 @@
    %7D
-%0A%0A        @Override%0A        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) %7B%0A            cause.printStackTrace();%0A            ctx.close();%0A        %7D
 %0A   

handle errors gracefully in body handler