package com.example.iot_controlhost.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

import androidx.annotation.Nullable;

import com.example.iot_controlhost.utils.PollingTask;
import com.example.iot_controlhost.utils.global.HardwareSetting;
import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.network.NetState;
import com.example.iot_controlhost.utils.network.TopicClass;
import com.xuexiang.rxutil2.rxbus.RxBusUtils;
import com.xuexiang.rxutil2.rxjava.RxJavaUtils;
import com.xuexiang.rxutil2.rxjava.task.RxIOTask;

import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;

/**
 * MQTT-后台服务
 */
public class MQTTService extends Service {
    /**
     * MQTT连接状态
     */
    private static boolean MQTTConnect = false;
    /**
     * MQTT客户端
     */
    private static MqttAndroidClient client;
    /**
     * 连接设置
     */
    private MqttConnectOptions conOpt;
    /**
     * 客户端标识
     */
    private String clientId;
    /**
     * MQTT重连次数记录
     */
    private int reConnectedTimes = 0;

    private PollingTask pollingTask;

    @Override
    public void onCreate() {
        super.onCreate();
        pollingTask = PollingTask.getInstance("MQTTService");
        init();
    }

    private void init() {// 服务器地址（协议+地址+端口号）
        String uri = "tcp://iot.bbitcn.net:14001";
//        String uri = "tcp://10.0.4.30:14001";// 何
        clientId = HardwareSetting.getHostId();
        client = new MqttAndroidClient(this, uri, clientId);
        // 设置MQTT监听并且接受消息
        client.setCallback(mqttCallback);
        conOpt = new MqttConnectOptions();
        // 清除缓存
        conOpt.setCleanSession(true);
        // 设置超时时间，单位：秒
        conOpt.setConnectionTimeout(10);
        // 心跳包发送间隔，单位：秒
        conOpt.setKeepAliveInterval(20);
        // 用户名
        conOpt.setUserName("bbitcn");
        // 密码 将字符串转换为字符串数组
        conOpt.setPassword(new String("bbitcn").toCharArray());
        // 遗嘱 在断开时发送给服务器的信息
        String message = "{\"terminal_uid\":\"" + clientId + "\"}";
        MyLog.network("MQTT遗嘱message:" + message);
        String topic = "myTopic";
        boolean retained = false;
        try {
            conOpt.setWill(topic, message.getBytes(), 0, retained);
        } catch (Exception e) {
            MyLog.network("MQTT设置遗嘱消息时发生异常（例如无法连接到服务器）");
            iMqttActionListener.onFailure(null, e);
        }
        MyLog.test("MQTT轮询任务开始");
        pollingTask.startPollingTaskOnIOThread(RxTag.MQTT_CONNECT, 5, () -> {
            if (!getMQTTConnect() && NetState.getNetworkType(getApplicationContext()) != 0) {
                if (reConnectedTimes == 0) {
                    MyLog.network("连接（MQTT）服务器......");
                } else {
                    MyLog.networkError("第" + reConnectedTimes + "次尝试重连（MQTT）服务器......");
                }
                try {
                    IMqttToken token = client.connect(conOpt, null, iMqttActionListener);
                    MyLog.network("MQTT连接服务器，Token=" + token);
                } catch (MqttException e) {
                    MyLog.networkError("MQTT连接服务器失败");
                    e.printStackTrace();
                }
            } else {
                reConnectedTimes = 0;
            }
        });
    }

