#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <linux/input.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sendfile.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <uthash/utlist.h>

#include "log.h"
#include "libuv_dbus.h"
#include "json_struct.h"
#include "inet_api.h"
#include "crypto.h"
#include "server_addr.h"
#ifdef ENABLE_COUNT_DEBUG
#include "monitor.h"
#endif

#ifdef ENABLE_COUNT_DEBUG
#define MON_MSG_PROC_STAT           ("Message Process")
#define MON_MSG_BST_PROC_STAT       ("Boardcast Message Process")
#define MON_USER_MSG_PROC_STAT      ("User Message Process")
#define MON_USER_MSG_BST_PROC_STAT  ("User Boardcast Message Process")
#endif

typedef void (*OnDBusSendError)(int, void*);
static void uvOpenKeyEventCb(uv_fs_t* puvFs);

typedef struct LOOP_TASK_ARRAY
{
    uv_loop_t*      pLoop;
    int             isRunning;

    struct LOOP_TASK_ARRAY     *next, *prev;
} *PLOOP_TASK_ARRAY;

typedef struct
{ 
    key_t       shmKey;
//    uint32_t    tarMask;
//    uint32_t    tmSend;
    uint32_t    msgSize;

    UT_hash_handle  hh;         ///< UT Hash handle
} UV_SHM_ITEM, *PUV_SHM_ITEM;

typedef struct
{
    long int            msgMask;
    unsigned char       pMsgContext[0];
} DBUS_MSG_DATA, *PDBUS_MSG_DATA;

typedef struct 
{
    DBusConnection*         pBus;
    const char*             pBusName;
    uint32_t                busCmd;
    JSON_ENGINE_TYPE        type;
    void*                   pStruct;
    int                     iSize;
    OnDBusAsyncSendTo       cbSendTo;
    int                     enBase64;
} DBUS_ASYNC_PARAMS, *PDBUS_ASYNC_PARAMS;

static LIBUV_DBUS_PARAMS g_LibuvDBusParam;

static uv_idle_t        g_uvIdleHandle;
static uv_timer_t       g_uvTimerPing;
static uv_fs_t          g_uvKeyEvent;
static WORKDAY_INFO     g_workDayArray;
static WIFI_STATUS      g_WifiConnStatus = WIFI_CONNECTED;
static PDBUS_MSG_PROC   g_pMsgProcList = NULL;
static uv_rwlock_t      g_uvLoopRwLock;
static PLOOP_TASK_ARRAY g_LoopArray     = NULL;
static unsigned int     g_EnHBLExit = TRUE;
static uv_rwlock_t      g_uvMsgProcRwLock;

#if USED_SHM_TO_DBUS
static uv_rwlock_t      g_uvShmHashRwLock;
static PUV_SHM_ITEM     g_pShmTbl       = NULL;

static void __addShmIdToTable(key_t shmKey, uint32_t tmSend, uint32_t tarMask, uint32_t msgSize)
{
	PUV_SHM_ITEM	pItem = NULL;

	uv_rwlock_rdlock(&g_uvShmHashRwLock);
	HASH_FIND_INT(g_pShmTbl, &shmKey, pItem);
	uv_rwlock_rdunlock(&g_uvShmHashRwLock);

	if(pItem == NULL)
	{
		pItem 			= (PUV_SHM_ITEM)malloc(sizeof(UV_SHM_ITEM));

		memset(pItem, 0, sizeof(UV_SHM_ITEM));
		pItem->shmKey	= shmKey;

		uv_rwlock_wrlock(&g_uvShmHashRwLock);
		HASH_ADD_INT(g_pShmTbl, shmKey, pItem);
		uv_rwlock_wrunlock(&g_uvShmHashRwLock);
	}

    pItem->tmSend	= tmSend;
    pItem->tarMask  = tarMask;
    pItem->msgSize  = msgSize;
}

static void __removeReqIdFromTable(key_t shmKey)
{
	PUV_SHM_ITEM	pItem = NULL;

	uv_rwlock_rdlock(&g_uvShmHashRwLock);
	HASH_FIND_INT(g_pShmTbl, &shmKey, pItem);
	uv_rwlock_rdunlock(&g_uvShmHashRwLock);

	if(pItem != NULL)
	{
		uv_rwlock_wrlock(&g_uvShmHashRwLock);
		HASH_DEL(g_pShmTbl, pItem);
		uv_rwlock_wrunlock(&g_uvShmHashRwLock);
		free(pItem);
	}
}

static void __uvShmTblTaskThreadCb(void *pParam)
{
    struct timeval  tv;
    PUV_SHM_ITEM	pItem = NULL, pTmpItem = NULL;

	while(TRUE)
	{
        gettimeofday(&tv, NULL);
		
		HASH_ITER(hh, g_pShmTbl, pItem, pTmpItem)
		{
            int msgId;

            if(tv.tv_sec - pItem->tmSend <= 60)
            {
                continue;
            }

            msgId = shmget((key_t)pItem->shmKey, pItem->msgSize, 0666 | IPC_CREAT);

            if(msgId == -1)
            {
                continue;
            }
            
            // Not Boardcast Message
            if((pItem->tarMask & 0xFFFF0000) != 0xFFFF0000)
            {
                PDBUS_MSG_DATA pData = (PDBUS_MSG_DATA)shmat(msgId, NULL, 0);

                if(pData == (void*)-1)
                {
                    continue;
                }

                // Nevery Recevied By Anyone
                if(pData->msgMask == pItem->tarMask)
                {
                    continue;
                }

                shmdt(pData);
                shmctl(msgId, IPC_RMID, 0);
                __removeReqIdFromTable((key_t)pItem->shmKey);
            }
        }

		sleep(1);
	}

    pthread_detach(pthread_self());
}
#endif

PLIBUV_DBUS_PARAMS DBusLibuvGetRuntime(void)
{
    if(g_LibuvDBusParam.pBus == NULL || g_LibuvDBusParam.pLoop == NULL)
    {
        return NULL;
    }
    
    return &g_LibuvDBusParam;
}

MODULE_NAME DBusLibGetModName(void)
{
    return g_LibuvDBusParam.modName;
}

uv_loop_t* GetDBusDefaultLoop(void)
{
    if(g_LibuvDBusParam.pBus == NULL || g_LibuvDBusParam.pLoop == NULL)
    {
        return uv_default_loop();
    }
    
    return g_LibuvDBusParam.pLoop;
}

static void uvAsyncCb(uv_async_t* pAsync)
{
    DBusConnection* pConn = (DBusConnection*)pAsync->data;
    dbus_connection_read_write(pConn, 0);

    while(dbus_connection_dispatch(pConn) == DBUS_DISPATCH_DATA_REMAINS);
}

static void uvTimeoutCb(uv_timer_t* pTimer)
{
    DBusTimeout* timeout = (DBusTimeout*)pTimer->data;
    dbus_timeout_handle(timeout);
}

static void uvPollCb(uv_poll_t* pPoll, int status, int events)
{
    DBusWatch* watch = (DBusWatch*)pPoll->data;
    unsigned int uvFlags = 0;

    if(events & UV_READABLE)
    {
        uvFlags |= DBUS_WATCH_READABLE;
    }

    if(events & UV_WRITABLE)
    {
        uvFlags |= DBUS_WATCH_WRITABLE;
    }

    dbus_watch_handle(watch, uvFlags);
}

static void uvIdleCb(uv_idle_t* phuvIdle)
{        
    usleep(1000);
}

static void uvFsAccessCb(uv_fs_t* puvFs)
{    
    if(puvFs->result != 0)
    {
        IHW_EnableLogLevel((LOG_LEVEL)(LOG_Fatal | LOG_Error | LOG_Warn | LOG_Debug | LOG_Info), 1);
    }

    uv_fs_req_cleanup(puvFs);
    free(puvFs);
}

