/*
 * Copyright (C) 2015-2018 Alibaba Group Holding Limited
 */

#include "iotx_ota_internal.h"
#include "iot_import_product.h"
#include "iot_export.h"
#include "iot_infra.h"

#if (OTA_SIGNAL_CHANNEL) == 1
    #include "prot/ota_mqtt.c"
#elif (OTA_SIGNAL_CHANNEL) == 2
    #include "prot/ota_coap.c"
#else
    #error "NOT support yet!"
#endif

#define OTA_API_MALLOC(size) LITE_malloc(size, MEM_MAGIC, "ota.api")
#define OTA_API_FREE(ptr)    LITE_free(ptr)

typedef struct  {
    const char *product_key;    /* point to product key */
    const char *device_name;    /* point to device name */

    uint32_t id;                /* message id */
    IOT_OTA_State_t state;      /* OTA state */
    IOT_OTA_Type_t type;        /* OTA Type */
    uint32_t size_last_fetched; /* size of last downloaded */
    uint32_t size_fetched;      /* size of already downloaded */
    uint32_t size_file;         /* size of file */
    char *purl;                 /* point to URL */
    char *version;              /* point to string */
    char md5sum[33];            /* MD5 string */

    void *md5;                  /* MD5 handle */
    void *sha256;               /* Sha256 handle */
    void *ch_signal;            /* channel handle of signal exchanged with OTA server */
    void *ch_fetch;             /* channel handle of download */

    /* cota */
    char *configId;
    uint32_t configSize;
    char *sign;
    char *signMethod;
    char *cota_url;
    char *getType;

    int err;                    /* last error code */

    ota_fetch_cb_fpt  fetch_cb;  /* fetch_callback */
    void  *user_data;            /* fetch_callback's user_data */

    cota_fetch_cb_fpt fetch_cota_cb;
    void  *cota_user_data;

} OTA_Struct_t, *OTA_Struct_pt;


/* check whether the progress state is valid or not */
/* return: true, valid progress state; false, invalid progress state. */
static int ota_check_progress(IOT_OTA_Progress_t progress)
{
    return ((progress >= IOT_OTAP_BURN_FAILED)
            && (progress <= IOT_OTAP_FETCH_PERCENTAGE_MAX));
}


int iotx_ota_set_fetch_callback(void *pt, ota_fetch_cb_fpt fetch_cb, void *user_data)
{
    OTA_Struct_pt ota_pt = (OTA_Struct_pt)pt;

    if (NULL == ota_pt || NULL == fetch_cb) {
        return -1;
    }

    ota_pt->fetch_cb = fetch_cb;
    ota_pt->user_data = user_data;
    return 0;
}


int iotx_ota_set_cota_fetch_callback(void *pt, cota_fetch_cb_fpt fetch_cb, void *user_data)
{
    OTA_Struct_pt ota_pt = (OTA_Struct_pt)pt;

    if (NULL == ota_pt || NULL == fetch_cb) {
        return -1;
    }

    ota_pt->fetch_cota_cb = fetch_cb;
    ota_pt->cota_user_data = user_data;
    return 0;
}

#ifdef DEV_OFFLINE_OTA_ENABLE
static int offline_ota_upgrade_cb(void* pctx, char *json)
{
    /*
    format req's json
    start dl ota_thread_create
    return ota_upgrade_cb(pctx,json);
    send resp
    */

    OTA_Struct_pt h_ota = (OTA_Struct_pt)pctx;
    const char *msg = json;
    uint32_t msg_len = strlen(json);

    const char *pvalue;
    uint32_t val_len;
    int is_ota = 0;
    int ret_offline_ota = DEV_OFFLINE_OTA_RSP_INVALID_PARAM;

    if (h_ota->state == IOT_OTAS_FETCHING) {
        OTA_LOG_INFO("In downloading state");
        return ret_offline_ota;
    }

    #if 0
    pvalue = otalib_JsonValueOf(msg, msg_len, "method", &val_len);
    if (NULL == pvalue) {
        OTA_LOG_ERROR("invalid json doc of OTA ");
        return ret_offline_ota;
    }

    /* check whether is positive message */
    char* method_value = "ota.local.start";
    if (!((strlen(method_value) == val_len) && (0 == strncmp(pvalue, method_value, val_len)))) {
        OTA_LOG_ERROR("fail state of json doc of OTA");
        return ret_offline_ota;
    }
    #endif

    /* get value of 'params' key */
    pvalue = otalib_JsonValueOf(msg, msg_len, "params", &val_len);
    if (NULL == pvalue) {
        OTA_LOG_ERROR("Not 'params' key in json doc of OTA");
        return ret_offline_ota;
    }

#ifdef DEV_OFFLINE_SECURE_OTA_ENABLE
    /*OTA sever must open secure OTA option*/
    pvalue = otalib_JsonValueOf(msg, msg_len, "digestsign", &val_len);
    if (NULL == pvalue) {
        OTA_LOG_ERROR("Not 'digestsign' key in json doc of OTA");
        return ret_offline_ota;
    }
#endif

    if (0 != otalib_GetParams(pvalue, val_len, &h_ota->purl, &h_ota->version, h_ota->md5sum, &h_ota->size_file)) {
        OTA_LOG_ERROR("Get config parameter failed");
        return ret_offline_ota;
    }

    if (NULL == (h_ota->ch_fetch = ofc_Init(h_ota->purl))) {
        OTA_LOG_ERROR("Initialize fetch module failed");
        return ret_offline_ota;
    }

    char version[FIRMWARE_VERSION_MAXLEN] = {0};
    HAL_GetFirmwareVersion(version);

    is_ota = strncmp(h_ota->version,version,strlen(h_ota->version));
    if(is_ota > 0) {
        h_ota->type = IOT_OTAT_FOTA_OFFLINE;
        h_ota->state = IOT_OTAS_FETCHING;
        ret_offline_ota = DEV_OFFLINE_OTA_RSP_OK;

        if (h_ota->fetch_cb) {
            h_ota->fetch_cb(h_ota->user_data, 0, h_ota->size_file, h_ota->purl, h_ota->version);
        }
    } else {
        ret_offline_ota = DEV_OFFLINE_OTA_RSP_SAME_VERSION;
    }
    return ret_offline_ota;
}
#endif

