/*
 * 
 */
#include "iotx_ota_internal.h"
#include "dm_ota.h"
#include "cJSON.h"
#include "kk_dm_api.h"
#include "kk_dm_msg.h"
#include <curl/curl.h>
#include "com_api.h"
#include "kk_log.h"

static dm_ota_ctx_t g_dm_ota_ctx;

static dm_ota_ctx_t *_dm_ota_get_ctx(void)
{
	return &g_dm_ota_ctx;
}

int dm_ota_init(void)
{
	dm_ota_ctx_t *ctx = _dm_ota_get_ctx();
	memset(ctx, 0, sizeof(dm_ota_ctx_t));

	HAL_GetProduct_Code(ctx->deviceCode);

	return SUCCESS_RETURN;
}

int dm_ota_sub(void)
{
	dm_ota_ctx_t *ctx = _dm_ota_get_ctx();
	void *handle = NULL;

	/* Init OTA Handle */
	handle = IOT_OTA_Init(ctx->productType, ctx->deviceCode, NULL);
	if (handle == NULL) {
		return FAIL_RETURN;
	}

	ctx->ota_handle = handle;

	return SUCCESS_RETURN;
}

int ota_uri_parse_pkdn(_IN_ char *uri, _IN_ int uri_len, _IN_ int start_deli, _IN_ int end_deli,
	_OU_ char productType[PRODUCT_TYPE_MAXLEN], _OU_ char deviceCode[DEVICE_CODE_MAXLEN])
{
    int start = 0, end = 0, slice = 0;
    int item_index = 0;
    int count = 0;

    if (uri == NULL || uri_len <= 0 || productType == NULL || deviceCode == NULL ||
        (strlen(productType) >= PRODUCT_TYPE_MAXLEN) || (strlen(deviceCode) >= DEVICE_CODE_MAXLEN)) {
        return INVALID_PARAMETER;
    }


    for (item_index = 0; item_index < uri_len; item_index++) {
        if (uri[item_index] == '/' && (item_index + 1) < uri_len) {
            count++;
            if (count == start_deli) {
                start = item_index;
            }else if (count == start_deli + 1){
                slice = item_index;
            }else if (count == end_deli){
                end = item_index;
            }
        }
    }

    if (end == 0){
        end = item_index;
    }
    
    /* dm_log_debug("URI Product Key: %.*s, Device Name: %.*s", slice - start - 1, uri + start + 1, end - slice - 1,
                 uri + slice + 1); */

    memcpy(productType, uri + start + 1, slice - start - 1);
    memcpy(deviceCode, uri + slice + 1, end - slice - 1);

	return SUCCESS_RETURN;
}


int dm_ota_setPKN(char productType[PRODUCT_TYPE_MAXLEN],char deviceCode[DEVICE_CODE_MAXLEN])
{
    dm_ota_ctx_t *ctx = _dm_ota_get_ctx();
    //int res = 0;                     
    memset(ctx->productType, 0, PRODUCT_TYPE_MAXLEN);
    memset(ctx->deviceCode, 0, DEVICE_CODE_MAXLEN);
    memcpy(ctx->productType, productType, PRODUCT_TYPE_MAXLEN);
    memcpy(ctx->deviceCode, deviceCode, DEVICE_CODE_MAXLEN);

    return SUCCESS_RETURN;
}


int dm_ota_deinit(void)
{
	dm_ota_ctx_t *ctx = _dm_ota_get_ctx();

	if (ctx->ota_handle) {
		IOT_OTA_Deinit(ctx->ota_handle);
		ctx->ota_handle = NULL;
	}

	return SUCCESS_RETURN;
}

#if 0
int dm_ota_switch_device(int devid)
{
    char pk[PRODUCT_KEY_MAXLEN] = {0};
    char dn[DEVICE_NAME_MAXLEN] = {0};
    char ds[DEVICE_SECRET_MAXLEN] = {0};
    int ret = dm_mgr_search_device_by_devid(devid, pk, dn, ds);
    void *ota_handle = NULL;
    int res = -1;
    dm_ota_ctx_t *ctx = NULL;

    if (SUCCESS_RETURN != ret) {
        dm_log_err("could not find device by id, ret is %d", ret);
        return FAIL_RETURN;
    }
    dm_log_info("do subdevice ota, pk, dn is %s, %s", pk, dn);

    ota_handle = NULL;
    res = dm_ota_get_ota_handle(&ota_handle);

    if (res != SUCCESS_RETURN) {
        return FAIL_RETURN;
    }

    /* if currently a device is doing OTA, do not interrupt */
    if (IOT_OTA_IsFetching(ota_handle)) {
        dm_log_info("OTA is processing, can not switch to another device");
        return FAIL_RETURN;
    }

    dm_ota_deinit();
    ctx = _dm_ota_get_ctx();
    memset(ctx, 0, sizeof(dm_ota_ctx_t));

    memcpy(ctx->product_key, pk, strlen(pk) + 1);
    memcpy(ctx->device_name, dn, strlen(dn) + 1);
    ret = dm_ota_sub();
    if (ret < 0) {
        dm_log_err("dm_ota_sub ret is %d, %s, %s\n", ret, pk, dn);
    }
    return ret;
}
#endif

