#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/vfs.h>
#include <uv.h>
#include <dbus/dbus.h>
#include <errno.h>
#include <iconv.h>
#include <locale.h>
#include <sys/sendfile.h>
#include <uthash/utstring.h>

#if defined(PLATFORM_R311) || defined (PLATFORM_CPU)
#include "log.h"
#include "libuv_dbus.h"
#include "crypto.h"
#include "json_struct.h"
#include "config_engine.h"
#include "inet_api.h"
#include "ota.h"
#else
#include <uvdbus/log.h>
#include <uvdbus/libuv_dbus.h>
#include <uvdbus/crypto.h>
#include <uvdbus/json_struct.h>
#include <uvdbus/config_engine.h>
#include <uvdbus/ota.h>
#include <uvdbus/inet_api.h>
#endif

#define MD5_CHKSUM_LEN              (16)
#define MD5_STR_LEN                 (32 + 4)

#define GET_STA_CNT(s)              (s & 0xFF)
#define SET_STA_CNT(s, v)           (s = (s & 0xFFFFFF00) | ((v) & 0xFF))

#define GET_STA_CMD(s)              ((s >> 8) & 0xFF)
#define SET_STA_CMD(s, v)           (s = (s & 0xFFFF00FF) | (((v) & 0xFF) << 8))

#define GET_STA_FLAG(s)             ((s >> 16) & 0xFF)
#define SET_STA_FLAG(s, v)          (s = (s & 0xFF00FFFF) | (((v) & 0xFF) << 16))

#define GET_STA_ERR(s)              ((s >> 24) & 0xFF)
#define SET_STA_ERR(s, v)           (s = (s & 0x00FFFFFF) | (((v) & 0xFF) << 24))

#define OTA_PARAMS_PARTITION        ("/dev/by-name/ota_info")
#define OTA_PARAMS_TAG              ("OTA163\0")
#define BOOT_IMG_FILE_NAME      	("boot.img")
#define ROOTFS_IMG_FILE_NAME    	("rootfs.img")
#define OTA_TEMP_DIR            	("/mnt/UDISK/ota/unzip")
#define OTA_FILE_DOWNLOAD_DIR   	("/mnt/UDISK/ota/dl")

typedef struct
{
    char            tags[8];
    unsigned int    otaStatus;
    char            otaVer[MD5_STR_LEN];
    char            bootChksum[MD5_STR_LEN];
    unsigned int    bootfileSize;
    char            rootfsChksum[MD5_STR_LEN];
    unsigned int    rootfsfileSize;
    char            paramsChksum[MD5_STR_LEN];
} OTA_PARAMS, *POTA_PARAMS;

typedef enum
{
    CMD_RUN_OTA     = 1,
    CMD_RUN_BOOT,
    CMD_RUN_VERIFY,
} OTA_COMMAND;

typedef enum
{
    FLAG_NORMAL     = 0,
    FLAG_EXEC_OTA   = 1,
} OTA_FLAG;



typedef struct
{
    unsigned int                fileSize;
    unsigned char               md5sum[MD5_CHKSUM_LEN];
    int reserved[4];
} IPL_DATA_ITEM, *PIPL_DATA_ITEM;

typedef struct
{
    OTA_FILE_INFO   fileInfo;

    char            pPath[MAX_PATH];
    int             dlStatus;
    int             retCode;
    uv_barrier_t*   puvBarrier;
} DOWNLOAD_TASK_INFO, *PDOWNLOAD_TASK_INFO;

static char             g_otaDownloadPath[MAX_PATH];
static SETUP_MODE       g_sSetupStatus = NORMAL_SETUP;

static unsigned int g_IsOTAMode = 0;
static uv_barrier_t g_otaBarrier;
static uv_loop_t*   g_pMainLoop = NULL;
static int          g_isDownloading = FALSE;
static OTA_PARAMS 	g_otaParams;

static void __ota_print_params(POTA_PARAMS pInfo, const char* pTips)
{
	LOG_EX2(LOG_Debug, "%s--------------------------\n", pTips ? pTips : "");
    LOG_EX2(LOG_Debug, "tags:           %s\n",      pInfo->tags);
    LOG_EX2(LOG_Debug, "status:         0x%08X\n",  pInfo->otaStatus);
    LOG_EX2(LOG_Debug, "version:        %s\n",      pInfo->otaVer);
    LOG_EX2(LOG_Debug, "boot_ver:       %s\n",      pInfo->bootChksum);
    LOG_EX2(LOG_Debug, "boot_size:      %u\n",      pInfo->bootfileSize);
    LOG_EX2(LOG_Debug, "rootfs_ver:     %s\n",      pInfo->rootfsChksum);
    LOG_EX2(LOG_Debug, "rootfs_size:    %u\n",      pInfo->rootfsfileSize);
	LOG_EX2(LOG_Debug, "params_chksum:  %s\n",      pInfo->paramsChksum);
}

