package cn.com.ty.lift.ud.neety.server.handler;

import cn.com.ty.lift.ud.neety.process.*;
import cn.com.ty.lift.ud.neety.protocol.IMDecoder;
import cn.com.ty.lift.ud.neety.protocol.IMMessage;
import cn.com.ty.lift.ud.neety.protocol.IMP;
import cn.com.ty.lift.ud.neety.support.WebSoketSupport;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@ChannelHandler.Sharable
public class WebSocketHandler extends SimpleChannelInboundHandler<Object> {

    private WebSocketServerHandshaker handShaker;
    private ChannelHandlerContext ctx;
    private WebSoketSupport process = new WebSoketSupport();

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 传统的HTTP接入
        if (msg instanceof FullHttpRequest) {
            handleHttpRequest(ctx, (FullHttpRequest)msg);
        }
        // WebSocket接入
        else if (msg instanceof WebSocketFrame) {
            handleWebSocket(ctx, (WebSocketFrame)msg);
        }
    }

    /**
     * 处理Http请求，完成WebSocket握手 注意：WebSocket连接第一次请求使用的是Http
     * 
     * @param ctx
     * @param request
     * @throws Exception
     */
    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        // 如果HTTP解码失败，返回HHTP异常
        if (!request.getDecoderResult().isSuccess() || (!"websocket".equals(request.headers().get("Upgrade")))) {
            sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }

        // 正常WebSocket的Http连接请求，构造握手响应返回
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://" + request.headers().get(HttpHeaders.Names.HOST), null, false);
        handShaker = wsFactory.newHandshaker(request);
        if (handShaker == null) { // 无法处理的websocket版本
            WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
        } else { // 向客户端发送websocket握手,完成握手
            handShaker.handshake(ctx.channel(), request);
            // 记录管道处理上下文，便于服务器推送数据到客户端
            this.ctx = ctx;
        }
    }

    /**
     * Http返回
     * 
     * @param ctx
     * @param request
     * @param response
     */
    private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request,
        FullHttpResponse response) {
        // 返回应答给客户端
        if (response.getStatus().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(response.getStatus().toString(), CharsetUtil.UTF_8);
            response.content().writeBytes(buf);
            buf.release();
            HttpHeaders.setContentLength(response, response.content().readableBytes());
        }

        // 如果是非Keep-Alive，关闭连接
        ChannelFuture f = ctx.channel().writeAndFlush(response);
        if (!HttpHeaders.isKeepAlive(request) || response.getStatus().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    /**
     * 处理Socket请求
     * 
     * @param ctx
     * @param frame
     * @throws Exception
     */
    private void handleWebSocket(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
        // 判断是否是关闭链路的指令
        if (frame instanceof CloseWebSocketFrame) {
            log.info("我在-关闭链路的指令");
            process.logout(ctx.channel());

            handShaker.close(ctx.channel(), (CloseWebSocketFrame)frame.retain());
            return;
        }
        // 判断是否是Ping消息
        if (frame instanceof PingWebSocketFrame) {
            log.info("我在-Ping消息");
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        // 当前只支持文本消息，不支持二进制消息
        if (!(frame instanceof TextWebSocketFrame)) {
            log.info("当前只支持文本消息，不支持二进制消息");
            throw new UnsupportedOperationException("当前只支持文本消息，不支持二进制消息");
        }
        String msg = ((TextWebSocketFrame)frame).text();

        // 处理来自客户端的WebSocket请求
        // 将字符串解析为自定义格式
        IMMessage messge = IMDecoder.decode(msg);
        if (null == messge) {
            return;
        }


        if (messge.getUserId().equals(-1)){
            log.info("游客关闭连接，结束下面的逻辑");
            process.logout(ctx.channel());
            handShaker.close(ctx.channel(), (CloseWebSocketFrame)frame.retain());
            return;
        }
        String cmd = messge.getCmd();
        String username = messge.getName();
        ProcessorInterface processor = null;

        // 发送消息业务处理类
        if (IMP.LOGINALL.getName().equals(cmd)) {
            log.info("[[{}]]登录-------------------->：{}", username, msg);
            processor = new LoginProcessor();
        }else if (IMP.INITROOM.getName().equals(cmd)) {
            //加载聊天室，如果聊天室不存在，就创建。如果客户端不在聊天室假如聊天室
            log.info("[[{}]]加载房间==========>：{}", username, msg);
            processor = new InitRoomProcessor();
        }  else if (IMP.CHAT.getName().equals(cmd)) {
            log.info("[[{}]]发送消息----------------》：{}", username, msg);
            processor = new ChatProcessor();
        } else if (IMP.WEBLOGINALL.getName().equals(cmd)) {
            log.info("[[{}]]我在-web端发送消息：{}", username, msg);
            processor = new WebLoginAllProcessor();
        } else if (IMP.LOOP.getName().equals(cmd)) {
            log.info("[[{}]] heartbeat ：{}", username, msg);
            return;
        } else {
            log.info("[[{}]]发送WebSoket协议异常：{}", username, msg);
            return;
        }
        processor.process(ctx.channel(), messge);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info(ctx.channel().remoteAddress().toString()+"====已经建立连接");
        super.channelActive(ctx);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
//        super.userEventTriggered(ctx, evt);
        if (evt instanceof IdleStateEvent){
            IdleStateEvent event = (IdleStateEvent)evt;
            if (event.state()== IdleState.READER_IDLE){
                log.info("{}-----》客户端操作空闲",process.getNickName(ctx.channel()));
                process.logout(ctx.channel());
                CloseWebSocketFrame fram = new CloseWebSocketFrame();
                handShaker.close(ctx.channel(), fram.retain());
            }
        }
    }

        @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("{}-----》客户端手动退出",process.getNickName(ctx.channel()));
        process.logout(ctx.channel());
        CloseWebSocketFrame fram = new CloseWebSocketFrame();
        handShaker.close(ctx.channel(), fram.retain());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("{}-----》客户端不活跃",process.getNickName(ctx.channel()));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//        super.exceptionCaught(ctx, cause);
        log.info("客户端异常---->"+cause.getMessage());
        cause.printStackTrace();
//        process.logout(ctx.channel());
    }

}
