#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <uv.h>
#include <dbus/dbus.h>
#include <errno.h>
#include <time.h>
#include <sqlite3.h>
#include <uthash/uthash.h>
#include <uthash/utlist.h>
#include <uthash/utstring.h>
#include <cjson/cJSON.h>

#include "log.h"
#include "crypto.h"
#include "json_struct.h"
#include "config_engine.h"
#include "skins_res.h"
#include "inet_api.h"

#define MAX_DOWNLOAD_ITEMS      (10)

#define DEBUG_SQL_CMD           (0)

typedef enum
{
	STATUS_CREATE_NEW	= 0,
	STATUS_DOWNLOADING,
	STATUS_DOWNLOADED,
} RES_DOWNLOAD_STATUS;

typedef enum
{
	UPG_STATUS_ADDNEW		= (1 << 0),
	UPG_STATUS_DL_NEW		= (1 << 1),
	UPG_STATUS_DOWNLOADED	= (1 << 2),
} UPGRADE_RES_STATUS;

typedef struct
{ 
    char*       pKeyName;
    char*       pResPath;

    UT_hash_handle  hh;         ///< UT Hash handle
} SKINS_CACHE_ITEM, *PSKINS_CACHE_ITEM;
static uv_rwlock_t g_SkinCacheRwLock;

#ifdef PLATFORM_CPU
#define DOWNLOAD_RES_ROOT_PATH			(DEF_SKINS_ROOT_PATH)
#else
#define DOWNLOAD_RES_ROOT_PATH			("/mnt/UDISK/resources/")
#endif

//select * FROM upgrade_skin WHERE resVersion == 4 ORDER BY resType DESC, RANDOM() LIMIT 1
//#define SKIN_GET_SQL_CMD        ("")

#define ATTACH_DB_SQL_CMD       ("ATTACH \'%s\' AS \'%s\'")
#define UPGRADE_TBL_SQL_CMD     ("CREATE TABLE IF NOT EXISTS %supgrade ("  \
	"ID             INTEGER PRIMARY KEY AUTOINCREMENT," \
	"keyName        TEXT     	    NOT NULL," \
    "resType        INTEGER         NOT NULL," \
	"priority       INTEGER         NOT NULL DEFAULT (1)," \
    "resVersion     TEXT            NOT NULL," \
	"localPath      TEXT            NOT NULL," \
	"serverURL      TEXT            NOT NULL," \
	"md5Chksum      TEXT            NOT NULL," \
	"resReadme      TEXT            NOT NULL DEFAULT (\'\')," \
	"resStatus      INTEGER         NOT NULL DEFAULT (1)," \
    "dlId           TEXT            NOT NULL DEFAULT (\'\')," \
    "dlStatus       INTEGER         NOT NULL DEFAULT (0)," \
    "dlRetrys       INTEGER         NOT NULL DEFAULT (0)," \
    "dlTime         TimeStamp       NOT NULL DEFAULT (datetime('now','localtime')));")

#define CREATE_TMP_TBL_SQL      "CREATE TABLE IF NOT EXISTS %stmp ("  \
	"ID             INTEGER PRIMARY KEY AUTOINCREMENT," \
	"localPath      TEXT           NOT NULL);"

static int                      g_isDlFinished      = FALSE;
static unsigned int             g_curDlItems        = 0;
static sqlite3*                 g_pMemDb            = NULL;
static uv_rwlock_t              g_uvSkinRwLock;
static uv_rwlock_t              g_uvDlRwLock;
static const unsigned int       g_tolDefaultSkins   = (sizeof(g_SkinDefaultResTable) / sizeof(g_SkinDefaultResTable[0]));
static PSKINS_CACHE_ITEM        g_pSkinCackeTbl = NULL;

char* TblGetSkinsResource(char *pKeyName, int *pResType, int *pVersion,  char **pComeFrom);
static int __getUpgradeInfo(const char *pUpdFilePath);
static int __reTryDlSkinResCb(void *pData, int argc, char **argv, char **azColName);
extern int InitSkinRomDatabase(sqlite3 *pDataBase);
static int g_isVerifyRes = FALSE;
static int g_isUpgradeRunning = FALSE;

static int __dumpSkinsInfo(PSKIN_RES_INFO pItem, int nItems)
{
    LOG_EX2(LOG_Debug, "Skins Items Information:\n");
    
    LOG_EX2(LOG_Debug, "-------------------------------------------------------------"
            "--------------------------------------------------------\n");

    LOG_EX2(LOG_Debug, "| Key Name |   Type   |           MD5 Checksum           |"
                    "                                      Contexts           "
                    "                         |\n");
    
    LOG_EX2(LOG_Debug, "-------------------------------------------------------------"
            "--------------------------------------------------------\n");

    for(int i = 0; i < nItems && pItem; i++, pItem++)
    {
        LOG_EX2(LOG_Debug, "| %-8s | %-8s | %-32s | %-80s |\n",
                pItem->pKeyName, pItem->pResVer, pItem->pMD5Chksum, pItem->pLocalPath);
    }
    
    LOG_EX2(LOG_Debug, "-------------------------------------------------------------"
            "--------------------------------------------------------\n");
}

static int __verifyResMD5Chksum(const char* pPath, const char* pChksum, int resType)
{
    const char*     pMD5Chksum;

    if(resType == TEXT_RES)
    {
        return (TRUE);
    }
    
    if(pPath == NULL || pChksum == NULL)
    {
        return (FALSE);
    }

    switch(resType)
    {
        case IMAGE_RES:
        case VOICE_RES:
            pMD5Chksum = EvpMD5HashFile(pPath);

            if(pMD5Chksum == NULL)
            {
                return (FALSE);
            }
            
            if(strcmp(pMD5Chksum, pChksum) != 0)
            {
                LOG_EX(LOG_Error, "%s MD5 Verify Error: %s -> %s\n", pPath, pMD5Chksum, pChksum);

                free((char*)pMD5Chksum);
                return (FALSE);
            }
            else
            {
                free((char*)pMD5Chksum);
            }
            
            break;

        default:
            return (FALSE);
    }

    return (TRUE);
}

static void __checkRes(sqlite3_context *pContext, int argc, sqlite3_value **argv)
{
    if(access(sqlite3_value_text(argv[0]), F_OK) != 0)
    {
        sqlite3_result_int(pContext, -1);
    }
    else if(__verifyResMD5Chksum(sqlite3_value_text(argv[0]), 
                                 sqlite3_value_text(argv[1]), 
                                 sqlite3_value_int(argv[2])) == FALSE)
    {
        sqlite3_result_int(pContext, -2);
    }
    else
    {
        sqlite3_result_int(pContext, 0);
    }
}

