package com.mogo.telematic;

import static com.mogo.telematic.client.status.ConnectState.STATUS_CONNECT_CLOSED;
import static com.mogo.telematic.client.status.ConnectState.STATUS_CONNECT_SUCCESS;

import android.content.Context;
import android.net.nsd.NsdServiceInfo;
import android.os.AsyncTask;
import android.os.Looper;
import android.util.Log;

import com.elegant.log.simplelog.Logger;
import com.mogo.telematic.client.NettyTcpClient;
import com.mogo.telematic.client.NsdClient;
import com.mogo.telematic.client.listener.MessageStateListener;
import com.mogo.telematic.client.listener.NettyClientListener;
import com.mogo.telematic.discovery.AbstractDiscoveryTask;
import com.mogo.telematic.discovery.DiscoveryDriverListener;
import com.mogo.telematic.discovery.DiscoveryDriverTask;
import com.mogo.telematic.discovery.bean.HostBean;
import com.mogo.telematic.discovery.bean.NetInfo;
import com.mogo.telematic.server.NSDServer;
import com.mogo.telematic.server.netty.NettyServerListener;
import com.mogo.telematic.server.netty.NettyTcpServer;

import java.util.ArrayList;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;


public class NSDNettyManager implements TelematicHandler.ITelematicListener {

    private static final String TAG = "NSDNettyManager";

    private static final long COUNTDOWN_TIME = 30 * 1000;
    private static final long INTERVAL_TIME = 10 * 1000;

    private volatile static NSDNettyManager sInstance;
    // client端用来过滤的
    public static final String SERVER_NAME = "NSD_SERVER";
    private NSDServer mNsdServer;
    private NettyTcpClient mNettyTcpClient;
    private NsdClient mNsdClient;
    private Context mContext;
    private NettyServerListener mListener;

    private long network_ip = 0;
    private long network_start = 0;
    private long network_end = 0;
    private String currentIp1 = "192.168.1.0";
    private String currentIp8 = "192.168.8.0";
    private String currentIp = currentIp1;

    // 重新扫描时使用
    private String mUuid;
    private boolean mIsTaxi;
    private NettyClientListener mClientListener;
    private volatile boolean isCanceled = true;

    private volatile AbstractDiscoveryTask discoveryTask;

    private NSDNettyManager() {
        TelematicHandler.init();
        TelematicHandler.setListener(this);
    }

    public static NSDNettyManager getInstance() {
        if (sInstance == null) {
            synchronized (NSDNettyManager.class) {
                if (sInstance == null) {
                    sInstance = new NSDNettyManager();
                }
            }
        }
        return sInstance;
    }

    private final NettyServerListener mDefaultServerListener = new NettyServerListener() {

        @Override
        public void onMessageResponseServer(Object msg, Channel channel) {
            if (msg instanceof MogoProtocolMsg && mListener != null) {
                mListener.onMessageResponseServer(msg, channel);
            }
        }

        @Override
        public void onStartServer() {
            registerNsdServer();
            if (mListener != null) {
                mListener.onStartServer();
            }
        }

        @Override
        public void onStopServer() {
            if (mNsdServer != null) {
                mNsdServer.stopNSDServer();
            }
            if (mListener != null) {
                mListener.onStopServer();
            }
        }

        @Override
        public void onChannelConnect(Channel channel) {
            if (mListener != null) {
                mListener.onChannelConnect(channel);
            }
        }

        @Override
        public void onChannelDisConnect(Channel channel) {
            if (mListener != null) {
                mListener.onChannelDisConnect(channel);
            }
        }

        @Override
        public void onReceiveClientSign(Channel channel, String sn) {
            if (mListener != null) {
                mListener.onReceiveClientSign(channel, sn);
            }
        }
    };

    /**
     * 启动Netty和NSD服务的Server端
     *
     * @param context
     * @param listener
     */
    public void startNSDNettyServer(Context context, NettyServerListener listener) {
        if (context == null) {
            throw new RuntimeException("Context must not be null!");
        } else {
            mContext = context;
        }
        mListener = listener;
        NettyTcpServer nettyTcpServer = NettyTcpServer.getInstance();
        if (!nettyTcpServer.isServerStart()) {
            nettyTcpServer.setListener(mDefaultServerListener);
            nettyTcpServer.start();
        } else {
            Logger.i(TAG, "服务端已开启，不要重复启动！");
        }
    }

    public void startNSDNettyServerWithSN(Context context, NettyServerListener listener, String sn) {
        NettyTcpServer.sSERVER_SN = sn;
        startNSDNettyServer(context, listener);
    }

    public boolean isServerStart() {
        return NettyTcpServer.getInstance().isServerStart();
    }

    public void sendMogoProtocolMsgToClient(MogoProtocolMsg mogoProtocolMsg, ChannelFutureListener listener) {
        NettyTcpServer.getInstance().sendMsgToClient(mogoProtocolMsg, listener);
    }

    public void sendMsgToAllClients(MogoProtocolMsg mogoProtocolMsg) {
        NettyTcpServer.getInstance().sendMsgToAllClients(mogoProtocolMsg);
    }

