package com.smilcool.server.base.config.netty.handler;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 自定义心跳机制 Handler
 *
 * @author Angus
 * @date 2018/12/24
 */
@Slf4j
@Sharable
@Component
public class HeartbeatHandler extends ChannelInboundHandlerAdapter {

    /**
     * 发送到远程节点的 PING 消息
     */
    private static final PingWebSocketFrame PING = new PingWebSocketFrame();


    /**
     * 实现 userEventTriggered() 方法以发送 PING 消息
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        // 判断 evt 是否是 IdleStateEvent（用于触发用户事件，包含读空闲、写空闲、读写空闲）
        if (evt instanceof IdleStateEvent) {
            Channel channel = ctx.channel();
            IdleState state = ((IdleStateEvent) evt).state();
            switch (state) {
                case READER_IDLE:
                    log.debug("channel-{} 进入读空闲...", channel.id().asShortText());
                    break;
                case WRITER_IDLE:
                    log.debug("channel-{} 进入写空闲...", channel.id().asShortText());
                    break;
                case ALL_IDLE:
                    log.debug("channel-{} 进入读写空闲...", channel.id().asShortText());
                    // 发送 PING 消息（副本，同时增加引用次数防止被释放），并在发送失败时关闭该连接，以防资源浪费
                    // ctx.writeAndFlush(PING.retainedDuplicate())
                    //        .addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                    // 关闭 channel，节省资源
                    channel.close();
                    break;
                default:
                    throw new IllegalArgumentException("Unhandled: state = " + state);
            }
        } else {
            // 不是 IdleStateEvent 事件，所以将它传递给下一个 ChannelInboundHandler
            super.userEventTriggered(ctx, evt);
        }
    }
}