static void uvReadKeyEventCb(uv_fs_t* puvFs)
{
    if(puvFs->result < 0)
    {
        uv_fs_req_cleanup(puvFs);
        return;
    }
	else if(puvFs->result == 0)
    {
        uv_fs_t uvClose;
        uv_fs_close(g_LibuvDBusParam.pLoop, &uvClose, g_uvKeyEvent.result, NULL);        
    } 
    else
    {
        uv_buf_t* puvIov = (uv_buf_t*)puvFs->data;

        if(puvIov->len == sizeof(struct input_event))
        {
            struct input_event* pKeyEvt = (struct input_event*)puvIov->base;

            if(g_LibuvDBusParam.onKeyCb)
            {
//                LOG_EX(LOG_Info, "type = %u, code = %u, value = %u\n", pKeyEvt->type, pKeyEvt->code, pKeyEvt->value);
                g_LibuvDBusParam.onKeyCb(pKeyEvt->type, pKeyEvt->code, pKeyEvt->value);
            }
        }
    }

    uv_fs_req_cleanup(puvFs);    

    usleep(1000);
    uv_fs_open(g_LibuvDBusParam.pLoop, &g_uvKeyEvent, R16_TINA_KEY_EVENT_PATH, O_RDONLY, 0, uvOpenKeyEventCb);
}

static void uvOpenKeyEventCb(uv_fs_t* puvFs)
{
    static uv_buf_t uvIoV;
    static struct input_event keyEvent;
    
    if(puvFs->result < 0)
    {
        LOG_EX(LOG_Error, "Open Key Event File[%s] Error: %d\n", R16_TINA_KEY_EVENT_PATH, puvFs->result);
        uv_fs_req_cleanup(puvFs);
        return;
    }

    uvIoV = uv_buf_init((void*)&keyEvent, sizeof(struct input_event));
    puvFs->data = (void*)&uvIoV;
    
    uv_fs_read(g_LibuvDBusParam.pLoop, &g_uvKeyEvent, puvFs->result, &uvIoV, 1, -1, uvReadKeyEventCb);
    uv_fs_req_cleanup(puvFs);
    
    return;
}

static void DBusAsyncFreeCb(void* pData)
{
    uv_async_t* pAsync = (uv_async_t*)pData;

    if(pAsync)
    {
        pAsync->data = NULL;
        uv_close((uv_handle_t*)pAsync, (uv_close_cb)free);
    }
}

static void DBusPollFreeCb(void* pData)
{
    uv_poll_t* pPoll = (uv_poll_t*)pData;

    if(pPoll)
    {
        pPoll->data = NULL;
        
        uv_ref((uv_handle_t*)pPoll);
        uv_poll_stop(pPoll);
        uv_close((uv_handle_t*)pPoll, (uv_close_cb)free);
    }    
}

static dbus_bool_t DBusAddWatchCb(DBusWatch* pWatch, void* pData)
{
    static int isCreate = 0;
    int fdDBus, uvPollFlags = 0;
    unsigned int dBusWatchFlags;
    uv_poll_t* pPoll = NULL;
    uv_loop_t* pLoop = (uv_loop_t*)pData;
    
    if(!dbus_watch_get_enabled(pWatch)
       || dbus_watch_get_data(pWatch) != NULL
       || isCreate != 0)
    {
        return TRUE;
    }

    fdDBus = dbus_watch_get_unix_fd(pWatch);
    dBusWatchFlags = dbus_watch_get_flags(pWatch);

    if(dBusWatchFlags & DBUS_WATCH_READABLE)
    {
        uvPollFlags |= UV_READABLE;
    }

    if(dBusWatchFlags & DBUS_WATCH_WRITABLE)
    {
        uvPollFlags |= UV_WRITABLE;
    }

    pPoll = (uv_poll_t*)malloc(sizeof(uv_poll_t));
    pPoll->data = (void*)pWatch;
    
    uv_poll_init(pLoop, pPoll, fdDBus);
    uv_poll_start(pPoll, uvPollFlags, uvPollCb);
    LOG_EX(LOG_Debug, "Create POOL by FD: %d\n", fdDBus);

    uv_unref((uv_handle_t*)pPoll);

    dbus_watch_set_data(pWatch, (void*)pPoll, DBusPollFreeCb);

    isCreate = 1;
    return TRUE;
}

static void DBusRemoveWatchCb(DBusWatch* pWatch, void* pData)
{
    uv_poll_t* pPoll = (uv_poll_t*)dbus_watch_get_data(pWatch);

    if(pPoll)
    {
        dbus_watch_set_data(pWatch, NULL, NULL);
    }
}

static void DBusNotifyWatchCb(DBusWatch* pWatch, void* pData)
{
    if(dbus_watch_get_enabled(pWatch))
    {
        DBusAddWatchCb(pWatch, pData);
    }
    else
    {
        DBusRemoveWatchCb(pWatch, pData);
    }
}

static void DBusTimeoutFreeCb(void* pData)
{
    uv_timer_t* pTimer = (uv_timer_t*)pData;

    if(pTimer == NULL)
    {
        return;
    }

    pTimer->data =  NULL;
    uv_timer_stop(pTimer);
    uv_unref((uv_handle_t*)pTimer);
    uv_close((uv_handle_t*)pTimer, (uv_close_cb)free);
}

static dbus_bool_t DBusAddTimeoutCb(DBusTimeout* pTimeout, void* pData)
{
    uv_timer_t* pTimer = NULL;
    uv_loop_t* pLoop = (uv_loop_t*)pData;

    if(!dbus_timeout_get_enabled(pTimeout)
       || dbus_timeout_get_data(pTimeout) != NULL)
    {
        return TRUE;
    }

    pTimer = (uv_timer_t*)malloc(sizeof(uv_timer_t));
    pTimer->data = pTimeout;
    
    uv_timer_init(pLoop, pTimer);
    uv_timer_start(pTimer, uvTimeoutCb, dbus_timeout_get_interval(pTimeout), 0);

    dbus_timeout_set_data(pTimeout, (void*)pTimer, DBusTimeoutFreeCb);
    return TRUE;
}

static void DBusRemoveTimeoutCb(DBusTimeout* pTimeout, void* pData)
{
    uv_timer_t* pTimer =  (uv_timer_t*)dbus_timeout_get_data(pTimeout);

    if(pTimer)
    {
        dbus_timeout_set_data(pTimeout, NULL, NULL);
    }
}

static void DBusNotifyTimeoutCb(DBusTimeout* pTimeout, void* pData)
{
    if(dbus_timeout_get_enabled(pTimeout))
    {
        DBusAddTimeoutCb(pTimeout, pData);
    }
    else
    {
        DBusRemoveTimeoutCb(pTimeout, pData);
    }
}

static void DBusWakeupMainLoopCb(void* pData)
{
    uv_async_t* pAsync = (uv_async_t*)pData;
    uv_async_send(pAsync);
}

static void FreeDBusOnMsgCb(uv_work_t* pWork, int status)
{
    PDBUS_MSG_PACK pMsg = (PDBUS_MSG_PACK)pWork->data;

    if(pMsg)
    {
        free(pMsg);
    }
    free(pWork);
}