static int __cleanupSkinTbl(void)
{
    int         rc;
    char*       pErrMsg     = NULL;
    UT_string*  pSqlCmd = NULL;

    utstring_new(pSqlCmd);
    utstring_printf(pSqlCmd, "DROP TABLE %s.%s", SKIN_USER_DB, SKIN_TBL_NAME);

    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);
	
    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Delete Tbl %s Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
		utstring_free(pSqlCmd);
        return -ERR_SQLITE3_CREATE_TABLE;
    }

    utstring_renew(pSqlCmd);
    utstring_printf(pSqlCmd, CREATE_SKIN_TBL_SQL, SKIN_USER_DB".");

    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);
	
    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Create Tbl %s Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
		utstring_free(pSqlCmd);
        return -ERR_SQLITE3_CREATE_TABLE;
    }

    utstring_free(pSqlCmd);
    return 0;
}

static int __cleanupResTbl(void)
{
    int         rc;
    char*       pErrMsg     = NULL;
    UT_string*  pSqlCmd = NULL;

    utstring_new(pSqlCmd);
    utstring_printf(pSqlCmd, "DROP TABLE %s.%s", SKIN_USER_DB, RES_TBL_NAME);

    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);
	
    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Delete Tbl %s Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
		utstring_free(pSqlCmd);
        return -ERR_SQLITE3_CREATE_TABLE;
    }

    utstring_renew(pSqlCmd);
    utstring_printf(pSqlCmd, CREATE_RES_TBL_SQL, SKIN_USER_DB".");

    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);
	
    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Create Tbl %s Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
		utstring_free(pSqlCmd);
        return -ERR_SQLITE3_CREATE_TABLE;
    }

    utstring_free(pSqlCmd);
    return 0;
}

static int __cleanupUpgTbl(void)
{
    int         rc;
    char*       pErrMsg     = NULL;
    UT_string*  pSqlCmd = NULL;

    utstring_new(pSqlCmd);
    utstring_printf(pSqlCmd, "DROP TABLE %s.upgrade", SKIN_USER_DB);

    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);
	
    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Delete Tbl %s Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
		utstring_free(pSqlCmd);
        return -ERR_SQLITE3_CREATE_TABLE;
    }

    utstring_renew(pSqlCmd);
    utstring_printf(pSqlCmd, UPGRADE_TBL_SQL_CMD, SKIN_USER_DB".");
    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, 0, &pErrMsg);
	
    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Create Tbl %s Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
		utstring_free(pSqlCmd);
        return -ERR_SQLITE3_CREATE_TABLE;
    }

    utstring_free(pSqlCmd);
    return 0;
}

static int __cleanupTmpTbl(void)
{
    int         rc;
    char*       pErrMsg     = NULL;
    UT_string*  pSqlCmd = NULL;

    utstring_new(pSqlCmd);
    utstring_printf(pSqlCmd, "DROP TABLE main.tmp");

    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);
	
    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Delete Tbl %s Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
		utstring_free(pSqlCmd);
        return -ERR_SQLITE3_CREATE_TABLE;
    }

    utstring_renew(pSqlCmd);
    utstring_printf(pSqlCmd, CREATE_TMP_TBL_SQL, "main.");
    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, 0, &pErrMsg);
	
    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Create Tbl %s Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
		utstring_free(pSqlCmd);
        return -ERR_SQLITE3_CREATE_TABLE;
    }

    utstring_free(pSqlCmd);
    return 0;
}

static int __skinCreateCfgDB(void)
{
    int         rc          = 0;
    char*       pErrMsg     = NULL;
    UT_string	*pSqlCmd	= NULL;

	utstring_new(pSqlCmd);
	utstring_printf(pSqlCmd, "CREATE VIRTUAL TABLE main.%s USING %s;"
							 "CREATE VIRTUAL TABLE main.%s USING %s;",
					SKIN_TBL_NAME, SKIN_MODE_NAME, RES_TBL_NAME, RES_MODE_NAME);

    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);
	
    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Create Tbl %s Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
		utstring_free(pSqlCmd);
        return -ERR_SQLITE3_CREATE_TABLE;
    }

	utstring_free(pSqlCmd);

    return (0);
}

static int __skinCreateCfgFile(const char* pCfgFilePath)
{
    char*           pErrMsg     = NULL;
    int             rc          = 0;
    static sqlite3* pSqlFileDB  = NULL;
    UT_string       *pSqlCmd    = NULL;

    rc = sqlite3_open(":memory:", &g_pMemDb);

    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "sqlite3_open memory: %s\n", pCfgFilePath);
        return -ERR_OPEN_SQLITE3_DB;
    }

    utstring_new(pSqlCmd);
    utstring_printf(pSqlCmd, CREATE_TMP_TBL_SQL, "");

    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, 0, &pErrMsg);

    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Create Tbl %s Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
        unlink(pCfgFilePath);
		sqlite3_close(g_pMemDb);
        utstring_free(pSqlCmd);
        return -ERR_SQLITE3_CREATE_TABLE;
    }

    rc = sqlite3_open_v2(pCfgFilePath, &pSqlFileDB, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);

    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "sqlite3_open_v2 error: %s\n", pCfgFilePath);
        sqlite3_close(g_pMemDb);
        utstring_free(pSqlCmd);
        return -ERR_OPEN_SQLITE3_DB;
    }

    utstring_renew(pSqlCmd);
    utstring_printf(pSqlCmd, CREATE_SKIN_TBL_SQL""CREATE_RES_TBL_SQL, "", "");

    rc = sqlite3_exec(pSqlFileDB, utstring_body(pSqlCmd), NULL, 0, &pErrMsg);

    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Create Tbl %s Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
        unlink(pCfgFilePath);
		sqlite3_close(g_pMemDb);
        utstring_free(pSqlCmd);
        return -ERR_SQLITE3_CREATE_TABLE;
    }

    utstring_renew(pSqlCmd);
    utstring_printf(pSqlCmd, UPGRADE_TBL_SQL_CMD, "");
    rc = sqlite3_exec(pSqlFileDB, utstring_body(pSqlCmd), NULL, 0, &pErrMsg);

    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Create Tbl %s Error: %s\n", UPGRADE_TBL_SQL_CMD, pErrMsg);
        sqlite3_free(pErrMsg);
        unlink(pCfgFilePath);
		sqlite3_close(g_pMemDb);
        utstring_free(pSqlCmd);
        return -ERR_SQLITE3_CREATE_TABLE;
    }

    sqlite3_close(pSqlFileDB);

    utstring_renew(pSqlCmd);
    utstring_printf(pSqlCmd, ATTACH_DB_SQL_CMD, pCfgFilePath, SKIN_USER_DB);

    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, 0, &pErrMsg);
    
    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Attach Database %s Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        utstring_free(pSqlCmd);
        sqlite3_free(pErrMsg);
        unlink(pCfgFilePath);
		sqlite3_close(g_pMemDb);
        return -ERR_SQLITE3_CREATE_TABLE;
    }

    utstring_free(pSqlCmd);

    rc = sqlite3_create_function(g_pMemDb, "ResCheck", 3, SQLITE_UTF8, NULL, __checkRes, NULL, NULL);

    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Don't Support Resurces Verify Function\n");
    }

    if(InitSkinRomDatabase(g_pMemDb) != 0)
    {
		sqlite3_close(g_pMemDb);
        return (-ERR_SQL_REG_MODULE);
    }

	rc = __skinCreateCfgDB();

	if(rc != 0)
	{
		sqlite3_close(g_pMemDb);
		return rc;
	}

    return (0);
}

