#include "kk_dm_msg.h"
#include "kk_dm_mng.h"
#include "kk_tsl_api.h"

#include "lite-cjson.h"
#include "cJSON.h"
#include "com_api.h"


const char DM_URI_SYS_PREFIX[]                        DM_READ_ONLY = "/sys/%s/%s/";
const char DM_URI_THING_EVENT_PROPERTY_POST[]		  DM_READ_ONLY = "thing/event/property/post";
const char DM_URI_THING_SERVICE_PROPERTY_SET_REPLY[]  DM_READ_ONLY = "thing/service/property/set_reply";
const char DM_URI_THING_EVENT_POST[]				  DM_READ_ONLY = "thing/event/%.*s/post";
const char DM_URI_THING_SERVICE_RESPONSE[]			  DM_READ_ONLY = "thing/service/%.*s_reply";
const char DM_URI_THING_EVENT_PROPERTY_POST_METHOD[]  DM_READ_ONLY = "thing.event.property.post";



const char DM_MSG_REQUEST[] DM_READ_ONLY = "{\"id\":\"%d\",\"version\":\"%s\",\"params\":%.*s,\"method\":\"%s\"}";

void kk_sendData2app(void *uri, void *payload){
    cJSON *root=cJSON_CreateObject();
    cJSON_AddStringToObject(root, "topic", uri);
    cJSON_AddStringToObject(root, "payload", payload);
    void *buf = cJSON_Print(root);
    kk_ipc_send(IPC_MID2APP, buf, strlen(buf) + 1);
    free(buf);
    cJSON_Delete(root);
    
}

const char DM_MSG_THING_SUB_REGISTER_METHOD[] = "thing.sub.register";
const char DM_MSG_THING_SUB_REGISTER_PARAMS[] = "[{\"productKey\":\"%s\",\"deviceName\":\"%s\"}]";

int dm_msg_thing_sub_register(_IN_ char product_key[PRODUCT_KEY_MAXLEN],
                              _IN_ char device_name[DEVICE_NAME_MAXLEN],
                              _OU_ dm_msg_request_t *request)
{
    int params_len = 0;
    char *params = NULL;

    if (request == NULL || product_key == NULL || device_name == NULL ||
        (strlen(product_key) >= PRODUCT_KEY_MAXLEN) ||
        (strlen(device_name) >= DEVICE_NAME_MAXLEN) ||
        (strlen(request->product_key) >= PRODUCT_KEY_MAXLEN) ||
        (strlen(request->device_name) >= DEVICE_NAME_MAXLEN)) {
        return INVALID_PARAMETER;
    }

    params_len = strlen(DM_MSG_THING_SUB_REGISTER_PARAMS) + strlen(product_key) + strlen(device_name) + 1;
    params = malloc(params_len);
    if (params == NULL) {
        return MEMORY_NOT_ENOUGH;
    }

    memset(params, 0, params_len);
    snprintf(params, params_len, DM_MSG_THING_SUB_REGISTER_PARAMS, product_key, device_name);

    /* Get Params */
    request->params = params;
    request->params_len = strlen(request->params);

    /* Get Method */
    request->method = (char *)DM_MSG_THING_SUB_REGISTER_METHOD;

    return SUCCESS_RETURN;
}

