/** @file       log.c
    @brief      系统日志接口文件
    @version    1.0.0
*/
#include <sys/time.h>
#include <fcntl.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <uthash/utlist.h>
#include <uv.h>
#include <uthash/utstring.h>
#include <uthash/utringbuffer.h>
#include <linux/mii.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include <net/if.h>
#ifdef PLATFORM_R16
#include <cutils/cpu_info.h>
#endif

#include "log.h"
#include "smart_sound.h"
#include "inet_api.h"
#include "crypto.h"
#include "libuv_dbus.h"
#include "server_addr.h"

#define SHOW_CONSOLE_RED        ("\033[31;48m\033[1m%s\033[0m%c")
#define SHOW_CONSOLE_YELLOW     ("\033[33;48m\033[1m%s\033[0m%c")
#define SHOW_CONSOLE_GREEN      ("\033[32;48m\033[1m%s\033[0m%c")
#define SHOW_CONSOLE_BLUE       ("\033[34;48m\033[1m%s\033[0m%c")

#ifdef PLATFORM_CPU
#define LOG_FILE_BASEDIR            (".")
#else
#define LOG_FILE_BASEDIR            ("/tmp")
#endif

#ifdef PLATFORM_R16
#define NIC_NAME                    ("wlan0")
#else
#define NIC_NAME                    ("enp3s0")
#endif

#define MAX_LOG_ITEM                (1000)
#define MAX_LOG_FILE_SIZE           (1024 * 1024)
#define LOG_PRE_SIZE                (512)

#if 0
#define LOG_MAIL_USERNAME			("pv1_es2@hotmail.com")
#define	LOG_MAIL_PASSWORD		    ("netEase163")
#define LOG_MAIL_SERVER             ("smtp://smtp-mail.outlook.com")
#define LOG_MAIL_PORT               (587)
#else
#define LOG_MAIL_USERNAME			("pv1_es2@163.com")
#define LOG_MAIL_LOGPASSWD          ("netEase163")
#define	LOG_MAIL_PASSWORD		    ("pv1Dev163")
#define LOG_MAIL_SERVER             ("smtp.163.com")
#define LOG_MAIL_PORT               (25)
#endif

#define	LOG_MAIL_RECEIVER		    ("pv1_es2@163.com")

#define SYS_POINT_UPLOAD_TIME       (600)

typedef struct
{
    char*           pDeviceId;
    char            savePath[MAX_PATH];
    int             iMaxCacheSize;
    int             iEveryUploadTime;
    time_t          lastPostTime;
    time_t          lastMarkTime;
    FILE*           pMarkFile;
    uv_rwlock_t     uvRwLock;
    uv_thread_t     uvIOThread;
} SYSPOINT_INFO, *PSYSPOINT_INFO;

typedef struct SYSPOINT_ITEM
{
    char* pContent;
    struct SYSPOINT_ITEM *next, *prev;
} *PSYSPOINT_ITEM;

typedef struct LOG_ITEM
{
    LOG_LEVEL       level;
    int             isPrinted;
    int             isAddTags;
    struct timeval  timestamp;
    char            *pLogContent;
    struct LOG_ITEM *next, *prev;
}*PLOG_ITEM;

typedef struct LOG_BACKUP
{
    time_t              timestamp;
    char                *plogFilePath;
    char                *pGzFilePath;
    unsigned int        sendTimes;
    unsigned int        tolLogs;
    struct LOG_BACKUP   *next, *prev;
} *PLOG_BACKUP;

typedef struct
{
    pid_t           pid;
    char*           pChipId;
    char*           pChipSerial;
    char            exeName[MAX_PATH];
    char            logFilePath[MAX_PATH];
    FILE            *pLogFile;
} LOG_PROCESS_INFO, *PLOG_PROCESS_INFO;

static int                      g_bEnableLog = FALSE;        // 是否启用 Log 功能
static int                      g_bEnMailBackup = FALSE;
static char*                    g_pEmailBox = NULL;
static int                      g_bEnLogToFile  = TRUE;
static int                      g_bEnLogToServer = TRUE;
static char                     g_strLogTag[32];            // Log 标志
static unsigned int             g_LogRdPos = 0;
static pthread_t                g_logThreadId;
static pthread_t                g_backupThreadId;
static LOG_PROCESS_INFO         g_LogProcessInfo;
static uv_rwlock_t              g_uvLogLock;
static uv_rwlock_t              g_uvLogBackLock;
static PLOG_ITEM                g_pLogItemList = NULL;
static PLOG_BACKUP              g_pLogBackupList = NULL;
static int                      g_logSock = -1;
struct sockaddr_in              g_logAddr;

static SYSPOINT_INFO            g_SysPonitInfo;
static PSYSPOINT_ITEM           g_pSysPointArray = NULL;
static PHTTP_POST_ATTACH        g_pLogParamsArray = NULL;


static int g_iMinLevel = LOG_Fatal | LOG_Error | LOG_Warn | LOG_Debug | LOG_Info | LOG_Step;

/**
 * @brief Log 调试等级转字符串
 * @param level 调试等级
 * @return 调试等级对应的字符串
 */
const char* LogLevelToStr(LOG_LEVEL level)
{
    switch(level)
    {
        case LOG_Test:
            return "T";

        case LOG_Info:
            return "I";

        case LOG_Call:
            return "C";

        case LOG_Debug:
            return "D";

        case LOG_Warn:
            return "W";

        case LOG_Error:
            return "E";

        case LOG_Fatal:
            return "F";

        case LOG_Step:
            return "S";

        case LOG_Devp:
            return "V";    

        case LOG_Unknown:
            return "U";

        case LOG_All:
            return "A";

        default:
            return "?";
    }

    return "U";
}