static void __onDlFileCb(void *pData, unsigned int size, 
                         const char *pReqUrl, const char* pDlPath, 
                         const char *pTaskUuid, int iFinished, void *pUserData)
{
    char* pId = (char*)pUserData;
    UT_string *pSqlCmd  = NULL;
    int     rc          = 0;
    char*   pErrMsg     = NULL;

    if(g_curDlItems > 0)
    {
        g_curDlItems--;
    }

    if(iFinished == 0 && pId != NULL)
    {
        //LOG_EX(LOG_Debug, "Request(%s): [%s] --> [%s] Response: [%u] OK\n", pTaskUuid, pReqUrl, pDlPath, size);

        utstring_new(pSqlCmd);
        utstring_printf(pSqlCmd, "UPDATE %s.upgrade SET dlStatus = %d, dlRetrys = %d, "
                                 "dlTime = datetime(\'now\', \'localtime\') WHERE ID = %s AND "
                                 "ResCheck(localPath, md5Chksum, resType) = 0",
                        SKIN_USER_DB, STATUS_DOWNLOADED, 0, pId);

        rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);

        if(rc != SQLITE_OK)
        {
            LOG_EX(LOG_Error, "SQL Query Error: %s\n", pErrMsg);
            sqlite3_free(pErrMsg);
        }

        utstring_free(pSqlCmd);
    }
    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 %s Error: %d\n", pReqUrl, iFinished);
        }

        utstring_new(pSqlCmd);
        utstring_printf(pSqlCmd, "SELECT * FROM %s.upgrade WHERE ID = %s AND dlRetrys < 5 ", SKIN_USER_DB, pId);

        //fprintf(stdout, "SQL:\n%s\n", utstring_body(pSqlCmd));
        rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), __reTryDlSkinResCb, NULL, &pErrMsg);
        if(rc != SQLITE_OK)
        {
            LOG_EX(LOG_Error, "SQL %s Query Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
            sqlite3_free(pErrMsg);
        }

        utstring_free(pSqlCmd);
    }

    if(pId)
    {
        free(pId);
    }
}

static int __reTryDlSkinResCb(void *pData, int argc, char **argv, char **azColName)
{
    UT_string *pSqlCmd  = NULL;
    char*   pErrMsg     = NULL;
    
    const char* pDlTaskId = InetHttpDlFileAsync(argv[6], 
                            argv[5],
                            __onDlFileCb,
                            NULL,
                            strdup(argv[0]));

    if(pDlTaskId != NULL)
    {
        int  rc = 0;

        utstring_new(pSqlCmd);
        utstring_printf(pSqlCmd, "UPDATE %s.upgrade SET dlId = \'%s\', dlRetrys = dlRetrys + 1, "
                                 "dlTime = datetime(\'now\', \'localtime\') WHERE ID = %s",
                        SKIN_USER_DB, pDlTaskId, argv[0]);

        rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);

        if(rc != SQLITE_OK)
        {
            LOG_EX(LOG_Error, "SQL Query Error: %s\n", pErrMsg);
            sqlite3_free(pErrMsg);
        }

        utstring_free(pSqlCmd);
    }

    return 0;
}

static int __loadDownloadSkinsCb(void *pData, int argc, char **argv, char **azColName)
{
    const char *pDlTaskId;
	char* pItemId	= argv[0];
	char* pURL		= argv[6];
	char* pDlPath	= argv[5];
	char* pDlId		= argv[10];
    int dlStatus    = strtol(argv[3], NULL, 10); 
    int dlRetry     = strtol(argv[4], NULL, 10) + 1; 
  
    if(dlStatus == STATUS_DOWNLOADING)
    {
		if(strlen(pDlId) > 0)
		{
			InetCancelDownload(pDlId);
		}

        dlRetry++;
    }

    //LOG_EX(LOG_Debug, "Download: %s --> %s\n", pURL, pDlPath);

    pDlTaskId = InetHttpDlFileAsync(pURL, 
                            pDlPath,
                            __onDlFileCb,
                            NULL,
                            strdup(pItemId));

    if(pDlTaskId)
    {
        UT_string *pSqlCmd  = NULL;
        int     rc          = 0;
        char*   pErrMsg     = NULL;

        g_curDlItems++;

        utstring_new(pSqlCmd);
        utstring_printf(pSqlCmd, "UPDATE %s.upgrade SET dlStatus = %d, dlRetrys = %d, "
                                 "dlId = \'%s\', dlTime = datetime(\'now\', \'localtime\') WHERE ID = %s",
                        SKIN_USER_DB, STATUS_DOWNLOADING, dlRetry, pDlTaskId, pItemId);
#if DEBUG_SQL_CMD
        LOG_EX(LOG_Debug, "SQL CMD: [%s]\n", utstring_body(pSqlCmd));
#endif

        rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);

        if(rc != SQLITE_OK)
        {
            LOG_EX(LOG_Error, "SQL Query Error: %s\n", pErrMsg);
            sqlite3_free(pErrMsg);
        }

        utstring_free(pSqlCmd);
    }
    else
    {
        LOG_EX(LOG_Error, "Download %s error\n", pURL);
    }

	return (0);
}