const char DM_MSG_THING_SUB_UNREGISTER_METHOD[] DM_READ_ONLY = "thing.sub.unregister";
const char DM_MSG_THING_SUB_UNREGISTER_PARAMS[] DM_READ_ONLY = "[{\"productKey\":\"%s\",\"deviceName\":\"%s\"}]";
int dm_msg_thing_sub_unregister(_IN_ char product_key[PRODUCT_KEY_MAXLEN],
                                  _IN_ char device_name[DEVICE_NAME_MAXLEN],
                                  _OU_ dm_msg_request_t *request)
{
    int params_len = 0;
    char *params = NULL;

    if (request == NULL || product_key == NULL || device_name == NULL ||
        (strlen(product_key) >= PRODUCT_KEY_MAXLEN) ||
        (strlen(device_name) >= DEVICE_NAME_MAXLEN) ||
        (strlen(request->product_key) >= PRODUCT_KEY_MAXLEN) ||
        (strlen(request->device_name) >= DEVICE_NAME_MAXLEN)) {
        return INVALID_PARAMETER;
    }

    params_len = strlen(DM_MSG_THING_SUB_UNREGISTER_PARAMS) + strlen(product_key) + strlen(device_name) + 1;
    params = malloc(params_len);
    if (params == NULL) {
    return MEMORY_NOT_ENOUGH;
    }
    memset(params, 0, params_len);
    snprintf(params, params_len, DM_MSG_THING_SUB_UNREGISTER_PARAMS, product_key, device_name);

    /* Get Params */
    request->params = params;
    request->params_len = strlen(request->params);

    /* Get Method */
    request->method = (char *)DM_MSG_THING_SUB_UNREGISTER_METHOD;

    return SUCCESS_RETURN;
}

const char DM_MSG_THING_TOPO_ADD_SIGN_SOURCE[] DM_READ_ONLY = "clientId%sdeviceName%sproductKey%stimestamp%s";
const char DM_MSG_THING_TOPO_ADD_METHOD[] DM_READ_ONLY = "thing.topo.add";
const char DM_MSG_THING_TOPO_ADD_PARAMS[] DM_READ_ONLY =
          "[{\"productKey\":\"%s\",\"deviceName\":\"%s\",\"signmethod\":\"%s\",\"sign\":\"%s\",\"timestamp\":\"%s\",\"clientId\":\"%s\"}]";
int dm_msg_thing_topo_add(_IN_ char product_key[PRODUCT_KEY_MAXLEN],
                        _IN_ char device_name[DEVICE_NAME_MAXLEN],
                        _IN_ char device_secret[DEVICE_SECRET_MAXLEN], _OU_ dm_msg_request_t *request)
{
    char *params = NULL;
    int params_len = 0;
    char timestamp[DM_UTILS_UINT64_STRLEN] = {0};
    char client_id[PRODUCT_KEY_MAXLEN + DEVICE_NAME_MAXLEN + 1] = {0};
    char *sign_source = NULL;
    int sign_source_len = 0;
    char *sign_method = DM_MSG_SIGN_METHOD_HMACSHA1;
    char sign[65] = {0};


    if (request == NULL || product_key == NULL ||
        device_name == NULL || device_secret == NULL ||
        (strlen(product_key) >= PRODUCT_KEY_MAXLEN) ||
        (strlen(device_name) >= DEVICE_NAME_MAXLEN) ||
        (strlen(device_secret) >= DEVICE_SECRET_MAXLEN) ||
        (strlen(request->product_key) >= PRODUCT_KEY_MAXLEN) ||
        (strlen(request->device_name) >= DEVICE_NAME_MAXLEN)) {
        return INVALID_PARAMETER;
    }

    /* TimeStamp */
    HAL_Snprintf(timestamp, DM_UTILS_UINT64_STRLEN, "%llu", (unsigned long long)HAL_UptimeMs());
    /* dm_log_debug("Time Stamp: %s", timestamp); */

    /* Client ID */
    HAL_Snprintf(client_id, PRODUCT_KEY_MAXLEN + DEVICE_NAME_MAXLEN + 1, "%s.%s", product_key, device_name);

    /* Sign */
    sign_source_len = strlen(DM_MSG_THING_TOPO_ADD_SIGN_SOURCE) + strlen(client_id) +
                    strlen(device_name) + strlen(product_key) + strlen(timestamp) + 1;
    sign_source = malloc(sign_source_len);
    if (sign_source == NULL) {
      return MEMORY_NOT_ENOUGH;
    }
    memset(sign_source, 0, sign_source_len);
    HAL_Snprintf(sign_source, sign_source_len, DM_MSG_THING_TOPO_ADD_SIGN_SOURCE, client_id,
               device_name, product_key, timestamp);

    /* dm_log_debug("Sign Srouce: %s", sign_source); */
#if 0
  if (strcmp(sign_method, DM_MSG_SIGN_METHOD_HMACMD5) == 0) {
      utils_hmac_md5(sign_source, strlen(sign_source), sign, device_secret, strlen(device_secret));
  } else if (strcmp(sign_method, DM_MSG_SIGN_METHOD_HMACSHA1) == 0) {
      utils_hmac_sha1(sign_source, strlen(sign_source), sign, device_secret, strlen(device_secret));
  } else if (strcmp(sign_method, DM_MSG_SIGN_METHOD_HMACSHA256) == 0) {
      utils_hmac_sha256(sign_source, strlen(sign_source), sign, device_secret, strlen(device_secret));
  } else {
      DM_free(sign_source);
      return FAIL_RETURN;
  }
#else
  //utils_hmac_sha1(sign_source, strlen(sign_source), sign, device_secret, strlen(device_secret));
#endif
  free(sign_source);
  /* dm_log_debug("Sign : %s", sign); */

  /* Params */
  request->method = (char *)DM_MSG_THING_TOPO_ADD_METHOD;
  params_len = strlen(DM_MSG_THING_TOPO_ADD_PARAMS) + strlen(product_key) + strlen(device_name) +
               strlen(sign_method) + strlen(sign) + strlen(timestamp) + strlen(client_id) + 1;
  params = malloc(params_len);

  if (params == NULL) {
      return MEMORY_NOT_ENOUGH;
  }
  memset(params, 0, params_len);
  HAL_Snprintf(params, params_len, DM_MSG_THING_TOPO_ADD_PARAMS, product_key, device_name,
               sign_method, sign, timestamp, client_id);

  request->params = params;
  request->params_len = strlen(request->params);

  return SUCCESS_RETURN;
}