#pragma pack (push)
#pragma pack (1)
typedef struct
{
    unsigned short  logSeq;
    unsigned int    pid;
    unsigned int    timeStamp;
    unsigned int    nanotime;
    unsigned int    logLevel;
    char            logContent[0];
} LOG_PROTO_CONTENT, *PLOG_PROTO_CONTENT;
#pragma pack (pop)

static struct HTTP_POST_ATTACH g_PostLogParams[] = 
{
    {"CPUID",           "", NULL, NULL},
    {"MAC",             "", NULL, NULL},
    {"Version",         "", NULL, NULL},
    {"BuildTime",       "", NULL, NULL},
    {"SerialNumber",    "", NULL, NULL},
    {"Datetime",        "", NULL, NULL},
    {"content",         "", NULL, NULL},
};

static void __initPostLogParams(void)
{
    char    *pMacAddr  = NULL;
    struct  tm  localTime;
    time_t  timeStamp       = time((time_t*)NULL);

    GetShellExecResult("ifconfig wlan0 | grep HWaddr | awk \'{print $5}\'", &pMacAddr);

    localtime_r(&timeStamp, &localTime);
    
    strncpy(g_PostLogParams[0].keyValue, g_LogProcessInfo.pChipId, MAX_HTTP_POST_SIZE);
    strncpy(g_PostLogParams[1].keyValue, (pMacAddr && strlen(pMacAddr) > 0) ? pMacAddr : "00:00:00:00:00:00", MAX_HTTP_POST_SIZE);
    strncpy(g_PostLogParams[2].keyValue, GetCurrentVersion(), MAX_HTTP_POST_SIZE);
    sprintf(g_PostLogParams[3].keyValue, "Build: %s %s", __DATE__, __TIME__);
    strncpy(g_PostLogParams[4].keyValue, g_LogProcessInfo.pChipSerial, MAX_HTTP_POST_SIZE);

    for(int i = 0; i < sizeof(g_PostLogParams) / sizeof(g_PostLogParams[0]); i++)
    {
        LL_APPEND(g_pLogParamsArray, &g_PostLogParams[i]);
    }

    if(pMacAddr)
    {
        free(pMacAddr);
    }
}

int HttpPostLogFile(char* pSession)
{
    int ret;
    struct  tm  localTime;
    const char* pFileName = "/tmp/backuplog.tar.gz";
    const char* pLastFile = "/mnt/UDISK/backuplog_nand.tar.gz";
    char*   pLogServerURL = GetCurServerAddr(LOG_MODULE);
    time_t  timeStamp     = time((time_t*)NULL);

    ret = system(" /usr/sbin/backuplocalfiles.sh");
    
    if(access(pFileName, F_OK) != 0
       && access(pLastFile, F_OK) != 0)
    {
        return -ERR_FILE_NOT_EXISTS;
    }

    localtime_r(&timeStamp, &localTime);

    memset(g_PostLogParams[5].keyValue, 0, MAX_HTTP_POST_SIZE);
    sprintf(g_PostLogParams[5].keyValue, "%04u-%02u-%02u %02u:%02u:%02u", 
                localTime.tm_year + 1900,
                localTime.tm_mon + 1, 
                localTime.tm_mday,
                localTime.tm_hour,
                localTime.tm_min,
                localTime.tm_sec);
    
    memset(g_PostLogParams[6].keyValue, 0, MAX_HTTP_POST_SIZE);

    if(pSession && strlen(pSession) >= 2)
    {
        strncpy(g_PostLogParams[6].keyValue, pSession, MAX_HTTP_POST_SIZE);
    }
    else
    {
        strncpy(g_PostLogParams[6].keyValue, "{}", MAX_HTTP_POST_SIZE);
    }

    if(access(pFileName, F_OK) == 0)
    {
        ret = InetHttpUploadFileSync(pLogServerURL, pFileName, g_pLogParamsArray);

        if(ret == 0)
        {
            LOG_EX(LOG_Debug, "Upload Log Data [%s] To Server [%s]\n", pFileName, pLogServerURL);
            unlink(pFileName);
        }
        else
        {
            LOG_EX(LOG_Error, "Upload Log Data [%s] To Server [%s]\n", pFileName, pLogServerURL);
        }
    }
    
    if(access(pLastFile, F_OK) == 0)
    {
        ret = InetHttpUploadFileSync(pLogServerURL, pLastFile, g_pLogParamsArray);

        if(ret == 0)
        {
            LOG_EX(LOG_Debug, "Upload Log Data [%s] To Server [%s]\n", pLastFile, pLogServerURL);
            unlink(pLastFile);
        }
        else
        {
            LOG_EX(LOG_Error, "Upload Log Data [%s] To Server [%s]\n", pLastFile, pLogServerURL);
        }
    }

    //fprintf(stdout, "Upload Log Data [%s] To Server [%s], size = %d\n", pFileName, UPL_HTTP_URL, fileSize);

    return ret;
}