    public void sendMsgToSpecifiedClient(MogoProtocolMsg mogoProtocolMsg, Channel channel, ChannelFutureListener listener) {
        NettyTcpServer.getInstance().sendMsgToSpecifiedClient(mogoProtocolMsg, channel, listener);
    }

    /**
     * 服务器端注册一个可供NSD探测到的网络 Ip 地址，便于给展示叫号机连接此socket
     */
    private final Runnable mNsdServerRunnable = new Runnable() {
        @Override
        public void run() {
            if (mNsdServer == null) {
                mNsdServer = new NSDServer();
            }
            mNsdServer.startNSDServer(mContext, NettyTcpServer.SERVER_NAME, NettyTcpServer.SERVER_PORT);

            mNsdServer.setRegisterState(new NSDServer.IRegisterState() {
                @Override
                public void onServiceRegistered(NsdServiceInfo serviceInfo) {
                    Logger.i(TAG, "已注册服务onServiceRegistered: " + serviceInfo.toString());
                    //已经注册可停止该服务
//                    nsdServer.stopNSDServer();
                }

                @Override
                public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {

                }

                @Override
                public void onServiceUnregistered(NsdServiceInfo serviceInfo) {

                }

                @Override
                public void onUnRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {

                }
            });
        }
    };

    private void registerNsdServer() {
        new Thread(mNsdServerRunnable).start();
    }

    /**
     * 选择连接的Client端的channel
     *
     * @param channel
     */
    public void selectChannel(Channel channel) {
        NettyTcpServer.getInstance().selectorChannel(channel);
    }

