#include "aly.h"

/*
    函数内容：HTTP事件回调
    函数参数：
            esp_http_client_event_t *evt--事件指针
    返回值：esp_err_t--错误代码
*/
static esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    switch (evt->event_id) {
    case HTTP_EVENT_ERROR:
        ESP_LOGD(MQTT_TAG, "HTTP_EVENT_ERROR");
        break;
    case HTTP_EVENT_ON_CONNECTED:
        ESP_LOGD(MQTT_TAG, "HTTP_EVENT_ON_CONNECTED");
        break;
    case HTTP_EVENT_HEADER_SENT:
        ESP_LOGD(MQTT_TAG, "HTTP_EVENT_HEADER_SENT");
        break;
    case HTTP_EVENT_ON_HEADER:
        ESP_LOGD(MQTT_TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
        break;
    case HTTP_EVENT_ON_DATA:
        ESP_LOGD(MQTT_TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
        break;
    case HTTP_EVENT_ON_FINISH:
        ESP_LOGD(MQTT_TAG, "HTTP_EVENT_ON_FINISH");
        break;
    case HTTP_EVENT_DISCONNECTED:
        ESP_LOGD(MQTT_TAG, "HTTP_EVENT_DISCONNECTED");
        break;
    case HTTP_EVENT_REDIRECT:
  	    ESP_LOGD(MQTT_TAG, "HTTP_EVENT_REDIRECT");
  	    break; 
    }
    return ESP_OK;
}

/*
    函数内容：MQTT事件回调
    函数参数：
            esp_mqtt_event_handle_t event--事件参数
    返回值：esp_err_t--错误代码
*/
 esp_mqtt_client_handle_t client;   //MQTT客户端句柄
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
{
    esp_mqtt_client_handle_t client = event->client;    //事件句柄

    int msg_id; //消息ID号

    // 根据事件ID确定事件内容
    switch (event->event_id)
    {
    case MQTT_EVENT_CONNECTED: // MQTT连接成功事件
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_CONNECTED");
        // 使用QOS订阅指定主题
        msg_id = esp_mqtt_client_subscribe(client, AliyunSubscribeTopic_user_get, 0); // 订阅阿里云自定义消息 topic

        msg_id = esp_mqtt_client_subscribe(client, AliyunOTAupgrade, 0); // 订阅阿里云OTA topic

        ESP_LOGI(MQTT_TAG, "sent subscribe successful, msg_id=%d", msg_id);

        break;
    case MQTT_EVENT_DISCONNECTED: // MQTT断开连接
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_DISCONNECTED");
        break;

    case MQTT_EVENT_SUBSCRIBED: // MQTT订阅成功事件
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);

        // 发布自定义topic 测试信息
        msg_id = esp_mqtt_client_publish(client, AliyunPublishTopic_user_update, mqtt_publish_Testdata, strlen(mqtt_publish_Testdata), 0, 0);

        // 上报阿里云OTA版本
        msg_id = esp_mqtt_client_publish(client, AliyunOTAInform, mqtt_OTA_Data, strlen(mqtt_OTA_Data), 0, 0);

        ESP_LOGI(MQTT_TAG, "sent publish successful, msg_id=%d", msg_id);
        break;
    case MQTT_EVENT_UNSUBSCRIBED: // MQTT订阅失败事件
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_PUBLISHED: // MQTT发布事件
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_DATA: // MQTT接收到数据事件
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_DATA");
        printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
        printf("DATA=%.*s\r\n", event->data_len, event->data);

        // 根据阿里云topic进行数据解析
        Get_ALY_Topic(event->topic, event->data);
        break;
    case MQTT_EVENT_ERROR: // MQTT错误事件
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_ERROR");
        break;
    default: // 其他事件
        ESP_LOGI(MQTT_TAG, "Other event id:%d", event->event_id);
        break;
    }
    return ESP_OK;
}

// MQTT事件回调
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    ESP_LOGD(MQTT_TAG, "Event dispatched from event loop base=%s, event_id=%ld", base, event_id);
    mqtt_event_handler_cb(event_data);
}

/*
    函数内容：进行阿里云连接
    函数参数：无
    返回值：  无
*/

void user_mqtt_app_start(void)
{
     esp_mqtt_client_config_t mqtt_cfg = {
		.broker.address.hostname = Aliyun_host,
		.broker.address.port = Aliyun_port,
        .broker.address.transport = MQTT_TRANSPORT_OVER_TCP,
		.credentials.client_id = Aliyun_client_id,
		.credentials.username = Aliyun_username,
		.credentials.authentication.password = Aliyun_password,


    };
    client = esp_mqtt_client_init(&mqtt_cfg);                                             // 创建MQTT客户端句柄
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client); // 注册MQTT事件
    esp_mqtt_client_start(client);                                                        // 启动MQTT客户端
}

/*
    函数内容：解析阿里云的OTA升级数据--并下载更新内容
    函数参数：const char *Data---阿里云下发的数据
    返回值    无
*/