static int __logNetworkSend(PLOG_ITEM pItem)
{
    PLOG_PROTO_CONTENT  pLogInfo;
    unsigned char       *pBuf;
    int ret, totalSize = 0;
    static unsigned short sendSeq = 0;

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

    totalSize = sizeof(LOG_PROTO_CONTENT) + strlen(pItem->pLogContent);

    pBuf = (unsigned char *)malloc(totalSize);

    if(pBuf == NULL)
    {
        return (-ERR_MALLOC_MEMORY);
    }

    memset(pBuf, 0, totalSize);

    pLogInfo = (PLOG_PROTO_CONTENT)pBuf;

    pLogInfo->logSeq        = htons(sendSeq++);
    pLogInfo->timeStamp     = htonl(pItem->timestamp.tv_sec);
    pLogInfo->nanotime      = htonl(pItem->timestamp.tv_usec);
    pLogInfo->pid           = htonl(g_LogProcessInfo.pid);
    pLogInfo->logLevel      = htonl(pItem->level);
 
    memcpy(pLogInfo->logContent, pItem->pLogContent, strlen(pItem->pLogContent));

    ret = sendto(g_logSock, pBuf, totalSize, 0, 
             (struct sockaddr *)&g_logAddr, sizeof(g_logAddr));

    if(ret == totalSize)
    {
        return (0);
    }
    else
    {
        //LOG_EX(LOG_Error, "Send %d bytes, need %d bytes\n", ret, totalSize);
        return (-ERR_NETWORK_SEND);
    }
}

static void __cleanupBackupItem(PLOG_BACKUP pItem)
{
    if(pItem)
    {
        LL_DELETE(g_pLogBackupList, pItem);

        if(pItem->plogFilePath)
        {
            if(access(pItem->plogFilePath, F_OK) == 0)
            {
                unlink(pItem->plogFilePath);
            }

            free(pItem->plogFilePath);
        }

        if(pItem->pGzFilePath)
        {
            if(access(pItem->pGzFilePath, F_OK) == 0)
            {
                unlink(pItem->pGzFilePath);
            }

            free(pItem->pGzFilePath);
        }

        free(pItem);
    }
}

static void* __uvLogBackupProc(void *pParams)
{
    SMTP_MAIL_CONFIG        smtpCfg;
    char                    gzLogPath[MAX_PATH];
    struct tm               localTime;
    const char *pFrom       = LOG_MAIL_USERNAME;
    const char *pTo[]       = {g_pEmailBox, NULL};
    char *pMacAddr          = NULL;
    
    //const char *pCc[] = {"xajhuang@qq.com", "xajhuang@163.com", NULL};

    memset(&smtpCfg, 0, sizeof(SMTP_MAIL_CONFIG));

    smtpCfg.pUserName   = LOG_MAIL_USERNAME;
    smtpCfg.pPassword   = LOG_MAIL_PASSWORD;
    smtpCfg.pSmtpServer = LOG_MAIL_SERVER;
    smtpCfg.smtpPort    = LOG_MAIL_PORT;

#ifdef PLATFORM_CPU
    GetShellExecResult("ifconfig enp3s0 | grep HWaddr | awk \'{print $5}\'", &pMacAddr);
#else
    GetShellExecResult("ifconfig wlan0 | grep HWaddr | awk \'{print $5}\'", &pMacAddr);
#endif

    if(pMacAddr == NULL || strlen(pMacAddr) == 0)
    {
        pMacAddr = strdup("00:00:00:00:00:00");
    }

    while(TRUE)
    {
        PLOG_BACKUP pItem = NULL, pTmp  = NULL;
        time_t  timeStamp       = time((time_t*)NULL);
        localtime_r(&timeStamp, &localTime);

		uv_rwlock_wrlock(&g_uvLogBackLock);

        LL_FOREACH_SAFE(g_pLogBackupList, pItem, pTmp)
        {
            const char *pAttact[2];
            UT_string               *pTitle, *pMessage, *pDatetime;
            char*   pMD5Val;

            pAttact[1] = NULL;

            if(pItem->plogFilePath == NULL || strlen(pItem->plogFilePath) == 0)
            {
                __cleanupBackupItem(pItem);
                continue;
            }

            if(timeStamp - pItem->timestamp >= 3600)
            {
                __cleanupBackupItem(pItem);
                continue;
            }

            if(pItem->sendTimes >= 3)
            {
                __cleanupBackupItem(pItem);
                continue;
            }
#if 0
            if(g_bEnLogToServer)
            {
                __logHttpPostFile(pItem->plogFilePath);
                //InetHttpUploadFileSync(UPL_HTTP_URL, pItem->pGzFilePath, NULL);
            }
#endif            
            if(pItem->pGzFilePath == NULL)
            {
                memset(gzLogPath, 0, MAX_PATH);
                sprintf(gzLogPath, "%s/%d_%s_%u.log.gz", 
                        LOG_FILE_BASEDIR, g_LogProcessInfo.pid, g_LogProcessInfo.exeName, pItem->tolLogs);

                if(GZipFileCompress(pItem->plogFilePath, gzLogPath) != 0)
                {
                    LOG_EX(LOG_Error, "Create Gzip File Error: %s\n", gzLogPath);
                    continue;
                }
                else
                {
                    pItem->pGzFilePath  = strdup(gzLogPath);
                }
            }     
            
            pMD5Val = (char*)EvpMD5HashFile(pItem->pGzFilePath);       

            utstring_new(pTitle);
            utstring_new(pMessage);
            utstring_new(pDatetime);

            utstring_printf(pDatetime, "%04u-%02u-%02u %02u:%02u:%02u", 
                            localTime.tm_year + 1900,
                            localTime.tm_mon + 1, 
                            localTime.tm_mday,
                            localTime.tm_hour,
                            localTime.tm_min,
                            localTime.tm_sec);   

            utstring_printf(pTitle, "Log [%s]-[%s]-[%s]", 
                            g_LogProcessInfo.exeName, 
                            g_LogProcessInfo.pChipSerial,
                            utstring_body(pDatetime));

            utstring_printf(pMessage, "<html><body>\r\n"
                                  "<p><span style=\"font-size:24px;\"><strong>Summary information</strong></span><span style=\"font-size:24px;\">:</span></p>"
                                  "<p><br /></p>"
                                  "<p><table style=\"width:35%%;\" cellpadding=\"2\" cellspacing=\"0\" border=\"1\" bordercolor=\"#000000\">"
                                  "<tbody>"
                                  "<tr><td><b>Items</b><br /></td><td>%u<br /></td></tr>"
                                  "<tr><td><b>Process PID</b><br /></td><td>%d<br /></td></tr>"
                                  "<tr><td><b>Process Name</b><br /></td><td>%s<br /></td></tr>"
                                  "<tr><td><b>CPU ID</b><br /></td><td>%s<br /></td></tr>"
                                  "<tr><td><b>Serial</b><br /></td><td>%s<br /></td></tr>"
                                  "<tr><td><b>Mac Address</b><br /></td><td>%s<br /></td></tr>"
                                  "<tr><td><b>File Checksum</b><br /></td><td>%s<br /></td></tr>"
                                  "<tr><td><b>Log Level</b><br /></td><td>0x%08X<br /></td></tr>"
                                  "<tr><td><b>Datetime</b><br /></td><td>%s<br /></td></tr>"  
                                  "</tbody>"
                                  "</table>"
                                  "</p>"
                                  "</body></html>\r\n", 
                            pItem->tolLogs,
                            g_LogProcessInfo.pid,
                            g_LogProcessInfo.exeName,
                            g_LogProcessInfo.pChipId,
                            g_LogProcessInfo.pChipSerial,
                            pMacAddr,
                            SAFE_STRING_VALUE(pMD5Val),
                            g_iMinLevel,
                            utstring_body(pDatetime));

            pAttact[0] = pItem->pGzFilePath;
#if 0
            if(g_bEnMailBackup &&
               InetSmtpSendEmail(pFrom, pTo, NULL, utstring_body(pTitle), 
                                 utstring_body(pMessage), pAttact, &smtpCfg) == 0)
            {
                __cleanupBackupItem(pItem);
            }
            else
            {
                LOG_EX(LOG_Error, "Send email error: %s\n", pItem->pGzFilePath);
            }
#endif
            pItem->sendTimes++;

            if(pMD5Val)
            {
                free(pMD5Val);
            }

            utstring_free(pTitle);
            utstring_free(pMessage);
            utstring_free(pDatetime);
        }

		uv_rwlock_wrunlock(&g_uvLogBackLock);
        
        sleep(1);
    }

    free(pMacAddr);
    pthread_detach(pthread_self());
    return (NULL);
}