static void __uvDownloadResThread(void *pParams)
{
	char*   	pErrMsg = NULL;
	UT_string*  pSqlCmd = NULL;

    LOG_EX(LOG_Debug, "Beging Download Skins\n");

    while(!g_isDlFinished)
    {
        int rc = 0;

        utstring_new(pSqlCmd);
        utstring_printf(pSqlCmd, "SELECT *, STRFTIME(\"%%s\", datetime(\'now\', \'localtime\')) "
							"- STRFTIME(\"%%s\", upgrade.dlTime) AS diff FROM %s.upgrade "
							 "WHERE resStatus = %d AND (dlRetrys < 5 OR diff > 600) "
                             "AND (dlStatus = %d OR (dlStatus = %d AND diff > 600)) "
                             "LIMIT 10 - %d",
					SKIN_USER_DB, UPG_STATUS_DL_NEW, STATUS_CREATE_NEW, STATUS_DOWNLOADING, g_curDlItems);
#if DEBUG_SQL_CMD
        LOG_EX(LOG_Debug, "SQL CMD: [%s]\n", utstring_body(pSqlCmd));
#endif        
        rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), __loadDownloadSkinsCb, NULL, &pErrMsg);

        if(rc != SQLITE_OK)
        {
            LOG_EX(LOG_Error, "SQL Query Error: %s\n", pErrMsg);
            sqlite3_free(pErrMsg);
        }

        utstring_free(pSqlCmd);
        
        sleep(1);
    }

    LOG_EX(LOG_Debug, "Download Skins Finished\n");

    pthread_detach(pthread_self());
}

static int __sqlCbRemoveUnusedFile(void *pData, int argc, char **argv, char **azColName)
{
    LOG_EX(LOG_Debug, "Clean up resources: [%s]\n", argv[1]);

    if(access(argv[1], F_OK) == 0
       && strncmp(argv[1], DEF_SKINS_ROOT_PATH, strlen(DEF_SKINS_ROOT_PATH)) != 0)
    {
        unlink(argv[1]);
    }
}

static int __createResTblCb(void *pData, int argc, char **argv, char **azColName)
{
    int     	rc      = 0;
	char*   	pErrMsg = NULL;
	UT_string*  pSqlCmd = NULL;

    //DEBUG_SQL_CALLBACK_DATA(argc, argv, azColName);

    utstring_new(pSqlCmd);
    utstring_printf(pSqlCmd, "INSERT INTO %s.%s "
                             "VALUES (NULL, \'%s\', \'%s\', \'%s\', \'%s\')",
                        SKIN_USER_DB, RES_TBL_NAME, argv[0], argv[1], argv[2], argv[3]);
    
    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);

    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "[%s] Query Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
        utstring_free(pSqlCmd);
        return -1;
    }

    utstring_free(pSqlCmd);

    return 0;
}

static int __createSkinTblCb(void *pData, int argc, char **argv, char **azColName)
{
    int     	rc      = 0;
	char*   	pErrMsg = NULL;
	UT_string*  pSqlCmd = NULL;

    //DEBUG_SQL_CALLBACK_DATA(argc, argv, azColName);

    utstring_renew(pSqlCmd);
    utstring_printf(pSqlCmd, "INSERT INTO %s.%s "
                             "VALUES (NULL, \'%s\', %s, %s, %s, \'%s\')",
                    SKIN_USER_DB, SKIN_TBL_NAME, 
                    argv[1], argv[2], argv[3], argv[0], argv[4]);

    //fprintf(stdout, "AutoId: %lld: %s\n", sqlite3_last_insert_rowid(g_pMemDb), argv[3]);
    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);

    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "[%s] Query Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
        return -1;
    }

    utstring_free(pSqlCmd);
    return 0;
}

