package com.imdroid.beidou.test_device.service;

import com.imdroid.common.util.ThreadManager;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

public class TCPClient {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private String host;

    private int port;

    private Bootstrap bootstrap;
    private EventLoopGroup group;
    private Channel channel;
    LocalDateTime connectTime = LocalDateTime.now();
    TCPListener listener;

    public void start() {
        new Thread(this::connect, "forwarder tcp-client").start();
    }

    public void init(String dest_addr, int dest_port, TCPListener listener) {
        this.host = dest_addr;
        this.port = dest_port;
        this.listener = listener;

        //客户端需要一个事件循环组
        group = new NioEventLoopGroup();
        //创建客户端启动对象
        // bootstrap 可重用, 只需在NettyClient实例化的时候初始化即可.
        bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        //加入处理器
                        ch.pipeline().addLast(new TcpMessageHandler(TCPClient.this));
                    }
                });
    }

    public void connect() {
        logger.info("netty client starting");
        //启动客户端去连接服务器端
        try {
            ChannelFuture cf = bootstrap.connect(host, port);
            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (!future.isSuccess()) {
                        //重连交给后端线程执行
                        future.channel().eventLoop().schedule(() -> {
                            logger.info("tcp client reconnect");
                            try {
                                connect();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }, 5000, TimeUnit.MILLISECONDS);
                    } else {
                        logger.info("tcp client start success!");
                    }
                }
            });
            //对通道关闭进行监听
            this.channel = cf.channel();
            this.channel.closeFuture().sync();
        } catch (Exception e) {
            logger.error("netty client error:", e);
        }
    }

    public void writeAndFlush(String json) {
        ByteBuf sendBuffer = Unpooled.buffer();
        sendBuffer.writeBytes(json.getBytes(StandardCharsets.UTF_8));
        channel.writeAndFlush(sendBuffer).addListener(future -> {
            if (future.isSuccess()) {
                logger.info("send to tcp:"+host+" succeed.");
            } else {
                logger.info("send to tcp:"+host+" failed.");
                if(listener!=null){
                    listener.onMessage("failed");
                }
            }
        });
    }

    public void onConnected(){
        connectTime = LocalDateTime.now();
    }

    public void onDisconnect(){
        if(connectTime.isBefore(LocalDateTime.now().minusMinutes(1))) {
            connect();
        }
        else{
            ThreadManager.getScheduledThreadPool().schedule(() -> {
                try {
                    connect();
                } catch (Exception e) {
                    logger.error(e.toString());
                }
            },60, TimeUnit.SECONDS);
        }
    }

    public void onMessage(String msg){
        if(listener!=null){
            listener.onMessage(msg);
        }
    }

}