static esp_err_t Parse_Data(const char *Data)
{
    char ALY_url[OTA_URL_SIZE] = {0};
    // char *str;

    cJSON *root = NULL;
    root = cJSON_Parse(Data);

    cJSON *code = cJSON_GetObjectItem(root, "code");    //状态码
    ESP_LOGI(MQTT_TAG, "Code:%s", code->valuestring);

    cJSON *data = cJSON_GetObjectItem(root, "data");    //数据字段名

    cJSON *size = cJSON_GetObjectItem(data, "size");    //升级包文件大小，单位：字节。
    ESP_LOGI(MQTT_TAG, "Size:%d", size->valueint);

    cJSON *sign = cJSON_GetObjectItem(data, "sign");    //OTA升级包文件的签名。
    ESP_LOGI(MQTT_TAG, "Sign:%s", sign->valuestring);

    cJSON *version = cJSON_GetObjectItem(data, "version");  //设备升级包的版本信息。
    ESP_LOGI(MQTT_TAG, "Version:%s", version->valuestring);

    cJSON *signMethod = cJSON_GetObjectItem(data, "signMethod");    //签名方法。取值：SHA256，MD5
    ESP_LOGI(MQTT_TAG, "SignMethod:%s", signMethod->valuestring);

    cJSON *url = cJSON_GetObjectItem(data, "url");  //升级包在对象存储（OSS）上的存储地址
    ESP_LOGI(MQTT_TAG, "URL:%s", url->valuestring);

    memcpy(ALY_url, url->valuestring, strlen(url->valuestring)); // https升级时使用
    /*str = strstr(url->valuestring, "//iot");  //HTTP升级时使用
    if(str!=NULL)
    {
        ESP_LOGI(MQTT_TAG, "Test:%s", str);
        sprintf(ALY_url, "http:%s", str);
        ESP_LOGI(MQTT_TAG, "ALY_url:%s", ALY_url);
    }*/

    cJSON *md5 = cJSON_GetObjectItem(data, "md5");  //当签名方法为MD5时，除了会给sign赋值外还会给md5赋值。
    ESP_LOGI(MQTT_TAG, "Md5:%s", md5->valuestring);

    cJSON *id = cJSON_GetObjectItem(root, "id");    //消息ID号。每个消息ID在当前设备中具有唯一性。
    ESP_LOGI(MQTT_TAG, "ID:%d", id->valueint);

    cJSON *message = cJSON_GetObjectItem(root, "message");  //结果信息。
    ESP_LOGI(MQTT_TAG, "Message:%s", message->valuestring);

    cJSON_Delete(root); //释放json资源
    root = NULL;
    esp_http_client_config_t config = {0};
   // esp_http_client_config_t config = { 
       config .url = ALY_url,                     //升级包地址
        config.cert_pem = (char *)server_cert_pem_start,  //设备证书校验
        config.event_handler = _http_event_handler,       //http回调
        config.timeout_ms = 2000;
        config.keep_alive_enable = true;

        //.buffer_size = 2048,                         // 调整缓冲区大小
        //.keep_alive_enable = true,                  //保持活跃
  //  };

    esp_err_t ret = esp_https_ota(&config);         //开始进行OTA升级

    return ret;
}

/*
    函数内容：通过阿里云下发的topic进行对应的数据解析
    函数参数：
            const char *topic---topic数据
            const char *data----内容数据
    返回值：无
*/
void Get_ALY_Topic(const char *topic, const char *data)
{
    if (strstr(topic, "/ota/device/upgrade/a1o6FKg9Pc7/nFP00TvHvsNYzjLEewRU") != NULL) // 如果是阿里云的OTA升级数据
    {
        esp_err_t ret = 0;      //错误代码变量

        int msg_id=0;           //消息ID号

        ESP_LOGI(MQTT_TAG, "OTA Data");

        ret = Parse_Data(data); // 进行OTA数据解析获取升级数据

        if (ret == ESP_OK)  //如果OTA升级成功
        {
            // 构造JSON格式数据，该数据用于反馈给阿里云物联网平台，作用是通知升级包接收进度
            cJSON *Wroot = cJSON_CreateObject();    
            cJSON *Pitem = cJSON_CreateObject();

            cJSON_AddItemToObject(Wroot, "id", cJSON_CreateString("001"));  //消息ID
            cJSON_AddItemToObject(Wroot, "params", Pitem);                  //二层字段名
            cJSON_AddItemToObject(Pitem, "step", cJSON_CreateString("100"));    //当前进度
            cJSON_AddItemToObject(Pitem, "desc", cJSON_CreateString("OTA update successfully !"));  //内容描述

            char ota_inform_buf[256] = {0}; //发送消息的一个数组

            int len = strlen(cJSON_Print(Wroot));   //获取消息的长度

            memcpy(ota_inform_buf, cJSON_Print(Wroot), len); // 将JSON格式数据复制到数组中，将以数组的形式传递给发布函数

            ESP_LOGI(MQTT_TAG, "ota_inform_buf:%s,len:%d", ota_inform_buf,len); //打印提示信息

            msg_id = esp_mqtt_client_publish(client, AliyunOTAprogress, ota_inform_buf, strlen(ota_inform_buf), 0, 0);  //MQTT发布消息

            ESP_LOGI(MQTT_TAG, "sent publish ota inform successful, msg_id=%d", msg_id);//打印提示信息

            cJSON_Delete(Wroot);    //释放json资源

            esp_restart(); // ESP32设备重启，重启后将执行刚才下载的程序
        }
        else
        {
            ESP_LOGE(MQTT_TAG, "OTA failed");
        }

        vTaskDelay(pdMS_TO_TICKS(100));
    }
}
