#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/file.h>
#include <unistd.h>
#include <errno.h>

#include "log.h"
#include "config_engine.h"
#include "libuv_dbus.h"

#define PROCESS_MUTEX_KEY                       ("/root/config_server.pid")
#define CFG_SYNC_TIME_OF_SECONDS                (1)


static PCFG_ITEM __cfgAddItem(const char *pKeyName, const char *pKeyValue, int iType, int overWrite, int saveToDB, int* pErr);

typedef struct
{ 
    char        keyName[MAX_CFG_KEY_NAME];
    uint32_t    tmCached;

    UT_hash_handle  hh;         ///< UT Hash handle
} CFG_CACHE_ITEM, *PCFG_CACHE_ITEM;

static sqlite3*         g_pSqlFileDB        = NULL;
static PCFG_ITEM        g_pCfgItems         = NULL;
static PCFG_CACHE_ITEM  g_pCfgCacheItems    = NULL;
static int              g_bIsCfgSvr         = FALSE;
static uv_rwlock_t      g_uvHashRwLock;
static uv_rwlock_t      g_uvCacheRwLock;
static uv_timer_t       g_uvSyncSvr;

#if 0
#define INT_TBL_SQL_CMD             ("CREATE TABLE IF NOT EXISTS integer("  \
                 "keyName        TEXT           PRIMARY KEY NOT NULL," \
                 "keyValue       INTEGER        NOT NULL," \
                 "keyModule      INTEGER        NOT NULL," \
                 "CreatedTime    TimeStamp      NOT NULL DEFAULT (datetime('now','localtime')));")

#define STRING_TBL_SQL_CMD          ("CREATE TABLE IF NOT EXISTS string("  \
                 "keyName        TEXT           PRIMARY KEY NOT NULL," \
                 "keyValue       TEXT           NOT NULL," \
                 "keyModule      INTEGER        NOT NULL," \
                 "CreatedTime    TimeStamp      NOT NULL DEFAULT (datetime('now','localtime')));")

#define FLOAT_TBL_SQL_CMD           ("CREATE TABLE IF NOT EXISTS double("  \
                 "keyName        TEXT           PRIMARY KEY NOT NULL," \
                 "keyValue       NUMERIC        NOT NULL," \
                 "keyModule      INTEGER        NOT NULL," \
                 "CreatedTime    TimeStamp      NOT NULL DEFAULT (datetime('now','localtime')));")                 
#endif

#define FIRMWARE_TBL_SQL_CMD          ("CREATE TABLE IF NOT EXISTS %s ("  \
                 "ID             INTEGER PRIMARY KEY AUTOINCREMENT," \
                 "keyName        TEXT           UNIQUE NOT NULL," \
                 "keyValue       TEXT           NOT NULL," \
                 "keyType        INTEGER        NOT NULL);")


static const char* __cfgGetKeyTypeString(int keyType)
{
    switch(keyType)
    {
        case 0:
            return ("string");
        case 1:
            return ("integer");
        case 2:
            return ("double");
    }

    return ("Unknown Type");
}


static int __cfgItem2CfgRsp(PCFG_ITEM pCfgItem, PCFG_API_RSP pRsp)
{
    if(pCfgItem == NULL || pRsp == NULL)
    {
        return (-ERR_INPUT_PARAMS);
    }

    memset(pRsp->keyValue, 0, MAX_CFG_KEY_VALUE);
    pRsp->keyType  = pCfgItem->keyType;
       
    if(pCfgItem->keyType == CFG_TYPE_STRING)
    {
        strncpy(pRsp->keyValue, pCfgItem->pStrValue, MAX_CFG_KEY_VALUE);
    }
    else if(pCfgItem->keyType == CFG_TYPE_INT)
    {
        sprintf(pRsp->keyValue, "%d", pCfgItem->intValue);
    }
    else if(pCfgItem->keyType == CFG_TYPE_DOUBLE)
    {
        sprintf(pRsp->keyValue, "%f", pCfgItem->doubleValue);
    }

    pRsp->errNo = 0;

    return (0);
}

