package com.xueyi.nlt.netty.client.handler;

import com.xueyi.nlt.netty.client.message.ReceiveMessage;
import com.xueyi.nlt.netty.client.WebsocketConfig;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.HandshakeComplete;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;

public class NettyWebsocketClientHandler extends SimpleChannelInboundHandler<Object> {
    private Logger logger = LoggerFactory.getLogger(NettyWebsocketClientHandler.class);

    private WebsocketConfig websocketConfig;

    private final WebSocketClientHandshaker handshaker;

    private ChannelPromise handshakeFuture;

    private ReceiveMessage receiveMessage;

//    private HeartBeat heartBeat;

    public NettyWebsocketClientHandler(WebSocketClientHandshaker handshaker) {
        this.handshaker = handshaker;
    }

    public NettyWebsocketClientHandler(WebSocketClientHandshaker handshaker, ReceiveMessage receiveMessage) {
        this.handshaker = handshaker;
        this.receiveMessage = receiveMessage;
    }

    public ChannelFuture handshakeFuture() {
        return handshakeFuture;
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        handshakeFuture = ctx.newPromise();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception{
        super.channelActive(ctx);
        handshaker.handshake(ctx.channel());
        logger.info("channelActive");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception{
        super.channelInactive(ctx);
        logger.info("WebSocket Client disconnected!");
    }

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

    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        Channel ch = ctx.channel();
        if (!handshaker.isHandshakeComplete()) {
            try {
                handshaker.finishHandshake(ch, (FullHttpResponse) msg);
                logger.info("WebSocket Client connected!");
                handshakeFuture.setSuccess();
            } catch (WebSocketHandshakeException e) {
                logger.info("WebSocket Client failed to connect, " + e.getMessage());
                handshakeFuture.setFailure(e);
            }
            return;
        }

        if (msg instanceof FullHttpResponse) {
            FullHttpResponse response = (FullHttpResponse) msg;
            throw new IllegalStateException(
                    "Unexpected FullHttpResponse (getStatus=" + response.getStatus() +
                            ", content=" + response.content().toString(CharsetUtil.UTF_8) + ')');
        }

        WebSocketFrame frame = (WebSocketFrame) msg;
        if (frame instanceof TextWebSocketFrame) {
            /**
             * 我们主要是用TextWebSocketFrame
             */
            TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;
            receiveMessage.onMessage(ch, textFrame.text());
        } else if (frame instanceof PongWebSocketFrame) {
            logger.info("WebSocket Client received pong");
            receiveMessage.onMessage(ch, frame);
        } else if (frame instanceof CloseWebSocketFrame) {
            logger.info("WebSocket Client received close Frame");
            //执行后将关闭
            receiveMessage.onMessage(ch, frame);
            //receive a closing frame to shutdown the event loop
            ch.eventLoop().shutdownGracefully();
            ch.close().sync();
        }else if(frame instanceof BinaryWebSocketFrame){
            BinaryWebSocketFrame binaryFrame = (BinaryWebSocketFrame)msg;
            ByteBuf buf = binaryFrame.content();
            if (buf.isReadable()){
                int availableBytesNumber = buf.readableBytes();
                byte[] receivedBytes = new byte[availableBytesNumber];
                buf.readBytes(receivedBytes);
                receiveMessage.onMessage(ch, receivedBytes);
            }
            //buf.release();
            // byte [] bytes = receivedBytes;
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
//        if (evt instanceof IdleStateEvent) {
//            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
//            switch (idleStateEvent.state()) {
//                case WRITER_IDLE:
//                    handlerWriterIdleEvent(ctx);
//                    break;
//                case READER_IDLE:
//                    handlerReaderIdleEvent(ctx);
//                    break;
//                case ALL_IDLE:
//                    handlerAllIdleEvent(ctx);
//                    break;
//                default:
//                    break;
//            }
//        } else {
//            super.userEventTriggered(ctx, evt);
//        }
        if(evt instanceof HandshakeComplete) {
            HandshakeComplete handshake = (WebSocketServerProtocolHandler.HandshakeComplete)evt;
            //http request header
            HttpHeaders headers = handshake.requestHeaders();

            //http request uri: /chat?accesskey=hello
            String              uri    = handshake.requestUri();

            //TODO: parse uri parameters to map ...
            Map<String, String> params ;

            //put to channel context
//            ctx.channel().attr(RequestParams).set(params);
        }

    }


//    protected void handlerWriterIdleEvent(ChannelHandlerContext ctx){
//        heartBeat.ping(ctx);
//    }
//
//    protected void handlerReaderIdleEvent(ChannelHandlerContext ctx){
//        heartBeat.ping(ctx);
//    }
//
//    protected void handlerAllIdleEvent(ChannelHandlerContext ctx){
//        heartBeat.ping(ctx);
//    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        if (!handshakeFuture.isDone()) {
            handshakeFuture.setFailure(cause);
        }
        logger.info("exceptionCaught "+ cause.getMessage());
    }


    public WebsocketConfig getWebsocketConfig() {
        return websocketConfig;
    }

    public void setWebsocketConfig(WebsocketConfig websocketConfig) {
        this.websocketConfig = websocketConfig;
    }

//    public HeartBeat getHeartBeat() {
//        return heartBeat;
//    }

//    public void setHeartBeat(HeartBeat heartBeat) {
//        this.heartBeat = heartBeat;
//    }
}