static int ota_callback(void *pcontext, const char *msg, uint32_t msg_len, iotx_ota_topic_types_t type)
{
    const char *pvalue;
    uint32_t val_len;

    OTA_Struct_pt h_ota = (OTA_Struct_pt) pcontext;

    if (h_ota->state == IOT_OTAS_FETCHING) {
        OTA_LOG_INFO("In downloading state");
        return -1;
    }

    switch (type) {
        case IOTX_OTA_TOPIC_TYPE_DEVICE_REQUEST:
        case IOTX_OTA_TOPIC_TYPE_DEVICE_UPGRATE: {
            pvalue = otalib_JsonValueOf(msg, msg_len, "message", &val_len);
            if (NULL == pvalue) {
                OTA_LOG_ERROR("invalid json doc of OTA ");
                return -1;
            }

            /* check whether is positive message */
            if (!((strlen("success") == val_len) && (0 == strncmp(pvalue, "success", val_len)))) {
                OTA_LOG_ERROR("fail state of json doc of OTA");
                return -1;
            }

            /* get value of 'data' key */
            pvalue = otalib_JsonValueOf(msg, msg_len, "data", &val_len);
            if (NULL == pvalue) {
                OTA_LOG_ERROR("Not 'data' key in json doc of OTA");
                return -1;
            }

            if (0 != otalib_GetParams(pvalue, val_len, &h_ota->purl, &h_ota->version, h_ota->md5sum, &h_ota->size_file)) {
                OTA_LOG_ERROR("Get config parameter failed");
                return -1;
            }

            if (NULL == (h_ota->ch_fetch = ofc_Init(h_ota->purl))) {
                OTA_LOG_ERROR("Initialize fetch module failed");
                return -1;
            }

            h_ota->type = IOT_OTAT_FOTA;
            h_ota->state = IOT_OTAS_FETCHING;

            if (h_ota->fetch_cb) {
                h_ota->fetch_cb(h_ota->user_data, 0, h_ota->size_file, h_ota->purl, h_ota->version);
            }
        }
        break;

        case IOTX_OTA_TOPIC_TYPE_CONFIG_GET: {
            pvalue = otalib_JsonValueOf(msg, msg_len, "code", &val_len);
            if (NULL == pvalue) {
                OTA_LOG_ERROR("invalid json doc of OTA ");
                return -1;
            }

            /* check whether is positive message */
            if (!((strlen("200") == val_len) && (0 == strncmp(pvalue, "200", val_len)))) {
                OTA_LOG_ERROR("fail state of json doc of OTA");
                return -1;
            }

            /* get value of 'data' key */
            pvalue = otalib_JsonValueOf(msg, msg_len, "data", &val_len);
            if (NULL == pvalue) {
                OTA_LOG_ERROR("Not 'data' key in json doc of OTA");
                return -1;
            }

            if (0 != otalib_GetConfigParams(pvalue, val_len, &h_ota->configId, &h_ota->configSize,
                                            &h_ota->sign, &h_ota->signMethod, &h_ota->cota_url, &h_ota->getType))  {
                OTA_LOG_ERROR("Get firmware parameter failed");
                return -1;
            }

            h_ota->size_file = h_ota->configSize;
            h_ota->size_fetched = 0;
            if (NULL != h_ota->md5) {
                otalib_MD5Deinit(h_ota->md5);
            }
            h_ota->md5 = otalib_MD5Init();

            if (NULL != h_ota->sha256) {
                otalib_Sha256Deinit(h_ota->sha256);
            }
            h_ota->sha256 = otalib_Sha256Init();

            if (NULL == (h_ota->ch_fetch = ofc_Init(h_ota->cota_url))) {
                OTA_LOG_ERROR("Initialize fetch module failed");
                return -1;
            }

            h_ota->type = IOT_OTAT_COTA;
            h_ota->state = IOT_OTAS_FETCHING;

            if (h_ota->fetch_cota_cb) {
                h_ota->fetch_cota_cb(h_ota->user_data, 0, h_ota->configId, h_ota->configSize, h_ota->sign, h_ota->signMethod,
                                     h_ota->cota_url, h_ota->getType);
            }
        }
        break;

        case IOTX_OTA_TOPIC_TYPE_CONFIG_PUSH: {
            /* get value of 'params' key */
            pvalue = otalib_JsonValueOf(msg, msg_len, "params", &val_len);
            if (NULL == pvalue) {
                OTA_LOG_ERROR("Not 'data' key in json doc of OTA");
                return -1;
            }

            if (0 != otalib_GetConfigParams(pvalue, val_len, &h_ota->configId, &h_ota->configSize,
                                            &h_ota->sign, &h_ota->signMethod, &h_ota->cota_url, &h_ota->getType)) {
                OTA_LOG_ERROR("Get firmware parameter failed");
                return -1;
            }

            h_ota->size_file = h_ota->configSize;
            h_ota->size_fetched = 0;
            if (NULL != h_ota->md5) {
                otalib_MD5Deinit(h_ota->md5);
            }
            h_ota->md5 = otalib_MD5Init();

            if (NULL != h_ota->sha256) {
                otalib_Sha256Deinit(h_ota->sha256);
            }
            h_ota->sha256 = otalib_Sha256Init();

            if (NULL == (h_ota->ch_fetch = ofc_Init(h_ota->cota_url))) {
                OTA_LOG_ERROR("Initialize fetch module failed");
                return -1;
            }

            h_ota->type = IOT_OTAT_COTA;
            h_ota->state = IOT_OTAS_FETCHING;

            if (h_ota->fetch_cota_cb) {
                h_ota->fetch_cota_cb(h_ota->user_data, 0, h_ota->configId, h_ota->configSize, h_ota->sign, h_ota->signMethod,
                                     h_ota->cota_url, h_ota->getType);
            }
        }
        break;

        default:
            return -1;
            break;
    }

    return 0;
}

