package fr.zng.xxzx.netty.pcws;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelMatcher;
import io.netty.channel.group.ChannelMatchers;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.CharsetUtil;

import org.apache.log4j.Logger;


/**
 * ClassName:MyWebSocketServerHandler Function: 
 * 
 * @author hxy
 */
public class PcWebSocketServerHandler extends SimpleChannelInboundHandler<Object> {

    private static final Logger logger = Logger
            .getLogger(PcWebSocketServerHandler.class);

    private WebSocketServerHandshaker handshaker;

    /**
     * channel 通道 action 活跃的
     * 当客户端主动链接服务端的链接后，这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 添加
        Global.group.add(ctx.channel());
        logger.info("客户端与服务端连接开启："
                + ctx.channel().remoteAddress().toString());
    }

    /**
     * channel 通道 Inactive 不活跃的
     * 当客户端主动断开服务端的链接后，这个通道就是不活跃的。也就是说客户端与服务端关闭了通信通道并且不可以传输数据
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 移除
        Global.group.remove(ctx.channel());
        logger.info("客户端与服务端连接关闭："
                + ctx.channel().remoteAddress().toString());
    }

    /**
     * channel 通道 Read 读取 Complete 完成 在通道读取完成后会在这个方法里通知，对应可以做刷新操作 ctx.flush()
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    @SuppressWarnings("deprecation")
    private void handleHttpRequest(ChannelHandlerContext ctx,
            FullHttpRequest req) {
        // 如果HTTP解码失败，返回HHTP异常
        if (!req.getDecoderResult().isSuccess()
                || (!"websocket".equals(req.headers().get("Upgrade")))) {
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        String uri = req.getUri();
        // 构造握手响应返回，本机测试
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                "ws://" + req.headers().get(HttpHeaders.Names.HOST) + uri,
                null, false);
        handshaker = wsFactory.newHandshaker(req);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory
                    .sendUnsupportedWebSocketVersionResponse(ctx.channel());
        } else {
            handshaker.handshake(ctx.channel(), req);
        }
        
    }

    /**
     * 回复数据
     * @param ctx
     * @param req
     * @param res
     */
    @SuppressWarnings("deprecation")
    private static void sendHttpResponse(ChannelHandlerContext ctx,
            FullHttpRequest req, DefaultFullHttpResponse res) {
        logger.info("sendHttpResponse");
        // 返回应答给客户端
        if (res.getStatus().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(),
                    CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
        }
        // 如果是非Keep-Alive，关闭连接
        ChannelFuture f = ctx.channel().writeAndFlush(res);
        if (!HttpHeaders.isKeepAlive(req) || res.getStatus().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    /**
     * exception 异常 Caught 抓住 抓住异常，当发生异常的时候，可以做一些相应的处理，比如打印日志、关闭链接
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    /**
     * 接收客户端发送的消息 channel 通道 Read 读
     * 简而言之就是从通道中读取数据，也就是服务端接收客户端发来的数据。但是这个数据在不进行解码时它是ByteBuf类型的
     */
	@Override
	protected void messageReceived(ChannelHandlerContext ctx, Object msg)
			throws Exception {
        
     

        // 传统的HTTP接入
        if (msg instanceof FullHttpRequest) {
            // 上下文匹配 ws://ip/{wsUri}
            if (!PcWebSocketServer.KEY.equalsIgnoreCase(((FullHttpRequest) msg).getUri())) {
                logger.info("[ERROR] webSocket数据格式匹配错误 ws://ip/{wsUri}");
                return;
            }
            handleHttpRequest(ctx, ((FullHttpRequest) msg));
            Global.group.writeAndFlush(new TextWebSocketFrame(ctx.channel().remoteAddress() + "你连接上了"));
            // WebSocket接入
        } else if (msg instanceof TextWebSocketFrame) {
            
            TextWebSocketFrame text = (TextWebSocketFrame) msg;
            
            logger.info(text.text());
            
            Global.group.writeAndFlush(new TextWebSocketFrame("ccccc"), new FrChannelMatcher(ctx.channel()));
        }
        
    }

}