package com.wdkl.ncs.android.middleware.tcp;

import com.wdkl.ncs.android.middleware.common.Constant;
import com.wdkl.ncs.android.middleware.common.MessageEvent;

import org.greenrobot.eventbus.EventBus;

import java.util.Scanner;
import java.util.concurrent.TimeUnit;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.CharsetUtil;

//单例
public class TcpClient {
   private String TAG = TcpClient.class.getSimpleName();

    private NioEventLoopGroup workGroup;
    public Channel channel;
    private Bootstrap bootstrap;

    //数据处理
    private TcpClientHandler tcpClientHandler = new TcpClientHandler();
    //是否运行中
    public boolean isRunning = false;
    //重试间隔
    private Integer retrySeconds = 5;
    //重试计数
    private Integer retryTimes = 1;
    private Integer reconnetTimes = 0;
    //tcp是否完成初始化
    private boolean inited = false;

    private TcpCallBack tcpCallBack;
    private boolean connecting = false;


    //单例
    private static class TcpClientHolder{
        private static TcpClient instance = new TcpClient();
    }

    public static TcpClient getInstance(){
        return TcpClientHolder.instance;
    }

    //初始化Netty Tcp Client 并连接
    public void init(String serverIP, Integer serverPort, Integer heartBeatSeconds) {

        final Integer hbSeconds = heartBeatSeconds;
        workGroup = new NioEventLoopGroup(2);
        bootstrap = new Bootstrap();
        bootstrap.group(workGroup)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 15*1000)
                .option(ChannelOption.SO_KEEPALIVE,true)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        // 这里将LengthFieldBasedFrameDecoder添加到pipeline的首位，因为其需要对接收到的数据
                        // 进行长度字段解码，这里也会对数据进行粘包和拆包处理
                        socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(2048, 0, 2, 0, 2));
                        // LengthFieldPrepender是一个编码器，主要是在响应字节数据前面添加字节长度字段
                        socketChannel.pipeline().addLast(new LengthFieldPrepender(2));
                        //心跳包应当小于服务器间隔
//                        socketChannel.pipeline().addLast(new IdleStateHandler(0, hbSeconds, 0, TimeUnit.SECONDS));
                        socketChannel.pipeline().addLast(new IdleStateHandler(hbSeconds*2, hbSeconds, 0, TimeUnit.SECONDS));
                        socketChannel.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
                        socketChannel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
                        socketChannel.pipeline().addLast(tcpClientHandler);
                    }
                }).remoteAddress(serverIP,serverPort);
        inited = true;
        doConnect();
        System.out.println("connect server host: " + serverIP + ", port: " + serverPort);
    }

    //独立连接方法，用于重新连接
    public synchronized void doConnect(){
        if (!inited) {
            System.out.println("tcp is not initialized");
            return;
        }

        if (channel != null && (channel.isActive() || channel.isOpen())){
            System.out.println("TcpClient connecting");
            return;
        }

        //正在连接
        if (connecting || Constant.TCP_CONNECTED) {
            System.out.println("Tcp is connecting or connected");
            return;
        }
        connecting = true;

        System.out.println("tcp connect start");
        ChannelFuture future = bootstrap.connect().addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                connecting = false;
                if (channelFuture.isSuccess()){
                    isRunning = true;
                    channel = channelFuture.channel();
                    retryTimes = 1;
                    System.out.println("tcp connect success");
                } else {
                    //连接失败时的处理
                    isRunning = false;
                    System.out.println("tcp connect retry : " + retryTimes);
                    channelFuture.channel().eventLoop().schedule(new Runnable() {
                        @Override
                        public void run() {
                            retryTimes++;
                            if (retryTimes > 30) { //重试30次还没连成功，等10分钟后再试
                                retryTimes=1;
                                reconnetTimes++;
                                channelFuture.channel().eventLoop().schedule(new Runnable() {
                                    @Override
                                    public void run() {
                                        System.out.println("TcpClientHandler 重新连接，第" + retryTimes + "次" + ", 重试" + reconnetTimes + "次");
                                        if (reconnetTimes > 2) {
                                            EventBus.getDefault().post(new MessageEvent(0, Constant.EVENT_RESTART_APP));
                                        } else {
                                            doConnect();
                                        }
                                    }
                                }, 60*5, TimeUnit.SECONDS);

                            }else{
                                doConnect();
                            }

                        }
                    }, 5, TimeUnit.SECONDS);
                }
            }
        });
    }

    //发送消息，线程安全
    public synchronized void sendMsg(String content){
        if (tcpClientHandler != null) {
            tcpClientHandler.sendMsg(content);
            if (tcpCallBack != null) {
                tcpCallBack.tcpMsg(content);
            }
        }
    }


    public Channel getChannel() {
        return channel;
    }

    public void setTcpCallback(TcpCallBack callback) {
        tcpCallBack = callback;
    }

    public interface TcpCallBack {
        void tcpMsg(String msg);
    }
}