static void __uvSyncResThread(void *pParams)
{
    int tolCnt = 0;
    PSKINS_CACHE_ITEM pItem = NULL, pTemp = NULL;

    LOG_EX(LOG_Debug, "Beging Sync Download Skins\n");

    while(TRUE)
    {
        UT_string *pSqlCmd  = NULL;
        int     rc          = 0;
        char*   pErrMsg     = NULL;
        char    **pResult   = NULL;
        int     iRow = 0, iCol = 0;
        int     nItems      = 0;

        utstring_new(pSqlCmd);
        utstring_printf(pSqlCmd, "SELECT COUNT(*) FROM %s.upgrade "
                                 "WHERE resStatus = %d AND dlStatus != %d", 
                        SKIN_USER_DB, UPG_STATUS_DL_NEW, STATUS_DOWNLOADED);
#if DEBUG_SQL_CMD
        LOG_EX(LOG_Debug, "SQL CMD: [%s]\n", utstring_body(pSqlCmd));
#endif        
        rc = sqlite3_get_table(g_pMemDb, utstring_body(pSqlCmd), &pResult, &iRow, &iCol, &pErrMsg);

        if(rc != SQLITE_OK)
        {
            LOG_EX(LOG_Error, "Run SQL \n[%s]\n Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
            sqlite3_free(pErrMsg);
        }

        utstring_free(pSqlCmd);

        nItems = strtol(pResult[1], NULL, 10);
        sqlite3_free_table(pResult);

        if(tolCnt++ % 20 == 0)
        {
            LOG_EX(LOG_Debug, "Download: Remain %d\n", nItems);
        }

        if(nItems == 0)
        {
            g_isDlFinished = TRUE;
            utstring_new(pSqlCmd);
            sqlite3_exec(g_pMemDb, "BEGIN TRANSACTION;", NULL, NULL, NULL);

            utstring_printf(pSqlCmd, "INSERT INTO main.tmp "
                                     "SELECT %s.ID, %s.localPath FROM %s.%s "
                                     "LEFT JOIN %s.upgrade on %s.md5Chksum = upgrade.md5Chksum "
                                     "WHERE upgrade.md5Chksum IS NULL;",
                                     RES_TBL_NAME, RES_TBL_NAME, SKIN_USER_DB, RES_TBL_NAME, 
                                     SKIN_USER_DB, RES_TBL_NAME);
#if DEBUG_SQL_CMD
            LOG_EX(LOG_Debug, "SQL CMD: [%s]\n", utstring_body(pSqlCmd));
#endif
            rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);

            if(rc != SQLITE_OK)
            {
               LOG_EX(LOG_Error, "Run SQL \n[%s]\n Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
               sqlite3_free(pErrMsg);
               sqlite3_exec(g_pMemDb, "ROLLBACK;", NULL, NULL, NULL);
               utstring_free(pSqlCmd);
               break;
            }

            __cleanupSkinTbl();
            __cleanupResTbl();
            
            utstring_renew(pSqlCmd);

            utstring_printf(pSqlCmd, "SELECT resVersion, localPath, serverURL, md5Chksum, "
                                     "keyName, resType, priority, resReadme, resStatus, dlStatus "
                                     "FROM %s.upgrade WHERE resStatus = %d "
                                     "OR (resStatus = %d AND dlStatus = %d) "
                                     "GROUP BY md5Chksum",
                            SKIN_USER_DB, UPG_STATUS_DOWNLOADED, 
                            UPG_STATUS_DL_NEW, STATUS_DOWNLOADED);
#if DEBUG_SQL_CMD
            LOG_EX(LOG_Debug, "SQL CMD: [%s]\n", utstring_body(pSqlCmd));
#endif
            rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), __createResTblCb, NULL, &pErrMsg);

            if(rc != SQLITE_OK)
            {
                LOG_EX(LOG_Error, "Run SQL \n[%s]\n Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
                sqlite3_free(pErrMsg);
                sqlite3_exec(g_pMemDb, "ROLLBACK;", NULL, NULL, NULL);
                utstring_free(pSqlCmd);
                break;
            }
            else
            {
                utstring_renew(pSqlCmd);
                utstring_printf(pSqlCmd, "SELECT %s.ID, keyName, resType, priority, "
                                         "resReadme, resStatus, dlStatus, %s.md5Chksum "
                                     "FROM %s.upgrade, %s.res WHERE %s.md5Chksum = upgrade.md5Chksum"
                                     " AND (resStatus = %d OR (resStatus = %d AND dlStatus = %d)) "
                                     "ORDER BY keyName",
                                RES_TBL_NAME, RES_TBL_NAME, SKIN_USER_DB, SKIN_USER_DB, RES_TBL_NAME,
                                UPG_STATUS_DOWNLOADED, UPG_STATUS_DL_NEW, STATUS_DOWNLOADED);
#if DEBUG_SQL_CMD
                LOG_EX(LOG_Debug, "SQL CMD: [%s]\n", utstring_body(pSqlCmd));
#endif
                rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), __createSkinTblCb, NULL, &pErrMsg);

                if(rc != SQLITE_OK)
                {
                    LOG_EX(LOG_Error, "Run SQL \n[%s]\n Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
                    sqlite3_free(pErrMsg);
                    sqlite3_exec(g_pMemDb, "ROLLBACK;", NULL, NULL, NULL);
                    utstring_free(pSqlCmd);
                    break;
                }
                else
                {
                    sqlite3_exec(g_pMemDb, "SELECT * FROM main.tmp", __sqlCbRemoveUnusedFile, NULL, NULL);
                    utstring_free(pSqlCmd);

                    if(__cleanupUpgTbl() != 0)
                    {
                        sqlite3_exec(g_pMemDb, "ROLLBACK;", NULL, NULL, NULL);
                    }
                    else
                    {
                        sqlite3_exec(g_pMemDb, "END TRANSACTION;", NULL, NULL, NULL);
                        break;
                    }
                }
            }
        }

        sleep(1);
    }
    
    LOG_EX(LOG_Debug, "Sync Download Skins Finished\n");
    
    uv_rwlock_wrlock(&g_SkinCacheRwLock);
    HASH_ITER(hh, g_pSkinCackeTbl, pItem, pTemp)
    {
        free(pItem->pKeyName);
        free(pItem->pResPath);
        free(pItem);
        pItem = NULL;
    }

    g_pSkinCackeTbl = NULL;
    uv_rwlock_wrunlock(&g_SkinCacheRwLock);

    g_isUpgradeRunning = FALSE;
    pthread_detach(pthread_self());
}

static int __upgStatusTblCb(void *pData, int argc, char **argv, char **azColName)
{
    int     	rc      = 0;
	char*   	pErrMsg = NULL;
	UT_string*  pSqlCmd = NULL;

    DEBUG_SQL_CALLBACK_DATA(argc, argv, azColName);
    
    utstring_new(pSqlCmd);
    utstring_printf(pSqlCmd, "UPDATE %s.upgrade SET resStatus = %d WHERE ID = %s",
                        SKIN_USER_DB, UPG_STATUS_DL_NEW, argv[2]);
    
    rc = sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, NULL, &pErrMsg);

    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "[%s] Query Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
        utstring_free(pSqlCmd);
        return -1;
    }
    
    utstring_free(pSqlCmd);

    return 0;
}

static int __saveUpgradeInfo(PSKIN_RES_INFO pInfo, int nItems)
{
    int         isError = FALSE;
    char*       pErrMsg = NULL;
	UT_string*  pSqlCmd = NULL;

    if(pInfo == NULL || nItems <= 0)
    {
        return (-ERR_INPUT_PARAMS);
    }

	utstring_new(pSqlCmd);

    sqlite3_exec(g_pMemDb, "BEGIN TRANSACTION;", NULL, NULL, NULL);
    __cleanupUpgTbl();
    __cleanupTmpTbl();

    for(int i = 0; i < nItems; i++)
    {
        UT_string*      pPath      = NULL;
        PSKIN_RES_INFO  pItem       = &pInfo[i];

        utstring_new(pPath);
        utstring_renew(pSqlCmd);

#if 0
        if(pItem->resType == VOICE_RES)
        {
            utstring_printf(pPath, "%svoice/%s", DOWNLOAD_RES_ROOT_PATH, basename_v2(pItem->pLocalPath));
        }

        else if(pItem->resType == IMAGE_RES)
        {
            utstring_printf(pPath, "%simage/%s", DOWNLOAD_RES_ROOT_PATH, basename_v2(pItem->pLocalPath));
        }
        else if(pItem->resType == TEXT_RES)
        {
            utstring_printf(pPath, "%s", pItem->pLocalPath);
        }
        else
        {
            utstring_free(pPath);
            continue;
        }
#else
        utstring_printf(pPath, "%svoice/%s", DOWNLOAD_RES_ROOT_PATH, basename_v2(pItem->pLocalPath));
#endif        
        utstring_printf(pSqlCmd, "INSERT INTO %s.upgrade (keyName, resType, resVersion, localPath, serverURL, md5Chksum)"
                           " SELECT \'%s\', %d, \'%s\', \'%s\', \'%s\', \'%s\' ",
                        SKIN_USER_DB, pItem->pKeyName, pItem->resType, pItem->pResVer, utstring_body(pPath), 
						pItem->pLocalPath, pItem->pMD5Chksum);

#if DEBUG_SQL_CMD
        LOG_EX(LOG_Debug, "SQL CMD: [%s]\n", utstring_body(pSqlCmd));
#endif
                        
        if((sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, 0, &pErrMsg) != SQLITE_OK))
        {
            LOG_EX(LOG_Error, "Add Skins[%d] %s To Database Error: %s\n", i, pItem->pKeyName, utstring_body(pSqlCmd));
            LOG_EX(LOG_Error, "SQL Error: %s\n", pErrMsg);
            sqlite3_free(pErrMsg);
            isError = TRUE;
            utstring_free(pPath);
            break;
        }

        utstring_free(pPath);
    }

	utstring_renew(pSqlCmd);

	utstring_printf(pSqlCmd, "UPDATE %s.upgrade SET resStatus = %d "
							 "WHERE EXISTS (SELECT %s.keyName, upgrade.md5Chksum FROM main.%s, user_db.upgrade "
                            "WHERE upgrade.keyName = %s.keyName)",
					SKIN_USER_DB, UPG_STATUS_DL_NEW, 
					SKIN_TBL_NAME, SKIN_TBL_NAME, 
					SKIN_TBL_NAME);