static int __cfgMsg2CfgItem(int keyType,  const char *pKeyName, const char *pKeyValue, PCFG_ITEM pCfgItem)
{
    int     iValue;
    double  dValue;
    char    *pEndPtr = NULL;

    if(pKeyName == NULL || pKeyValue == NULL || pCfgItem == NULL)
    {
        return (-ERR_INPUT_PARAMS);
    }

    strcpy(pCfgItem->pKeyName, pKeyName);
    pCfgItem->keyType   = keyType;
    pCfgItem->keyModule = DBusLibuvGetRuntime()->modName;

    switch(keyType)
    {
        case CFG_TYPE_STRING:
            strcpy(pCfgItem->pStrValue, pKeyValue);
            break;

        case CFG_TYPE_INT:
            iValue = strtol(pKeyValue, NULL, 10);

            if(errno == ERANGE || errno == EINVAL)
            {
                return (-ERR_STR_CONVERT);
            }
            else
            {
                pCfgItem->intValue = iValue;
            }
            break;

        case CFG_TYPE_DOUBLE:
            dValue = strtod(pKeyValue, &pEndPtr);

            if((errno == ERANGE) || (dValue == 0 && (strcmp(pKeyValue, pEndPtr) == 0)))
            {
                return (-ERR_STR_CONVERT);
            }
            else
            {
                pCfgItem->doubleValue = dValue;
            }
            
            break;

        default :
            return (-ERR_UNKNOWN_TYPE);
    }

    return (0);
}

static int __cfgMsg2CfgItemV2(int keyType,  const char *pKeyName, const char *pKeyValue, PCFG_ITEM* pItem)
{
    int         iSize;
    PCFG_ITEM   pCfgItem    = (PCFG_ITEM)malloc(sizeof(CFG_ITEM));        
    char*       pCache      = NULL;

    if(pKeyName == NULL || pKeyValue == NULL || pCfgItem == NULL)
    {
        free(pCfgItem);
        return (-ERR_INPUT_PARAMS);
    }

    switch(keyType)
    {
        case CFG_TYPE_STRING:
            iSize += strlen(pKeyValue) + 1;
            pCache = (char*)malloc(iSize);
            memset(pCache, 0, iSize);
            pCfgItem->pKeyName  = pCache;
            pCfgItem->pStrValue = pCache + strlen(pKeyName) + 1;
            __cfgMsg2CfgItem(pCfgItem->keyType, pKeyName, pKeyValue, pCfgItem);;
            break;

        case CFG_TYPE_INT:
            pCfgItem->pKeyName  = (char*)malloc(iSize);
            memset(pCfgItem->pKeyName, 0, iSize);
            strcpy(pCfgItem->pKeyName, pKeyName);
            __cfgMsg2CfgItem(pCfgItem->keyType, pKeyName, pKeyValue, pCfgItem);;
            break;

        case CFG_TYPE_DOUBLE:
            pCfgItem->pKeyName  = (char*)malloc(iSize);
            memset(pCfgItem->pKeyName, 0, iSize);
            strcpy(pCfgItem->pKeyName, pKeyName);
            __cfgMsg2CfgItem(pCfgItem->keyType, pKeyName, pKeyValue, pCfgItem);;
            break;

        default:
            free(pCfgItem);
            return -ERR_UNKNOWN_TYPE;
    }

    *pItem = pCfgItem;

    return 0;
}

int Sqlite3SyncDB(sqlite3* pSqlMemory, const char* pMemDbName, const char* pDBFilePath, const char* pFileDbName, int isSave)
{
    int             rc;
    sqlite3*        pSqlFile;
    sqlite3_backup* pSqlBackup;
    sqlite3*        pSyncDest;
    sqlite3*        pSyncSrc;

    rc = sqlite3_open(pDBFilePath, &pSqlFile);

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

    if(pDBFilePath == NULL || strlen(pDBFilePath) == 0)
    {
        pMemDbName = "main";
    }

    if(pFileDbName == NULL || strlen(pFileDbName) == 0)
    {
        pFileDbName = "main";
    }
    
    pSyncSrc    = isSave ? pSqlMemory   : pSqlFile;
    pSyncDest   = isSave ? pSqlFile     : pSqlMemory;
    
    pSqlBackup  = sqlite3_backup_init(pSyncDest, pMemDbName, pSyncSrc, pFileDbName);
    
    if(pSqlBackup != NULL)
    {
#if 1
        do 
        {
            rc = sqlite3_backup_step(pSqlBackup, 10);
            
            if(rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED)
            {
                sqlite3_sleep(100);
            }
        } while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED);
#else
        sqlite3_backup_step(pSqlBackup, -1);
#endif
        sqlite3_backup_finish(pSqlBackup);
    }
    else
    {
        LOG_EX(LOG_Error, "Backup Database Error\n");
    }

    rc = sqlite3_errcode(pSyncDest);

    return rc;
}