#if 0
static void DBusOnBoardcastMsgWorkCb(uv_work_t* pWork)
#else
static int DBusOnBoardcastMsgWorkCb(PDBUS_MSG_PACK pMsg)
#endif
{
    
    pMsg->isBstMsg =  TRUE;
    // Message context on dbus message pad
    if(pMsg->msgSize < DBUS_MSG_MAX_PAD_SIZE)
    {        
        if(pMsg->busCmd == CMD_WIFI_STATE_NTF)
        {
            int err = 0;
            PWIFI_STATUS_PRO pWifiInfo = (PWIFI_STATUS_PRO)Json2Struct((const char *)pMsg->pMsg, 
                                        JSON_WIFI_STATUS_NOTIFY, FALSE, &err);

            //LOG_EX(LOG_Debug, "pWifiInfo: %s\n", pMsg->pMsg);
            if(pWifiInfo && err == 0)
            {
                if(pWifiInfo->wifi_evt == 0)
                {
                    g_WifiConnStatus = WIFI_CONNECTED;
                }
                else
                {
                    g_WifiConnStatus = WIFI_DISCONNECTED;
                }
            }

            if(pWifiInfo)
            {
                free(pWifiInfo);
            }
        }
        else if(pMsg->busCmd == CMD_CFG_UPG_NOTIFY)
        {
        }
        else if(pMsg->busCmd == CMD_LOG_CONFIG)
        {
            int err = 0;
            PLOG_CFG_PROTOCOL pCfgInfo = (PLOG_CFG_PROTOCOL)Json2Struct((const char *)pMsg->pMsg, 
                                        JSON_ENGINE_LOG_CFG_CMD, FALSE, &err);

            //LOG_EX(LOG_Debug, "pCfgInfo: %s\n", pMsg->pMsg);
            if(pCfgInfo && err == 0)
            {
                UpgradLogConfigure(pCfgInfo);
            }

            if(pCfgInfo)
            {
                free(pCfgInfo);
            }
        }
        else if(pMsg->busCmd != CMD_MISC_PING)
        {
            pMsg->msgDests = 0xFFFFFFFF;
            if(g_LibuvDBusParam.onMsgCb)
            {
                g_LibuvDBusParam.onMsgCb(g_LibuvDBusParam.pLoop, g_LibuvDBusParam.pBus, pMsg);
            }

            return 1;
        }     
    }    

    return 0;
}

#if 0
static void DBusOnMsgWorkAPICb(uv_work_t* pWork)
#else
static int DBusOnMsgWorkAPICb(PDBUS_MSG_PACK pMsg)
#endif
{
    int             err = 0;

    pMsg->isBstMsg = FALSE;

    // Message context on dbus message pad
    if(pMsg->msgSize < DBUS_MSG_MAX_PAD_SIZE)
    {    
        if(pMsg->busCmd >= CMD_CFG_ADD_REQ && pMsg->busCmd < CMD_CFG_UPG_NOTIFY)
        {
            OnCfgMsgProcess(pMsg->msgSrc, pMsg->busCmd, pMsg->pMsg);
        }
        else if(pMsg->busCmd == CMD_LOG_CONFIG)
        {
            PLOG_CFG_PROTOCOL pCfgInfo = (PLOG_CFG_PROTOCOL)Json2Struct((const char *)pMsg->pMsg, 
                                        JSON_ENGINE_LOG_CFG_CMD, FALSE, &err);

            //LOG_EX(LOG_Debug, "pCfgInfo: %s\n", pMsg->pMsg);
            if(pCfgInfo && err == 0)
            {
                UpgradLogConfigure(pCfgInfo);
            }

            if(pCfgInfo)
            {
                free(pCfgInfo);
            }
        }
        else if(pMsg->busCmd == CMD_WORKDAY_DB_RSP)
        {
            PWORKDAY_INFO pWorkDayInfo = (PWORKDAY_INFO)Json2Struct((const char *)pMsg->pMsg, 
                                        JSON_ENGINE_WORKDAY_REQ, FALSE, &err);

            //LOG_EX(LOG_Debug, "WorkDay: %s\n", pMsg->pMsg);
            if(pWorkDayInfo && err == 0)
            {
                memcpy(&g_workDayArray, pWorkDayInfo, sizeof(WORKDAY_INFO));
                g_workDayArray.isReady = TRUE;
                LOG_EX(LOG_Debug, "Sync Alarm Database: %s\n", pMsg->pMsg);
            }

            //LOG_EX2(LOG_Debug, "Database: %s\n", pMsg->pMsg);

            if(pWorkDayInfo)
            {
                free(pWorkDayInfo);
            }
        }
        else if(pMsg->busCmd != CMD_MISC_PING)
        {
            if(g_LibuvDBusParam.onMsgCb)
            {
                g_LibuvDBusParam.onMsgCb(g_LibuvDBusParam.pLoop, g_LibuvDBusParam.pBus, pMsg);
            }

            return 1;
        }
    }
    else // More than 4K size used Share Memory
    {
        LOG_EX(LOG_Error, "Receive Message Error Size: %d\n", pMsg->msgSize);
#if 0
        PDBUS_MSG_DATA pData = NULL;
        int  key = strtol(pMsg->pMsg, NULL, 10);
        
        int msgId = shmget((key_t)key, pMsg->msgSize, 0666 | IPC_CREAT);

        if(msgId == -1)
        {
            return;
        }

        pData = (PDBUS_MSG_DATA)shmat(msgId, NULL, 0);

        if(pData == (void*)-1)
        {
            return;
        }

        //print_hex_dump_bytes("send_", 2, pData, pMsg->msgSize);

        pMsg->pMsg = pData->pMsgContext;
        pMsg->msgSize -= sizeof(long int);

        if(pMsg->busCmd != CMD_MISC_PING)
        {
            g_LibuvDBusParam.onMsgCb(g_LibuvDBusParam.pLoop, g_LibuvDBusParam.pBus, pMsg);
        }

        pData->msgMask &= ~(1 << g_LibuvDBusParam.modName);
        
        // Cleanup Share Memory
        if(pData->msgMask == 0)
        {
            shmctl(msgId, IPC_RMID, 0);
#if USED_SHM_TO_DBUS
            __removeReqIdFromTable((key_t)key);
#endif
        }

        shmdt(pData);
#endif
    }      
    
    return 0;          
}