static int g_ota_is_initialized = 0;

/* Initialize OTA module */
void *IOT_OTA_Init(const char *product_key, const char *device_name, void *ch_signal)
{
    OTA_Struct_pt h_ota = NULL;

    if (1 == g_ota_is_initialized) {
        OTA_LOG_ERROR("iot ota has been initialized");
        return NULL;
    }

    if ((NULL == product_key) || (NULL == device_name)) {
        OTA_LOG_ERROR("one or more parameters is invalid");
        return NULL;
    }

    if (NULL == (h_ota = OTA_MALLOC(sizeof(OTA_Struct_t)))) {
        OTA_LOG_ERROR("allocate failed");
        return NULL;
    }
    memset(h_ota, 0, sizeof(OTA_Struct_t));
    h_ota->type = IOT_OTAT_NONE;
    h_ota->state = IOT_OTAS_UNINITED;

    h_ota->ch_signal = osc_Init(product_key, device_name, ch_signal, ota_callback, h_ota);
    if (NULL == h_ota->ch_signal) {
        OTA_LOG_ERROR("initialize signal channel failed");
        goto do_exit;
    }

#ifdef DEV_OFFLINE_OTA_ENABLE
    dev_offline_ota_module_init(h_ota, offline_ota_upgrade_cb);
#endif

    h_ota->md5 = otalib_MD5Init();
    if (NULL == h_ota->md5) {
        OTA_LOG_ERROR("initialize md5 failed");
        goto do_exit;
    }
    h_ota->sha256 = otalib_Sha256Init();
    if (NULL == h_ota->sha256) {
        OTA_LOG_ERROR("initialize sha256 failed");
        goto do_exit;
    }

    h_ota->product_key = product_key;
    h_ota->device_name = device_name;
    h_ota->state = IOT_OTAS_INITED;
    g_ota_is_initialized = 1;
    return h_ota;

do_exit:

    if (NULL != h_ota->ch_signal) {
        osc_Deinit(h_ota->ch_signal);
    }

    if (NULL != h_ota->md5) {
        otalib_MD5Deinit(h_ota->md5);
    }

    if (NULL != h_ota->sha256) {
        otalib_Sha256Deinit(h_ota->sha256);
    }

    if (NULL != h_ota) {
        OTA_FREE(h_ota);
    }

    return NULL;

#undef AOM_INFO_MSG_LEN
}