static int __sqlite3LoadCb(void* data, int argc, char** argv, char** azColName)
{

    if(argc == 4)
    {
        int         iSize;
        PCFG_ITEM   pCfgItem    = (PCFG_ITEM)malloc(sizeof(CFG_ITEM));        
        char*       pCache      = NULL;
        int         ret         = 0;

        pCfgItem->keyType       = atoi(argv[3]);
        pCfgItem->keyModule     = *(int*)data;
        iSize = strlen(argv[1]) + 1;

        switch(pCfgItem->keyType)
        {
            case CFG_TYPE_STRING:
                iSize += strlen(argv[2]) + 1;
                pCache = (char*)malloc(iSize);
                memset(pCache, 0, iSize);
                pCfgItem->pKeyName  = pCache;
                pCfgItem->pStrValue = pCache + strlen(argv[1]) + 1;
                ret = __cfgMsg2CfgItem(pCfgItem->keyType, argv[1], argv[2], pCfgItem);;
                break;

            case CFG_TYPE_INT:
                pCfgItem->pKeyName  = (char*)malloc(iSize);
                memset(pCfgItem->pKeyName, 0, iSize);
                strcpy(pCfgItem->pKeyName, argv[1]);
                ret = __cfgMsg2CfgItem(pCfgItem->keyType, argv[1], argv[2], pCfgItem);;
                break;

            case CFG_TYPE_DOUBLE:
                pCfgItem->pKeyName  = (char*)malloc(iSize);
                memset(pCfgItem->pKeyName, 0, iSize);
                strcpy(pCfgItem->pKeyName, argv[1]);
                ret = __cfgMsg2CfgItem(pCfgItem->keyType, argv[1], argv[2], pCfgItem);;
                break;

            default:
                free(pCfgItem);
                return -ERR_UNKNOWN_TYPE;
        }

        uv_rwlock_wrlock(&g_uvHashRwLock);
        HASH_ADD_STR(g_pCfgItems, pKeyName, pCfgItem);
        uv_rwlock_wrunlock(&g_uvHashRwLock);

        if(ret == 0)
        {
            CfgItemPrint("Load Configure: ", pCfgItem);
        }
    }
    else
    {
        LOG_EX(LOG_Error, "argc = %d\n", argc);
    }

    return 0;
}

static int __cfgLoadGlobalConfig(void)
{
    int     rc      = 0;
    char*   pErrMsg = NULL;
    char    sqlCmd[1024];
    PLIBUV_DBUS_PARAMS pContext = DBusLibuvGetRuntime();

    memset(sqlCmd, 0, 1024);
    sprintf(sqlCmd, "SELECT * FROM %s", MODULE_ALIAS_NAME(MODULE_CONFIGURE));

    // get global configure
    rc = sqlite3_exec(g_pSqlFileDB, sqlCmd, __sqlite3LoadCb, (void*)MODULE_CONFIGURE, &pErrMsg);

    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "SQLite3 Query Error: %s\n", pErrMsg);
        return -ERR_SQL_QUERY;
    }

    if(pContext->modName != MODULE_CONFIGURE)
    {
        memset(sqlCmd, 0, 1024);
        sprintf(sqlCmd, "SELECT * FROM %s", MODULE_ALIAS_NAME(pContext->modName));

        // get local configure
        rc = sqlite3_exec(g_pSqlFileDB, sqlCmd, __sqlite3LoadCb, (void*)&pContext->modName, &pErrMsg);

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

    return 0;
}


void CfgItemPrint(const char* pPrefix, PCFG_ITEM pCfgItem)
{
    if(pCfgItem == NULL)
    {
        fprintf(stderr, "CfgItemPrint: NULL\n");
        return;
    }
    
    switch(pCfgItem->keyType)
    {
        case 0:            
            LOG_EX(LOG_Debug, "%s[%s] = \"%s\", \ttype = %s, \tmodule = %s\n", pPrefix ? pPrefix : "",
                   pCfgItem->pKeyName, pCfgItem->pStrValue, __cfgGetKeyTypeString(pCfgItem->keyType), ModuleNameToString(pCfgItem->keyModule));
            break;

        case 1:            
            LOG_EX(LOG_Debug, "%s[%s] = %d, \ttype = %s, \tmodule = %s\n", pPrefix ? pPrefix : "",
                pCfgItem->pKeyName, pCfgItem->intValue, __cfgGetKeyTypeString(pCfgItem->keyType), ModuleNameToString(pCfgItem->keyModule));
            break;

        case 2:            
            LOG_EX(LOG_Debug, "%s[%s] = %f, \ttype = %s, \tmodule = %s\n", pPrefix ? pPrefix : "",
                pCfgItem->pKeyName, pCfgItem->doubleValue, __cfgGetKeyTypeString(pCfgItem->keyType), ModuleNameToString(pCfgItem->keyModule));
            break;

        default:
            LOG_EX(LOG_Error, "Unknown type =  %d\n", pCfgItem->keyType);
            break;
    }

}

