package com.ccwangluo.ccim.socket;


import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

import com.ccwangluo.ccim.CCIMClient;
import com.ccwangluo.ccim.listener.MessageListener;
import com.ccwangluo.ccim.util.ErrorCode;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import ccim.protocol.Chat;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
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.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;

public class NettyChatClient implements SocketClient {

    private final Logger logger = Logger.getLogger(CCIMClient.class.getName());
    private MessageListener msglitener;
    private ChannelFuture channelFuture;
    private final String ip;
    private final int port;
    private Bootstrap bootstrap;
    private Context context;
    EventLoopGroup group = new NioEventLoopGroup();

    private ExecutorService executor = Executors.newSingleThreadExecutor();
    ScheduledExecutorService timerExcutor;


    public NettyChatClient(Context context, String ip, int port) {
        this.ip = ip;
        this.port = port;
        this.context = context;
        //注意：客户端使用的不是 ServerBootStrap 而是 Bootstrap
        bootstrap = new Bootstrap();
        //设置相关参数
        bootstrap.group(group) //设置线程组
                .channel(NioSocketChannel.class) //设置客户端通道的实现类（使用反射）
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        // Decoders
                        pipeline.addLast("frameDecoder",
                                new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));
                        pipeline.addLast("protobufDecoder",
                                new ProtobufDecoder(Chat.ChatResponseMessage.getDefaultInstance()));

                        // Encoder
                        pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
                        pipeline.addLast("protobufEncoder", new ProtobufEncoder());
                        pipeline.addLast(new NettyClientHandler()); //加入自己的处理器
                    }
                });
    }

    @Override
    public void connect(MessageListener msglistner) {
        this.msglitener = msglistner;
        connect();
    }


    private void connect() {
        executor.execute(() -> {
            try {
                channelFuture = bootstrap.connect(ip, port).sync();
            } catch (Exception e) {
                e.printStackTrace();
                if (msglitener != null){
                    msglitener.onServerDisConnect(ErrorCode.NET_ERROR);
                }
            }
        });
    }


    public boolean getConnectivityStatus(Context context) {
        ConnectivityManager cm = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        if (null != activeNetwork) {
            if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI)
                return true;

            if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE)
                return true;
        }
        return false;
    }

    @Override
    public void sendMsg(Object msg) {
        if (channelFuture == null) {
            return;
        }
        executor.execute(() -> {
            if (channelFuture.channel().isActive()) {
                channelFuture.channel().writeAndFlush(msg);
            } else {
                connect();
            }
        });
    }


    @Override
    public void destroy() {
        if (timerExcutor != null) {
            timerExcutor.shutdown();
        }
        if (channelFuture != null) {
            channelFuture.channel().close();
        }
    }

    @Override
    public boolean isActive() {
        if (channelFuture != null && channelFuture.channel().isActive()) {
            return true;
        }
        connect();
        return false;
    }

    @Override
    public void heartBeat(Long frequency, Chat.ChatRequestMessage build) {
        if (timerExcutor != null) {
            timerExcutor.shutdown();
        }
        timerExcutor = Executors.newScheduledThreadPool(1);
        timerExcutor.schedule(new Runnable() {
            @Override
            public void run() {
                if (getConnectivityStatus(context)) {
                    if (channelFuture != null && channelFuture.channel().isActive()) {
                        logger.info("check connect is active");
                        channelFuture.channel().writeAndFlush(build);
                    } else {
                        try {
                            logger.info("reconnect");
                            connect();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }, frequency, TimeUnit.MILLISECONDS);

    }

    public class NettyClientHandler extends ChannelInboundHandlerAdapter {
        /**
         * 当通道就绪就会触发
         */
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            logger.info("channelActive");
            msglitener.onServerConnect();
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            super.channelInactive(ctx);
            logger.info("channelInactive");
            msglitener.onServerDisConnect(ErrorCode.NET_TIME_OUT);
        }

        /**
         * 当通道有读取事件时，会触发
         */
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            Chat.ChatResponseMessage data = (Chat.ChatResponseMessage) msg;
            msglitener.onMessageListener(data);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    }

}