/* deinitialize OTA module */
int IOT_OTA_Deinit(void *handle)
{
    OTA_Struct_pt h_ota = (OTA_Struct_pt) handle;

    if (NULL == h_ota) {
        OTA_LOG_ERROR("handle is NULL");
        return IOT_OTAE_INVALID_PARAM;
    }

    if (IOT_OTAS_UNINITED == h_ota->state) {
        OTA_LOG_ERROR("handle is uninitialized");
        h_ota->err = IOT_OTAE_INVALID_STATE;
        return -1;
    }

    g_ota_is_initialized = 0;

    if (NULL != h_ota->ch_signal) {
        osc_Deinit(h_ota->ch_signal);
    }

    if (NULL != h_ota->ch_fetch) {
        ofc_Deinit(h_ota->ch_fetch);
    }

    if (NULL != h_ota->md5) {
        otalib_MD5Deinit(h_ota->md5);
    }

    if (NULL != h_ota->sha256) {
        otalib_Sha256Deinit(h_ota->sha256);
    }

    if (NULL != h_ota->purl) {
        OTA_FREE(h_ota->purl);
    }

    if (NULL != h_ota->version) {
        OTA_FREE(h_ota->version);
    }

    if (NULL != h_ota->configId) {
        OTA_FREE(h_ota->configId);
    }

    if (NULL != h_ota->sign) {
        OTA_FREE(h_ota->sign);
    }

    if (NULL != h_ota->signMethod) {
        OTA_FREE(h_ota->signMethod);
    }

    if (NULL != h_ota->cota_url) {
        OTA_FREE(h_ota->cota_url);
    }

    if (NULL != h_ota->getType) {
        OTA_FREE(h_ota->getType);
    }

    OTA_FREE(h_ota);
    return 0;
}


#define OTA_VERSION_STR_LEN_MIN     (1)
#define OTA_VERSION_STR_LEN_MAX     (32)

int IOT_OTA_ReportVersion(void *handle, const char *version)
{
#define MSG_INFORM_LEN  (128)

    int ret, len;
    char *msg_informed;
    OTA_Struct_pt h_ota = (OTA_Struct_pt) handle;

    if ((NULL == h_ota) || (NULL == version)) {
        OTA_LOG_ERROR("one or more invalid parameter");
        return IOT_OTAE_INVALID_PARAM;
    }

    len = strlen(version);
    if ((len < OTA_VERSION_STR_LEN_MIN) || (len > OTA_VERSION_STR_LEN_MAX)) {
        OTA_LOG_ERROR("version string is invalid: must be [1, 32] chars");
        h_ota->err = IOT_OTAE_INVALID_PARAM;
        return -1;
    }

    if (IOT_OTAS_UNINITED == h_ota->state) {
        OTA_LOG_ERROR("handle is uninitialized");
        h_ota->err = IOT_OTAE_INVALID_STATE;
        return -1;
    }

    if (NULL == (msg_informed = OTA_MALLOC(MSG_INFORM_LEN))) {
        OTA_LOG_ERROR("allocate for msg_informed failed");
        h_ota->err = IOT_OTAE_NOMEM;
        return -1;
    }

    ret = otalib_GenInfoMsg(msg_informed, MSG_INFORM_LEN, h_ota->id, version);
    if (ret != 0) {
        OTA_LOG_ERROR("generate inform message failed");
        h_ota->err = ret;
        ret = -1;
        goto do_exit;
    }

    ret = osc_ReportVersion(h_ota->ch_signal, msg_informed);
    if (0 != ret) {
        OTA_LOG_ERROR("Report version failed");
        h_ota->err = ret;
        ret = -1;
        goto do_exit;
    }
    ret = 0;

do_exit:
    if (NULL != msg_informed) {
        OTA_FREE(msg_informed);
    }
    return ret;

#undef MSG_INFORM_LEN
}

int iotx_req_image(void *handle, const char *version)
{
#define MSG_REQUEST_LEN  (128)

    int ret, len;
    char *msg_informed;
    OTA_Struct_pt h_ota = (OTA_Struct_pt) handle;

    if ((NULL == h_ota) || (NULL == version)) {
        OTA_LOG_ERROR("one or more invalid parameter");
        return IOT_OTAE_INVALID_PARAM;
    }

    len = strlen(version);
    if ((len < OTA_VERSION_STR_LEN_MIN) || (len > OTA_VERSION_STR_LEN_MAX)) {
        OTA_LOG_ERROR("version string is invalid: must be [1, 32] chars");
        h_ota->err = IOT_OTAE_INVALID_PARAM;
        return -1;
    }

    if (IOT_OTAS_UNINITED == h_ota->state) {
        OTA_LOG_ERROR("handle is uninitialized");
        h_ota->err = IOT_OTAE_INVALID_STATE;
        return -1;
    }

    if (IOT_OTAS_FETCHING == h_ota->state) {
        OTA_LOG_ERROR("ota is busying");
        h_ota->err = IOT_OTAE_INVALID_STATE;
        return -1;
    }

    if (NULL == (msg_informed = OTA_MALLOC(MSG_REQUEST_LEN))) {
        OTA_LOG_ERROR("allocate for msg_informed failed");
        h_ota->err = IOT_OTAE_NOMEM;
        return -1;
    }

    ret = otalib_GenInfoMsg(msg_informed, MSG_REQUEST_LEN, h_ota->id, version);
    if (ret != 0) {
        OTA_LOG_ERROR("generate request image message failed");
        h_ota->err = ret;
        ret = -1;
        goto do_exit;
    }

    ret = osc_RequestImage(h_ota->ch_signal, msg_informed);
    if (0 != ret) {
        OTA_LOG_ERROR("Request image failed");
        h_ota->err = ret;
        ret = -1;
        goto do_exit;
    }
    ret = 0;

do_exit:
    if (NULL != msg_informed) {
        OTA_FREE(msg_informed);
    }
    return ret;

#undef MSG_REQUEST_LEN
}