int CfgAddGlobalConfig(const char *pKeyName, const char *pKeyValue, CFG_DATA_TYPE keyType)
{
    CFG_API_REQ req;
    int ret;

    if(pKeyName == NULL || pKeyValue == NULL || strlen(pKeyName) == 0 || strlen(pKeyValue) == 0)
    {
        return (-ERR_INPUT_PARAMS);
    }

    if(keyType < 0 || keyType > CFG_TYPE_DOUBLE)
    {
        return (-ERR_INPUT_PARAMS);
    }
    
    memset(&req, 0, sizeof(CFG_API_REQ));

    strncpy(req.keyName, pKeyName, MAX_CFG_KEY_NAME);
    strncpy(req.keyValue, pKeyValue, MAX_CFG_KEY_VALUE);

    ret = DBusJsonSendToCommand(NULL, 
                                g_pModInfoTable[MODULE_CONFIGURE].modAliase, 
                                CMD_CFG_ADD_REQ, 
                                JSON_ENGINE_CFG_REQ, 
                                &req, TRUE);

    if(ret == 0)
    {
        return (-ERR_CFG_WAIT_RSP);
    }
    else
    {
        return (ret);
    }
}

int CfgAddKeyValue(const char *pKeyName, PCFG_ITEM pItem, int saveToDB)
{
    int         err      = 0;
    PCFG_ITEM   pCfgItem = NULL;
    
    if(pItem == NULL || pKeyName == NULL || strlen(pKeyName) == 0)
    {
        return (-ERR_INPUT_PARAMS);
    }

    uv_rwlock_rdlock(&g_uvHashRwLock);
    HASH_FIND_STR(g_pCfgItems, pKeyName, pCfgItem);
    uv_rwlock_rdunlock(&g_uvHashRwLock);

    if(pCfgItem != NULL)
    {
        return (-ERR_CFG_ITEM_EXIST);
    }

    if(pItem->keyType == CFG_TYPE_STRING)
    {
        __cfgAddItem(pKeyName, (const char*)pItem->pStrValue, pItem->keyType, FALSE, saveToDB, &err);
    }
    else
    {
        char buf[256];
        memset(buf, 0, 256);

        if(pItem->keyType == CFG_TYPE_INT)
        {
            sprintf(buf, "%d", pItem->intValue);
        }
        else if(pItem->keyType == CFG_TYPE_DOUBLE)
        {
            sprintf(buf, "%f", pItem->doubleValue);
        }

        __cfgAddItem(pKeyName, (const char*)buf, pItem->keyType, FALSE, saveToDB, &err);
    }

    return (err);
}

int CfgChangeKeyValue(const char *pKeyName, PCFG_ITEM pItem, int saveToDB)
{
    int         err      = 0;
    PCFG_ITEM   pCfgItem = NULL;
    
    if(pItem == NULL || pKeyName == NULL || strlen(pKeyName) == 0)
    {
        return (-ERR_INPUT_PARAMS);
    }

    uv_rwlock_rdlock(&g_uvHashRwLock);
    HASH_FIND_STR(g_pCfgItems, pKeyName, pCfgItem);
    uv_rwlock_rdunlock(&g_uvHashRwLock);

    if(pCfgItem == NULL)
    {
        return (-ERR_CFG_NOITEM);
    }

    HASH_DEL(g_pCfgItems, pCfgItem);

    if(pItem->keyType == CFG_TYPE_STRING)
    {
        __cfgAddItem(pKeyName, (const char*)pItem->pStrValue, pItem->keyType, FALSE, saveToDB, &err);
    }
    else
    {
        char buf[256];
        memset(buf, 0, 256);

        if(pItem->keyType == CFG_TYPE_INT)
        {
            sprintf(buf, "%d", pItem->intValue);
        }
        else if(pItem->keyType == CFG_TYPE_DOUBLE)
        {
            sprintf(buf, "%f", pItem->doubleValue);
        }

        __cfgAddItem(pKeyName, (const char*)buf, pItem->keyType, FALSE, saveToDB, &err);
    }

    return (err);
}

