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;
    }

    public boolean readMessage(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof io.netty.handler.codec.http.HttpRequest) {
            this.request = new RakamHttpRequest(ctx, (io.netty.handler.codec.http.HttpRequest) msg);
            if (HttpHeaders.is100ContinueExpected(request)) {
                ctx.writeAndFlush(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));
            } else {
                routes.handle(request);
            }
            return true;
        }
        return false;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        if(readMessage(ctx, msg)) {
            return;
        }

        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);
        }
    }

    @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 AttributeKey<Integer> START_TIME;
        private final HttpServerHandler serverHandler;

        public DebugHttpServerHandler(ConcurrentSet<ChannelHandlerContext> activeChannels, AttributeKey<Integer> START_TIME, HttpServerHandler handler) {
            this.activeChannels = activeChannels;
            this.START_TIME = START_TIME;
            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) {
            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));
            }
        }
    }
}



@@ -1578,35 +1578,52 @@
 ctx, Object msg)
+ throws Exception
  %7B%0A
-
         if(readM
@@ -2978,32 +2978,90 @@
 ketFrame) msg);%0A
+        %7D else %7B%0A            super.channelRead(ctx, msg);%0A
         %7D%0A    %7D%0A
@@ -4166,32 +4166,49 @@
 ctx, Object msg)
+ throws Exception
  %7B%0A            s
@@ -4235,32 +4235,33 @@
 Read(ctx, msg);%0A
+%0A
             if (
@@ -4476,24 +4476,24 @@
 s()/1000));%0A
-
             
@@ -4499,23 +4499,196 @@
  %7D%0A        %7D
+%0A%0A        @Override%0A        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) %7B%0A            cause.printStackTrace();%0A            ctx.close();%0A        %7D
 %0A    %7D%0A%7D%0A%0A%0A

send message to super if it's not handled