    /**
     * 向 MQTT 服务器发布消息
     *
     * @param topic 其他订阅了相同主题的客户端可以接收到这条消息
     * @param msg   消息
     */
    public static void publish(String topic, String msg) {
        RxJavaUtils.doInIOThread(new RxIOTask<Object>(null) {
            @Override
            public Void doInIOThread(Object o) {
                try {
                    if (client != null && getMQTTConnect()) {
                        if (client.isConnected()) {
                            //0：表示最多传递一次，不保证可靠性传递；false：不保留消息
                            client.publish(topic, msg.getBytes(), 0, false);
                        }
                    }
                } catch (MqttException e) {
                    MyLog.networkError("MQTT消息发送失败");
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    public static boolean getMQTTConnect() {
        return MQTTConnect;
    }

    /**
     * 设置MQTT状态
     *
     * @param state 目标状态
     */
    public static void setMQTTConnect(boolean state) {
        MQTTConnect = state;
        //提示主界面更改服务器状态图标
        RxBusUtils.get().post(RxTag.UPDATE_MAIN, 4);
    }

    /**
     * 处理 MQTT 连接操作的结果和状态的回调
     */
    private IMqttActionListener iMqttActionListener = new IMqttActionListener() {

        @Override
        public void onSuccess(IMqttToken arg0) {
            MyLog.network("服务器连接成功");
            HardwareSetting.setRegistered();
            setMQTTConnect(true);
            TopicClass.flushPendingLogs();
            try {
                // 订阅myTopic话题
                client.subscribe(TopicClass.arealistGet(), 1);
                client.subscribe(TopicClass.hostbaseinfoGet(), 1);
                client.subscribe(TopicClass.areaenvcontrolGet(), 1);
                client.subscribe(TopicClass.arearunmodelGet(), 1);
                client.subscribe(TopicClass.serianetGet(), 1);
                MyLog.network("订阅主题成功");
                // 连接成功以后
                // 刷新状态
                TopicClass.refreshCatch();
                // 获取可用性信息
                TopicClass.getAreaList();
                // 上传最新的控制器信息
                TopicClass.uploadDeviceState(true); //初次连接MQTT 上传最新的控制器信息
                // 上传最新的传感器信息
                TopicClass.uploadSensorData();
            } catch (MqttException e) {
                MyLog.networkError("订阅主题失败");
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(IMqttToken arg0, Throwable arg1) {
            arg1.printStackTrace();
            MyLog.networkError("服务器连接失败");
            setMQTTConnect(false);
        }
    };

    /**
     * MQTT监听并且接受消息
     */
    private MqttCallback mqttCallback = new MqttCallback() {

        @Override
        public void messageArrived(String topic, MqttMessage message) throws Exception {
            MyLog.network("MQTT接收到消息:\n\ttopic:" + topic + "\n\tqos:" + message.getQos() + "\n\tretained:" + message.isRetained());
            String msg = new String(message.getPayload());
            if (topic.equals(TopicClass.arealistGet())) {
                MyLog.network("MQTT接收到'获取区域集合'消息");
                TopicClass.handleAreaListMessage(msg);
            } else if (topic.equals(TopicClass.areaenvcontrolGet())) {
                MyLog.network("MQTT接收到'远程控制指令'消息");
                TopicClass.handleUploadControlMessage(msg);
            } else if (topic.equals(TopicClass.arearunmodelGet())) {
                MyLog.network("MQTT接收到'区域环境运行模式'消息");
                TopicClass.handleAreaRunModelMessage(msg);
            } else if (topic.equals(TopicClass.serianetGet())) {
                MyLog.network("MQTT接收到'数据透传'消息");
                TopicClass.hanldeSerianetMessage(msg);
            }
        }

        //主题发布成功
        @Override
        public void deliveryComplete(IMqttDeliveryToken arg0) {
            try {
                MyLog.network("主题发布成功:" + arg0.getMessage());
                setMQTTConnect(true);
            } catch (MqttException e) {
                setMQTTConnect(false);
                MyLog.networkError("主题发布失败:deliveryComplete异常：" + e.getMessage());
            }
        }

        @Override
        public void connectionLost(Throwable arg0) {
            MyLog.network("MQTT服务器失去连接");
            setMQTTConnect(false);
        }
    };

    @Override
    public void onDestroy() {
        pollingTask.stopAllPollingTasks();
        super.onDestroy();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