int CfgGetKeyValue(const char* pKeyName, PCFG_ITEM* pItem)
{    
    PCFG_ITEM   pCfgItem = NULL;
    PLIBUV_DBUS_PARAMS pContext = DBusLibuvGetRuntime();

    if(pItem == NULL || pKeyName == NULL || strlen(pKeyName) == 0)
    {
        return (-ERR_INPUT_PARAMS);
    }

    uv_rwlock_rdlock(&g_uvHashRwLock);
    HASH_FIND_STR(g_pCfgItems, pKeyName, pCfgItem);
    uv_rwlock_rdunlock(&g_uvHashRwLock);

    if(pCfgItem == NULL)
    {
        if(pContext->modName == MODULE_CONFIGURE)
        {
            return (-ERR_CFG_NOITEM);
        }
        else
        {
            CFG_API_REQ req;
			int         ret      = 0;
            memset(&req, 0, sizeof(CFG_API_REQ));
            strncpy(req.keyName, pKeyName, MAX_CFG_KEY_NAME);
            ret = DBusJsonSendToCommand(NULL, g_pModInfoTable[MODULE_CONFIGURE].modAliase, CMD_CFG_GET_REQ, JSON_ENGINE_CFG_REQ, &req, TRUE);

            if(ret == 0)
            {
                return (-ERR_CFG_WAIT_RSP);
            }
            else
            {
                return (ret);
            }
        }
    }

    *pItem  = pCfgItem;

    return 0;
}

static int __cfgCreateCfgFile(const char* pCfgFilePath)
{
    /*const char* pSqlIntDB         = NULL;
    const char* pSqlFloatDB       = NULL;
    const char* pSqlStringDB      = NULL;	*/    
    char*       pErrMsg           = NULL;
    int         rc                = 0;
    int         i                 = 0;
    
    rc = sqlite3_open_v2(pCfgFilePath, &g_pSqlFileDB, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);

    if(rc)
    {
        LOG_EX(LOG_Error, "sqlite3_open_v2 error: %s\n", pCfgFilePath);
        return -ERR_OPEN_SQLITE3_DB;
    }

    for(i = 0; (i < sizeof(g_pModInfoTable) / sizeof(g_pModInfoTable[0])); i++)
    {
        char sqlBuf[1024];

        memset(sqlBuf, 0, 1024);

        sprintf(sqlBuf, FIRMWARE_TBL_SQL_CMD, MODULE_ALIAS_NAME(i));

        rc = sqlite3_exec(g_pSqlFileDB, sqlBuf, NULL, 0, &pErrMsg);

        if(rc != SQLITE_OK)
        {
            uv_fs_t uvFs;
            LOG_EX(LOG_Error, "Create Tbl[%d] Error: %s\n", i, pErrMsg);
            sqlite3_free(pErrMsg);
            uv_fs_unlink(GetDBusDefaultLoop(), &uvFs, pCfgFilePath, NULL);
            return -ERR_SQLITE3_CREATE_TABLE;
        }
    }
    
    return 0;
}

#if 0
static void onSigTermProgressExit(int sign)
{
    if(g_bIsCfgSvr)
    {
    }
    
    kill(getpid(), SIGKILL);
}

static void onProgressExit(void)
{
    onSigTermProgressExit(0);
}
#endif


static int __cfgCheckCfgSvrRun(void)
{
    int rc;
    int fd = open(PROCESS_MUTEX_KEY, O_CREAT | O_RDWR, 0666);

    if(fd == -1)
    {
        LOG_EX(LOG_Debug, "Open File Error\n");
    }

    rc = flock(fd, LOCK_EX | LOCK_NB);

    if(rc == -1)
    {
        LOG_EX(LOG_Debug, "Configure server running\n");
        return FALSE;
    }    
    else
    {   
        LOG_EX(LOG_Debug, "Configure server stoped\n");        
        return TRUE;
    }
}

static int __cfgAddToCache(const char *pKeyName)
{
    PCFG_CACHE_ITEM pItem   = NULL;

    uv_rwlock_rdlock(&g_uvCacheRwLock);
    HASH_FIND_STR(g_pCfgCacheItems, pKeyName, pItem);
    uv_rwlock_rdunlock(&g_uvCacheRwLock);

    if(pItem != NULL)
    {
        HASH_DEL(g_pCfgCacheItems, pItem);
        free(pItem);
    }

    pItem = (PCFG_CACHE_ITEM)malloc(sizeof(CFG_CACHE_ITEM));

    strncpy(pItem->keyName, pKeyName, MAX_CFG_KEY_NAME);
    pItem->tmCached = LIBUV_CURRENT_TIME_MS();

    uv_rwlock_wrlock(&g_uvCacheRwLock);
    HASH_ADD_STR(g_pCfgCacheItems, keyName, pItem);
    uv_rwlock_wrunlock(&g_uvCacheRwLock);

    return (0);
}