int IOT_OTA_ReportProgress(void *handle, IOT_OTA_Progress_t progress, const char *msg)
{
#define MSG_REPORT_LEN  (256)

    int ret = -1;
    char *msg_reported;
    OTA_Struct_pt h_ota = (OTA_Struct_pt) handle;

    if (NULL == handle) {
        OTA_LOG_ERROR("handle is NULL");
        return IOT_OTAE_INVALID_PARAM;
    }

    if (IOT_OTAS_UNINITED == h_ota->state) {
        OTA_LOG_ERROR("handle is uninitialized");
        h_ota->err = IOT_OTAE_INVALID_STATE;
        return -1;
    }
    if (!ota_check_progress(progress)) {
        OTA_LOG_ERROR("progress is a invalid parameter");
        h_ota->err = IOT_OTAE_INVALID_PARAM;
        return -1;
    }

#ifdef DEV_OFFLINE_OTA_ENABLE
    if (progress == IOT_OTAP_FETCH_FAILED) {
        if(h_ota->type == IOT_OTAT_FOTA_OFFLINE) {
            /*notify 10 times to assure APP to receive successfully */
            uint8_t i = 0;
            for(; i < 10; i++) {
                dev_notify_offline_ota_result(DEV_OFFLINE_OTA_RSP_DOWNLOAD_FAILED);
                HAL_SleepMs(300);
            }
        }
    }
#endif
    if (NULL == (msg_reported = OTA_MALLOC(MSG_REPORT_LEN))) {
        OTA_LOG_ERROR("allocate for msg_reported failed");
        h_ota->err = IOT_OTAE_NOMEM;
        return -1;
    }

    ret = otalib_GenReportMsg(msg_reported, MSG_REPORT_LEN, h_ota->id, progress, msg);
    if (0 != ret) {
        OTA_LOG_ERROR("generate reported message failed");
        h_ota->err = ret;
        goto do_exit;
    }

    ret = osc_ReportProgress(h_ota->ch_signal, msg_reported);
    if (0 != ret) {
        OTA_LOG_ERROR("Report progress failed");
        h_ota->err = ret;
        goto do_exit;
    }

    ret = 0;

do_exit:
    if (NULL != msg_reported) {
        OTA_FREE(msg_reported);
    }
    return ret;

#undef MSG_REPORT_LEN
}

int iotx_ota_get_config(void *handle, const char *configScope, const char *getType, const char *attributeKeys)
{
#define MSG_REPORT_LEN  (256)

    int ret = -1;
    char *msg_get;
    char topic[OTA_MQTT_TOPIC_LEN] = {0};
    OTA_Struct_pt h_ota = (OTA_Struct_pt) handle;
    iotx_mqtt_topic_info_t topic_info;

    memset(&topic_info, 0, sizeof(iotx_mqtt_topic_info_t));

    if (NULL == handle) {
        OTA_LOG_ERROR("handle is NULL");
        return IOT_OTAE_INVALID_PARAM;
    }

    if (IOT_OTAS_UNINITED == h_ota->state) {
        OTA_LOG_ERROR("handle is uninitialized");
        h_ota->err = IOT_OTAE_INVALID_STATE;
        return -1;
    }

    if (IOT_OTAS_FETCHING == h_ota->state) {
        OTA_LOG_ERROR("ota is busying");
        h_ota->err = IOT_OTAE_INVALID_STATE;
        return -1;
    }

    if (NULL == (msg_get = OTA_MALLOC(MSG_REPORT_LEN))) {
        OTA_LOG_ERROR("allocate for msg_reported failed");
        h_ota->err = IOT_OTAE_NOMEM;
        return -1;
    }

    if (0 > HAL_Snprintf(topic,
                         OTA_MQTT_TOPIC_LEN,
                         "/sys/%s/%s/thing/config/get",
                         h_ota->product_key,
                         h_ota->device_name)) {
        goto do_exit;
    };

    if (0 > HAL_Snprintf(msg_get,
                         MSG_REPORT_LEN,
                         "{\"id\" : %d,\"version\":\"1.0\",\"params\":{\"configScope\":\"%s\",\"getType\":\"%s\",\"attributeKeys\":\"%s\"},\"method\":\"thing.config.get\"}",
                         h_ota->id,
                         configScope,
                         getType,
                         attributeKeys)) {
        goto do_exit;
    };
    OTA_LOG_INFO(msg_get);
    topic_info.qos = IOTX_MQTT_QOS0;
    topic_info.payload = (void *)msg_get;
    topic_info.payload_len = strlen(msg_get);

    ret = osc_RequestConfig(h_ota->ch_signal, topic, &topic_info);
    if (ret < 0) {
        OTA_LOG_ERROR("publish failed");
        return IOT_OTAE_OSC_FAILED;
    }

    ret = 0;

do_exit:
    if (NULL != msg_get) {
        OTA_FREE(msg_get);
    }
    return ret;

#undef MSG_REPORT_LEN
}


