package com.aliyun.iot.demo.shadow;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.iot.util.AliyunWebUtils;
import com.aliyun.iot.util.LogUtil;
import com.aliyun.iot.util.SignUtil;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * 设备影子功能使用demo
 */
public class SimpleClient4Shadow {

    /**
     * 认证服务器地址 每个区域不一样
     */
    private static String authUrl = "https://com.aliyun.iot-auth.cn-shanghai.aliyuncs.com/auth/devicename";

    /**
     * 设备key和secret信息
     */
    private static String deviceName = "";
    private static String productKey = "";
    private static String deviceSecret = "";

    /**
     * 设备影子topic
     */
    private static String shadowAckTopic = "/shadow/get/" + productKey + "/" + deviceName;
    private static String shadowUpdateTopic = "/shadow/update/" + productKey + "/" + deviceName;

    /**
     * 影子版本号
     */
    private static long shadowVersion = 0;

    /**
     * 根据属性key-value 生成shadow json格式数据
     *
     * @param attributeMap
     * @return
     */
    private static String genUpdateShadowMsg(Map<String, Object> attributeMap) {
        Set<String> attSet = attributeMap.keySet();
        Map<String, Object> attMap = new LinkedHashMap<String, Object>();
        for (String attKey : attSet) {
            attMap.put(attKey, attributeMap.get(attKey));
        }

        Map<String, Object> reportedMap = new LinkedHashMap<String, Object>();
        reportedMap.put("reported", attMap);

        Map<String, Object> shadowJsonMap = new LinkedHashMap<String, Object>();
        shadowJsonMap.put("method", "update");
        shadowJsonMap.put("state", reportedMap);

        //shadow version自增
        shadowVersion++;

        shadowJsonMap.put("version", shadowVersion);

        return JSON.toJSONString(shadowJsonMap);
    }

    /**
     * 生成clean shadow json数据
     *
     * @param reportMsg
     * @return
     */
    private static String genCleanShadowMsg(String reportMsg) {

        Map<String, Object> stateMap = new LinkedHashMap<String, Object>();

        if (reportMsg == null || reportMsg.length() == 0) {
            stateMap.put("reported", "null");
        } else {
            JSONObject reportJsonObj = JSON.parseObject(reportMsg);
            Set<String> attSet = reportJsonObj.keySet();

            Map<String, Object> attMap = new LinkedHashMap<String, Object>();
            for (String attKey : attSet) {
                attMap.put(attKey, reportJsonObj.getString(attKey));
            }
            stateMap.put("reported", attMap);
        }
        stateMap.put("desired", "null");

        Map<String, Object> cleanShadowMap = new LinkedHashMap<String, Object>();
        cleanShadowMap.put("method", "update");
        cleanShadowMap.put("state", stateMap);

        shadowVersion++;
        cleanShadowMap.put("version", shadowVersion);

        return JSON.toJSONString(cleanShadowMap);
    }

    /**
     * 删除影子某个属性
     * 只需要把属性value置为"null"即可
     *
     * @param attributeMap
     * @return
     */
    private static String genDeleteShadowMsg(Map<String, Object> attributeMap) {
        Set<String> attSet = attributeMap.keySet();
        Map<String, Object> attMap = new LinkedHashMap<String, Object>();
        for (String attKey : attSet) {
            attMap.put(attKey, attributeMap.get(attKey));
        }

        Map<String, Object> reportedMap = new LinkedHashMap<String, Object>();
        reportedMap.put("reported", attMap);

        Map<String, Object> shadowJsonMap = new LinkedHashMap<String, Object>();
        shadowJsonMap.put("method", "delete");
        shadowJsonMap.put("state", reportedMap);

        //shadow version自增
        shadowVersion++;

        shadowJsonMap.put("version", shadowVersion);

        return JSON.toJSONString(shadowJsonMap);
    }