static void __logColorOutput(const char* pColFmt, UT_string* pLog)
{
    if(pLog == NULL)
    {
        return;
    }

    if(pColFmt == NULL)
    {
        print("%s", utstring_body(pLog));
    }
    else
    {
        if(utstring_find(pLog, -1, "\n", 1) == utstring_len(pLog) - 1)
        {
            char* pLogArray = utstring_body(pLog);
            pLogArray[utstring_len(pLog) - 1] = 0;

            print(pColFmt, pLogArray, '\n');

            strcat(pLogArray, "\n");
        }
        else
        {
            print(pColFmt, utstring_body(pLog), '\0');
        }
    }
}

static void* __logOutputThread(void *p)
{
    while(TRUE)
    {
		int isWriteLog = FALSE;
        PLOG_ITEM pItem = NULL, pTmp = NULL;
      
		uv_rwlock_wrlock(&g_uvLogLock);

        LL_FOREACH_SAFE(g_pLogItemList, pItem, pTmp)
        {
            UT_string *pLogStr;
            struct tm lTime;
            int logFileSize = 0;

            if(++g_LogRdPos % 100 == 0)
            {
                GET_FILE_SIZE(g_LogProcessInfo.logFilePath, logFileSize);
            }
            
            localtime_r(&(pItem->timestamp.tv_sec), &lTime);
            
            utstring_new(pLogStr);

            if(pItem->isAddTags)
            {
                utstring_printf(pLogStr, "[%04d-%02d-%02d %02d:%02d:%02d.%03ld] [%s] %s",
                        lTime.tm_year + 1900, lTime.tm_mon + 1, lTime.tm_mday,
                        lTime.tm_hour, lTime.tm_min, lTime.tm_sec, pItem->timestamp.tv_usec / 1000,
                        LogLevelToStr(pItem->level), pItem->pLogContent);
            }
            else
            {
                utstring_printf(pLogStr, "%s", pItem->pLogContent);
            }

            if(pItem->isPrinted == FALSE)
            {
                if(pItem->level & LOG_Error
                   || pItem->level & LOG_Fatal)
                {
                    __logColorOutput(SHOW_CONSOLE_RED, pLogStr);
                }
                else if(pItem->level & LOG_Warn
                        || pItem->level & LOG_Unknown)
                {
                    __logColorOutput(SHOW_CONSOLE_YELLOW, pLogStr);
                }
                else if(pItem->level & LOG_Test
                        || pItem->level & LOG_Call)
                {
                    __logColorOutput(SHOW_CONSOLE_BLUE, pLogStr);
                }
                else if(pItem->level & LOG_Devp)
                {
                    __logColorOutput(SHOW_CONSOLE_GREEN, pLogStr);
                }
                else
                {
                    print("%s", utstring_body(pLogStr));
                }

                pItem->isPrinted = TRUE;
            }

            if(g_logSock != -1 && GetCurrWIFIConnStatus() == WIFI_CONNECTED)
            {
                __logNetworkSend(pItem);
            }

            if(g_LogProcessInfo.pLogFile != NULL && g_bEnLogToFile)
            {
                if(logFileSize >= MAX_LOG_FILE_SIZE)
                {
                    fflush(g_LogProcessInfo.pLogFile);
                    fclose(g_LogProcessInfo.pLogFile);
#if 0
                    if(g_bEnMailBackup)
                    {
                        PLOG_BACKUP pBackup = (PLOG_BACKUP)malloc(sizeof(struct LOG_BACKUP));
                        char path[MAX_PATH];
                    
                        memset(path, 0, MAX_PATH);
                        sprintf(path, "%s/%s.bak_%u.log", 
                                LOG_FILE_BASEDIR, 
                                basename_v2(g_LogProcessInfo.logFilePath), 
                                g_LogRdPos);
                        rename(g_LogProcessInfo.logFilePath, path);

                        if(pBackup)
                        {
                            memset(pBackup, 0, sizeof(struct LOG_BACKUP));
                            pBackup->timestamp      = time(NULL);
                            pBackup->plogFilePath   = strdup(path);
                            pBackup->sendTimes      = 0;
                            pBackup->tolLogs        = g_LogRdPos - 1;

                            uv_rwlock_wrlock(&g_uvLogBackLock);
                            LL_APPEND(g_pLogBackupList, pBackup);
                            uv_rwlock_wrunlock(&g_uvLogBackLock);
                        }
                    }
#endif
                    g_LogProcessInfo.pLogFile = fopen(g_LogProcessInfo.logFilePath, "w+");
                }

                if(g_LogProcessInfo.pLogFile)
                {
                    fwrite(utstring_body(pLogStr), 1, utstring_len(pLogStr), g_LogProcessInfo.pLogFile);
                }

				isWriteLog = TRUE;
            }
			
            LL_DELETE(g_pLogItemList, pItem);
            
            utstring_free(pLogStr);
            free(pItem->pLogContent);
            free(pItem);

            if(g_LogRdPos % 100 == 0)
            {
                break;
            }
        }

		uv_rwlock_wrunlock(&g_uvLogLock);

        usleep(1000);
        if(g_LogProcessInfo.pLogFile != NULL && isWriteLog)
        {
            fflush(g_LogProcessInfo.pLogFile);
        }
    }

    pthread_detach(pthread_self());
    return (NULL);
}

