package com.imdroid.beidou_fwd.service;

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;

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, host+":"+port+" 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("{}:{} tcp connecting...",host,port);
        //启动客户端去连接服务器端
        try {
            ChannelFuture cf = bootstrap.connect(host, port);
            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (!future.isSuccess()) {
                        logger.info("{}:{} tcp connect failed. {}",host,port,future.cause().toString());
                        //重连交给后端线程执行
                        /*future.channel().eventLoop().schedule(() -> {
                            logger.info("{}:{} tcp client reconnect",host,port);
                            try {
                                connect();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }, 5000, TimeUnit.MILLISECONDS);*/
                    } else {
                        /*future.channel().config().setWriteBufferWaterMark(new WriteBufferWaterMark(
                                1024 * 1024,   // low
                                4 *1024*1024    // high
                        ));*/
                        logger.info("{}:{} tcp client start success!",host,port);
                    }
                }
            });
            //对通道关闭进行监听
            this.channel = cf.channel();
            this.channel.closeFuture().sync();
        } catch (Exception e) {
            logger.error(host+":"+port+" tcp connect error:", e);
        }
    }

    boolean tryReconnect() throws Exception{
        new Thread(this::connect, host+":"+port+" forwarder tcp-client").start();
        for(int i=0; i<20; i++){
            Thread.sleep(50);
            if(channel!=null && channel.isActive()) return true;
        }
        return false;
    }

    public void writeAndFlush(String json) {
        ByteBuf sendBuffer = Unpooled.buffer();
        sendBuffer.writeBytes(json.getBytes(StandardCharsets.UTF_8));
        //logger.info("send to {}: {}",host,json);
        if(channel==null || !channel.isActive()){
            try {
                if(!tryReconnect()) return;
            } catch (Exception e) {
                logger.error(e.toString());
            }
        }
        channel.writeAndFlush(sendBuffer).addListener(future -> {
            if (future.isSuccess()) {
                logger.info("send to tcp:"+host+" succeed.");
            } else {
                logger.info("send to tcp: {} failed. {}",host,future.cause().toString());
                if(listener!=null){
                    listener.onMessage("failed");
                }
            }
        });
    }

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

    public void onDisconnect(boolean isIdle){
        /*if(connectTime.isBefore(LocalDateTime.now().minusMinutes(1))) {
            connect();
        }
        else{
            ThreadManager.getScheduledThreadPool().schedule(() -> {
                try {
                    connect();
                } catch (Exception e) {
                    logger.error(e.toString());
                }
            },isIdle?30:10, TimeUnit.SECONDS);
        }*/
    }

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

}