static DBusHandlerResult DBusOnMsgCb(DBusConnection* pConn, DBusMessage* pMsg, void* user_data)
{    
#if 0
    struct timeval tmBegin, tmEnd;
    long long diffTm;
#endif
    DBusError error;
    PDBUS_MSG_PROC  pMsgProc = NULL;

    PDBUS_MSG_PACK  pMsgPack = (PDBUS_MSG_PACK)malloc(sizeof(DBUS_MSG_PACK));

    if(pMsgPack == NULL)
    {
        LOG_EX(LOG_Error, "Receive Message: No Memory\n");
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

    memset(pMsgPack, 0, sizeof(DBUS_MSG_PACK)); 

    dbus_error_init(&error);
    
    if(dbus_message_is_signal(pMsg, DBUS_MESSAGE_INTERFACE_NAME, "Notify"))
    {                      
        if(dbus_message_get_args(pMsg, &error,
            DBUS_TYPE_UINT32,   &pMsgPack->msgSrc,        // from
            DBUS_TYPE_UINT32,   &pMsgPack->msgDests,      // to  -1 means all except it's self
#if USED_SHM_TO_DBUS            
            DBUS_TYPE_UINT32,   &pMsgPack->tmTickMSec,    // timestamp for msecond
#endif            
            DBUS_TYPE_UINT32,   &pMsgPack->busCmd,        // command type
            DBUS_TYPE_STRING,   &pMsgPack->pMsg,          // message context if had      
            DBUS_TYPE_INVALID))
        {   
            pMsgPack->msgSize   = strlen((char*)pMsgPack->pMsg);
            // reset timeout timer
            if(g_LibuvDBusParam.onHblCb && pMsgPack->msgSrc != g_LibuvDBusParam.modName)
            {
                HeartDaemonUpgrade(pMsgPack->msgSrc);
            }
            
            // Dispatch message except from it's self
            if(pMsgPack->msgSrc != g_LibuvDBusParam.modName
                && pMsgPack->msgDests & (1 << g_LibuvDBusParam.modName))
            {
                if(g_LibuvDBusParam.onMsgCb == NULL)
                {
                    pMsgProc = (PDBUS_MSG_PROC)malloc(sizeof(struct DBUS_MSG_PROC));

                    if(pMsgProc == NULL)
                    {
                        LOG_EX(LOG_Error, "Receive Message: No Memory\n");
                        free(pMsgPack);
                        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
                    }

                    memset(pMsgProc, 0, sizeof(struct DBUS_MSG_PROC));
                    memcpy(&pMsgProc->msgContent, pMsgPack, sizeof(DBUS_MSG_PACK));
                    pMsgProc->msgContent.pMsg = strdup(pMsgPack->pMsg);
                    pMsgProc->msgFrom   = 0;

                    uv_rwlock_wrlock(&g_uvMsgProcRwLock);
                    DL_APPEND(g_pMsgProcList, pMsgProc);
                    uv_rwlock_wrunlock(&g_uvMsgProcRwLock);
                }
                else
                {
                    DBusOnBoardcastMsgWorkCb(pMsgPack);
                }
            }
        }
        else
        {
            LOG_EX(LOG_Error, "Receive Notify Message Error: %s\n", error.message);
            dbus_error_free(&error);
        }

        free(pMsgPack);
        return DBUS_HANDLER_RESULT_HANDLED;
    }
    else if(dbus_message_is_method_call(pMsg, DBUS_MESSAGE_INTERFACE_NAME, "API"))
    {
        if(dbus_message_get_args(pMsg, &error,
            DBUS_TYPE_UINT32,   &pMsgPack->msgSrc,        // from
            DBUS_TYPE_UINT32,   &pMsgPack->msgDests,      // to  -1 means all except it's self
#if USED_SHM_TO_DBUS            
            DBUS_TYPE_UINT32,   &pMsgPack->tmTickMSec,    // timestamp for msecond
#endif            
            DBUS_TYPE_UINT32,   &pMsgPack->busCmd,        // command type
            DBUS_TYPE_UINT32,   &pMsgPack->msgSize,       // message size(in bytes)
            DBUS_TYPE_STRING,   &pMsgPack->pMsg,          // message context if had      
            DBUS_TYPE_INVALID))
        {           
            // reset timeout timer             
            if(g_LibuvDBusParam.onHblCb && pMsgPack->msgSrc != g_LibuvDBusParam.modName)
            {
                HeartDaemonUpgrade(pMsgPack->msgSrc);
            }
            
            // Dispatch message except from it's self
            if(pMsgPack->msgSrc != g_LibuvDBusParam.modName
                && pMsgPack->msgDests == (1 << g_LibuvDBusParam.modName))
            {
                if(g_LibuvDBusParam.onMsgCb == NULL)
                {
                    pMsgProc = (PDBUS_MSG_PROC)malloc(sizeof(struct DBUS_MSG_PROC));

                    if(pMsgProc == NULL)
                    {
                        LOG_EX(LOG_Error, "Receive Message: No Memory\n");
                        free(pMsgPack);
                        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
                    }

                    memset(pMsgProc, 0, sizeof(struct DBUS_MSG_PROC));
                    memcpy(&pMsgProc->msgContent, pMsgPack, sizeof(DBUS_MSG_PACK));
                    pMsgProc->msgContent.pMsg = strdup(pMsgPack->pMsg);
                    pMsgProc->msgFrom   = 1;

                    uv_rwlock_wrlock(&g_uvMsgProcRwLock);
                    DL_APPEND(g_pMsgProcList, pMsgProc);
                    uv_rwlock_wrunlock(&g_uvMsgProcRwLock);         
                }
                else
                {
                    DBusOnMsgWorkAPICb(pMsgPack);
                }
            }
        }
        else
        {
            LOG_EX(LOG_Error, "Receive API Message Error: %s\n", error.message);
            dbus_error_free(&error);
        }
        
        free(pMsgPack);
        return DBUS_HANDLER_RESULT_HANDLED;
    }   
    
    free(pMsgPack);
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

static void freeSHMResource(int shmId, void* pData)
{
    // Cleanup Share Memory
    if(pData)
    {
        shmdt(pData);
    }
    
    shmctl(shmId, IPC_RMID, 0);
}

static void FreeDBusSendToAsyncCb(uv_work_t* pWork, int status)
{
    PDBUS_ASYNC_PARAMS  pParam  = (PDBUS_ASYNC_PARAMS)pWork->data;
    free(pParam->pStruct);
    free(pParam);
    free(pWork);
}

static void DBusSendToAsyncCb(uv_work_t* pWork)
{
    int err = 0;
    PDBUS_ASYNC_PARAMS  pParam  = (PDBUS_ASYNC_PARAMS)pWork->data;
    const char *pJsonStr = Struct2Json(pParam->pStruct, pParam->type, pParam->enBase64, &err);

    if(pJsonStr == NULL || err != 0)
    {
        pParam->cbSendTo(err);
        return;
    }

    err = DBusSendToCommand(pParam->pBus, pParam->pBusName, pParam->busCmd, pJsonStr);

    free((void*)pJsonStr);

    pParam->cbSendTo(err);
}

int DBusJsonSendToCommandAsync(DBusConnection*                   pBus, 
                    const char*             pBusName,
                    uint32_t                busCmd,
                    JSON_ENGINE_TYPE        type,
                    void*                   pStruct,
                    int                     iSize,
                    OnDBusAsyncSendTo       cbSendTo,
                    int                     enBase64)
{
    PDBUS_ASYNC_PARAMS  pParam  = NULL;
    uv_work_t*          puvWork = NULL;
    
    if(cbSendTo == NULL)
    {
        return 0;
    }

    pParam = (PDBUS_ASYNC_PARAMS)malloc(sizeof(DBUS_ASYNC_PARAMS));
    puvWork = (uv_work_t*)malloc(sizeof(uv_work_t));
    
    pParam->pBus        = pBus;
    pParam->pBusName    = pBusName;
    pParam->busCmd      = busCmd;
    pParam->type        = type;
    pParam->iSize       = iSize;
    pParam->pStruct     = malloc(iSize);    
    pParam->cbSendTo    = cbSendTo;
    pParam->enBase64    = enBase64;
    memcpy(pParam->pStruct, pStruct, iSize);
    
    puvWork->data = (void*)pParam;
    uv_queue_work(g_LibuvDBusParam.pLoop, puvWork, DBusSendToAsyncCb, FreeDBusSendToAsyncCb);

    return 0;
}

int DBusJsonSendToCommand(DBusConnection*                   pBus, 
                    const char*             pBusName,
                    uint32_t                busCmd,
                    JSON_ENGINE_TYPE        type,
                    void*                   pStruct,
                    int                     enBase64)
{
    int ret, err = 0;    
    const char* pJsonStr = Struct2Json(pStruct, type, enBase64, &err);
    
    if(pJsonStr == NULL || err != 0)
    {
        return err;
    }
    
    ret = DBusSendToCommand(pBus, pBusName, busCmd, pJsonStr);

    free((void*)pJsonStr);

    return ret;
}

int DBusJsonBoardcastCommand(DBusConnection*                pBus, 
                                uint32_t                msgToMask, 
                                uint32_t                busCmd, 
                                JSON_ENGINE_TYPE        type,
                                void*                   pStruct,
                                int                     enBase64)
{   
    int ret, err = 0;
    const char* pJsonStr = Struct2Json(pStruct, type, enBase64, &err);

    if(pJsonStr == NULL || err != 0)
    {
        return err;
    }
    
    ret = DBusBoardcastCommand(pBus, msgToMask, busCmd, pJsonStr);

    free((void*)pJsonStr);
    return ret;
}

static unsigned int __getShmReqId(void)
{
    static unsigned int g_shmReqId = 1;
    unsigned int iReqId;

    if(g_shmReqId >= 0xFFFF - 1)
    {
        g_shmReqId = 1;
    }
    
    iReqId = __sync_fetch_and_add(&g_shmReqId, 1);

    return (iReqId & 0xFFFF) | (g_LibuvDBusParam.modName << 16);
}

int DBusSendToCommand(DBusConnection*     pBus, 
                    const char*             pBusName,
                    uint32_t                busCmd,
                    const char*             pContext)
{
#if USED_SHM_TO_DBUS            
    struct timeval          tv;
#endif
    int                     i;
    int                     msgId;
    //char                    msgContext[DBUS_MSG_MAX_PAD_SIZE];
    DBusMessage*            pMsg        = NULL;
    uint32_t                msgLen      = 0;
    uint32_t                msgToMask   = 0;
    OnDBusSendError         pErrorCb    = NULL;
    uint8_t*                pMsgInfo    = NULL;
    PDBUS_MSG_DATA          pShmData    = NULL;
    const char*             pPath       = NULL;
    char*                   pMsgContent = NULL;
    
#if 0
    if(pContext == NULL || (msgLen = strlen(pContext)) <= 0)
    {
        return (-ERR_INPUT_PARAMS);
    }
#else
    if(pContext == NULL)
    {
        pContext = "";
        msgLen = 0;
    }
    else
    {
        msgLen = strlen(pContext);
    }
#endif

    pMsgContent = (char*)malloc(msgLen + 1);

    if(pMsgContent == NULL)
    {
        LOG_EX(LOG_Error, "Malloc memory %d error\n", msgLen + 1);
        return -ERR_MALLOC_MEMORY;
    }

    pMsgInfo = (uint8_t*)pMsgContent;

    memset(pMsgContent, 0, msgLen + 1);

    if(pBus == NULL)
    {
        pBus = g_LibuvDBusParam.pBus;
    }

    for(i = 0; (i < sizeof(g_pModInfoTable) / sizeof(g_pModInfoTable[0])); i++)
    {
        // Skip match it'self
        if(strcmp(g_pModInfoTable[i].modAliase, pBusName) == 0)
        {   
            msgToMask = 1 << i;
            pPath = g_pModInfoTable[i].modPath;
            break;
        }
    }
    
    pMsg = dbus_message_new_method_call(pBusName, 
                        pPath, 
                        DBUS_MESSAGE_INTERFACE_NAME,
                        "API");

    if(pMsg == NULL)
    {
        free(pMsgContent);
        LOG_EX(LOG_Error, "DBus Create Message Error\n");
        return -ERR_DBUS_CREATE_MSG;
    }

    dbus_message_set_no_reply(pMsg, TRUE);

#if USED_SHM_TO_DBUS            
    gettimeofday(&tv, NULL);
#endif

    if(msgLen < DBUS_MSG_MAX_PAD_SIZE)
    {            
        strcpy(pMsgContent, pContext);
    }
    else
    {   
#if 0
        int msgKey = __getShmReqId();
        msgLen += sizeof(long int);

        // Make message with Memory Share
        msgId = shmget((key_t)msgKey, msgLen, 0666 | IPC_CREAT);
        
        if(msgId == -1)
        {
            perror("shmget_");
            return (-ERR_CREATE_SHM);
        }
        else
        {
            pShmData = (PDBUS_MSG_DATA)shmat(msgId, NULL, 0);

            if(pShmData == (void*)-1)
            {
                return -ERR_MAP_SHM;
            }

            pShmData->msgMask   = msgToMask;

            memcpy(pShmData->pMsgContext, pContext, msgLen);
            sprintf(pMsgInfo, "%d", msgKey);

            shmdt(pShmData);
            //print_hex_dump_bytes("send_", 2, pShmData, msgLen);
            //strcpy(pMsgInfo, (void*)&msgKey, sizeof(key_t));

#if USED_SHM_TO_DBUS
            __addShmIdToTable((key_t)msgKey, tv.tv_sec, msgToMask, msgLen);
#endif
            pErrorCb = freeSHMResource;    
        }
//#else
#endif
        free(pMsgContent);
        LOG_EX(LOG_Error, "Send Message size %d more than DBUS_MSG_MAX_PAD_SIZE, busCmd = %u, pBusName = %s\n", 
               msgLen, busCmd, pBusName);
        return -ERR_INPUT_PARAMS;
    }
        
    dbus_message_append_args(pMsg, 
            DBUS_TYPE_UINT32,   &g_LibuvDBusParam.modName,      // from
            DBUS_TYPE_UINT32,   &msgToMask,                     // to  -1 means all except it's self
#if USED_SHM_TO_DBUS                        
            DBUS_TYPE_UINT32,   &tv.tv_sec,                     // timestamp for msecond
#endif            
            DBUS_TYPE_UINT32,   &busCmd,                        // command type
            DBUS_TYPE_UINT32,   &msgLen,                        // message size(in bytes)
            DBUS_TYPE_STRING,   &pMsgInfo,
            // msgLen[0, 512): pad to message; msgLen[512, ~): memory map key, 
            //DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pMsgInfo, (msgLen < DBUS_MSG_MAX_PAD_SIZE) ? msgLen : sizeof(key_t),
            DBUS_TYPE_INVALID);
    
    free(pMsgContent);

    if(!dbus_connection_send(pBus, pMsg, NULL))
    {
        LOG_EX(LOG_Error, "Send Message Error\n");
        if(pErrorCb)
        {
            pErrorCb(msgId, pShmData);
        }

        return -ERR_BUS_SEND_MSG;
    }
    
    //dbus_connection_flush(pBus);
    dbus_message_unref(pMsg);

    usleep(100);

    return 0;
}

int DBusBoardcastCommand(DBusConnection*       pBus, 
                                uint32_t       msgToMask, 
                                uint32_t       busCmd, 
                                const char*    pContext)
{
#if USED_SHM_TO_DBUS            
    struct timeval          tv;
#endif        
    DBusMessage*            pMsg        = NULL;
    
    if(pContext == NULL)
    {
        pContext = "";
    }
    
    if(strlen(pContext) >= DBUS_MSG_MAX_PAD_SIZE)
    {
        LOG_EX(LOG_Error, "Msg size = %u more than DBUS_MSG_MAX_PAD_SIZE\n", strlen(pContext));
        return -ERR_DBUS_MSG_TO_LARGE;
    }
    
    if(pBus == NULL)
    {
        pBus = g_LibuvDBusParam.pBus;
    }
    
    pMsg = dbus_message_new_signal(g_LibuvDBusParam.pBusPath, DBUS_MESSAGE_INTERFACE_NAME, "Notify");

    if(pMsg == NULL)
    {
        return -ERR_DBUS_CREATE_MSG;
    }
    
    dbus_message_set_no_reply(pMsg, TRUE);        
    
#if USED_SHM_TO_DBUS            
    gettimeofday(&tv, NULL);
#endif

    dbus_message_append_args(pMsg, 
            DBUS_TYPE_UINT32,   &g_LibuvDBusParam.modName,      // from
            DBUS_TYPE_UINT32,   &msgToMask,                     // to  -1 means all except it's self
#if USED_SHM_TO_DBUS                        
            DBUS_TYPE_UINT32,   &tv.tv_sec,                     // timestamp for msecond
#endif            
            DBUS_TYPE_UINT32,   &busCmd,                        // command type
            DBUS_TYPE_STRING,   &pContext,
            DBUS_TYPE_INVALID);

    if(!dbus_connection_send(pBus, pMsg, NULL))
    {   
        return -ERR_BUS_SEND_MSG;
    }
    
    //dbus_connection_flush(pBus);
    dbus_message_unref(pMsg);

    usleep(100);
    return 0;
}

static void __addNewLoopTask(uv_loop_t* pLoop)
{
    PLOOP_TASK_ARRAY pItem = NULL;
    PLOOP_TASK_ARRAY pTask = NULL;
    if(pLoop == NULL)
    {
        return;
    }

    uv_rwlock_wrlock(&g_uvLoopRwLock);
    LL_FOREACH(g_LoopArray, pItem)
    {
        if(pItem->pLoop == pLoop)
        {
            LOG_EX(LOG_Warn, "Loop %p is added\n", pLoop);
            uv_rwlock_wrunlock(&g_uvLoopRwLock);
            return;
        }
    }

    pTask = (PLOOP_TASK_ARRAY)malloc(sizeof(struct LOOP_TASK_ARRAY));
    if(pTask == NULL)
    {
        LOG_EX(LOG_Error, "Malloc Memory Error\n");
        return;
    }
    memset(pTask, 0, sizeof(struct LOOP_TASK_ARRAY));
    pTask->pLoop = pLoop;
    pTask->isRunning = FALSE;
    LL_APPEND(g_LoopArray, pTask);
    uv_rwlock_wrunlock(&g_uvLoopRwLock);
}

static void __uvLoopRuntime(void *pParam)
{
    uv_loop_t* pLoop = (uv_loop_t*)pParam;

    if(pLoop)
    {
        while(TRUE)
        {
            uv_run(pLoop, UV_RUN_DEFAULT);
            usleep(1000);
        }
    }

    pthread_detach(pthread_self());
}

static void __runUVLoopTask(uv_loop_t* pLoop, void* pCallback)
{
    uv_thread_t uvThread;
    if(pLoop == NULL)
    {
        return;
    }
    
    uv_thread_create(&uvThread, __uvLoopRuntime, pLoop);
}

void RunUVLoop(uv_loop_t *pLoop)
{
#if 1
    int more;

    while(TRUE)
    {
        more = uv_run(g_LibuvDBusParam.pLoop, UV_RUN_ONCE);

        if(more == FALSE)
        {
            more = uv_loop_alive(g_LibuvDBusParam.pLoop);

            if(uv_run(g_LibuvDBusParam.pLoop, UV_RUN_NOWAIT) != 0)
            {
                more = TRUE;
            }
        }
    }
#else
    int more;

    do
    {
        if(pLoop && pLoop != g_LibuvDBusParam.pUserLoop)
        {
            more = uv_run(pLoop, UV_RUN_ONCE);

            if(more == FALSE)
            {
                more = uv_loop_alive(pLoop);

                if(uv_run(pLoop, UV_RUN_NOWAIT) != 0)
                {
                    more = TRUE;
                }
            }
        }

        if(g_LibuvDBusParam.pUserLoop)
        {
            more = uv_run(g_LibuvDBusParam.pUserLoop, UV_RUN_ONCE);

            if(more == FALSE)
            {
                more = uv_loop_alive(g_LibuvDBusParam.pUserLoop);

                if(uv_run(g_LibuvDBusParam.pUserLoop, UV_RUN_NOWAIT) != 0)
                {
                    more = TRUE;
                }
            }
        }

        if(g_LibuvDBusParam.pLoop)
        {
            more = uv_run(g_LibuvDBusParam.pLoop, UV_RUN_ONCE);

            if(more == FALSE)
            {
                more = uv_loop_alive(g_LibuvDBusParam.pLoop);

                if(uv_run(g_LibuvDBusParam.pLoop, UV_RUN_NOWAIT) != 0)
                {
                    more = TRUE;
                }
            }
        }
    }
    while(TRUE);
//#else
    //__runUVLoopTask(pLoop, NULL);
    //__addNewLoopTask(pLoop);
#endif
}

static void __uvDBusRecvProc(void *pParams)
{
    while(TRUE)
    {
        DBusMessage* pMsg = NULL;
        dbus_connection_read_write(g_LibuvDBusParam.pBus, 0);

        pMsg = dbus_connection_pop_message(g_LibuvDBusParam.pBus);

        if(pMsg != NULL)
        {
            DBusOnMsgCb(g_LibuvDBusParam.pBus, pMsg, NULL);
        }

        usleep(100);
    }
}

void DBusMsgCleanup(PDBUS_MSG_PACK pMsg)
{
    if(pMsg)
    {
        if(pMsg->pMsg)
        {
            free(pMsg->pMsg);
        }

        free(pMsg);
    }
}

PDBUS_MSG_PACK DBusGetMessage(void)
{
    int iCount, ret = 0;
    PDBUS_MSG_PACK pMsg = NULL;
    PDBUS_MSG_PROC pItem = NULL, pTmp = NULL;

    if(g_LibuvDBusParam.onMsgCb)
    {
        return NULL;
    }

    DL_COUNT(g_pMsgProcList, pItem, iCount);

    if(iCount == 0)
    {
        return pMsg;
    }

    pItem = NULL;
    uv_rwlock_wrlock(&g_uvMsgProcRwLock);
    DL_FOREACH_SAFE(g_pMsgProcList, pItem, pTmp)
    {
        if(pItem->msgFrom == 0)
        {
            ret = DBusOnBoardcastMsgWorkCb(&pItem->msgContent);
        }
        else
        {
            ret = DBusOnMsgWorkAPICb(&pItem->msgContent);
        }

        if(ret != 0)
        {
            pMsg = (PDBUS_MSG_PACK)malloc(sizeof(DBUS_MSG_PACK));

            if(pMsg)
            {
                memset(pMsg, 0, sizeof(DBUS_MSG_PACK));
                memcpy(pMsg, &pItem->msgContent, sizeof(DBUS_MSG_PACK));
                pMsg->pMsg = strdup(pItem->msgContent.pMsg);
            }
        }

        DL_DELETE(g_pMsgProcList, pItem);
        free(pItem->msgContent.pMsg);
        free(pItem);

        break;
    }
    uv_rwlock_wrunlock(&g_uvMsgProcRwLock);

    return pMsg;
}

#if 0
static void __uvMsgProc(void *pParams)
{
#ifdef ENABLE_COUNT_DEBUG
    struct timeval tmBegin, tmEnd;
    long long diffTm;
#endif

    while(TRUE)
    {
        int iMaxProcMsg = 100;
        PDBUS_MSG_PROC pItem = NULL, pTmp = NULL;

        uv_rwlock_wrlock(&g_uvMsgProcRwLock);
        DL_FOREACH_SAFE(g_pMsgProcList, pItem, pTmp)
        {
            if(--iMaxProcMsg == 0)
            {
                break;
            }

#ifdef ENABLE_COUNT_DEBUG
            gettimeofday(&tmBegin, NULL);
#endif
            if(pItem->msgFrom == 0)
            {
                DBusOnBoardcastMsgWorkCb(&pItem->msgContent);
#ifdef ENABLE_COUNT_DEBUG
                gettimeofday(&tmEnd, NULL);
                diffTm = (tmEnd.tv_sec * 1000000 + tmEnd.tv_usec) - (tmBegin.tv_sec * 1000000 + tmBegin.tv_usec);
                MonUpgradeStatistical(MON_MSG_BST_PROC_STAT, diffTm);
#endif
            }
            else
            {
                DBusOnMsgWorkAPICb(&pItem->msgContent);
#ifdef ENABLE_COUNT_DEBUG
                gettimeofday(&tmEnd, NULL);
                diffTm = (tmEnd.tv_sec * 1000000 + tmEnd.tv_usec) - (tmBegin.tv_sec * 1000000 + tmBegin.tv_usec);
                MonUpgradeStatistical(MON_MSG_PROC_STAT, diffTm);
#endif
            }

            if(pItem->msgContent.pMsg)
            {
                free(pItem->msgContent.pMsg);
            }

            DL_DELETE(g_pMsgProcList, pItem);
            free(pItem);
        }
    	uv_rwlock_wrunlock(&g_uvMsgProcRwLock);
        usleep(1000);
    }

    pthread_detach(pthread_self());
}
#endif

void SetHBLAutoExit(int flag)
{
    g_EnHBLExit = flag ? TRUE : FALSE;
}

static void __dBusDeameonCb(MODULE_NAME modName, int status)
{
    LOG_EX(status == 0 ? LOG_Info : LOG_Error, 
           "Daemon %s(%d) Msg: [%s]\n", ModuleNameToString(modName), modName,
           status == 0 ? "Connect" : "Disconnect");

    if(status != 0 && modName == MODULE_CONTROLLER && g_EnHBLExit)
    {
        sleep(1);
        //exit(0);
    }
}

static void __waitUDISKMount(void)
{
#ifdef PLATFORM_R16
    const char* pDoneStat       = "done";
    const char* pBootStatFile   = "/tmp/booting_state";
    char buf[5];
    FILE* pFile;

    // wait system create setup status file
    fprintf(stdout, "Wait boot status file create ......\n");
    while(access(pBootStatFile, F_OK) != 0)
    {
        usleep(10000);
    }
    
    pFile = fopen(pBootStatFile, "rb");

    if(pFile == NULL)
    {
        fprintf(stdout, "Open boot status file error\n");
        return;
    }

    fprintf(stdout, "Wait boot status done ......\n");

    // when UDISK mount, file /tmp/booting_state content is "done"
    do
    {
        fseek(pFile, 0, SEEK_SET);
        memset(buf, 0, 5);
        fread(buf, 1, 4, pFile);        //  read 4 bytes status tags

        usleep(10000);
    }
    while(strncmp(buf, pDoneStat, strlen(pDoneStat)) != 0);

    fclose(pFile);

    fprintf(stdout, "Boot status done ......\n");
#endif
}

int GetServerModeFromCC(int defValue, int* pErr)
{
    char* pSvrMode = NULL;
    int iValue = defValue;

    GetShellExecResult("curl --silent http://localhost:1705/httpenv/999 | grep 'env:' | awk '{print $3}'", &pSvrMode);
    
    if(pSvrMode == NULL)
    {
        if(pErr)
        {
            *pErr = -ERR_NO_ITEMS;
        }
        return defValue;
    }

    iValue = strtol(pSvrMode, NULL, 10);
    free(pSvrMode);

    if(errno == EINVAL || errno == ERANGE)
    {
        if(pErr)
        {
            *pErr = -ERR_STR_CONVERT;
        }

        return defValue;
    }

    if(pErr)
    {
        *pErr = 0;
    }

    return iValue;
}

DBusConnection* DBusWithLibuvInit(uv_loop_t*            pUserLoop, 
                                const char*             pBusName,
                                OnDBusMessage           cbOnMsg,
                                OnDaemonMsg             cbOnHbl,
                                OnKeyEvent              cbOnKey,
                                int*                    pErrno)
{
    int i, ret = 0;
    DBusError error;
    SERVER_MODULE_TYPE svrMode;
    uv_async_t* pAsync = NULL;
    DBusConnection* pBus = NULL;
	uv_fs_t* puvFsReq;
    //uv_thread_t uvMsgProcThread;
    //uv_thread_t uvMsgRecvThread;

    //uv_thread_t uvLoopThread;
    char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH - 1];
#if USED_SHM_TO_DBUS
    uv_thread_t uvSyncThread;
#endif
    //uv_loop_t *pLoop = uv_loop_new();
    uv_loop_t *pLoop = pUserLoop;

    memset(&g_LibuvDBusParam, 0, sizeof(LIBUV_DBUS_PARAMS));

    for(i = 0; (i < sizeof(g_pModInfoTable) / sizeof(g_pModInfoTable[0])); i++)
    {
        // Skip match it'self
        if(strcmp(g_pModInfoTable[i].modAliase, pBusName) == 0)
        {   
            g_LibuvDBusParam.modName        = g_pModInfoTable[i].modName;
            g_LibuvDBusParam.pBusName       = g_pModInfoTable[i].modAliase;
            g_LibuvDBusParam.pBusPath       = g_pModInfoTable[i].modPath;
            break;
        }        
    }

    memset(&g_workDayArray, 0, sizeof(WORKDAY_INFO));
    srand(time(NULL));
	
	if(pLoop == NULL || pBusName == NULL || pErrno == NULL)
    {
		if(pErrno)
		{
			*pErrno = -ERR_INPUT_PARAMS;
		}
		
        LOG_EX(LOG_Error, "Input params error: pLoop = %p, pBusName = %p, pErrno = %p\n", 
                pLoop, pBusName, pErrno);
        return NULL;
    }
	
    puvFsReq   = (uv_fs_t*)malloc(sizeof(uv_fs_t));
   
    // wait UDISK mount, configure file save in UDISK partition
    __waitUDISKMount();

    CfgFileInit();   

    IHW_InitLOG(strrchr(pBusName, '.') + 1, NULL, TRUE);
    IHW_EnableLogLevel(LOG_Fatal | LOG_Error | LOG_Warn | LOG_Debug | LOG_Info, 1);
    IHW_RunLogService();

    APP_BUILD_INFO(strrchr(pBusName, '.') + 1, GetCurrentVersion());
   
    i = 0;
    do
    {
        svrMode = CfgGetIntValueV1("Global.ServerMode", PUBLISH_MODE, &ret);

        if(ret != 0)
        {
            sleep(1);
        }
        else
        {
            usleep(1000);
        }

        LOG_EX(LOG_Debug, "ServerMode = %d, Error = %d\n", svrMode, ret);
    } while (ret != 0 && i++ <= 3);
    
    if(ret != 0)
    {
        LOG_EX(LOG_Error, "Read Server Mode Error: ret = %d\n", ret);
        svrMode = CfgGetIntValueV2("ServerMode", PUBLISH_MODE, &ret);

        if(ret == 0)
        {
            LOG_EX(LOG_Warn, "Recovery Server Mode OK: ServerMode = %d\n", svrMode);
        }
        else
        {
            LOG_EX(LOG_Error, "CfgGetInvValueV2 Read Server Mode Error: ret = %d\n", ret);
            svrMode = GetServerModeFromCC(PUBLISH_MODE, &ret);

            if(ret == 0)
            {
                LOG_EX(LOG_Warn, "Netease Controller Server Mode OK: ServerMode = %d\n", svrMode);
            }
            else
            {
                svrMode = PUBLISH_MODE;
                LOG_EX(LOG_Error, "GetServerModeFromCC Read Server Mode Error: "
                                  "ret = %d, Set to default: PUBLISH_MODE\n", ret);
            }
        }
    }

    SetCurrentServerMode(svrMode);
    DumpCurServerAddr("Default");

#if USED_SHM_TO_DBUS    
    uv_rwlock_init(&g_uvShmHashRwLock);
#endif

    uv_rwlock_init(&g_uvMsgProcRwLock);
    uv_rwlock_init(&g_uvLoopRwLock);

    uv_fs_access(pLoop, puvFsReq, "/mnt/UDISK/debug.dbg", F_OK, uvFsAccessCb);   

    memset(rule, 0, DBUS_MAXIMUM_MATCH_RULE_LENGTH - 1);
    srand(time(NULL));
    
    //setenv("UV_THREADPOOL_SIZE", "128", 1);
    
	dbus_error_init(&error);

    pBus = dbus_bus_get(DBUS_BUS_SESSION, &error);

	if (dbus_error_is_set(&error)) 
	{
		LOG_EX(LOG_Error, "dbus: Could not acquire the session bus\n");

		dbus_error_free(&error);
		*pErrno = -ERR_GET_BUS;
		return NULL;
	}

	ret = dbus_bus_request_name(pBus, pBusName, DBUS_NAME_FLAG_REPLACE_EXISTING, &error);

	if(dbus_error_is_set(&error))
	{
        LOG_EX(LOG_Error, "dbus: Could not request dbus name\n");

		dbus_error_free(&error);
		*pErrno = -ERR_REQUEST_BUS_NAME;
		return NULL;
	}

	if(ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
	{
        LOG_EX(LOG_Error, "dbus: Could not request dbus name\n");

		dbus_error_free(&error);
		*pErrno = -ERR_REQUEST_BUS_NAME;
		return NULL;
	}
#if 1
    if(!dbus_connection_set_watch_functions(pBus,
                                        DBusAddWatchCb,
                                        DBusRemoveWatchCb,
                                        DBusNotifyWatchCb,
                                        (void*)pLoop, NULL))
    {
        LOG_EX(LOG_Error, "dbus: Could not set watch function\n");

		*pErrno = -ERR_SET_WATCH_FUNCTION;
		return NULL;
    }

    if(!dbus_connection_set_timeout_functions(pBus,
                                          DBusAddTimeoutCb,
                                          DBusRemoveTimeoutCb,
                                          DBusNotifyTimeoutCb,
                                          (void*)pLoop, NULL))
    {
        LOG_EX(LOG_Error, "dbus: Could not set watch function\n");

		*pErrno = -ERR_SET_TIMEOUT_FUNCTION;
		return NULL;
    }
#endif

    pAsync = malloc(sizeof(uv_async_t));
    pAsync->data = (void*)pBus;

    uv_async_init(pLoop, pAsync, uvAsyncCb);
    uv_unref((uv_handle_t*)pAsync);
    
#if 1
    dbus_connection_set_wakeup_main_function(pBus,
                                         DBusWakeupMainLoopCb,
                                         (void*)pAsync, DBusAsyncFreeCb);
#endif

    sprintf(rule, "type='signal', interface='%s'", DBUS_MESSAGE_INTERFACE_NAME);
    dbus_bus_add_match(pBus, rule, &error);

    if(dbus_error_is_set(&error))
    {
        LOG_EX(LOG_Error, "dbus_bus_add_match [%s] error: %s\n", DBUS_MESSAGE_INTERFACE_NAME, error.message);
        dbus_error_free(&error);
        *pErrno = -ERR_BUS_MATCH;
        return NULL;
    }
#if 1
    if(!dbus_connection_add_filter(pBus, DBusOnMsgCb, pLoop, NULL))
    {
        LOG_EX(LOG_Error, "dbus_connection_add_filter error\n");
        *pErrno = -ERR_BUS_SET_MSG_CB;
        return NULL;
    }
#endif

    uv_idle_init(pLoop, &g_uvIdleHandle);    
    g_uvIdleHandle.data = pBus;
    uv_idle_start(&g_uvIdleHandle, uvIdleCb);

    if(cbOnKey)
    {
        uv_fs_open(pLoop, &g_uvKeyEvent, R16_TINA_KEY_EVENT_PATH, O_RDONLY, 0, uvOpenKeyEventCb);
        g_LibuvDBusParam.onKeyCb        = cbOnKey;
    }
    
    g_LibuvDBusParam.pLoop              = pLoop;
    g_LibuvDBusParam.pUserLoop          = pUserLoop;
    g_LibuvDBusParam.pBus               = pBus;
    g_LibuvDBusParam.onMsgCb            = cbOnMsg;

#if 0
    if(cbOnHbl)
    {
        g_LibuvDBusParam.onHblCb        = cbOnHbl;
        HeartDaemonInit(g_LibuvDBusParam.modName, HEART_LOST_DELAY, cbOnHbl);
    }
#else
    g_LibuvDBusParam.onHblCb            = __dBusDeameonCb;
    HeartDaemonInit(g_LibuvDBusParam.modName, HEART_LOST_DELAY, __dBusDeameonCb);
#endif

#if USED_SHM_TO_DBUS
    uv_thread_create(&uvSyncThread, __uvShmTblTaskThreadCb, NULL);
#endif

#ifdef ENABLE_COUNT_DEBUG
    MonitorInit();
    MonAddNewItem(MON_MSG_PROC_STAT, 100000);
    MonAddNewItem(MON_MSG_BST_PROC_STAT, 100000);
    MonAddNewItem(MON_USER_MSG_PROC_STAT, 100000);
    MonAddNewItem(MON_USER_MSG_BST_PROC_STAT, 100000);
#endif

    InetInit();
    EvpSystemInit();

#if 0
    if(g_LibuvDBusParam.onMsgCb)
    {
        uv_thread_create(&uvMsgProcThread, __uvMsgProc, NULL);
    }
#endif

    return pBus;
}

int DBusWithLibuvCfgInit(OnCfgMsg cbOnCfgMsg)
{
    CfgGlobalEnvInit();

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

    g_LibuvDBusParam.onCfgCb    = cbOnCfgMsg;

    return (0);
}

static int __reqWorkDayInfo(int year)
{
    int ret = 0;
    WORKDAY_INFO reqInfo;

    memset(&reqInfo, 0, sizeof(WORKDAY_INFO));

    if(year <= 0)
    {
        struct  tm  localTime;
        time_t tmStamp     = time((time_t*)NULL);
            
        localtime_r(&tmStamp, &localTime);

        reqInfo.year    = localTime.tm_year;
    }
    else
    {
        reqInfo.year    = year;
    }

    if(reqInfo.year < (2018 - 1900))
    {
        return 0;
    }

    ret = DBusJsonSendToCommand(NULL,
                g_pModInfoTable[MODULE_CONTROLLER].modAliase,
                CMD_WORKDAY_DB_REQ, 
                JSON_ENGINE_WORKDAY_REQ,
                &reqInfo, FALSE);

    return (ret);
}

int IsHolidayDBSynced(void)
{
    return g_workDayArray.isReady;
}

int CurrentIsWorkDay(int year, int day)
{
    static unsigned int i = 0;

    if(year != 0)
    {
        LOG_EX(LOG_Debug, "CurrentIsWorkDay: year = %d, day = %d\n", year, day);
    }

    if(day > 365)
    {
        LOG_EX(LOG_Error, "Error Input Params: day = %d\n", day);
        return (-ERR_INPUT_PARAMS);
    }

    if(g_workDayArray.year <= 0)
    {
        __reqWorkDayInfo(year);
        if(i++ % 10 == 0)
        {
            LOG_EX(LOG_Error, "Unsync Database: year = %d\n", year);
        }
        return (-ERR_UNINIT_ITEM);
    }

    if(day < 0 || year <= 0)
    {
        struct  tm  localTime;
        time_t tmStamp     = time((time_t*)NULL);
        localtime_r(&tmStamp, &localTime);

        if(day < 0)
        {
            day = localTime.tm_yday;
        }

        if(year <= 0)
        {
            year = localTime.tm_year;
        }
    }

    if(year != g_workDayArray.year - 1900)
    {
        __reqWorkDayInfo(year);
        LOG_EX(LOG_Error, "Have No Current Year Database: year = %d, g_workDayArray.year = %d\n", 
               year, g_workDayArray.year);

        return (-ERR_NO_ITEMS);
    }

    // 0 work day, 1 holiday
    return (g_workDayArray.days[day] == 0);
}

WIFI_STATUS GetCurrWIFIConnStatus(void)
{
    return g_WifiConnStatus;
}