/**
 * @brief 设置调试等级
 * @param level     调试等级
 * @param iEnable   1 打开调试等级， 0 关闭调试等级
 */
void IHW_EnableLogLevel(LOG_LEVEL level, int iEnable)
{
    if(iEnable > 0)
    {
        g_iMinLevel |= level;
    }
    else
    {
        g_iMinLevel &= ~(level);
    }
}

static int __getCfgFromCfgFile(void)
{
    int ret = 0;
    g_bEnableLog        = CfgGetBoolValue("Global.Log.Enable", g_bEnableLog);
    g_iMinLevel         = CfgGetIntValue("Global.Log.Level", g_iMinLevel);
    g_bEnMailBackup     = CfgGetBoolValue("Global.Log.LogToEMail.Enable", g_bEnMailBackup);
    g_pEmailBox         = CfgGetStringValue("Global.Log.LogToEMail.EMail", LOG_MAIL_RECEIVER);
    g_bEnLogToFile      = CfgGetBoolValue("Global.Log.LogToFile", g_bEnLogToFile);
    g_bEnLogToServer    = CfgGetBoolValue("Global.Log.LogToServer", g_bEnLogToServer);

#if 0
    LOG_EX(LOG_Debug, "g_bEnableLog = %d\n", g_bEnableLog);
    LOG_EX(LOG_Debug, "g_iMinLevel = 0x%X\n", g_iMinLevel);
    LOG_EX(LOG_Debug, "g_bEnMailBackup = %d\n", g_bEnMailBackup);
    LOG_EX(LOG_Debug, "g_pEmailBox = %s\n", g_pEmailBox);
    LOG_EX(LOG_Debug, "g_bEnLogToFile = %d\n", g_bEnLogToFile);
    LOG_EX(LOG_Debug, "g_bEnLogToServer = %d\n", g_bEnLogToServer);
#endif
    
    ret = CfgGetBoolValue("Global.Log.LogToUDPServer.Enable", FALSE);

    if(ret)
    {
        char *pIpAddr = NULL;
        int udpPortBase = CfgGetBoolValue("Global.Log.LogToUDPServer.UdpBasePort", 10000);
        int port = udpPortBase + DBusLibGetModName();

        if(g_logSock != -1)
        {
            close(g_logSock);
            g_logSock = -1;
        }

        pIpAddr         = CfgGetStringValue("Global.Log.LogToUDPServer.UdpServerIp", NULL);

        LOG_EX(LOG_Debug, "UDP Ipaddr = %s, port = %d\n", pIpAddr, port);

        if(pIpAddr)
        {
            memset(&g_logAddr, 0, sizeof(g_logAddr));
            g_logAddr.sin_family        = AF_INET;
            g_logAddr.sin_port          = htons(port);
            g_logAddr.sin_addr.s_addr   = inet_addr(pIpAddr);

            g_logSock   = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        }
    }
}
/**
 * @brief           初始化系统日志功能
 * @param pLogTag   系统日志标志
 * @param pPath     系统日志保存路径
 * @param bEnable   打开/关闭调试信息
 */