static void __printfOTANotifyCmd(const char* pTags, POTA_DATA_INFO pInfo)
{
    if(pTags && strlen(pTags) > 0)
    {
        LOG_EX2(LOG_Debug, "%s:\n", pTags);
        LOG_EX2(LOG_Debug, "**************************************************************\n");
    }

    LOG_EX2(LOG_Debug, "Version        :       %d\n", pInfo->version);
    LOG_EX2(LOG_Debug, "OTA Command    :       %d(0x%08X)\n", pInfo->otaCmd, pInfo->otaCmd);
    LOG_EX2(LOG_Debug, "OTA Type       :       %d(0x%08X)\n", pInfo->otaMode, pInfo->otaMode);

    LOG_EX2(LOG_Debug, "---------------------------------------------------------\n");
    LOG_EX2(LOG_Debug, "\tURL        :       %s\n", pInfo->otaFileInfo.url);
    LOG_EX2(LOG_Debug, "\tMD5        :       %s\n", pInfo->otaFileInfo.md5);
    LOG_EX2(LOG_Debug, "\tsize       :       %u\n", pInfo->otaFileInfo.size);
}

static int __otaRspStatus(OTA_STATUS_TYPE type, int val)
{
    int ret = 0;
    OTA_RSP_STATUS oStatus;

    if(OTA_ERR_CODE == type && val != 0)
    {
        g_IsOTAMode = FALSE;
    }

    memset(&oStatus, 0, sizeof(OTA_RSP_STATUS));

    oStatus.status  = type;
    oStatus.val     = val;

    LOG_EX(LOG_Debug, "Rsponse: %d --> %d\n", type, val);

    if(type == OTA_UPGRADE_START || type == OTA_REBOOT_SYSTEM || type == OTA_RECOVERY_START)
    {
        ret = DBusJsonBoardcastCommand(NULL, 0xFFFFFFFF, CMD_OTA_STATUS, JSON_ENGINE_OTA_RSP, &oStatus, FALSE);
    }
    else
    {
        ret = DBusJsonSendToCommand(NULL,
                                    g_pModInfoTable[MODULE_CONTROLLER].modAliase,
                                    CMD_OTA_STATUS,
                                    JSON_ENGINE_OTA_RSP,
                                    &oStatus, FALSE);
    }

    return (ret);
}

void __onHttpResponseCb(void* pData, unsigned int size, const char* pReqUrl, const char* pDlPath, const char* pTaskUuid, int iFinished, void* pUserData)
{
    PDOWNLOAD_TASK_INFO pDlInfo = (PDOWNLOAD_TASK_INFO)pUserData;

    if(iFinished == 0)
    {
        LOG_EX(LOG_Debug, "Request(%s): [%s] --> [%s] Response: [%u] OK\n", pTaskUuid, pReqUrl, pDlPath, size);
    }
    else if(iFinished == 1)
    {
        LOG_EX(LOG_Error, "Request(%s): [%s] --> [%s] Response: [%u] Error\n", pTaskUuid, pReqUrl, pDlPath, size);
    }
    else
    {
        LOG_EX(LOG_Error, "Download Error Code: %d\n", iFinished);
    }

    if(pDlInfo)
    {
        uv_thread_t         uvThread;
        pDlInfo->retCode    = iFinished;
        LOG_EX(LOG_Debug, "Cleanup barrier\n");
        uv_barrier_wait(pDlInfo->puvBarrier);
    }
    else
    {
        LOG_EX(LOG_Debug, "pDlInfo == NULL\n");
    }
}

void __onProgressNotifyCb(const char* pReqUrl, const char* pTaskUuid, unsigned char uPercent, void* pUserData)
{
    static unsigned char preCnt = 0;

    if(preCnt != uPercent && uPercent != 0)
    {
        preCnt = uPercent;
        __otaRspStatus(OTA_DOWNLOAD_PROGRESS, uPercent);
        LOG_EX(LOG_Debug, "[%s]:  %u%%\n", pReqUrl, uPercent);
    }
}

static int __ota_read_params(POTA_PARAMS pInfo)
{
    uv_fs_t             uvOpen, uvRead, uvClose;
    int                 ret, rdSize = 0;
    unsigned char       md5Buf[MD5_CHKSUM_LEN];
	char 				md5str[MD5_STR_LEN];
	
    uv_loop_t*  pLoop   = g_pMainLoop;
    uv_buf_t    uvBuf   = uv_buf_init((char*)pInfo, sizeof(OTA_PARAMS));

    if(pInfo == NULL)
    {
        return (-ERR_INPUT_PARAMS);
    }

	memset(md5str, 0, MD5_STR_LEN);
    memset(pInfo, 0, sizeof(OTA_PARAMS));

    ret = system("sync");

    if(uv_fs_open(pLoop, &uvOpen, OTA_PARAMS_PARTITION, O_RDWR, 0, NULL) == -1)
    {
        LOG_EX(LOG_Error, "Open Partition %s Error\n", OTA_PARAMS_PARTITION);
        return (-ERR_OPEN_FILE);
    }

    if((rdSize = uv_fs_read(pLoop, &uvRead, uvOpen.result, &uvBuf, 1, -1, NULL)) != sizeof(OTA_PARAMS))
    {
        LOG_EX(LOG_Error, "Read Partition Error: Read %d Bytes, Need Read %d Bytes %d\n", rdSize, sizeof(OTA_PARAMS));
        return (-ERR_READ_FILE);
    }

    uv_fs_close(pLoop, &uvClose, uvOpen.result, NULL);

    if(strncmp(pInfo->tags, OTA_PARAMS_TAG, 8) != 0)
    {
        LOG_EX(LOG_Error, "Check Magic Head Error: [%s], need [%s]\n", pInfo->tags, OTA_PARAMS_TAG);
        return (-ERR_NO_INIT_IPL3);
    }

    EvpMD5HashBuf((const unsigned char*)pInfo, sizeof(OTA_PARAMS) - MD5_STR_LEN, md5Buf, &rdSize);

	IHW_bin2hex(md5str, md5Buf, MD5_CHKSUM_LEN);
	
    if(strcmp(pInfo->paramsChksum, md5str) != 0)
    {
        LOG_EX(LOG_Error, "Verify MD5 Error: %s --> %s\n", md5str, pInfo->paramsChksum);
        return (-ERR_BAD_IPL3);
    }

    return (0);
}