#if DEBUG_SQL_CMD
        LOG_EX(LOG_Debug, "SQL CMD: [%s]\n", utstring_body(pSqlCmd));
#endif

	if(sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), __upgStatusTblCb, 0, &pErrMsg) != SQLITE_OK)
	{
		LOG_EX(LOG_Error, "Upgrade Status: %s\n", utstring_body(pSqlCmd));
		LOG_EX(LOG_Error, "SQL Error: %s\n", pErrMsg);
		sqlite3_free(pErrMsg);
        isError = TRUE;
	}

	utstring_renew(pSqlCmd);
	
	utstring_printf(pSqlCmd, "UPDATE %s.upgrade SET resStatus = %d "
							"WHERE EXISTS (SELECT %s.md5Chksum FROM %s.%s "
                            "WHERE upgrade.md5Chksum = %s.md5Chksum);"
							"UPDATE %s.upgrade SET resStatus = %d, localPath = "
                            "(SELECT %s.localPath FROM main.%s "
                            "WHERE upgrade.md5Chksum = %s.md5Chksum) "
							"WHERE EXISTS (SELECT %s.md5Chksum FROM main.%s "
                            "WHERE upgrade.md5Chksum = %s.md5Chksum);",
						SKIN_USER_DB, UPG_STATUS_DOWNLOADED, 
						RES_TBL_NAME, SKIN_USER_DB, RES_TBL_NAME,
						RES_TBL_NAME,
						SKIN_USER_DB, UPG_STATUS_DOWNLOADED, 
                        RES_TBL_NAME, RES_TBL_NAME, RES_TBL_NAME,
						RES_TBL_NAME, RES_TBL_NAME, RES_TBL_NAME);

#if DEBUG_SQL_CMD
    LOG_EX(LOG_Debug, "SQL CMD: [%s]\n", utstring_body(pSqlCmd));
#endif

	if(sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), NULL, 0, &pErrMsg) != SQLITE_OK)
	{
		LOG_EX(LOG_Error, "Upgrade Status: %s\n", utstring_body(pSqlCmd));
		LOG_EX(LOG_Error, "SQL Error: %s\n", pErrMsg);
		sqlite3_free(pErrMsg);
        isError = TRUE;
	}
   
	utstring_free(pSqlCmd);


    if(isError == TRUE)
    {
        sqlite3_exec(g_pMemDb, "ROLLBACK;", NULL, NULL, NULL);
        return (-ERR_SQL_QUERY);
    }
    else
    {
        uv_thread_t uvSyncThread, uvDlThread;
        sqlite3_exec(g_pMemDb, "END TRANSACTION;", NULL, NULL, NULL);
        g_curDlItems = 0;
        g_isDlFinished = FALSE;
        uv_thread_create(&uvDlThread, __uvDownloadResThread, NULL);
        uv_thread_create(&uvSyncThread, __uvSyncResThread, NULL);
    }

    return 0;
}

static int __loadPreUpgradeInfo(void)
{
    UT_string *pSqlCmd  = NULL;
    int     rc          = 0;
    char*   pErrMsg     = NULL;
    char    **pResult   = NULL;
    int     iRow = 0, iCol = 0;
    int     nItems = 0;

    utstring_new(pSqlCmd);
    utstring_printf(pSqlCmd, "SELECT COUNT(*) FROM %s.upgrade WHERE resStatus = %d AND dlStatus != %d",
                    SKIN_USER_DB, UPG_STATUS_DL_NEW, STATUS_DOWNLOADED);
    
    rc = sqlite3_get_table(g_pMemDb, utstring_body(pSqlCmd), &pResult, &iRow, &iCol, &pErrMsg);

    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Run SQL \n[%s]\n Error: %s\n", utstring_body(pSqlCmd), pErrMsg);
        sqlite3_free(pErrMsg);
    }

    utstring_free(pSqlCmd);

    nItems = strtol(pResult[1], NULL, 10);
    sqlite3_free_table(pResult);

    if(nItems > 0)
    {
        uv_thread_t uvSyncThread, uvDlThread;
        g_curDlItems = 0;
        g_isDlFinished = FALSE;
        g_isUpgradeRunning = TRUE;
        uv_thread_create(&uvDlThread, __uvDownloadResThread, NULL);
        uv_thread_create(&uvSyncThread, __uvSyncResThread, NULL);
        LOG_EX(LOG_Debug, "Load Pre Download Items: %d\n", nItems);
    }
}

static int __getEnumResInfo(void)
{
    __saveUpgradeInfo((PSKIN_RES_INFO)g_emuUpgradeInfo, sizeof(g_emuUpgradeInfo) / sizeof(g_emuUpgradeInfo[0]));
}