const char DM_MSG_COMBINE_LOGIN_SIGN_SOURCE[] DM_READ_ONLY = "clientId%sdeviceName%sproductKey%stimestamp%s";
const char DM_MSG_COMBINE_LOGIN_METHOD[] DM_READ_ONLY = "combine.login";
const char DM_MSG_COMBINE_LOGIN_PARAMS[] DM_READ_ONLY =
            "{\"productKey\":\"%s\",\"deviceName\":\"%s\",\"clientId\":\"%s\",\"timestamp\":\"%s\",\"signMethod\":\"%s\",\"sign\":\"%s\",\"cleanSession\":\"%s\"}";
int dm_msg_combine_login(_IN_ char product_key[PRODUCT_KEY_MAXLEN],
                         _IN_ char device_name[DEVICE_NAME_MAXLEN],
                         _IN_ char device_secret[DEVICE_SECRET_MAXLEN], _OU_ dm_msg_request_t *request)
{
    char *params = NULL;
    int params_len = 0;
    char timestamp[DM_UTILS_UINT64_STRLEN] = {0};
    char client_id[PRODUCT_KEY_MAXLEN + DEVICE_NAME_MAXLEN + 20] = {0};
    char *sign_source = NULL;
    int sign_source_len = 0;
    char *sign_method = DM_MSG_SIGN_METHOD_HMACSHA1;
    char sign[64] = {0};


    if (request == NULL || product_key == NULL ||
        device_name == NULL || device_secret == NULL ||
        (strlen(product_key) >= PRODUCT_KEY_MAXLEN) ||
        (strlen(device_name) >= DEVICE_NAME_MAXLEN) ||
        (strlen(device_secret) >= DEVICE_SECRET_MAXLEN) ||
        (strlen(request->product_key) >= PRODUCT_KEY_MAXLEN) ||
        (strlen(request->device_name) >= DEVICE_NAME_MAXLEN)) {
        return INVALID_PARAMETER;
    }

    /* TimeStamp */
    HAL_Snprintf(timestamp, DM_UTILS_UINT64_STRLEN, "%llu", (unsigned long long)HAL_UptimeMs());
    /* dm_log_debug("Time Stamp: %s", timestamp); */

    /* Client ID */
    HAL_Snprintf(client_id, PRODUCT_KEY_MAXLEN + DEVICE_NAME_MAXLEN + 20, "%s.%s|_v=sdk-c-3.0.1|", product_key, device_name);

    /* Sign */
    sign_source_len = strlen(DM_MSG_COMBINE_LOGIN_SIGN_SOURCE) + strlen(client_id) +
                      strlen(device_name) + strlen(product_key) + strlen(timestamp) + 1;
    sign_source = malloc(sign_source_len);
    if (sign_source == NULL) {
        return DM_MEMORY_NOT_ENOUGH;
    }
    memset(sign_source, 0, sign_source_len);
    HAL_Snprintf(sign_source, sign_source_len, DM_MSG_COMBINE_LOGIN_SIGN_SOURCE, client_id,
                 device_name, product_key, timestamp);

    /* dm_log_debug("Sign Srouce: %s", sign_source); */
#if 0
    if (strcmp(sign_method, DM_MSG_SIGN_METHOD_HMACMD5) == 0) {
        utils_hmac_md5(sign_source, strlen(sign_source), sign, device_secret, strlen(device_secret));
    } else if (strcmp(sign_method, DM_MSG_SIGN_METHOD_HMACSHA1) == 0) {
        utils_hmac_sha1(sign_source, strlen(sign_source), sign, device_secret, strlen(device_secret));
    } else if (strcmp(sign_method, DM_MSG_SIGN_METHOD_HMACSHA256) == 0) {
        utils_hmac_sha256(sign_source, strlen(sign_source), sign, device_secret, strlen(device_secret));
    } else {
        DM_free(sign_source);
        return FAIL_RETURN;
    }
#else
    //utils_hmac_sha1(sign_source, strlen(sign_source), sign, device_secret, strlen(device_secret));
#endif
    free(sign_source);
    /* dm_log_debug("Sign : %s", sign); */

    /* Params */
    request->method = (char *)DM_MSG_COMBINE_LOGIN_METHOD;
    params_len = strlen(DM_MSG_COMBINE_LOGIN_PARAMS) + strlen(product_key) + strlen(device_name) +
                 strlen(sign_method) + strlen(sign) + strlen(timestamp) + strlen(client_id) + 1;
    params = malloc(params_len);

    if (params == NULL) {
        return DM_MEMORY_NOT_ENOUGH;
    }
    memset(params, 0, params_len);
    HAL_Snprintf(params, params_len, DM_MSG_COMBINE_LOGIN_PARAMS, product_key, device_name,
                 client_id, timestamp, sign_method, sign, "true");

    request->params = params;
    request->params_len = strlen(request->params);

    return SUCCESS_RETURN;
}