static int __ota_save_params(POTA_PARAMS pInfo)
{	
	int 				ret, wrSize = 0;
	uv_fs_t 			uvOpen, uvWrite, uvSync, uvClose;
	unsigned char		md5Buf[MD5_CHKSUM_LEN];
	char				buf[MAX_PATH];
	char				cmd[MAX_PATH];
	char 				md5str[MD5_STR_LEN];
	
	uv_loop_t*	pLoop	= g_pMainLoop;
	uv_buf_t	uvBuf	= uv_buf_init((char*)pInfo, sizeof(OTA_PARAMS));

	if(pInfo == NULL)
	{
		return (-ERR_INPUT_PARAMS);
	}

	memset(md5str, 0, MD5_STR_LEN);
	
	if(strncmp(pInfo->tags, OTA_PARAMS_TAG, 8) != 0)
	{
		memset(pInfo->tags , 0, 8);
		strcpy(pInfo->tags, OTA_PARAMS_TAG);
	}

	EvpMD5HashBuf((const unsigned char *)pInfo, sizeof(OTA_PARAMS) - MD5_STR_LEN, md5Buf, &wrSize);

	if(wrSize != MD5_CHKSUM_LEN)
	{
		LOG_EX(LOG_Error, "Error MD5: size = %d, need %d\n", wrSize, MD5_CHKSUM_LEN);
		return (-ERR_BAD_IPL3);
	}

	IHW_bin2hex(md5str, md5Buf, MD5_CHKSUM_LEN);
	memset(pInfo->paramsChksum, 0, MD5_STR_LEN);
	strcpy(pInfo->paramsChksum, md5str);

	memset(buf, 0, MAX_PATH);
	ret = system("mkdir -p /tmp/ota_param/");
	sprintf(buf, "/tmp/ota_param/ota_%s.params", pInfo->otaVer);

	if(uv_fs_open(pLoop, &uvOpen, buf, O_RDWR | O_CREAT | O_TRUNC, 
			   S_IRUSR | S_IWUSR, NULL) == -1)
	{
		LOG_EX(LOG_Error, "Open File %s Error\n", buf);
		return (-ERR_OPEN_FILE);
	}

	if((wrSize = uv_fs_write(pLoop, &uvWrite, uvOpen.result, &uvBuf, 1, -1, NULL)) != sizeof(OTA_PARAMS))
	{
		LOG_EX(LOG_Error, "Write Error: Read %d Bytes, Need Read %d Bytes %d\n", wrSize, sizeof(OTA_PARAMS));
		return (-ERR_READ_FILE);
	}

	uv_fs_fdatasync(pLoop, &uvSync, uvOpen.result, NULL);
	uv_fs_close(pLoop, &uvClose, uvOpen.result, NULL);

	memset(cmd, 0, MAX_PATH);
	sprintf(cmd, "dd if=%s of=/dev/by-name/ota_info bs=4k conv=sync > /dev/zero && sync", buf);

	ret = system(cmd);
	
	return (0);
}