void IHW_InitLOG(const char* pLogTag, const char* pPath, int bEnable)
{
    int ret;
    char strPath[MAX_PATH];

    memset(&g_logAddr, 0, sizeof(g_logAddr));
    uv_rwlock_init(&g_uvLogLock);
    g_LogRdPos = 0;
    memset(g_strLogTag, 0, 32);

    memset(&g_LogProcessInfo, 0, sizeof(LOG_PROCESS_INFO));

    if(pLogTag == NULL)
    {
        strcpy(g_strLogTag, "");
    }
    else
    {
        strncpy(g_strLogTag, pLogTag, 31);
    }

    memset(strPath, 0, MAX_PATH);

    g_LogProcessInfo.pid    = getpid();
    if(readlink("/proc/self/exe", strPath, MAX_PATH) == -1)
    {
        strcpy(g_LogProcessInfo.exeName, pLogTag);
    }
    else
    {
        char *pExeName = strrchr(strPath, '/');

        if(pExeName == NULL)
        {
            strncpy(g_LogProcessInfo.exeName, strPath, MAX_PATH - 1);
        }
        else
        {
            strncpy(g_LogProcessInfo.exeName, pExeName + 1, MAX_PATH - 1);
        }
    }
    
    //fprintf(stdout, "pid = %d, name = %s\n", g_LogProcessInfo.pid, g_LogProcessInfo.exeName);
    g_LogProcessInfo.pChipId = GetCpuChipId();
    g_LogProcessInfo.pChipSerial = GetCpuSerial();
    memset(g_LogProcessInfo.logFilePath, 0, MAX_PATH);
    sprintf(g_LogProcessInfo.logFilePath, "%s/%s_%d.log", LOG_FILE_BASEDIR, g_LogProcessInfo.exeName, g_LogProcessInfo.pid);

    memset(strPath, 0, MAX_PATH);
    sprintf(strPath, "rm -f %s/%s_*.log > /dev/zero", LOG_FILE_BASEDIR, g_LogProcessInfo.exeName);
    ret = system(strPath);

    g_LogProcessInfo.pLogFile = fopen(g_LogProcessInfo.logFilePath, "w+");

    g_bEnableLog = bEnable;

    __getCfgFromCfgFile();

#if 0
    LOG_CFG_PROTOCOL logCfg;
    
    memset(&logCfg, 0, sizeof(LOG_CFG_PROTOCOL));
    logCfg.cfgCmd = CMD_LOG_NETWORK;
    logCfg.iParams1 = inet_addr("10.240.84.163");
    logCfg.iParams2 = htons(10000);
    UpgradLogConfigure(&logCfg);
#endif

    __initPostLogParams();
}

void IHW_RunLogService(void)
{
    pthread_create(&g_logThreadId,      NULL, __logOutputThread, NULL);
    pthread_create(&g_backupThreadId,   NULL, __uvLogBackupProc, NULL);
}

static void __logTo(LOG_LEVEL level, int isAddTag, char* pMsg, int isPrint)
{
    PLOG_ITEM   pLogItem;
    
    pLogItem = (PLOG_ITEM)malloc(sizeof(struct LOG_ITEM));

    if(pLogItem == NULL)
    {
        return;
    }

    pLogItem->pLogContent   = strdup(pMsg);
    pLogItem->isPrinted     = isPrint ? FALSE : TRUE;
    pLogItem->level         = level;
    pLogItem->isAddTags     = isAddTag ? TRUE : FALSE;
    gettimeofday(&(pLogItem->timestamp), NULL);
    
    uv_rwlock_wrlock(&g_uvLogLock);
    LL_APPEND(g_pLogItemList, pLogItem);
    uv_rwlock_wrunlock(&g_uvLogLock);
}

void IHW_LogStrWithoutPrint(int level, char* pMsg)
{
    __logTo(level, TRUE, pMsg, FALSE);
}

void IHW_LogRawString(int level, char* pMsg)
{
    __logTo(level, TRUE, pMsg, TRUE);
}

/**
 * @brief           输出调试信息
 * @param cFlag     调试信息开关
 * @param pMsg      调试信息内容
 */
void IHW_LOG_UNTAG(LOG_LEVEL level, const char* pMsg, ...)
{
    va_list     arg_ptr;
    UT_string   *pLogContent;

    if(!g_bEnableLog)
    {
        return;
    }

    // 检查调试等级
    if(!(g_iMinLevel & level))
    {
        return;
    }
    
    utstring_new(pLogContent);
    va_start(arg_ptr, pMsg);
    utstring_printf_va(pLogContent, pMsg, arg_ptr);
    va_end(arg_ptr);
    
    __logTo(level, FALSE, utstring_body(pLogContent), TRUE);
    
    utstring_free(pLogContent);
}

/**
 * @brief           输出调试信息
 * @param cFlag     调试信息开关
 * @param pMsg      调试信息内容
 */
void IHW_LOG(LOG_LEVEL level, const char* pMsg, ...)
{
    UT_string*  pLogContent = NULL;
    va_list     arg_ptr;
    
    if(!g_bEnableLog)
    {
        return;
    }

    // 检查调试等级
    if(!(g_iMinLevel & level))
    {
        return;
    }

    utstring_new(pLogContent);
    va_start(arg_ptr, pMsg);
    utstring_printf_va(pLogContent, pMsg, arg_ptr);
    va_end(arg_ptr);

    __logTo(level, TRUE, utstring_body(pLogContent), TRUE);

    utstring_free(pLogContent);
}