int SkinUpgrade(char* pUpdData)
{
    int nItems = 0;
    PSKIN_RES_INFO pUpgResInfo;
    cJSON *pRoot, *pList;

#if 1
    FILE *pFile = fopen("/mnt/UDISK/skinupgrade.txt", "w+");
    fwrite(pUpdData, 1, strlen(pUpdData), pFile);
    fclose(pFile);
#endif

	if (pUpdData == NULL || strlen(pUpdData) == 0)
    {
        LOG_EX(LOG_Error, "Input Params Error\n");
        return (-ERR_INPUT_PARAMS);
    }

    LOG_EX(LOG_Debug, "Upgrade Cmd: [%s]\n", pUpdData);
    //fprintf(stdout, "%s\n", pUpdData);

    pRoot = cJSON_Parse(pUpdData);

    if(pRoot == NULL)
    {
        LOG_EX(LOG_Error, "Parse Json Error\n");
        return (-ERR_NO_ITEMS);
    }

    nItems = cJSON_GetArraySize(pRoot);

    if(nItems <= 0)
    {
        LOG_EX(LOG_Error, "Upgrade Item: %d\n", nItems);
        cJSON_Delete(pRoot);
        return (-ERR_NO_ITEMS);
    }
    
    pUpgResInfo = (PSKIN_RES_INFO)malloc(sizeof(SKIN_RES_INFO) * nItems);
    
    if(pUpgResInfo == NULL)
    {
        LOG_EX(LOG_Error, "Malloc %d Memory Error\n", nItems);
        cJSON_Delete(pRoot);
        return (-ERR_MALLOC_MEMORY);
    }
    
    LOG_EX(LOG_Debug, "Array Size = %d\n", nItems);

    memset(pUpgResInfo, 0, sizeof(SKIN_RES_INFO) * nItems);
    pList = pRoot->child;

    for(int i = 0; i < nItems; i++)
    {
        pUpgResInfo[i].resType      = VOICE_RES;//cJSON_GetObjectItem(pList, "resourceType")->valueint;
        pUpgResInfo[i].pResVer      = strdup(cJSON_GetObjectItem(pList, "resourceVersion")->valuestring);
        pUpgResInfo[i].pKeyName     = strdup(cJSON_GetObjectItem(pList, "resourceName")->valuestring);
        pUpgResInfo[i].pLocalPath   = strdup(cJSON_GetObjectItem(pList, "url")->valuestring);
        pUpgResInfo[i].pMD5Chksum   = strdup(cJSON_GetObjectItem(pList, "md5")->valuestring);

        pList = pList->next;
    }
    
    cJSON_Delete(pRoot);

    g_isUpgradeRunning = TRUE;
    __dumpSkinsInfo(pUpgResInfo, nItems);
	__saveUpgradeInfo(pUpgResInfo, nItems);

    free(pUpgResInfo);
}

char* SQLiteGetSkinsResource(char *pKeyName, int *pResType, int *pVersion,  char **pComeFrom)
{
    char        *pSkinPath  = strdup("");
    int         rc          = 0;
    char*       pErrMsg     = NULL;
    UT_string   *pSqlCmd    = NULL;
    char        **pResult   = NULL;
    int         iRow = 0, iCol = 0;
    int         queCol  = 13;
    int         colPath = 21;

    utstring_new(pSqlCmd);

    if(g_isVerifyRes)
    {
        utstring_printf(pSqlCmd, "SELECT *, RANDOM() AS rnd, ResCheck(localPath, md5Chksum, resType) as resOK "
                                 "FROM %s.%s, %s.%s\n"
                                 "WHERE %s.keyName = \'%s\' AND resOK = 0 AND %s.resId = %s.ID\n"
                                 "UNION\n"
                                 "SELECT *, RANDOM() AS rnd, ResCheck(localPath, md5Chksum, resType) as resOK "
                                 "FROM main.%s, main.%s\n"
                                 "WHERE %s.keyName = \'%s\' AND resOK = 0 AND %s.resId = %s.ID\n"
                                 "ORDER BY priority DESC, rnd "
                                 "LIMIT 1", 
                        SKIN_USER_DB, SKIN_TBL_NAME, SKIN_USER_DB, RES_TBL_NAME, 
                        SKIN_TBL_NAME, pKeyName, SKIN_TBL_NAME, RES_TBL_NAME,
                        SKIN_TBL_NAME, RES_TBL_NAME, 
                        SKIN_TBL_NAME, pKeyName, SKIN_TBL_NAME, RES_TBL_NAME);
    }
    else 
    {
        utstring_printf(pSqlCmd, "SELECT *, RANDOM() AS rnd "
                                 "FROM %s.%s, %s.%s\n"
                                 "WHERE %s.keyName = \'%s\' AND %s.resId = %s.ID\n"
                                 "UNION\n"
                                 "SELECT *, RANDOM() AS rnd "
                                 "FROM main.%s, main.%s\n"
                                 "WHERE %s.keyName = \'%s\' AND %s.resId = %s.ID\n"
                                 "ORDER BY priority DESC, rnd "
                                 "LIMIT 1", 
                        SKIN_USER_DB, SKIN_TBL_NAME, SKIN_USER_DB, RES_TBL_NAME, 
                        SKIN_TBL_NAME, pKeyName, SKIN_TBL_NAME, RES_TBL_NAME,
                        SKIN_TBL_NAME, RES_TBL_NAME, 
                        SKIN_TBL_NAME, pKeyName, SKIN_TBL_NAME, RES_TBL_NAME);
        queCol = 12;
        colPath = 20;
    }

    rc = sqlite3_get_table(g_pMemDb, utstring_body(pSqlCmd), &pResult, &iRow, &iCol, &pErrMsg);

    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Run SQL \n[%s]\n Error: %s(%d)\n", utstring_body(pSqlCmd), pErrMsg, rc);
        sqlite3_free(pErrMsg);
        utstring_free(pSqlCmd);
        return (pSkinPath);
    }

    if(iRow == 1 && iCol == queCol)
    {

        if(access(pResult[colPath], F_OK) != 0)
        {
            LOG_EX(LOG_Error, "Res %s --> %s not exists\n", pKeyName, pResult[colPath]);
        }
        else
        {
            free(pSkinPath);
            pSkinPath = strdup(pResult[colPath]);
        }
#if 0
        for(int i = 0; i <= 21; i++)
        {
            LOG_EX(LOG_Debug, "[%d]: {%s}\n", i, pResult[i]);
        }
#endif
    }

#if 0
    int index = iCol;
    for(int i = 0; i < iRow; i++)
    {
        fprintf(stdout, "----------%d[%d, %d]---------\n", i, iRow, iCol);

        for(int j = 0; j < iCol; j++)
        {
            int tblCol = i * iCol + j;

            fprintf(stdout, "%s(%d, %d) = [%s]\n", pResult[tblCol], j, index, pResult[index]);
            index++;
        }
    }