static int __otaRunning(char* pCheckSumFile, int ver)
{
    ssize_t         rdRet;
    size_t          rdSize;
    unsigned char   strMD5[MD5_CHKSUM_STR_LEN];
    char            dirPath[MAX_PATH];
    char            basePath[MAX_PATH];
    const char      *pChkMD5        = NULL;
    char            *pRdLine        = NULL;
    char            *pChkSumFile    =  pCheckSumFile;
    FILE            *pFile          = fopen(pChkSumFile, "r");
    int             ret, i          = 0;
    int             step            = 0;
    
    if(pFile == NULL)
    {
        LOG_EX(LOG_Error, "Open File %s Error\n", pChkSumFile);
        return (-ERR_OPEN_FILE);
    }

    memset(dirPath, 0, MAX_PATH);
    dirname_v2(pChkSumFile, dirPath);

    LOG_EX(LOG_Debug, "Step %d: Check OTA image .......\n", step++);
    // check download ota file
    while((rdRet = getline(&pRdLine, &rdSize, pFile)) != -1)
    {
        char* pToken = NULL;
        char *pItem[2];
        char pMD5Val[MD5_CHKSUM_LEN];
        char pMD5Str[MD5_CHKSUM_STR_LEN];
        int  fileSize = 0;
        
        if(rdSize == 0 || pRdLine == NULL)
        {
            continue;
        }

        if(pRdLine[strlen(pRdLine) - 1] == '\n')
        {
            pRdLine[strlen(pRdLine) - 1] = 0;
        }
        
        i = 0;
        
        for(pToken = strtok(pRdLine, " "); pToken != NULL && i <= 1; pToken = strtok(NULL, " "), i++)
        {
            pItem[i] = pToken;
        }

        //fprintf(stdout, "md5 = %s, file = \'%s\'\n", pItem[0], pItem[1]);
       
        if(pItem[0] == NULL || pItem[1] == NULL || strlen(pItem[0]) != 32 || strlen(pItem[1]) == 0)
        {
            free(pRdLine);
            fclose(pFile);
            return (-ERR_MD5_CHECK_SUM);
        }
        
        memset(basePath, 0, MAX_PATH);
        sprintf(basePath, "%s/%s", dirPath, pItem[1]);

        LOG_EX(LOG_Debug, "OTA File: %s --> %s\n", basePath, pItem[0]);
        
        if((ret = EvpMD5HashFileV2(basePath, pMD5Val)) != 0)
        {
            free(pRdLine);
            fclose(pFile);
            LOG_EX(LOG_Error, "%s Check MD5 error: %d\n", ret);
            return (-ERR_MD5_FILE);
        }
        
        memset(pMD5Str, 0, MD5_CHKSUM_STR_LEN);
        IHW_bin2hex(pMD5Str, pMD5Val, MD5_CHKSUM_LEN);

        //LOG_EX(LOG_Debug, "%s Check MD5 Info: [%s] need [%s]\n", basePath, pMD5Str, pItem[0]);
        
        if(strcmp(pMD5Str, pItem[0]) != 0)
        {            
            free(pRdLine);
            fclose(pFile);
            LOG_EX(LOG_Error, "%s Check MD5 error: [%s] need [%s]\n", basePath, pMD5Str, pItem[0]);
            return (-ERR_MD5_FILE);
        }
        
        if(strcmp(pItem[1], BOOT_IMG_FILE_NAME) == 0)
        {
			GET_FILE_SIZE(basePath, fileSize);
			memset(g_otaParams.bootChksum, 0, MD5_STR_LEN);
			strncpy(g_otaParams.bootChksum, pMD5Str, MD5_STR_LEN);
			g_otaParams.bootfileSize = fileSize;
        }
        else if(strcmp(pItem[1], ROOTFS_IMG_FILE_NAME) == 0)
        {
            GET_FILE_SIZE(basePath, fileSize);
            memset(g_otaParams.rootfsChksum, 0, MD5_STR_LEN);
			strncpy(g_otaParams.rootfsChksum, pMD5Str, MD5_STR_LEN);
			g_otaParams.rootfsfileSize = fileSize;
        }
    }

    if(pRdLine)
    {
        free(pRdLine);
    }
    fclose(pFile);

    __otaRspStatus(OTA_VERIFY_FILE, NO_OTA_STATUS_VAL_TAG);
 
    LOG_EX(LOG_Debug, "Step %d: Save OTA Information .......\n", step++);

	memset(g_otaParams.otaVer, 0, MD5_STR_LEN);
	sprintf(g_otaParams.otaVer, "%d.%d.%d.%d", 
		(ver >> 24) & 0xFF,
		(ver >> 16) & 0xFF,
		(ver >> 8) & 0xFF,
		ver & 0xFF);

	SET_STA_CMD(g_otaParams.otaStatus, CMD_RUN_OTA);
	
    ret = __ota_save_params(&g_otaParams);

    if(ret != 0)
    {
        LOG_EX(LOG_Error, "Save OTA Params Error: %d\n", ret);
        return (-ERR_OTA_WRITE_PARAMS);
    }

    //print_hex_dump_bytes("IPL3", DUMP_PREFIX_ADDRESS, &g_iplInfo, sizeof(IPL_DATA_INFO));
	__ota_print_params(&g_otaParams, "OTA Finished");

    LOG_EX(LOG_Debug, "Step %d: Waitting for reboot ......\n", step++);

    return (0);
}