void IHW_DisableLogOut(void)
{
    g_bEnableLog = FALSE;
}

void IHW_EnableLogOut(void)
{
    g_bEnableLog = TRUE;
}

#if 0
void LogUploadCurLogFile(void)
{
    UT_string *pFn = NULL, *pCmd = NULL;
    utstring_new(pFn);
    utstring_new(pCmd);

    utstring_printf(pFn, "/tmp/%s_%s.bak.log", g_LogProcessInfo.exeName, g_LogProcessInfo.pChipId);
    utstring_printf(pCmd, "cp %s %s", g_LogProcessInfo.logFilePath, utstring_body(pFn));
    
    __logHttpPostFile(utstring_body(pFn));
    //InetHttpUploadFileSync(UPL_HTTP_URL, utstring_body(pFn), NULL);

    utstring_free(pFn);
    utstring_free(pCmd);
}
#endif

#if 0
void UploadLogFile(char* pFilePath)
{
    int fileSize = 0;
        
    if(pFilePath == NULL || strlen(pFilePath) == 0)
    {
        return;
    }

    GET_FILE_SIZE(pFilePath, fileSize);

    if(fileSize <= 0)
    {
        LOG_EX(LOG_Warn, "File Size Error: %d\n", fileSize);
        return;
    }

    __logHttpPostFile(pFilePath);
}
#endif

void UpgradLogConfigure(PLOG_CFG_PROTOCOL pCfg)
{
    switch(pCfg->cfgCmd)
    {
        case CMD_LOG_ENABLE:
            g_bEnableLog = pCfg->iParams1;
            break;

        case CMD_LOG_FILE:
            if(pCfg->iParams1 == FALSE)
            {
                g_bEnMailBackup = g_bEnLogToFile = FALSE;
            }
            else
            {
                g_bEnLogToFile = TRUE;
            }
            break;

        case CMD_LOG_MAIL:
            g_bEnMailBackup = pCfg->iParams1;
            break;

        case CMD_LOG_LEVEL:
            if(pCfg->iParams2 == FALSE)
            {
                if(pCfg->iParams1 == 0)
                {
                    g_iMinLevel = LOG_Fatal | LOG_Error | LOG_Devp;
                }
                else
                {
                    g_iMinLevel &= ~(pCfg->iParams1);
                }
            }
            else
            {
                g_iMinLevel |= pCfg->iParams1;
            }
            break;

        case CMD_LOG_NETWORK:
            if(pCfg->iParams1 == 0)
            {
                memset(&g_logAddr, 0, sizeof(g_logAddr));
                close(g_logSock);
                g_logSock = -1;
            }
            else
            {
                if(g_logSock != -1)
                {
                    close(g_logSock);
                }

                memset(&g_logAddr, 0, sizeof(g_logAddr));
                g_logAddr.sin_family        = AF_INET;
                g_logAddr.sin_port          = htons((pCfg->iParams2 & 0xFFFF));
                g_logAddr.sin_addr.s_addr   = (pCfg->iParams1);

                g_logSock   = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
            }
            break;

        case CMD_LOG_SERVER:
            g_bEnLogToServer = pCfg->iParams1;
            break;

        default: break;
    }
}

const char* LogLeveToString(LOG_LEVEL lv)
{
    switch(lv)
    {
        case LOG_Fatal:     return "LOG_Fatal";
        case LOG_Error:     return "LOG_Error";
        case LOG_Warn:      return "LOG_Warn";
        case LOG_Debug:     return "LOG_Debug";
        case LOG_Info:      return "LOG_Info";
        case LOG_Test:      return "LOG_Test";
        case LOG_Call:      return "LOG_Call";
        case LOG_Devp:      return "LOG_Devp";
        case LOG_Step: 		return "LOG_Step";
        case LOG_Unknown:   return "LOG_Unknown";
        case LOG_All:       return "LOG_All";
        case LOG_Close:     return "LOG_Close"; 
    }

    return "Unknown";
}

static void __uvSysPointIOProc(void *pParams)
{
    while(TRUE)
    {
        PSYSPOINT_ITEM pItem = NULL, pTmp = NULL;
        int iFileSize;
        time_t tmNow = time(NULL);

        if(g_SysPonitInfo.pMarkFile)
        {
            uv_rwlock_wrlock(&g_SysPonitInfo.uvRwLock);
            LL_FOREACH_SAFE(g_pSysPointArray, pItem, pTmp)
            {
                fwrite(pItem->pContent, 1, strlen(pItem->pContent), g_SysPonitInfo.pMarkFile);
                free(pItem->pContent);
                LL_DELETE(g_pSysPointArray, pItem);
                free(pItem);
            }
            uv_rwlock_wrunlock(&g_SysPonitInfo.uvRwLock);
        }

        GET_FILE_SIZE(g_SysPonitInfo.savePath, iFileSize);

        if(g_SysPonitInfo.lastPostTime == 0 || tmNow < g_SysPonitInfo.lastPostTime)
        {            
            g_SysPonitInfo.lastPostTime = g_SysPonitInfo.lastMarkTime = tmNow;
            tmNow = 0;
        }
#if 0
        LOG_EX(LOG_Debug, "iFile = %d, MaxSize = %d, upTime = %d\n", 
            iFileSize, g_SysPonitInfo.iMaxCacheSize, g_SysPonitInfo.iEveryUploadTime);

        LOG_EX(LOG_Debug, "tm = %d, last = %d\n", tmNow, g_SysPonitInfo.lastPostTime);
#endif
        if(iFileSize > g_SysPonitInfo.iMaxCacheSize
           || tmNow - g_SysPonitInfo.lastPostTime > g_SysPonitInfo.iEveryUploadTime)
        {
            int fileSize = 0;

            GET_FILE_SIZE(g_SysPonitInfo.savePath, fileSize);

            if(fileSize > 0)
            {                
                SysPointMarkUpload();
                g_SysPonitInfo.lastPostTime = tmNow;
            }

            if(tmNow - g_SysPonitInfo.lastPostTime > g_SysPonitInfo.iEveryUploadTime)
            {
                SysPointMarkUpload();
                g_SysPonitInfo.lastPostTime = tmNow;
            }
        }

        if(g_SysPonitInfo.pMarkFile)
        {
            fflush(g_SysPonitInfo.pMarkFile);
        }

        sleep(1);
    }

    pthread_detach(pthread_self());
}