/* check whether is downloading */
int IOT_OTA_IsFetching(void *handle)
{
    OTA_Struct_pt h_ota = (OTA_Struct_pt)handle;

    if (NULL == handle) {
        OTA_LOG_ERROR("handle is NULL");
        return 0;
    }

    if (IOT_OTAS_UNINITED == h_ota->state) {
        OTA_LOG_ERROR("handle is uninitialized");
        h_ota->err = IOT_OTAE_INVALID_STATE;
        return 0;
    }

    return (IOT_OTAS_FETCHING == h_ota->state);
}


/* check whether fetch over */
int IOT_OTA_IsFetchFinish(void *handle)
{
    OTA_Struct_pt h_ota = (OTA_Struct_pt) handle;

    if (NULL == handle) {
        OTA_LOG_ERROR("handle is NULL");
        return 0;
    }

    if (IOT_OTAS_UNINITED == h_ota->state) {
        OTA_LOG_ERROR("handle is uninitialized");
        h_ota->err = IOT_OTAE_INVALID_STATE;
        return 0;
    }

    return (IOT_OTAS_FETCHED == h_ota->state);
}


int IOT_OTA_FetchYield(void *handle, char *buf, uint32_t buf_len, uint32_t timeout_s)
{
    int ret;
    OTA_Struct_pt h_ota = (OTA_Struct_pt) handle;

    if ((NULL == handle) || (NULL == buf) || (0 == buf_len)) {
        OTA_LOG_ERROR("invalid parameter");
        return IOT_OTAE_INVALID_PARAM;
    }

    if (IOT_OTAS_FETCHING != h_ota->state) {
        h_ota->err = IOT_OTAE_INVALID_STATE;
        return IOT_OTAE_INVALID_STATE;
    }

    ret = ofc_Fetch(h_ota->ch_fetch, buf, buf_len, timeout_s);
    if (ret < 0) {
        OTA_LOG_ERROR("Fetch firmware failed");
        h_ota->state = IOT_OTAS_FETCHED;
        h_ota->type = IOT_OTAT_NONE;
        h_ota->err = IOT_OTAE_FETCH_FAILED;

        if (h_ota->fetch_cb && h_ota->purl) {
            h_ota->fetch_cb(h_ota->user_data, 1, h_ota->size_file, h_ota->purl, h_ota->version);
            /* remove */
            h_ota->purl = NULL;
        } else if (h_ota->fetch_cota_cb && h_ota->cota_url) {
            h_ota->fetch_cota_cb(h_ota->user_data, 1, h_ota->configId, h_ota->configSize, h_ota->sign, h_ota->signMethod,
                                 h_ota->cota_url, h_ota->getType);
            /* remove */
            h_ota->cota_url = NULL;
        }
        h_ota->size_fetched = 0;
        return -1;
    } else if (0 == h_ota->size_fetched) {
        /* force report status in the first */
        IOT_OTA_ReportProgress(h_ota, IOT_OTAP_FETCH_PERCENTAGE_MIN, "Enter in downloading state");
    }

    otalib_MD5Update(h_ota->md5, buf, ret);
    otalib_Sha256Update(h_ota->sha256, buf, ret);
    h_ota->size_last_fetched = ret;
    h_ota->size_fetched += ret;

    if (h_ota->size_fetched >= h_ota->size_file) {
        h_ota->type = IOT_OTAT_NONE;
        h_ota->state = IOT_OTAS_FETCHED;
        if (h_ota->fetch_cb && h_ota->purl) {
            h_ota->fetch_cb(h_ota->user_data, 1, h_ota->size_file, h_ota->purl, h_ota->version);
            /* remove */
            h_ota->purl = NULL;
        } else if (h_ota->fetch_cota_cb && h_ota->cota_url) {
            h_ota->fetch_cota_cb(h_ota->user_data, 1, h_ota->configId, h_ota->configSize, h_ota->sign, h_ota->signMethod,
                                 h_ota->cota_url, h_ota->getType);
            /* remove */
            h_ota->cota_url = NULL;
        }
    }

    return ret;
}