static void __otaFromLocalImage(void* pParams)
{
    int ret, reTry;
    int step = 0;
    char buf[MAX_PATH];
    char* pFileName = NULL, *pMD5Val   = NULL;
    const char* pMD5Chk = NULL;
    int version = (int)(intptr_t)pParams;

    LOG_EX(LOG_Debug, "Step %d: Get OTA information .......\n", step++);
    memset(buf, 0, MAX_PATH);
    sprintf(buf, "%s/ota.md5", g_otaDownloadPath);

    if(access(buf, F_OK) != 0)
    {
        __otaRspStatus(OTA_ERR_CODE, -ERR_FILE_NOT_EXISTS);
        g_IsOTAMode = FALSE;
        return;
    }

    memset(buf, 0, MAX_PATH);
    sprintf(buf, "cat %s/ota.md5 | awk '{print $2}'", g_otaDownloadPath);

    if(GetShellExecResult(buf, &pFileName) != 0)
    {
        LOG_EX(LOG_Error, "Get File Name Error: %s\n", buf);
        __otaRspStatus(OTA_ERR_CODE, -ERR_READ_FILE);
        g_IsOTAMode = FALSE;
        return;
    }

    memset(buf, 0, MAX_PATH);
    sprintf(buf, "cat %s/ota.md5 | awk '{print $1}'", g_otaDownloadPath);

    if(GetShellExecResult(buf, &pMD5Val) != 0)
    {
        LOG_EX(LOG_Error, "Get MD5 Value Error: %s\n", buf);
        free(pFileName);
        __otaRspStatus(OTA_ERR_CODE, -ERR_READ_FILE);
        g_IsOTAMode = FALSE;
        return;
    }

    LOG_EX(LOG_Debug, "Step %d: Verify OTA image .......\n", step++);

    memset(buf, 0, MAX_PATH);
    sprintf(buf, "%s/%s", g_otaDownloadPath, pFileName);

    pMD5Chk = EvpMD5HashFile(buf);

    if(strcmp(pMD5Chk, pMD5Val) != 0)
    {
        LOG_EX(LOG_Error, "Check %s MD5 Error: %s --> %s\n", buf, pMD5Chk, pMD5Val);
        __otaRspStatus(OTA_ERR_CODE, -ERR_MD5_CHECK_SUM);
        g_IsOTAMode = FALSE;
        return;
    }

    free(pMD5Val);
    free(pFileName);
    free((void*)pMD5Chk);

    LOG_EX(LOG_Debug, "Step %d: Decompress OTA file .......\n", step++);

    pFileName = strdup(buf);

    __otaRspStatus(OTA_DECOMPRESS_FILE, NO_OTA_STATUS_VAL_TAG);

    memset(buf, 0, MAX_PATH);
    sprintf(buf, "mkdir -p %s/%d/ && tar -C %s/%d -xvzf %s > /dev/null",
            OTA_TEMP_DIR, version,
            OTA_TEMP_DIR, version,
            pFileName);
	free(pFileName);
	
    ret = system(buf);

    SetHBLAutoExit(FALSE);
    __otaRspStatus(OTA_UPGRADE_START, NO_OTA_STATUS_VAL_TAG);

	memset(buf, 0, MAX_PATH);
    sprintf(buf, "%s/%d/ota.md5", OTA_TEMP_DIR, version);
	
    reTry = 3;
    while(reTry--)
    {
        if((ret = __otaRunning(buf, version)) == 0)
        {
            break;
        }
    }

	if(ret == 0)
	{
		ret = system("rm -f /mnt/UDISK/ota/*.img /mnt/UDISK/ota/*.md5 > /dev/null");
		
		memset(buf, 0, MAX_PATH);
    	sprintf(buf, "cp %s/%d/* /mnt/UDISK/ota > /dev/null", OTA_TEMP_DIR, version);
		ret = system(buf);

		memset(buf, 0, MAX_PATH);
    	sprintf(buf, "rm %s/* > /dev/null", g_otaDownloadPath);
		ret = system(buf);
	}

    if(ret == ERR_OPEN_FILE
       || ret == ERR_MD5_FILE
       || ret == ERR_MD5_CHECK_SUM
       || ret == ERR_OTA_WRITE_BOOT
       || ret == ERR_OTA_WRITE_ROOTFS
       || ret == ERR_OTA_WRITE_PARAMS)
    {
        __otaRspStatus(OTA_ERR_CODE, -12345);
    }

    __otaRspStatus(OTA_REBOOT_SYSTEM, NO_OTA_STATUS_VAL_TAG);

    reTry = 300;

    while(reTry--)
    {
        LOG_EX(LOG_Debug, "System force reboot before %d seconds\n", reTry);
        sleep(1);
    }

    while(TRUE)
    {
        ret = system("reboot -f");
        sleep(5);
    }

    return;
}