int SysPointMarkInit(char* pDevId, int iMaxSize, int iPostTime)
{
    LOG_EX(LOG_Debug, "pDevId = %s, iMaxSize = %d, iPostTime = %d\n", pDevId, iMaxSize, iPostTime);

    if(g_SysPonitInfo.pDeviceId != NULL)
    {
        if(pDevId && strlen(pDevId) > 0)
        {
            free(g_SysPonitInfo.pDeviceId);
            g_SysPonitInfo.pDeviceId = strdup(pDevId);
        }

        if(iMaxSize > 0)
        {
            g_SysPonitInfo.iMaxCacheSize = iMaxSize;
        }
        else
        {
            g_SysPonitInfo.iMaxCacheSize = 1024 * 10;
        }

        if(iPostTime > 0)
        {
            g_SysPonitInfo.iEveryUploadTime = iPostTime;
        }
        else
        {
            g_SysPonitInfo.iEveryUploadTime = SYS_POINT_UPLOAD_TIME;
        }
    }
    else
    {
        memset(&g_SysPonitInfo, 0, sizeof(SYSPOINT_INFO));

        if(pDevId && strlen(pDevId) > 0)
        {
            g_SysPonitInfo.pDeviceId = strdup(pDevId);
        }

        if(iMaxSize > 0)
        {
            g_SysPonitInfo.iMaxCacheSize = iMaxSize;
        }
        else
        {
            g_SysPonitInfo.iMaxCacheSize = 1024 * 1024;
        }

        if(iPostTime > 0)
        {
            g_SysPonitInfo.iEveryUploadTime = iPostTime;
        }
        else
        {
            g_SysPonitInfo.iEveryUploadTime = SYS_POINT_UPLOAD_TIME;
        }
        
        sprintf(g_SysPonitInfo.savePath, "%s/sys_point_mark.log", LOG_FILE_BASEDIR);

        g_SysPonitInfo.pMarkFile = fopen(g_SysPonitInfo.savePath, "w+");

        if(g_SysPonitInfo.pMarkFile == NULL)
        {
            LOG_EX(LOG_Error, "Create File: %s Error\n", g_SysPonitInfo.savePath);
            return -ERR_OPEN_FILE;
        }

        uv_rwlock_init(&g_SysPonitInfo.uvRwLock);

        uv_thread_create(&g_SysPonitInfo.uvIOThread, __uvSysPointIOProc, NULL);
    }

    return 0;
}

int SysPointMark(char* pMarkInfo)
{
    PSYSPOINT_ITEM   pItem;

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

    if(g_SysPonitInfo.iMaxCacheSize <= 0)
    {
        return -ERR_UNSUPPORT;
    }

    pItem = (PSYSPOINT_ITEM)malloc(sizeof(struct SYSPOINT_ITEM));

    if(pItem == NULL)
    {
        return -ERR_MALLOC_MEMORY;
    }

    pItem->pContent     = strdup(pMarkInfo);
    g_SysPonitInfo.lastMarkTime = time(NULL);

    uv_rwlock_wrlock(&g_SysPonitInfo.uvRwLock);
    LL_APPEND(g_pSysPointArray, pItem);
    uv_rwlock_wrunlock(&g_SysPonitInfo.uvRwLock);

    //LOG_EX(LOG_Debug, "SysMark: %s\n", pItem->pContent);

    return 0;
}

int SysPointMarkUpload(void)
{
    int ret, size = 0;
    char path[MAX_PATH];

    memset(path, 0, MAX_PATH);
    sprintf(path, "%s.txt", g_SysPonitInfo.savePath);
   
    if(access(path, F_OK) == 0)
    {
        unlink(path);
    }

    GET_FILE_SIZE(g_SysPonitInfo.savePath, size);

    if(size <= 0)
    {
        LOG_EX(LOG_Debug, "Upload System Mark Data [%s] Is Empty, Skip......\n", g_SysPonitInfo.savePath);
        return 0;
    }

    rename(g_SysPonitInfo.savePath, path);
    
    fclose(g_SysPonitInfo.pMarkFile);
    g_SysPonitInfo.pMarkFile = fopen(g_SysPonitInfo.savePath, "w+");

    size = 0;

    do
    {
        ret = InetHttpUploadFileSync(GetCurServerAddr(MARK_POINT_MODULE), path, NULL);
        sleep(1);
    } while(ret != 0 && ++size < 3);

    LOG_EX(LOG_Debug, "Upload System Mark Data [%s] To Server [%s]\n", path, GetCurServerAddr(MARK_POINT_MODULE));

    unlink(path);

    return 0;
}

