package com.wecloud.im.netty.core;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import com.wecloud.dispatch.general.GeneralMessageHandler;
import com.wecloud.im.event.ClientOnlineStatusChangeEvent;
import com.wecloud.im.executor.BusinessThreadPool;
import com.wecloud.im.ws.manager.ChannelManager;

import static com.wecloud.im.ws.ImConstant.PING;
import static com.wecloud.im.ws.ImConstant.PONG;
import static com.wecloud.im.ws.ImConstant.READ_IDLE_CLOSE_COUNT;

/**
 * @Description app端 长连接事件处理
 * @Author hewei hwei1233@163.com
 * @Date 2019-07-26
 */
@Component
@ChannelHandler.Sharable
@Slf4j
public class WsReadHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    @Resource
    private ChannelManager channelManager;

    @Resource
    private GeneralMessageHandler generalMessageHandler;

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
        // 读空闲的计数清零
        ctx.channel().attr(ChannelManager.READ_IDLE_TIMES).set(0);

        String data = msg.text();
        try {
            if (data.isEmpty()) {
                return;
            }
            /*
             * 在此进入耗时业务线程池，  将不再阻塞netty的I/O线程，提高网络吞吐
             */
            BusinessThreadPool.BUSINESS_TASK_THREAD_POOL_EXECUTOR.execute(() ->
                    execute(ctx, data)
            );

        } catch (Exception e) {
            //返回错误
            ctx.channel().writeAndFlush(new TextWebSocketFrame("error=" + e + ",data=" + data));
            log.error(e.getMessage() + data, e);
        }
    }

    private void execute(ChannelHandlerContext ctx, String data) {
        Long clientId = ctx.channel().attr(ChannelManager.CLIENT_ID).get();
        try {

            if (PING.equals(data)) {
                log.info("收到心跳clientId:" + clientId);
                ctx.channel().writeAndFlush(new TextWebSocketFrame(PONG));
                return;
            }

            if (PONG.equals(data)) {
                log.info("收到心跳应用Pong,clientId:" + clientId);
                return;
            }
//            AbstractImCmdStrategy.process(clientId, ctx, data);
            generalMessageHandler.doMessage(clientId, ctx, data);


        } catch (Exception e) {
            log.error("系统繁忙data:" + data + ",clientId:" + clientId +
                    ",channelId:" + ctx.channel().id().asLongText(), e);
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        Long clientId = ctx.channel().attr(ChannelManager.CLIENT_ID).get();

        //读超时计时器
        Integer readIdleTimes = ctx.channel().attr(ChannelManager.READ_IDLE_TIMES).get();

        IdleStateEvent event = (IdleStateEvent) evt;

        String eventType = null;

        switch (event.state()) {
            case READER_IDLE:
                eventType = "读空闲:readIdleTimes=" + readIdleTimes;
                // 读空闲的计数加1
                ctx.channel().attr(ChannelManager.READ_IDLE_TIMES).set(readIdleTimes + 1);

                // 发ping
                ctx.channel().writeAndFlush(new TextWebSocketFrame(PING));
                break;
            case WRITER_IDLE:
                eventType = "写空闲";
                // 不处理
                break;
            case ALL_IDLE:
                eventType = "读写空闲";
                // 不处理
                break;
            default:
        }
        log.info(clientId + "超时事件：" + eventType);
        if (readIdleTimes >= READ_IDLE_CLOSE_COUNT) {
            log.info(clientId + ".读空闲超过5次关闭连接");
            ctx.channel().close();
        }
    }


    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        if(log.isInfoEnabled()) {
            Long clientId = ctx.channel().attr(ChannelManager.CLIENT_ID).get();
            log.info("HandlerAdded. CLIENT_ID:{}, channelId is {}", clientId, ctx.channel().id().asLongText());
        }
    }

    /**
     * 移除时触发， 不活跃的情况下会移除，会再次触发该事件
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {

        Long appId = ctx.channel().attr(ChannelManager.APPLICATION_ID).get();
        Long clientId = ctx.channel().attr(ChannelManager.CLIENT_ID).get();
        Integer platform = ctx.channel().attr(ChannelManager.PLATFORM).get();

        log.info("appId: {} platform: {} CLIENT_ID:{}, handlerRemoved. channelId is {}", appId, platform, clientId, ctx.channel().id().asLongText());
        // 关掉连接
        channelManager.offline(clientId, platform, ctx);

        // 下线通知
        long time = System.currentTimeMillis();
        String clientIp = ctx.channel().remoteAddress().toString();
        ClientOnlineStatusChangeEvent clientOnlineStatusChangeEvent = new ClientOnlineStatusChangeEvent(appId,
                clientId, 0, platform, time, clientIp);
        eventPublisher.publishEvent(clientOnlineStatusChangeEvent);

    }

}