static int __isPreDownloader(POTA_DATA_INFO pInfo)
{
    UT_string* pDlPath = NULL, *pChkPath = NULL;

    char* pFileName = NULL, *pMD5Val   = NULL;
    const char* pMD5Chk = NULL;

    if(pInfo == NULL)
    {
        return (-ERR_FILE_NOT_EXISTS);
    }

    utstring_new(pDlPath);
    utstring_new(pChkPath);
    utstring_printf(pDlPath, "%s/tina_r16_ota_%d.tar.gz", g_otaDownloadPath, pInfo->version);
    utstring_printf(pChkPath, "%s/ota.md5", g_otaDownloadPath);

    if(access(utstring_body(pDlPath), F_OK) != 0
       || access(utstring_body(pChkPath), F_OK) != 0)
    {
        utstring_free(pDlPath);
        utstring_free(pChkPath);
        return (-ERR_FILE_NOT_EXISTS);
    }

    utstring_renew(pChkPath);
    utstring_printf(pChkPath, "cat %s/ota.md5 | awk '{print $2}'", g_otaDownloadPath);

    if(GetShellExecResult(utstring_body(pChkPath), &pFileName) != 0)
    {
        LOG_EX(LOG_Error, "Get File Name Error: %s\n", utstring_body(pChkPath));
        utstring_free(pDlPath);
        utstring_free(pChkPath);
        return -ERR_FILE_NOT_EXISTS;
    }

    utstring_renew(pChkPath);
    utstring_printf(pChkPath, "cat %s/ota.md5 | awk '{print $1}'", g_otaDownloadPath);

    if(GetShellExecResult(utstring_body(pChkPath), &pMD5Val) != 0)
    {
        LOG_EX(LOG_Error, "Get MD5 Value Error: %s\n", utstring_body(pChkPath));
        free(pFileName);
        utstring_free(pDlPath);
        utstring_free(pChkPath);
        return -ERR_MD5_CHECK_SUM;
    }

    if(strcmp(pMD5Val, pInfo->otaFileInfo.md5) != 0)
    {
        LOG_EX(LOG_Debug, "Download File Chksum Diff: %s -> %s\n", pMD5Val, pInfo->otaFileInfo.md5);
        free(pFileName);
        free(pMD5Val);
        utstring_free(pDlPath);
        utstring_free(pChkPath);
        return -ERR_FILE_NOT_EXISTS;
    }

    utstring_renew(pChkPath);
    utstring_printf(pChkPath, "%s/%s", g_otaDownloadPath, pFileName);

    pMD5Chk = EvpMD5HashFile(utstring_body(pChkPath));

    if(pMD5Chk == NULL || strcmp(pMD5Chk, pMD5Val) != 0)
    {
        LOG_EX(LOG_Error, "Check %s MD5 Error: %s --> %s\n", utstring_body(pChkPath), pMD5Chk, pMD5Val);
        free(pFileName);
        free(pMD5Val);
        utstring_free(pDlPath);
        utstring_free(pChkPath);

        if(pMD5Chk)
        {
            free((void*)pMD5Chk);
        }

        return -ERR_MD5_CHECK_SUM;
    }

    free(pMD5Val);
    free(pFileName);
    free((void*)pMD5Chk);
    utstring_free(pDlPath);
    utstring_free(pChkPath);

    return (0);
}

static void __otaDownloadImageCb(void* pParams)
{
    POTA_DATA_INFO pInfo = (POTA_DATA_INFO)pParams;
    DOWNLOAD_TASK_INFO dlInfo;
    char buf[SIZE_1K];
    int ret, reTry = 3;

    g_isDownloading = TRUE;

    // Notify CC Beging Download
    __otaRspStatus(OTA_DOWNLOAD_FILE, 2);

    if(__isPreDownloader(pInfo) == 0)
    {
        g_isDownloading = FALSE;
        g_IsOTAMode = FALSE;
        __otaRspStatus(OTA_DOWNLOAD_PROGRESS, 100);
        __otaRspStatus(OTA_DOWNLOAD_FILE, 0);

        if(pInfo->otaMode == OTA_MODE_FORCE_NOW)
        {
            g_IsOTAMode = TRUE;
            __otaFromLocalImage((void*)(intptr_t)pInfo->version);
            g_IsOTAMode = FALSE;
        }

        free(pInfo);
        pthread_detach(pthread_self());
        return;
    }

    while(reTry--)
    {
        uv_barrier_t uvBarrier;
        uv_barrier_init(&uvBarrier, 2);

        memset(buf, 0, SIZE_1K);
        sprintf(buf, "rm -rf %s/*", g_otaDownloadPath);
        ret = system(buf);

        memset(&dlInfo, 0, sizeof(DOWNLOAD_TASK_INFO));
        sprintf(dlInfo.pPath, "%s/tina_r16_ota_%d.tar.gz", g_otaDownloadPath, pInfo->version);

        memcpy(&dlInfo.fileInfo, &pInfo->otaFileInfo, sizeof(OTA_FILE_INFO));

        dlInfo.dlStatus = 0;

        dlInfo.puvBarrier = &uvBarrier;

        InetHttpDlFileAsync(dlInfo.fileInfo.url,
                            dlInfo.pPath,
                            __onHttpResponseCb,
                            __onProgressNotifyCb,
                            &dlInfo);

        LOG_EX(LOG_Debug, ">>>>>>>>>>>>>>>>>>>Wait Download Finished.............\n");
        uv_barrier_wait(&uvBarrier);
        uv_barrier_destroy(&uvBarrier);
        LOG_EX(LOG_Debug, "<<<<<<<<<<<<<<<<<<<Download Finished.............\n");

        if(access(dlInfo.pPath, F_OK) != 0)
        {
            LOG_EX(LOG_Error, "Download File Error: %s\n", dlInfo.pPath);
            dlInfo.retCode = -ERR_MD5_CHECK_SUM;
        }

        if(dlInfo.retCode == 0)
        {
            const char* pChkVal = EvpMD5HashFile(dlInfo.pPath);

            if(strcmp(pChkVal, pInfo->otaFileInfo.md5) != 0)
            {
                LOG_EX(LOG_Error, "Download File MD5 Error: %s --> %s\n", pChkVal, pInfo->otaFileInfo.md5);
                dlInfo.retCode = -ERR_MD5_CHECK_SUM;
            }
            else
            {
                break;
            }
        }
    }

    if(dlInfo.retCode != 0)
    {
        ret = system(buf);
        g_IsOTAMode = FALSE;
        g_isDownloading = FALSE;

        if(dlInfo.retCode > 0)
        {
            dlInfo.retCode = -dlInfo.retCode;
        }

        __otaRspStatus(OTA_DOWNLOAD_FILE, dlInfo.retCode);
        pthread_detach(pthread_self());
        free(pInfo);
        return;
    }

    LOG_EX(LOG_Debug, "Step 1: Download File result: %d\n", dlInfo.retCode);

    memset(buf, 0, SIZE_1K);
    sprintf(buf, "echo \"%s  %s\" > %s/ota.md5",
            dlInfo.fileInfo.md5, basename_v2(dlInfo.pPath), g_otaDownloadPath);

    ret = system(buf);

    LOG_EX(LOG_Debug, "Step 2: Save MD5 File; %d\n", ret);

    g_IsOTAMode = FALSE;
    __otaRspStatus(OTA_DOWNLOAD_FILE, 0);

    if(pInfo->otaMode == OTA_MODE_FORCE_NOW)
    {
        g_IsOTAMode = TRUE;
        __otaFromLocalImage((void*)(intptr_t)pInfo->version);
        g_IsOTAMode = FALSE;
    }

    free(pInfo);

    g_isDownloading = FALSE;

    pthread_detach(pthread_self());
}