const char DM_MSG_COMBINE_LOGOUT_METHOD[] DM_READ_ONLY = "combine.logout";
const char DM_MSG_COMBINE_LOGOUT_PARAMS[] DM_READ_ONLY = "{\"productKey\":\"%s\",\"deviceName\":\"%s\"}";
int dm_msg_combine_logout(_IN_ char product_key[PRODUCT_KEY_MAXLEN],
                                    _IN_ char device_name[DEVICE_NAME_MAXLEN],
                                     _OU_ dm_msg_request_t *request)
{

    char *params = NULL;
    int params_len = 0;

    if (product_key == NULL || device_name == NULL ||
        (strlen(product_key) >= PRODUCT_KEY_MAXLEN ||
        (strlen(device_name) >= DEVICE_NAME_MAXLEN) ||
        request == NULL) ){
        return INVALID_PARAMETER;
    }

    /* Params */
    request->method = (char *)DM_MSG_COMBINE_LOGOUT_METHOD;
    params_len = strlen(DM_MSG_COMBINE_LOGOUT_PARAMS) + strlen(product_key) + strlen(device_name) + 1;
    params = malloc(params_len);

    if (params == NULL) {
        return MEMORY_NOT_ENOUGH;
    }
    memset(params, 0, params_len);
    HAL_Snprintf(params, params_len, DM_MSG_COMBINE_LOGOUT_PARAMS, product_key, device_name);

    request->params = params;
    request->params_len = strlen(request->params);

    return SUCCESS_RETURN;
 
}

  const char DM_MSG_THING_TOPO_DELETE_METHOD[] DM_READ_ONLY = "thing.topo.delete";
  const char DM_MSG_THING_TOPO_DELETE_PARAMS[] DM_READ_ONLY = "[{\"productKey\":\"%s\",\"deviceName\":\"%s\"}]";
  int dm_msg_thing_topo_delete(_IN_ char product_key[PRODUCT_KEY_MAXLEN],
                               _IN_ char device_name[DEVICE_NAME_MAXLEN],
                               _OU_ dm_msg_request_t *request)
  {
      char *params = NULL;
      int params_len = 0;
  
      if (request == NULL || product_key == NULL ||
          device_name == NULL ||
          (strlen(product_key) >= PRODUCT_KEY_MAXLEN) ||
          (strlen(device_name) >= DEVICE_NAME_MAXLEN) ||
          (strlen(request->product_key) >= PRODUCT_KEY_MAXLEN) ||
          (strlen(request->device_name) >= DEVICE_NAME_MAXLEN)) {
          return INVALID_PARAMETER;
      }
  
      /* Params */
      request->method = (char *)DM_MSG_THING_TOPO_DELETE_METHOD;
      params_len = strlen(DM_MSG_THING_TOPO_DELETE_PARAMS) + strlen(product_key) + strlen(device_name) + 1;
      params = malloc(params_len);
      if (params == NULL) {
          return MEMORY_NOT_ENOUGH;
      }
      memset(params, 0, params_len);
      HAL_Snprintf(params, params_len, DM_MSG_THING_TOPO_DELETE_PARAMS, product_key, device_name);
  
      request->params = params;
      request->params_len = strlen(request->params);
  
      return SUCCESS_RETURN;
  }