#endif

    sqlite3_free_table(pResult);
    utstring_free(pSqlCmd);

    if(strcmp(pSkinPath, "") == 0)
    {
        free(pSkinPath);
        return TblGetSkinsResource(pKeyName, pResType, pVersion, pComeFrom);
    }
    else
    {
        return (pSkinPath);
    }
}

char* TblGetSkinsResource(char *pKeyName, int *pResType, int *pVersion,  char **pComeFrom)
{
    
    for(int i = 0; i < sizeof(g_SkinDefaultResTable) / sizeof(g_SkinDefaultResTable[0]); i++)
    {
        if(strcmp(g_SkinDefaultResTable[i].pKeyName, pKeyName) == 0)
        {
            return strdup(g_SkinDefaultResTable[i].pLocalPath);
        }
    }

    return strdup("");
}

char* GetSkinsResource(char *pKeyName, int *pResType, int *pVersion,  char **pComeFrom)
{
    if(g_isUpgradeRunning)
    {
        return TblGetSkinsResource(pKeyName, pResType, pVersion, pComeFrom);
    }
    else
    {
        return SQLiteGetSkinsResource(pKeyName, pResType, pVersion, pComeFrom);
    }
}

unsigned int SkinsDefaultSize(void)
{
    return (g_tolDefaultSkins + 1);
}

PSKIN_RES_INFO SkinsItemById(int iId)
{
    if(iId < 0 || iId > g_tolDefaultSkins)
    {
        return (NULL);
    }

    return ((PSKIN_RES_INFO)&g_SkinDefaultResTable[iId - 1]);
}

static int __verifyResValuableCb(void *pData, int argc, char **argv, char **azColName)
{
    char sqlCmd[256];
    char*   pErrMsg     = NULL;

    if(argc < 3)
    {
        return 0;
    }

    sqlite3_exec(g_pMemDb, "BEGIN TRANSACTION;", NULL, NULL, NULL);
    for(int i = 0; i < argc; i++)
    {
        if(strcmp(azColName[i], "ID") == 0)
        {
            LOG_EX(LOG_Error, "Cleanup Bad Resources: %s\n", argv[i + 1]);

            memset(sqlCmd, 0, 256);
            sprintf(sqlCmd, "DELETE FROM %s.%s WHERE ID = %s", SKIN_USER_DB, RES_TBL_NAME, argv[i]);

            if(sqlite3_exec(g_pMemDb, sqlCmd, __verifyResValuableCb, 0, &pErrMsg) != SQLITE_OK)
        	{
        		LOG_EX(LOG_Error, "SQL: %s\nError: %s\n", sqlCmd, pErrMsg);
        		sqlite3_free(pErrMsg);
                sqlite3_exec(g_pMemDb, "ROLLBACK;", NULL, NULL, NULL);
                return -1;
        	}

            unlink(argv[i + 1]);
        }
    }
    sqlite3_exec(g_pMemDb, "END TRANSACTION;", NULL, NULL, NULL);

    return (0);
}

static void VerifySkinResInfo(void)
{
    UT_string *pSqlCmd  = NULL;
    char*   pErrMsg     = NULL;
    
    utstring_new(pSqlCmd);
    utstring_printf(pSqlCmd, "SELECT ID, localPath, md5Chksum FROM %s.%s "
                             "WHERE ResCheck(localPath, md5Chksum, 1) != 0", SKIN_USER_DB, RES_TBL_NAME);

    if(sqlite3_exec(g_pMemDb, utstring_body(pSqlCmd), __verifyResValuableCb, 0, &pErrMsg) != SQLITE_OK)
	{
		LOG_EX(LOG_Error, "SQL Error: %s\n", pErrMsg);
		sqlite3_free(pErrMsg);
        utstring_free(pSqlCmd);
        return;
	}

    utstring_free(pSqlCmd);
}

void SkinIsVerifyRes(int isVerify)
{
    g_isVerifyRes = isVerify;
}

int SkinInit(void)
{
    int         rc  = 0;
	UT_string*	pPath = NULL;
#if 0
    int         tmUsed = 0;
    struct timeval tmStart, tmEnd;

    if(DBusLibuvGetRuntime() == NULL)
    {
        int ret = 0;
        uv_thread_t uvThread;
        LOG_EX(LOG_Warn, "+++++++++++++++++++++++System Uninit\n");

        DBusConnection* pBus = DBusWithLibuvInit(GetDBusDefaultLoop(), 
                                                    g_pModInfoTable[MODULE_SKINS].modAliase, 
                                                 __dusOnMsg, 
                                                 NULL, 
                                                 NULL, //KeyEventCb, 
                                                 &ret);

        if(pBus == NULL)
        {
            LOG_EX(LOG_Error, "DBusWithLibuvInit Error: %d\n", ret);
            return -ERR_UNINIT_ITEM;
        }   

        uv_thread_create(&uvThread, uvLoopProc, NULL);
    }
#endif
    
    uv_rwlock_init(&g_uvSkinRwLock);
    uv_rwlock_init(&g_uvDlRwLock);
    uv_rwlock_init(&g_SkinCacheRwLock);

	utstring_new(pPath);

	utstring_printf(pPath, "mkdir -p %s/image > /dev/zero", DOWNLOAD_RES_ROOT_PATH);
	rc = system(utstring_body(pPath));

	utstring_renew(pPath);
	utstring_printf(pPath, "mkdir -p %s/voice > /dev/zero", DOWNLOAD_RES_ROOT_PATH);
	rc = system(utstring_body(pPath));

	utstring_free(pPath);

    rc  = __skinCreateCfgFile(SKINS_DB_PATH);

    if(rc != SQLITE_OK)
    {
        return rc;
    }

 //   gettimeofday(&tmStart, NULL);
    VerifySkinResInfo();
//    gettimeofday(&tmEnd, NULL);

//    tmUsed = (tmEnd.tv_sec * 1000000 + tmEnd.tv_usec) - (tmStart.tv_sec * 1000000 + tmStart.tv_usec);
//    LOG_EX(LOG_Debug, "VerifySkinResInfo used time: %d(us)\n", tmUsed);

    __loadPreUpgradeInfo();
    //__getEnumResInfo();
    //__getUpgradeInfo("./hotfix.txt");
    //__loadDownloadItems();

    //__dumpSkinsInfo();
}