static PDBUS_MSG_PACK __dBusOnMessage(uv_loop_t* pLoop, DBusConnection* pConn, PDBUS_MSG_PACK pMsg)
{
    int err;
    uv_thread_t uvThread;
    POTA_DATA_INFO pInfo = NULL;

    if(!pMsg || !pLoop || !pConn)
    {
        return NULL;
    }

    //fprintf(stdout, "Recv: cmd = %u, Json = [%s]\n", pMsg->busCmd, (const char *)pMsg->pMsg);

    switch(pMsg->busCmd)
    {
        case CMD_OTA_RUNNOW:
            if(g_IsOTAMode == 1)
            {
                LOG_EX(LOG_Info, "Trigger OTA Now.......\n");
                uv_barrier_wait(&g_otaBarrier);
            }
            else
            {
                __otaRspStatus(OTA_ERR_CODE, -ERR_OTA_NOT_READY);
                LOG_EX(LOG_Error, "Current Not In OTA Mode\n");
            }

            break;

        case CMD_MISC_QUERY_DL_STATUS:
            if(g_isDownloading)
            {
                __otaRspStatus(OTA_DOWNLOAD_FILE, 1);
            }
            else
            {
                __otaRspStatus(OTA_DOWNLOAD_FILE, 0);
            }

            break;

        case CMD_MISC_QUERY_OTA_STATUS:
            if(g_sSetupStatus == SYSTEM_OTA_OK)
            {
                __otaRspStatus(OTA_SUCCESED, 0);
            }
            else if(g_sSetupStatus == RECOVERY_SETUP)
            {
                __otaRspStatus(OTA_SUCCESED, -1);
            }
            else if(g_sSetupStatus == NORMAL_SETUP)
            {
                __otaRspStatus(OTA_SUCCESED, 1);
            }

            __otaRspStatus(OTA_CURRENT_VERSION, 0);
            __otaRspStatus(OTA_CURRENT_SETUP_MODE, g_sSetupStatus);
            __otaRspStatus(OTA_CURRENT_REBOOT_TIME, GET_STA_CNT(g_otaParams.otaStatus));
            break;

        case CMD_SYSTEM_STANDBY:
            LOG_EX(LOG_Debug, "System Ready ....\n");
            break;

        case CMD_OTA_NOTIFY:

            if(g_IsOTAMode == 0)
            {
                if(g_sSetupStatus == SYSTEM_OTA)
                {
                    LOG_EX(LOG_Debug, "Current System Mode: SYSTEM_OTA\n");
                }
                else if(g_sSetupStatus == RECOVERY_SETUP)
                {
                    LOG_EX(LOG_Debug, "Current System Mode: RECOVERY_SETUP\n");
                }
                else if(g_sSetupStatus == SYSTEM_OTA_OK)
                {
                    LOG_EX(LOG_Debug, "Current System Mode: SYSTEM_OTA_OK\n");
                }
                else
                {
                    LOG_EX(LOG_Debug, "Current System Mode: NORMAL_SETUP\n");
                }

                if(g_isDownloading)
                {
                    __otaRspStatus(OTA_DOWNLOAD_FILE, 1);
                    break;
                }

                pInfo = (POTA_DATA_INFO)Json2Struct((const char*)pMsg->pMsg, JSON_ENGINE_OTA_REQ, TRUE, &err);

                if(pInfo == NULL)
                {
                    __otaRspStatus(OTA_ERR_CODE, -ERR_INPUT_PARAMS);
                }
                else
                {
                    unsigned long long uDiskFreeSize = GetPartitionFreeSize("/mnt/UDISK/");
                    unsigned long long uUsedSize = 0;

                    g_IsOTAMode = TRUE;
#if TEST_OTA_SERVER
                    memset(&pInfo->otaFileInfo, 0, sizeof(OTA_FILE_INFO));
                    strcpy(pInfo->otaFileInfo.md5, "78bcf3236f54543301ee1a2f74282917");
                    strcpy(pInfo->otaFileInfo.url, "http://10.240.84.163/tina_r16_ota_2.tar.gz");
                    pInfo->otaFileInfo.size = 57106052;
#endif
                    __printfOTANotifyCmd("On OTA Notify Command++", pInfo);

                    uUsedSize = pInfo->otaFileInfo.size + (1024 * 1024 * 10);

                    if(uUsedSize > uDiskFreeSize)
                    {
                        LOG_EX(LOG_Error, "Error: File Size = %u bytes, Need %llu bytes, UDISK Free %llu bytes\n",
                               pInfo->otaFileInfo.size, uUsedSize, uDiskFreeSize);

						g_IsOTAMode = FALSE;                        
                        __otaRspStatus(OTA_DISK_FULL, pInfo->otaCmd);
                        break;
                    }

                    switch(pInfo->otaCmd)
                    {
                        case OTA_CMD_DOWNLOAD:
                            if(strlen(pInfo->otaFileInfo.md5) == 0
                               || strlen(pInfo->otaFileInfo.url) == 0)
                            {
                                g_IsOTAMode = FALSE;
                                LOG_EX(LOG_Error, "OTA Information Error\n");
                            }
                            else
                            {
                                err = uv_thread_create(&uvThread, __otaDownloadImageCb, pInfo);

                                if(err != 0)
                                {
                                    LOG_EX(LOG_Error, "Create Thread Error: %d\n", err);
                                }
                            }

                            break;

                        case OTA_CMD_USED_LOCAL_IMAGE:
                        case OTA_CMD_EXEC:
                            err = uv_thread_create(&uvThread, __otaFromLocalImage, (void*)(intptr_t)pInfo->version);

                            if(err != 0)
                            {
                                LOG_EX(LOG_Error, "Create Thread Error: %d\n", err);
                            }

                            free(pInfo);
                            break;

                        default:
                            __otaRspStatus(OTA_UNKNOWN_CMD, pInfo->otaCmd);
                            free(pInfo);
                            break;
                    }
                }
            }
            else
            {
                __otaRspStatus(OTA_ERR_CODE, -ERR_OTA_PRE_STATR);
            }

            break;

        default:
            break;
    }

    return NULL;
}