int IOT_OTA_Ioctl(void *handle, IOT_OTA_CmdType_t type, void *buf, size_t buf_len)
{
    OTA_Struct_pt h_ota = (OTA_Struct_pt) handle;

    if ((NULL == handle) || (NULL == buf) || (0 == buf_len)) {
        OTA_LOG_ERROR("invalid parameter");
        return IOT_OTAE_INVALID_PARAM;
    }

    if (h_ota->state < IOT_OTAS_FETCHING) {
        h_ota->err = IOT_OTAE_INVALID_STATE;
        return IOT_OTAE_INVALID_STATE;
    }

    switch (type) {
        case IOT_OTAG_COTA_CONFIG_ID: {
            char **value = (char **)buf;
            if (value == NULL || *value != NULL || h_ota->configId == NULL) {
                OTA_LOG_ERROR("Invalid parameter");
                h_ota->err = IOT_OTAE_INVALID_PARAM;
                return -1;
            } else {
                *value = OTA_API_MALLOC(strlen(h_ota->configId) + 1);
                if (*value == NULL) {
                    h_ota->err = IOT_OTAE_INVALID_PARAM;
                    return -1;
                }
                memset(*value, 0, strlen(h_ota->configId) + 1);
                memcpy(*value, h_ota->configId, strlen(h_ota->configId));
                return 0;
            }
        }
        break;
        case IOT_OTAG_COTA_CONFIG_SIZE: {
            if ((4 != buf_len) || (0 != ((unsigned long)buf & 0x3))) {
                OTA_LOG_ERROR("Invalid parameter");
                h_ota->err = IOT_OTAE_INVALID_PARAM;
                return -1;
            } else {
                *((uint32_t *)buf) = h_ota->configSize;
                return 0;
            }
        }
        break;
        case IOT_OTAG_COTA_SIGN: {
            char **value = (char **)buf;
            if (value == NULL || *value != NULL || h_ota->sign == NULL) {
                OTA_LOG_ERROR("Invalid parameter");
                h_ota->err = IOT_OTAE_INVALID_PARAM;
                return -1;
            } else {
                *value = OTA_API_MALLOC(strlen(h_ota->sign) + 1);
                if (*value == NULL) {
                    h_ota->err = IOT_OTAE_INVALID_PARAM;
                    return -1;
                }
                memset(*value, 0, strlen(h_ota->sign) + 1);
                memcpy(*value, h_ota->sign, strlen(h_ota->sign));
                return 0;
            }
        }
        break;
        case IOT_OTAG_COTA_SIGN_METHOD: {
            char **value = (char **)buf;
            if (value == NULL || *value != NULL || h_ota->signMethod == NULL) {
                OTA_LOG_ERROR("Invalid parameter");
                h_ota->err = IOT_OTAE_INVALID_PARAM;
                return -1;
            } else {
                *value = OTA_API_MALLOC(strlen(h_ota->signMethod) + 1);
                if (*value == NULL) {
                    h_ota->err = IOT_OTAE_INVALID_PARAM;
                    return -1;
                }
                memset(*value, 0, strlen(h_ota->signMethod) + 1);
                memcpy(*value, h_ota->signMethod, strlen(h_ota->signMethod));
                return 0;
            }
        }
        break;
        case IOT_OTAG_COTA_URL: {
            char **value = (char **)buf;
            if (value == NULL || *value != NULL || h_ota->cota_url == NULL) {
                OTA_LOG_ERROR("Invalid parameter");
                h_ota->err = IOT_OTAE_INVALID_PARAM;
                return -1;
            } else {
                *value = OTA_API_MALLOC(strlen(h_ota->cota_url) + 1);
                if (*value == NULL) {
                    h_ota->err = IOT_OTAE_INVALID_PARAM;
                    return -1;
                }
                memset(*value, 0, strlen(h_ota->cota_url) + 1);
                memcpy(*value, h_ota->cota_url, strlen(h_ota->cota_url));
                return 0;
            }
        }
        break;
        case IOT_OTAG_COTA_GETTYPE: {
            char **value = (char **)buf;
            if (value == NULL || *value != NULL || h_ota->getType == NULL) {
                OTA_LOG_ERROR("Invalid parameter");
                h_ota->err = IOT_OTAE_INVALID_PARAM;
                return -1;
            } else {
                *value = OTA_API_MALLOC(strlen(h_ota->getType) + 1);
                if (*value == NULL) {
                    h_ota->err = IOT_OTAE_INVALID_PARAM;
                    return -1;
                }
                memset(*value, 0, strlen(h_ota->getType) + 1);
                memcpy(*value, h_ota->getType, strlen(h_ota->getType));
                return 0;
            }
        }
        break;
        case IOT_OTAG_OTA_TYPE: {
            if ((4 != buf_len) || (0 != ((unsigned long)buf & 0x3))) {
                OTA_LOG_ERROR("Invalid parameter");
                h_ota->err = IOT_OTAE_INVALID_PARAM;
                return -1;
            } else {
                *((uint32_t *)buf) = h_ota->type;
                return 0;
            }
        }
        break;
        case IOT_OTAG_FETCHED_SIZE:
            if ((4 != buf_len) || (0 != ((unsigned long)buf & 0x3))) {
                OTA_LOG_ERROR("Invalid parameter");
                h_ota->err = IOT_OTAE_INVALID_PARAM;
                return -1;
            } else {
                *((uint32_t *)buf) = h_ota->size_fetched;
                return 0;
            }

        case IOT_OTAG_FILE_SIZE:
            if ((4 != buf_len) || (0 != ((unsigned long)buf & 0x3))) {
                OTA_LOG_ERROR("Invalid parameter");
                h_ota->err = IOT_OTAE_INVALID_PARAM;
                return -1;
            } else {
                *((uint32_t *)buf) = h_ota->size_file;
                return 0;
            };

        case IOT_OTAG_VERSION: {
            strncpy(buf, h_ota->version, buf_len);
            ((char *)buf)[buf_len - 1] = '\0';
        }
        break;

        case IOT_OTAG_MD5SUM:
            strncpy(buf, h_ota->md5sum, buf_len);
            ((char *)buf)[buf_len - 1] = '\0';
            break;

        case IOT_OTAG_CHECK_FIRMWARE:
            if ((4 != buf_len) || (0 != ((unsigned long)buf & 0x3))) {
                OTA_LOG_ERROR("Invalid parameter");
                h_ota->err = IOT_OTAE_INVALID_PARAM;
                return -1;
            } else if (h_ota->state != IOT_OTAS_FETCHED) {
                h_ota->err = IOT_OTAE_INVALID_STATE;
                OTA_LOG_ERROR("Firmware can be checked in IOT_OTAS_FETCHED state only");
                return -1;
            } else {
                int offline_ota_resp_code = DEV_OFFLINE_OTA_RSP_OK;
                char md5_str[33];
                otalib_MD5Finalize(h_ota->md5, md5_str);
                OTA_LOG_DEBUG("origin=%s, now=%s", h_ota->md5sum, md5_str);
                if (0 == strcmp(h_ota->md5sum, md5_str)) {
                    *((uint32_t *)buf) = 1;
                    offline_ota_resp_code = DEV_OFFLINE_OTA_RSP_OK;
                } else {
                    *((uint32_t *)buf) = 0;
                    offline_ota_resp_code = DEV_OFFLINE_OTA_RSP_VERIFY_FAILED;
                }
#ifdef DEV_OFFLINE_OTA_ENABLE
                if(h_ota->type == IOT_OTAT_FOTA_OFFLINE) {
                    /*notify 10 times to assure APP to receive successfully */
                    uint8_t i = 0;
                    for(; i < 10; i++) {
                        dev_notify_offline_ota_result(offline_ota_resp_code);
                        HAL_SleepMs(300);
                    }
                }
#else
                (void)offline_ota_resp_code;
#endif
                return 0;
            }
        case IOT_OTAG_CHECK_CONFIG:
            if ((4 != buf_len) || (0 != ((unsigned long)buf & 0x3))) {
                OTA_LOG_ERROR("Invalid parameter");
                h_ota->err = IOT_OTAE_INVALID_PARAM;
                return -1;
            } else if (h_ota->state != IOT_OTAS_FETCHED) {
                h_ota->err = IOT_OTAE_INVALID_STATE;
                OTA_LOG_ERROR("Config can be checked in IOT_OTAS_FETCHED state only");
                return -1;
            } else {
                if (0 == strncmp(h_ota->signMethod, "Md5", strlen(h_ota->signMethod))) {
                    char md5_str[33];
                    otalib_MD5Finalize(h_ota->md5, md5_str);
                    OTA_LOG_DEBUG("origin=%s, now=%s", h_ota->sign, md5_str);
                    if (0 == strcmp(h_ota->sign, md5_str)) {
                        *((uint32_t *)buf) = 1;
                    } else {
                        *((uint32_t *)buf) = 0;
                    }
                }
                if (0 == strncmp(h_ota->signMethod, "Sha256", strlen(h_ota->signMethod))) {
                    char sha256_str[65];
                    otalib_Sha256Finalize(h_ota->sha256, sha256_str);
                    OTA_LOG_DEBUG("origin=%s, now=%s", h_ota->sign, sha256_str);
                    if (0 == strcmp(h_ota->sign, sha256_str)) {
                        *((uint32_t *)buf) = 1;
                    } else {
                        *((uint32_t *)buf) = 0;
                    }
                }
                return 0;
            }
        case IOT_OTAG_RESET_FETCHED_SIZE: {
            h_ota->size_fetched = 0;
            return 0;
        }
        default:
            OTA_LOG_ERROR("invalid cmd type");
            h_ota->err = IOT_OTAE_INVALID_PARAM;
            return -1;
    }

    return 0;
}


/* Get last error code */
int IOT_OTA_GetLastError(void *handle)
{
    OTA_Struct_pt h_ota = (OTA_Struct_pt) handle;

    if (NULL == handle) {
        OTA_LOG_ERROR("handle is NULL");
        return  IOT_OTAE_INVALID_PARAM;
    }

    return h_ota->err;
}