static PCFG_ITEM __cfgAddItem(const char *pKeyName, const char *pKeyValue, int iType, int overWrite, int saveToDB, int* pErr)
{
    PCFG_ITEM   pCfgItem        = NULL;
    int iSize                   = 0;
    char *pCache                = NULL;

    uv_rwlock_rdlock(&g_uvHashRwLock);
    HASH_FIND_STR(g_pCfgItems, pKeyName, pCfgItem);
    uv_rwlock_rdunlock(&g_uvHashRwLock);

    if(pCfgItem != NULL)
    {   
        if(overWrite)
        {
            HASH_DEL(g_pCfgItems, pCfgItem);
            free(pCfgItem->pKeyName);
            free(pCfgItem);
        }
        else
        {
            if(pErr)
            {
                *pErr = -ERR_CFG_NOITEM;
            }

            return NULL;
        }
    }
    
    if(iType == CFG_TYPE_STRING)
    {
        iSize       = strlen(pKeyName) + strlen(pKeyValue) + 2;
    }
    else
    {
        iSize       = strlen(pKeyName) + 1;
    }

    pCache      = (char *)malloc(iSize);

    memset(pCache, 0, iSize);
    pCfgItem    = (PCFG_ITEM)malloc(sizeof(CFG_ITEM));

    
    pCfgItem->pKeyName  = pCache;

    if(iType == CFG_TYPE_STRING)
    {
        pCfgItem->pStrValue = pCache + strlen(pKeyName) + 1;
    }

    __cfgMsg2CfgItem(iType, pKeyName, pKeyValue, pCfgItem);

    uv_rwlock_wrlock(&g_uvHashRwLock);
    HASH_ADD_STR(g_pCfgItems, pKeyName, pCfgItem);
    uv_rwlock_wrunlock(&g_uvHashRwLock);

    // if need save this configure item to database
    if(saveToDB)
    {
        __cfgAddToCache(pKeyName);
    }

    if(pErr)
    {
        *pErr = 0;
    }

    return (pCfgItem);
}

static void __cfgSyncServer(void)
{       
    if(HASH_COUNT(g_pCfgCacheItems) > 0)
    {
        PCFG_CACHE_ITEM pItem = NULL, pTemp = NULL;

        sqlite3_exec(g_pSqlFileDB, "BEGIN TRANSACTION;", NULL, NULL, NULL);

        HASH_ITER(hh, g_pCfgCacheItems, pItem, pTemp)
        {
            PCFG_ITEM pCfgItem = NULL;

            if(CfgGetKeyValue(pItem->keyName, &pCfgItem) == 0)
            {
                char*   pSqlInsertCmd = (char*)malloc(1024 * 4);
                char*   pSqlUpgradeCmd = (char*)malloc(1024 * 4);
                memset(pSqlInsertCmd, 0, 1024 * 4);
                memset(pSqlUpgradeCmd, 0, 1024 * 4);

                if(pCfgItem->keyType == CFG_TYPE_STRING)
                {
                    sprintf(pSqlInsertCmd, "INSERT OR IGNORE INTO %s (keyName, keyValue, keyType) VALUES (\'%s\', \'%s\', %d)",
                            MODULE_ALIAS_NAME(pCfgItem->keyModule), pCfgItem->pKeyName, pCfgItem->pStrValue, pCfgItem->keyType);

                    sprintf(pSqlUpgradeCmd, "UPDATE %s SET keyValue = \'%s\', keyType = %d WHERE keyName = \'%s\'",
                            MODULE_ALIAS_NAME(pCfgItem->keyModule), pCfgItem->pStrValue, pCfgItem->keyType, pCfgItem->pKeyName);
                }
                else if(pCfgItem->keyType == CFG_TYPE_INT)
                {
                    sprintf(pSqlInsertCmd, "INSERT OR IGNORE INTO %s (keyName, keyValue, keyType) VALUES (\'%s\', \'%d\', %d)",
                            MODULE_ALIAS_NAME(pCfgItem->keyModule), pCfgItem->pKeyName, pCfgItem->intValue, pCfgItem->keyType);

                    sprintf(pSqlUpgradeCmd, "UPDATE %s SET keyValue = \'%d\', keyType = %d WHERE keyName = \'%s\'",
                            MODULE_ALIAS_NAME(pCfgItem->keyModule), pCfgItem->intValue, pCfgItem->keyType, pCfgItem->pKeyName);
                }
                else if(pCfgItem->keyType == CFG_TYPE_DOUBLE)
                {
                    sprintf(pSqlInsertCmd, "INSERT OR IGNORE INTO %s (keyName, keyValue, keyType) VALUES (\'%s\', \'%f\', %d)",
                            MODULE_ALIAS_NAME(pCfgItem->keyModule), pCfgItem->pKeyName, pCfgItem->doubleValue, pCfgItem->keyType);

                    sprintf(pSqlUpgradeCmd, "UPDATE %s SET keyValue = \'%f\', keyType = %d WHERE keyName = \'%s\'",
                            MODULE_ALIAS_NAME(pCfgItem->keyModule), pCfgItem->doubleValue, pCfgItem->keyType, pCfgItem->pKeyName);
                }
            
                if((sqlite3_exec(g_pSqlFileDB, pSqlInsertCmd, NULL, NULL, NULL) == SQLITE_OK)
                   && (sqlite3_exec(g_pSqlFileDB, pSqlUpgradeCmd, NULL, NULL, NULL) == SQLITE_OK))
                {
                    // Delete this Key
                    HASH_DEL(g_pCfgCacheItems, pItem);
                    LOG_EX(LOG_Debug, "Sync Configure %s To Database\n", pCfgItem->pKeyName);
                }
                else
                {
                     free(pSqlInsertCmd);
                     free(pSqlUpgradeCmd);
                    return;
                }
                
                free(pSqlInsertCmd);
                free(pSqlUpgradeCmd);
            }
            else
            {
                // Delete this Key
                HASH_DEL(g_pCfgCacheItems, pItem);
            }
        }

        sqlite3_exec(g_pSqlFileDB, "END TRANSACTION;", NULL, NULL, NULL);
    }
}
static void __uvThreadSyncCfg(void *pParams)
{
    while(TRUE)
    {
        __cfgSyncServer();
        sleep(CFG_SYNC_TIME_OF_SECONDS);
    }

    pthread_detach(pthread_self());
}