int main(int argc, char** argv)
{	
    int     i, ret = 0;
    char    buf[256];
    DBusConnection* pBus;
	int 	otaCmd, otaFlag, cnt;

    //struct tm *pTm = NULL;
    g_pMainLoop = GetDBusDefaultLoop();

    pBus = DBusWithLibuvInit(g_pMainLoop,
                             g_pModInfoTable[MODULE_OTA].modAliase,
                             __dBusOnMessage,
                             NULL,
                             NULL,
                             &ret);

    if(pBus == NULL)
    {
        fprintf(stderr, "DBusWithLibuvInit Error: %d\n", ret);
        return (-1);
    }

    g_IsOTAMode = 0;
    strcpy(g_otaDownloadPath, OTA_FILE_DOWNLOAD_DIR);

    memset(buf, 0, 256);
    sprintf(buf, "mkdir -p %s", OTA_FILE_DOWNLOAD_DIR);
    ret = system(buf);

	if(__ota_read_params(&g_otaParams) == 0)
	{
		__ota_print_params(&g_otaParams, "System setup");
	}

	otaCmd 	= GET_STA_CMD(g_otaParams.otaStatus);
	otaFlag	= GET_STA_FLAG(g_otaParams.otaStatus);
	ret 	= GET_STA_ERR(g_otaParams.otaStatus);
	cnt		= GET_STA_CNT(g_otaParams.otaStatus);
	
	if(ret != 0)
	{
		LOG_EX(LOG_Error, "System setup error: code = %d, flag = %d, otaCmd = %d\n",
			ret, otaFlag, otaCmd);
	}

	if(cnt >= 3)
	{
		LOG_EX(LOG_Error, "Reboot more than %d times\n", cnt);
	}	

	if(otaFlag == FLAG_EXEC_OTA)
	{
		g_sSetupStatus = SYSTEM_OTA_OK;		
	}
	else if(otaFlag == FLAG_NORMAL)
	{
		g_sSetupStatus = NORMAL_SETUP;
	}
	
	SET_STA_CNT(g_otaParams.otaStatus, 0);
	SET_STA_CMD(g_otaParams.otaStatus, CMD_RUN_BOOT);
	SET_STA_FLAG(g_otaParams.otaStatus, FLAG_NORMAL);

	__ota_save_params(&g_otaParams);

    // Make sure continue last download task
    __otaRspStatus(OTA_DOWNLOAD_FILE, -28);
	__otaRspStatus(OTA_CURRENT_VERSION, 0);
	
    RunUVLoop(g_pMainLoop);

    return (0);
}