    /**
     * 解析出desired信息
     *
     * @param message
     * @param sampleClient
     * @throws Exception
     */
    private static void parseDesiredMsg(MqttMessage message, MqttClient sampleClient) throws Exception {

        JSONObject shadowJsonObj = JSON.parseObject(message.toString());
        JSONObject payloadJsonObj = shadowJsonObj.getJSONObject("payload");

        shadowVersion = shadowJsonObj.getLong("version");
        System.out.println("shadowVersion:" + shadowVersion);

        //解析出desired
        JSONObject stateJsonObj = payloadJsonObj.getJSONObject("state");
        String desiredString = stateJsonObj.getString("desired");
        System.out.println("desiredString:" + desiredString);

        //TODO 根据desired信息做业务处理

        //清空shadow信息
        if (desiredString != null) {
            //reported字段可能为空
            String cleanShadowMsg = genCleanShadowMsg(stateJsonObj.getString("reported"));
            System.out.println("cleanShadowMsg:" + cleanShadowMsg);

            MqttMessage cleanShadowMqttMsg = new MqttMessage(cleanShadowMsg.getBytes("UTF-8"));
            message.setQos(1);
            sampleClient.publish(shadowUpdateTopic, cleanShadowMqttMsg);
            System.out.println("send clean shadow msg done");
        }
    }

    public static void main2(String... strings) throws Exception {

        /* 客户端设备 自己的一个标记 */
        String clientId = productKey + "&" + deviceName;

        Map<String, String> params = new HashMap<String, String>(16);

        /** 这个是对应用户在控制台注册的 设备productkey */
        params.put("productKey", productKey);

        /** 这个是对应用户在控制台注册的 设备name */
        params.put("deviceName", deviceName);
        params.put("timestamp", "" + System.currentTimeMillis());
        params.put("clientId", clientId);

        //签名
        params.put("sign", SignUtil.sign(params, deviceSecret, "hmacMD5"));

        //请求资源 mqtt
        params.put("resources", "mqtt");

        String result = AliyunWebUtils.doPost(authUrl, params, 5000, 5000);
        System.out.println("result=[" + result + "]");

        JSONObject mapResult;
        try {
            mapResult = JSON.parseObject(result);
        } catch (Exception e) {
            System.out.println("https auth result is invalid json fmt");
            return;
        }

        if ("200".equals(mapResult.getString("code"))) {
            LogUtil.print("认证成功!" + mapResult.get("data"));
            LogUtil.print("data=[" + mapResult + "]");
        } else {
            System.err.println("认证失败!");
            throw new RuntimeException(
                "认证失败:" + mapResult.get("code") + "," + mapResult.get("message"));
        }

        JSONObject data = (JSONObject)mapResult.get("data");

        //sign TODO 服务器返回的sign签名 防止域名劫持验证
        //mqtt服务器 TODO
        String targetServer = "ssl://"
            + data.getJSONObject("resources").getJSONObject("mqtt")
            .getString("host")
            + ":" + data.getJSONObject("resources").getJSONObject("mqtt")
            .getString("port");

        String token = data.getString("iotToken");
        String iotId = data.getString("iotId");

        //客户端ID格式:
        /* 设备端自定义的标记,字符范围[0-9][a-z][A-Z] */
        String mqttClientId = clientId;

        /* 认证后得到的云端iotId */
        String mqttUsername = iotId;

        /* 认证后得到的token 有效期7天 */
        String mqttPassword = token;

        System.err.println("mqttclientId=" + mqttClientId);

        connectMqtt(targetServer, mqttClientId, mqttUsername, mqttPassword);
    }