int dm_ota_get_ota_handle(void **handle)
{
	dm_ota_ctx_t *ctx = _dm_ota_get_ctx();

	if (handle == NULL || *handle != NULL) {
		return FAIL_RETURN;
	}

	if (ctx->ota_handle == NULL) {
		return FAIL_RETURN;
	}

	*handle = ctx->ota_handle;

	return SUCCESS_RETURN;
}
enum{
	OTA_STATE_DOWNLOAD_ING = 1,
	OTA_STATE_DOWNLOAD_FAIL,
	OTA_STATE_DOWNLOAD_OK,
	OTA_STATE_FIRMWARE_TRANSFER,
	OTA_STATE_FIRMWARE_OK,
	OTA_STATE_UPGRADE_OK,
	OTA_STATE_UPGRADE_FAIL,
};
static char *s_url = NULL;

int kk_publishProgress(int process,int state)
{
	char productCode[PRODUCT_CODE_LEN] = {0};
	char ccuID[PRODUCT_CODE_LEN] = {0};
	char msgId[MSG_MAX_LEN] = {0};
	HAL_GetProduct_Code(productCode);
	HAL_Get_ccuid(ccuID);
	iotx_report_id(msgId);
	cJSON *info = cJSON_CreateObject();
	cJSON_AddStringToObject(info, MSG_TYPE_STR, "/thing/event/otaProgress/post");
	cJSON_AddStringToObject(info, MSG_DEVICE_CODE_STR, ccuID);
	cJSON_AddStringToObject(info, MSG_PRODUCT_CODE_STR, productCode);
	char *infff=cJSON_Print(info);

    cJSON *payload = cJSON_CreateObject();
	cJSON_AddStringToObject(payload, "desc", "success");
	cJSON_AddStringToObject(payload, "version", "1.0");
	cJSON_AddStringToObject(payload, "code", "0");
	cJSON_AddStringToObject(payload, "msgId", msgId);	
	cJSON_AddStringToObject(payload, "identifier", "otaProgress");
	cJSON_AddStringToObject(payload, "method", "thing.event.otaProgress.post");
	cJSON *params = cJSON_CreateObject();
	cJSON_AddStringToObject(params, "productId", productCode);
	cJSON_AddNumberToObject(params, "state", state);
	cJSON_AddNumberToObject(params, "process", process);
	cJSON_AddItemToObject(payload, "params", params);
	char *payload11=cJSON_Print(payload);
	kk_sendData2app(infff,payload11,0);
	free(payload11);
	free(infff);
	cJSON_Delete(payload);
	cJSON_Delete(info);
    return 0;
     
}
size_t receive_data(void *buffer, size_t size, size_t nmemb, FILE *file){
    size_t r_size = fwrite(buffer, size, nmemb, file);;
    return r_size;
}
int progress_callback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{
    #if 1
    char processBuf[12] = {0};
    //printf("progress_callback clientp:%s,dltotal:%g,dlnow:%g,ultotal:%g,ulnow:%g\n",clientp,dltotal,dlnow,ultotal,ulnow);
    if(dlnow > 0){
        int process = (int)dlnow*100.0/dltotal;
        if(process % 10 == 0){
            //sprintf(processBuf,"%d",process);
            INFO_PRINT("progress_callback ,process:%d\n",process);
			kk_publishProgress(process,OTA_STATE_DOWNLOAD_ING);
            //lua_event_notify((char*)"ota_process",(char*)"process", processBuf);
        }
    }
    #endif
    return 0;
}
void dm_ota_start(char *url)
{
	int res = 0;
	char *progress_data = "* ";  
    CURL* curl = curl_easy_init();  
	if (NULL == curl){
        return ;
	}
    FILE *file = fopen(OTA_IMG_FILE,"w");
	curl_easy_setopt(curl, CURLOPT_URL, url);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receive_data); 
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)file);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);   
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION , progress_callback); 
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, progress_data);
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 3000);
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 180);
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);  //支持服务器跳转
	curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);  // enable TCP keep-alive for this transfer 
	curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 120L);	// keep-alive idle time to 120 seconds 
	curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 60L);	// interval time between keep-alive probes: 60 seconds	        
	res = curl_easy_perform(curl);
	if(res != CURLE_OK){
		kk_publishProgress(0,OTA_STATE_DOWNLOAD_FAIL);
        INFO_PRINT("curl_easy_perform error,res:%d\n",res);
    }  
	curl_easy_cleanup(curl);	
	fclose(file);
}
void dm_ota_start_MD5(char *url)
{
	int res = 0;
	char *md5_url = malloc(strlen(url)+10);
	if(md5_url == NULL){
		return;
	}	
	memset(md5_url,0x0,strlen(url)+10);
	sprintf(md5_url,"%s.md5",url);
    CURL* curl = curl_easy_init();  
	if (NULL == curl){
		free(md5_url);
        return ;
	}
	printf("--------->url md5:%s\n",md5_url);
    FILE *file = fopen(OTA_IMG_FILE_MD5,"w");
	curl_easy_setopt(curl, CURLOPT_URL, md5_url);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receive_data); 
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)file);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);   
    //curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION , progress_callback); 
    //curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, progress_data);
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 3000);
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 180);
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);  //支持服务器跳转
	curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);  // enable TCP keep-alive for this transfer 
	curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 120L);	// keep-alive idle time to 120 seconds 
	curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 60L);	// interval time between keep-alive probes: 60 seconds	        
	res = curl_easy_perform(curl);
	if(res != CURLE_OK){
        INFO_PRINT("curl_easy_perform error,res:%d\n",res);
		kk_publishProgress(0,OTA_STATE_DOWNLOAD_FAIL);
    }  
	curl_easy_cleanup(curl);	
	fclose(file);
	free(md5_url);
}
void dm_ota_handle(void *data){
	printf("dm_ota_handle ================== [%s]\n",(char*)data);
	cJSON *json,*url,*info;
	cJSON *params;
	cJSON *payload;
	json=cJSON_Parse(data);
	if (json == NULL) {
		printf("Error before: [%s]\n","cJSON_Parse");
		return;
	}
	printf("------------------------------5\n");
	payload = cJSON_GetObjectItem(json, MSG_PAYLOAD_STR);
	if(payload != NULL){
		params = cJSON_GetObjectItem(payload, MSG_PARAMS_STR);
		if(params == NULL){
			return;
		}
		printf("------------------------------2\n");
		url = cJSON_GetObjectItem(params, "url");
		if(url == NULL){
			return;
		}
		INFO_PRINT("OTA URL :%s\n",url->valuestring);
		if(s_url != NULL){
			free(s_url);
			s_url = NULL;
		}
		s_url = malloc(strlen(url->valuestring)+10);
		if(s_url == NULL){
			return;
		}
		memset(s_url,0x0,strlen(url->valuestring)+10);
		strcpy(s_url,url->valuestring);
		dm_ota_start(s_url);
		dm_ota_start_MD5(s_url);
		system("sync");
		kk_publishProgress(100,OTA_STATE_DOWNLOAD_OK);
		char CMD[128] = {0};
		sprintf(CMD,"/app/ccuApps/upgrade %s %s",OTA_IMG_FILE,OTA_IMG_FILE_MD5);
		INFO_PRINT("------------------------->CMD:%s\n",CMD);
		system(CMD);
		sleep(1);
		kk_publishProgress(100,OTA_STATE_UPGRADE_OK);
		sleep(2);
		system("reboot -f");		
	}
#if 0	
	info_root = cJSON_GetObjectItem(json, MSG_INFO_STR);
	info = cJSON_Parse(info_root->valuestring);
	typeJson = cJSON_GetObjectItem(info, MSG_TYPE_STR);	
	product_type = cJSON_GetObjectItem(info, MSG_PRODUCT_TYPE_STR);
	device_code = cJSON_GetObjectItem(info, MSG_DEVICE_CODE_STR);
	payload = cJSON_GetObjectItem(json, MSG_PAYLOAD_STR);

	printf(" payload= %s  \n",payload->valuestring );
	if (strstr(typeJson->valuestring,KK_THING_OTA_DEVICE_UPGRADE)){
		char buf[128] = {0};
		int len = 128;
        if (dm_ota_check(payload->valuestring, strlen(payload->valuestring)+1, IOTX_OTA_TOPIC_TYPE_DEVICE_UPGRATE) == 0){
            dm_ota_setPKN(product_type->valuestring,device_code->valuestring);
            dm_fota_perform_sync(buf, len);
        }else{
            printf("parse params error !!  \n");
        }
	}else {
		printf("invaild ota type: [%d]\n", atoi(typeJson->valuestring));
	}
#endif	
	cJSON_Delete(json);
	//cJSON_Delete(info);	
}

int  dm_ota_check(void* payload, int len, iotx_ota_topic_types_t type){
	void *ota_handle = NULL;

	void* otaHandle = NULL;

	dm_ota_get_ota_handle(&otaHandle);

    return ota_callback(otaHandle, payload, len,type);
}

int dm_ota_yield(int timeout_ms)
{
	void *data = NULL;
	int count = 0;

	if (timeout_ms <= 0) {
		return INVALID_PARAMETER;
	}

	while (CONFIG_DISPATCH_QUEUE_MAXLEN == 0 || count++ < CONFIG_DISPATCH_QUEUE_MAXLEN) {

		if (dm_queue_msg_next3(&data) == SUCCESS_RETURN) {
			//dm_queue_msg_t *msg = (dm_queue_msg_t *)data;
			printf("dm_ota_yield call \n");
			printf("------------------------------4\n");
			dm_ota_handle(data);

			free(data);
			data = NULL;
		} else {
			break;
		}
	}
	usleep(timeout_ms*1000);

	return SUCCESS_RETURN;
}