static void __cfgStartSyncGlobalCfgSvr(void)
{
    uv_thread_t uvSyncThread;

    uv_rwlock_init(&g_uvCacheRwLock);

    uv_thread_create(&uvSyncThread, __uvThreadSyncCfg, NULL);
}

static PCFG_ITEM __onCMD_CFG_GET_REQ(const char* pKeyName, int* pErr)
{
    PCFG_ITEM       pItem = NULL;

    *pErr = CfgGetKeyValue(pKeyName, &pItem);

    if(*pErr == 0)
    {
        return (pItem);
    }

    return (NULL);
}

static int __onCMD_CFG_ADD_REQ(const char* pKeyName, const char* pKeyValue, int keyType)
{
    PCFG_ITEM pCfgItem = NULL;
    int ret;

    if(pKeyName == NULL || pKeyValue == NULL || strlen(pKeyName) == 0 || strlen(pKeyValue) == 0)
    {
        return (-ERR_INPUT_PARAMS);
    }

    if(keyType < 0 || keyType > CFG_TYPE_DOUBLE)
    {
        return (-ERR_INPUT_PARAMS);
    }

    ret = __cfgMsg2CfgItemV2(keyType, pKeyName, pKeyValue, &pCfgItem);

    if(ret != 0)
    {
        return (ret);
    }

    return CfgAddKeyValue(pKeyName, pCfgItem, TRUE);
}

static void __cfgSvrProcessCmd(DBUS_CMD cmd, const char* pKeyName, const char* pKeyValue, int keyType, PCFG_API_RSP pRsp)
{
    PCFG_ITEM       pItem = NULL;
    int             err;

    memset(pRsp, 0, sizeof(CFG_API_RSP));
    pRsp->errNo    = -ERR_UNKNOWN_TYPE;

    if(pKeyName == NULL)
    {
        pRsp->errNo    = -ERR_INPUT_PARAMS;
        return;
    }
    
    strncpy(pRsp->keyName, pKeyName, MAX_CFG_KEY_NAME);
    
    switch(cmd)
    {
        case CMD_CFG_ADD_REQ:
            err = __onCMD_CFG_ADD_REQ(pKeyName, pKeyValue, keyType);

            pRsp->keyType  = keyType;
            pRsp->errNo    = err;
            strncpy(pRsp->keyName, pKeyName, MAX_CFG_KEY_NAME);
            strncpy(pRsp->keyValue, pKeyValue, MAX_CFG_KEY_VALUE);

            break;

        case CMD_CFG_GET_REQ:

            pItem = __onCMD_CFG_GET_REQ(pKeyName, &err);

            if(pItem == NULL || err != 0)
            {
                pRsp->keyType  = -1;
                pRsp->errNo    = err;
                strncpy(pRsp->keyName, pKeyName, MAX_CFG_KEY_NAME);
                return;
            }
            else
            {
                __cfgItem2CfgRsp(pItem, pRsp);
                return;
            }
        
            break;
    }

    return;
}