    /**
     * 搜索并连接Netty服务端
     *
     * @param context
     */
    public void searchAndConnectServer(Context context, String uuid, boolean isTaxi, NettyClientListener listener) {
        // 保存变量，给后续重新扫描使用
        mContext = context;
        mUuid = uuid;
        mIsTaxi = isTaxi;
        mClientListener = listener;
        if (isTaxi) {
            // 这里暂时关闭，原因是因为BUS上无法透穿虚拟机的网络限制
            if (mNsdClient == null) {
                mNsdClient = new NsdClient(context, SERVER_NAME, new NsdClient.IServerFound() {
                    @Override
                    public void onServerFound(NsdServiceInfo info, int port) {
                        if (info != null) {
                            String hostAddress = info.getHost().getHostAddress();
                            Logger.d(TAG, "NSD查询到指定服务器信息ip为：" + hostAddress + ",port为：" + port);
                            //获取到指定的地址，进行Netty的连接
                            if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
                                new Thread(() -> {
                                    connectServer(info, port, hostAddress, uuid);
                                }).start();
                            } else {
                                connectServer(info, port, hostAddress, uuid);
                            }
                        }
                    }

                    @Override
                    public void onServerFail() {
                        Logger.d(TAG, "NSD查询失败，未找到");
                    }
                });
            }
            mNsdClient.startNSDClient();
        }
        // BUS 由于采用的虚拟机，不在同一个局域网内，所以无法使用上面的方案
        else {
            scanDriverServer(uuid, listener);
        }

    }

    private void connectServer(NsdServiceInfo info, int port, String hostAddress, String uuid) {
        connectNettyServer(hostAddress, port, uuid);
        if (info.getServiceName().startsWith(SERVER_NAME)) {
            //扫描到以后停止
            if (mNsdClient != null) {
                mNsdClient.stopServiceDiscovery();
                mNsdClient = null;
            }
        }
    }

    private void scanDriverServer(String uuid, NettyClientListener listener) {
        // 启动发现司机屏幕任务
        cancelTask();
        discoveryTask = new DiscoveryDriverTask();
        network_ip = NetInfo.getUnsignedLongFromIp(currentIp);
        mClientListener = listener;

        int cidr = 24;
        // Detected IP
        int shift = (32 - cidr);
        if (cidr < 31) {
            network_start = (network_ip >> shift << shift) + 1;
            network_end = (network_start | ((1 << shift) - 1)) - 1;
        } else {
            network_start = (network_ip >> shift << shift);
            network_end = (network_start | ((1 << shift) - 1));
        }

        discoveryTask.setNetwork(network_ip, network_start, network_end);
        discoveryTask.addDiscoveryDriverListener(new DiscoveryDriverListener() {
            @Override
            public void onSuccess(ArrayList<HostBean> hostList) {
                // 这里返回数据代表了有司机端正在运行，这里遍历集合数据进行连接
                if (hostList != null && hostList.size() > 0) {
                    if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
                        ThreadPoolManager.getSingleThreadExecutor().execute(() -> {
                            realConnectServer(hostList, uuid);
                        });
                    } else {
                        realConnectServer(hostList, uuid);
                    }
                } else {
                    Logger.d(TAG, "扫描完成，未找到可连接司机屏幕服务");
                    // 这里做1、8网段的切换扫描
                    if (currentIp.equals(currentIp1)) {
                        currentIp = currentIp8;
                    } else {
                        currentIp = currentIp1;
                    }
                    scanDriverServer(uuid, listener);
                }
            }

            @Override
            public void onCancel() {

            }
        });
        discoveryTask.execute();
    }

    private void realConnectServer(ArrayList<HostBean> hostList, String uuid) {
        if (hostList == null || hostList.isEmpty()) return;
        String hostAddress = hostList.get(0).ipAddress;
        Logger.d(TAG, "查询到指定服务器信息ip为：" + hostAddress + ",port为：" + 1088);
        //获取到指定的地址，进行Netty的连接
        connectNettyServer(hostAddress, 1088, uuid);
    }

    private void connectNettyServer(String serverAddress, int port, String sign) {
        Logger.d(TAG, "connectNettyServer");
        if (serverAddress == null || serverAddress.length() == 0) {
            Logger.e(TAG, "Netty Server的ip不能为空！");
            return;
        }
        if (mNettyTcpClient != null && (mNettyTcpClient.isConnecting() || mNettyTcpClient.isConnected())) {
            return;
        }
        if (!isCanceled) {
            TelematicHandler.removeDelay();
        }
        mNettyTcpClient = new NettyTcpClient.Builder()
                .setHost(serverAddress)    //设置服务端地址
                .setTcpPort(port) //设置服务端端口号
                .setMaxReconnectTimes(5)    //设置最大重连次数
                .setReconnectIntervalTime(5)    //设置重连间隔时间。单位：秒
                .setSign(sign)    //设置客户端标识.(因为可能存在多个tcp连接)
                .setSendHeartBeat(true)
                .build();
        mNettyTcpClient.setListener(new NettyClientListener() {
            @Override
            public void onMessageResponseClient(Object msg, String sign, Channel channel) {
                if (mClientListener != null) {
                    mClientListener.onMessageResponseClient(msg, sign, channel);
                }
            }

            @Override
            public void onClientStatusConnectChanged(int statusCode, String content, Channel channel) {
                if (mClientListener != null) {
                    mClientListener.onClientStatusConnectChanged(statusCode, content, channel);
                }
                if (statusCode == STATUS_CONNECT_CLOSED) {
                    // 开启重连倒计时
                    isCanceled = false;
                    TelematicHandler.delay(COUNTDOWN_TIME);
                } else if (statusCode == STATUS_CONNECT_SUCCESS) {
                    TelematicHandler.removeDelay();
                    TelematicHandler.removeReconnect();
                }
            }
        });

        if (!mNettyTcpClient.getConnectStatus()) {
            mNettyTcpClient.connect();//连接服务器
        } else {
            Logger.d(TAG, "Client is connected.");
        }
    }

    public boolean getConnectStatus() {
        return mNettyTcpClient != null && mNettyTcpClient.getConnectStatus();
    }

    public String getConnServerIp() {
        if (mNettyTcpClient == null) {
            return "";
        } else {
            return mNettyTcpClient.getHost();
        }
    }

    public String getServerSn() {
        String sn = NettyTcpClient.sSERVER_SN;
        return sn;
    }

    public void sendMogoProtocolMsgToServer(MogoProtocolMsg mogoProtocolMsg, final MessageStateListener listener) {
        if (mNettyTcpClient != null) {
            mNettyTcpClient.sendMsgToServer(mogoProtocolMsg, listener);
        }
    }

    /**
     * 连接指定ip服务端
     *
     * @param ip
     */
    public void connectSpecifiedServer(String ip, int port, String uuid, NettyClientListener listener) {
        mClientListener = listener;
        if (mIsTaxi) {
            if (mNsdClient != null) {
                // 停止局域网内扫描
                mNsdClient.stopServiceDiscovery();
            }
        } else {
            cancelTask();
        }
        if (mNettyTcpClient != null) {
            // 断开已连接上的
            mNettyTcpClient.disconnect();
            mNettyTcpClient = null;
        }
        TelematicHandler.removeDelay();
        TelematicHandler.removeReconnect();
        connectNettyServer(ip, port, uuid);
    }

    private void cancelTask() {
        if (discoveryTask != null && discoveryTask.getStatus() == AsyncTask.Status.RUNNING) {
            discoveryTask.cancel(true);
        }
    }

    /**
     * 断开Netty连接
     */
    public void disconnect() {
        if (mNettyTcpClient != null) {
            mNettyTcpClient.disconnect();
        }
    }

    @Override
    public void delayCompleted() {
        Log.e(TAG, "收到delayCompleted事件");
        // 倒计时结束关闭客户端EventGroup，重新扫描ip
        TelematicHandler.removeDelay();
        TelematicHandler.removeReconnect();
        isCanceled = true;
        disconnect();
        searchAndConnectServer(mContext, mUuid, mIsTaxi, mClientListener);
    }

    @Override
    public void reconnectServer() {
        Log.e(TAG, "收到reconnectServer事件");
        TelematicHandler.removeReconnect();
        if (mNettyTcpClient != null) {
            mNettyTcpClient.reconnectServer();
        }
    }
}