int dm_msg_request (_IN_ dm_msg_request_t *request)
{
    int res = 0, payload_len = 0;
    char *payload = NULL, *uri = NULL;
    lite_cjson_t lite;

    if (request == NULL || request->params == NULL || request->method == NULL) {
        return INVALID_PARAMETER;
    }

    /* Request URI */
    res = kk_utils_service_name(request->service_prefix, request->service_name,
                                request->product_key, request->device_name, &uri);
    if (res != SUCCESS_RETURN) {
        return FAIL_RETURN;
    }

    payload_len = strlen(DM_MSG_REQUEST) + 10 + strlen(DM_MSG_VERSION) + request->params_len + strlen(
                              request->method) + 1;
    payload = malloc(payload_len);
    if (payload == NULL) {
        free(uri);
        return MEMORY_NOT_ENOUGH;
    }
    memset(payload, 0, payload_len);
    snprintf(payload, payload_len, DM_MSG_REQUEST, request->msgid,
                 DM_MSG_VERSION, request->params_len, request->params, request->method);

    memset(&lite, 0, sizeof(lite_cjson_t));
    res = lite_cjson_parse(payload, payload_len, &lite);
    if (res < SUCCESS_RETURN) {
        printf("Wrong JSON Format, URI: %s, Payload: %s", uri, payload);
        free(uri);
        free(payload);
        return FAIL_RETURN;
    }

    printf("DM Send Message, URI: %s, Payload: %s \n", uri, payload);

    //if (type & DM_MSG_DEST_CLOUD) {
    // dm_client_publish(uri, (unsigned char *)payload, strlen(payload), request->callback);
     kk_sendData2app(uri, payload);
    //}
    free(uri);
    free(payload);
    return SUCCESS_RETURN;
}