int CfgGlobalEnvInit(void)
{    
    int rc                = 0;

    uv_rwlock_init(&g_uvHashRwLock);

    rc = __cfgCreateCfgFile(GLOBAL_CFG_FILE_PATH);

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

#if 0
    rc = sqlite3_open_v2(":memory:", &g_pSqlMemDB, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);

    if(rc != SQLITE_OK)
    {
        LOG_EX(LOG_Error, "Create Memory SQLite3 DB Error\n");
        return -ERR_CREATE_SQLITE3_DB;
    }

    //rc = sqlite3SyncDatabase(g_pSqlMemDB, GLOBAL_CFG_FILE_PATH, 0);
#endif
    rc = __cfgLoadGlobalConfig();

    if(rc != SQLITE_OK)
    {
        return (-ERR_OPEN_SQLITE3_DB);
        //sqlite3_close(g_pSqlFileDB);
    }

#if 0
    g_bIsCfgSvr = cfgCheckCfgSvrRun();

    if(atexit(onProgressExit) != 0)
    {
        fprintf(stderr, "atexit error\n");
    }

    signal(SIGINT, onSigTermProgressExit);
#endif

    __cfgStartSyncGlobalCfgSvr();

    return 0;
}

void OnCfgMsgProcess(MODULE_NAME dest, DBUS_CMD busCmd, const char* pJsonStr)
{
    CFG_API_RSP         cfgRsp;
    int                 err = 0;
    PCFG_API_REQ        pReq = NULL;
    PCFG_API_RSP        pRsp = NULL;
    PLIBUV_DBUS_PARAMS  pParams = DBusLibuvGetRuntime();

    switch(busCmd)
    {
        case CMD_CFG_ADD_REQ:
        case CMD_CFG_GET_REQ:
            pReq = Json2Struct(pJsonStr, JSON_ENGINE_CFG_REQ, TRUE, &err);

            if(pReq != NULL && err == 0)
            {
                pRsp = &cfgRsp;
                __cfgSvrProcessCmd(busCmd, pReq->keyName, pReq->keyValue, pReq->keyType, pRsp);
                DBusJsonSendToCommand(NULL, g_pModInfoTable[dest].modAliase, busCmd + 1, JSON_ENGINE_CFG_RSP, pRsp, TRUE);
            }

            free(pReq);
            break;
#if 0
        case CMD_CFG_GET_REQ:
            pReq = Json2Struct(pJsonStr, JSON_ENGINE_CFG_REQ, TRUE, &err);

            if(pReq != NULL && err == 0)
            {
                pRsp = &cfgRsp;
                __cfgSvrProcessCmd(busCmd, pReq->keyName, NULL, pReq->keyType, pRsp);
                DBusJsonSendToCommand(NULL, g_pModInfoTable[dest].modAliase, CMD_CFG_GET_RSP, JSON_ENGINE_CFG_RSP, pRsp, TRUE);
            }
            break;
#endif
        case CMD_CFG_ADD_RSP:
        case CMD_CFG_GET_RSP:
            pRsp = Json2Struct(pJsonStr, JSON_ENGINE_CFG_RSP, TRUE, &err);

            if(pRsp != NULL && err == 0 && pRsp->errNo == 0)
            {
                // Add to local Hash Table cache
                PCFG_ITEM pCfgItem = __cfgAddItem(pRsp->keyName, pRsp->keyValue, pRsp->keyType, TRUE, FALSE, &err);

                if(err == 0 && pCfgItem != NULL)
                {
                    if(pParams->onCfgCb)
                    {
                        pParams->onCfgCb(busCmd, pCfgItem, 0);
                    }
                }
                else
                {
                    LOG_EX(LOG_Error, "Add Configure {[%s]<%d> = \"%s\"} Error: %d\n", 
                           pRsp->keyName, pRsp->keyValue, pRsp->keyType, err);
                } 
            }
            else
            {
                if(pParams->onCfgCb)
                {
                    pParams->onCfgCb(busCmd, NULL, (err == 0) ? pRsp->errNo : err);
                }

                LOG_EX(LOG_Error, "pRsp = %p, err = %d, rspErr = %d\n", pRsp, err, pRsp ? pRsp->errNo : -err);
            }

            free(pRsp);
            break;
    }
}