    private static void connectMqtt(String url, String clientId, String mqttUsername,
                                    String mqttPassword) throws Exception {

        MemoryPersistence persistence = new MemoryPersistence();
        SSLSocketFactory socketFactory = createSSLSocket();
        final MqttClient sampleClient = new MqttClient(url, clientId, persistence);
        MqttConnectOptions connOpts = new MqttConnectOptions();

        /* MQTT 3.1.1 */
        connOpts.setMqttVersion(4);
        connOpts.setSocketFactory(socketFactory);

        //设置是否自动重连
        connOpts.setAutomaticReconnect(true);

        //如果是true 那么清理所有离线消息,即qos1 或者 2的所有未接收内容
        connOpts.setCleanSession(false);

        connOpts.setUserName(mqttUsername);
        connOpts.setPassword(mqttPassword.toCharArray());
        connOpts.setKeepAliveInterval(65);
        LogUtil.print(clientId + "进行连接, 目的地: " + url);

        //sampleClient.setManualAcks(true);//不要自动回执ack
        sampleClient.connect(connOpts);

        sampleClient.setCallback(new MqttCallback() {

            @Override
            public void connectionLost(Throwable cause) {
                LogUtil.print("连接失败,原因:" + cause);
                cause.printStackTrace();
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                LogUtil.print("接收到消息,来至Topic [" + topic + "] , 内容是:["
                    + new String(message.getPayload(), "UTF-8") + "],  ");
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
                //如果是qos 0消息 token.resp是没有回复的
                LogUtil.print("消息发送成功! " + ((token == null || token.getResponse() == null) ? "null"
                    : token.getResponse().getKey()));
            }
        });
        LogUtil.print("连接成功:---");

        //订阅shadow topic
        sampleClient.subscribe(shadowAckTopic, new IMqttMessageListener() {
            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {

                LogUtil.print("收到消息:" + message + ",topic=" + topic);

                JSONObject shadowJsonObj = JSON.parseObject(message.toString());
                String shadowMethod = shadowJsonObj.getString("method");
                JSONObject payloadJsonObj = shadowJsonObj.getJSONObject("payload");

                /* method是reply,解析成功还是失败*/
                if ("reply".equals(shadowMethod)) {

                    String status = payloadJsonObj.getString("status");
                    String stateInfo = payloadJsonObj.getString("state");
                    if ("success".equals(status)) {
                        if (stateInfo == null) {
                            System.out.println("update shadow success");
                        } else {
                            //解析出desired信息
                            parseDesiredMsg(message, sampleClient);
                        }
                    } else {
                        JSONObject errorJsonObj = payloadJsonObj.getJSONObject("content");
                        String errorCode = errorJsonObj.getString("errorcode");
                        String errorMsg = errorJsonObj.getString("errormessage");
                        System.out.println("errorCode:" + errorCode);
                        System.out.println("errorMsg:" + errorMsg);
                    }

                }
                /* method是control,解析出desired和version信息 */
                else if ("control".equals(shadowMethod)) {
                    parseDesiredMsg(message, sampleClient);
                }
            }
        });

        //获取影子内容,解析出version信息
        String getShadowInfo = "{\"method\": \"get\"}";
        MqttMessage shadowMessage = new MqttMessage(getShadowInfo.getBytes("UTF-8"));
        shadowMessage.setQos(1);
        sampleClient.publish(shadowUpdateTopic, shadowMessage);

        //等待获取到版本号
        Thread.sleep(1000);

        //更新设备影子
        Map<String, Object> attMap = new HashMap<String, Object>(128);
        attMap.put("window", "open");
        attMap.put("led", "on");
        attMap.put("temperature", 28);
        attMap.put("light", "high");

        String shadowUpdateMsg = genUpdateShadowMsg(attMap);
        System.out.println("updateShadowMsg: " + shadowUpdateMsg);

        MqttMessage message = new MqttMessage(shadowUpdateMsg.getBytes("UTF-8"));
        message.setQos(1);
        sampleClient.publish(shadowUpdateTopic, message);

        //删除影子某个属性
        Map<String, Object> deleteAttMap = new HashMap<String, Object>(128);

        //把属性值设置为"null"
        deleteAttMap.put("temperature", "null");
        deleteAttMap.put("led", "null");
        deleteAttMap.put("window", "null");

        String deleteShadowMsg = genDeleteShadowMsg(deleteAttMap);
        System.out.println("deleteShadowMsg: " + deleteShadowMsg);

        MqttMessage deleteMessage = new MqttMessage(deleteShadowMsg.getBytes("UTF-8"));
        deleteMessage.setQos(1);
        sampleClient.publish(shadowUpdateTopic, deleteMessage);

    }

    private static SSLSocketFactory createSSLSocket() throws Exception {
        SSLContext context = SSLContext.getInstance("TLSV1.2");
        context.init(null, new TrustManager[] {new com.aliyun.iot.demo.iothub.ALiyunIotX509TrustManager()}, null);
        SSLSocketFactory socketFactory = context.getSocketFactory();
        return socketFactory;
    }
}