const char DM_MSG_RESPONSE_WITH_DATA[] DM_READ_ONLY = "{\"id\":\"%.*s\",\"code\":%d,\"data\":%.*s}";
int dm_msg_response(_IN_ kk_msg_request_payload_t *request, _IN_ kk_msg_response_t *response,
                    _IN_ char *data, _IN_ int data_len, _IN_ void *user_data)
{
    int res = 0, payload_len = 0;
    char *uri = NULL, *payload = NULL;
    lite_cjson_t lite;

    if (request == NULL || response == NULL || data == NULL || data_len <= 0) {
        return INVALID_PARAMETER;
    }

    /* Response URI */
    res = kk_utils_service_name(response->service_prefix, response->service_name,
                                response->product_key, response->device_name, &uri);
    if (res != SUCCESS_RETURN) {
        return FAIL_RETURN;
    }

    /* Response Payload */
    payload_len = strlen(DM_MSG_RESPONSE_WITH_DATA) + request->id.value_length + DM_UTILS_UINT32_STRLEN + data_len + 1;
    payload = malloc(payload_len);
    if (payload == NULL) {
        free(uri);
        return MEMORY_NOT_ENOUGH;
    }
    memset(payload, 0, payload_len);
    snprintf(payload, payload_len, DM_MSG_RESPONSE_WITH_DATA,
                 request->id.value_length, request->id.value, response->code, data_len, data);

    memset(&lite, 0, sizeof(lite_cjson_t));
    res = lite_cjson_parse(payload, payload_len, &lite);
    if (res < SUCCESS_RETURN) {
        printf("Wrong JSON Format, URI: %s, Payload: %s", uri, payload);
        free(uri);
        free(payload);
        return FAIL_RETURN;
    }

    printf("Send URI: %s, Payload: %s", uri, payload);

    //dm_client_publish(uri, (unsigned char *)payload, strlen(payload), NULL);
    kk_sendData2app(uri, payload);

    free(uri);
    free(payload);

    return SUCCESS_RETURN;
}

int dm_msg_response_parse(_IN_ char *payload, _IN_ int payload_len, _OU_ dm_msg_response_payload_t *response)
{
    lite_cjson_t lite, lite_message;

    if (payload == NULL || payload_len <= 0 || response == NULL) {
        return INVALID_PARAMETER;
    }

    if (kk_utils_json_parse(payload, payload_len, cJSON_Invalid, &lite) != SUCCESS_RETURN ||
        kk_utils_json_object_item(&lite, DM_MSG_KEY_ID, strlen(DM_MSG_KEY_ID), cJSON_Invalid, &response->id) != SUCCESS_RETURN ||
        kk_utils_json_object_item(&lite, DM_MSG_KEY_CODE, strlen(DM_MSG_KEY_CODE), cJSON_Invalid,
                                  &response->code) != SUCCESS_RETURN ||
        kk_utils_json_object_item(&lite, DM_MSG_KEY_DATA, strlen(DM_MSG_KEY_DATA), cJSON_Invalid,
                                  &response->data) != SUCCESS_RETURN) {
         printf("Current Request parse faild \n");                          
        return FAIL_RETURN;
    }

    printf("Current Request Message ID: %.*s", response->id.value_length, response->id.value);
    printf("Current Request Message Code: %d", response->code.value_int);
    printf("Current Request Message Data: %.*s", response->data.value_length, response->data.value);

    memset(&lite_message, 0, sizeof(lite_cjson_t));
    if (kk_utils_json_object_item(&lite, DM_MSG_KEY_MESSAGE, strlen(DM_MSG_KEY_MESSAGE), cJSON_Invalid,
                                  &response->message) == SUCCESS_RETURN) {
        printf("Current Request Message Desc: %.*s", response->message.value_length, response->message.value);
    }

    return SUCCESS_RETURN;
}


