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

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

#define LOG_SAVE_TIME                   (1000)

typedef struct
{
    long long       maxValue;
    long long       minValue;
    long long       avgValue;
} STATISTICAL_VALUE, *PSTATISTICAL_VALUE;

typedef struct
{
    long long           curVaule;
    long long           tolValue;
    unsigned int        tolCount;
    STATISTICAL_VALUE   cVal;
} CSTATISTICAL_INFO, *PSTATISTICAL_INFO;

typedef struct
{
    char*               pMonName;
    unsigned long       nCount;
    CSTATISTICAL_INFO   nCstInfo;
    uv_rwlock_t         rwLock;

    uv_timer_t          logTimer;
    unsigned int        logTime;

    UT_hash_handle  hh;         ///< UT Hash handle
} MONITOR_INFO, *PMONITOR_INFO;

static uv_rwlock_t      g_uvMonRwLock;
static PMONITOR_INFO    g_MonTbl = NULL;
static uv_loop_t*       g_MonLogLoop = NULL;

static void __uvMonLogProc(void *pParams)
{
    RunUVLoop(g_MonLogLoop);

    while(TRUE)
    {
        usleep(1000);
    }

    pthread_detach(pthread_self());
}

static void __logMonTimerProc(uv_timer_t* pTimer)
{
    PMONITOR_INFO pInfo = (PMONITOR_INFO)pTimer->data;

    if(pInfo && (pInfo->nCount + pInfo->nCstInfo.tolCount > 0))
    {
        UT_string* pMsg = NULL;
        utstring_new(pMsg);

        uv_rwlock_rdlock(&pInfo->rwLock);
        
        utstring_printf(pMsg, "%s Statistical Information:\n", pInfo->pMonName);
        //LOG_EX(LOG_Debug, "%s Statistical Information:\n", pInfo->pMonName);

        if(pInfo->nCount)
        {
            UT_string* pMsgCount = NULL;
            utstring_new(pMsgCount);
            utstring_printf(pMsgCount, "        Total Count = %lu\n", pInfo->nCount);
            utstring_concat(pMsg, pMsgCount);
            utstring_free(pMsgCount);
            //LOG_EX(LOG_Debug, "        Total Count = %u\n", pInfo->nCount);
        }

        if(pInfo->nCstInfo.tolCount > 0)
        {
            UT_string* pMsgStat = NULL;
            utstring_new(pMsgStat);
            utstring_printf(pMsgStat, "        Max Value   = %lld\n"
                                  "        Min Value   = %lld\n"
                                  "        Avg Value   = %lld\n"
                                  "    ---- Statistical by total %lld of %u times\n", 
                            pInfo->nCstInfo.cVal.maxValue,
                            pInfo->nCstInfo.cVal.minValue,
                            pInfo->nCstInfo.cVal.avgValue,
                            pInfo->nCstInfo.tolValue, 
                            pInfo->nCstInfo.tolCount);
            utstring_concat(pMsg, pMsgStat);
            utstring_free(pMsgStat);
#if 0
            LOG_EX(LOG_Debug, "        Max Value   = %lld\n", pInfo->nCstInfo.cVal.maxValue);
            LOG_EX(LOG_Debug, "        Min Value   = %lld\n", pInfo->nCstInfo.cVal.minValue);
            LOG_EX(LOG_Debug, "        Avg Value   = %lld\n", pInfo->nCstInfo.cVal.avgValue);
            LOG_EX(LOG_Debug, "    ---- Statistical by total %lld of %u times\n", 
                   pInfo->nCstInfo.tolValue, pInfo->nCstInfo.tolCount);
#endif
        }

        LOG_EX(LOG_Debug, "%s", utstring_body(pMsg));

        uv_rwlock_rdunlock(&pInfo->rwLock);
        utstring_free(pMsg);
    }
}

int MonitorInit(void)
{
    uv_thread_t uvMonLogThread;

    g_MonLogLoop    = uv_loop_new();

    uv_rwlock_init(&g_uvMonRwLock);

    uv_thread_create(&uvMonLogThread, __uvMonLogProc, NULL);

    return 0;
}

int MonAddNewItem(const char* pName, int logSaveTime)
{
    PMONITOR_INFO pInfo;

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

    uv_rwlock_rdlock(&g_uvMonRwLock);
    HASH_FIND_STR(g_MonTbl, pName, pInfo);
    uv_rwlock_rdunlock(&g_uvMonRwLock);

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

    pInfo = (PMONITOR_INFO)malloc(sizeof(MONITOR_INFO));

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

    memset(pInfo, 0, sizeof(MONITOR_INFO));

    pInfo->nCstInfo.cVal.minValue = INT_MAX;
    pInfo->logTime = logSaveTime;
    pInfo->pMonName = strdup(pName);

    if(pInfo->logTime > 0)
    {
        pInfo->logTimer.data = pInfo;
        uv_timer_init(g_MonLogLoop, &pInfo->logTimer);
        uv_timer_start(&pInfo->logTimer, __logMonTimerProc, pInfo->logTime, pInfo->logTime);
    }
    else
    {
        pInfo->logTime = 0;
    }

    uv_rwlock_init(&pInfo->rwLock);

    uv_rwlock_wrlock(&g_uvMonRwLock);
    HASH_ADD_STR(g_MonTbl, pMonName, pInfo);
    uv_rwlock_wrunlock(&g_uvMonRwLock);

    return 0;
}

int MonIncreaseCount(const char* pName)
{
    PMONITOR_INFO pInfo;

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

    uv_rwlock_rdlock(&g_uvMonRwLock);
    HASH_FIND_STR(g_MonTbl, pName, pInfo);
    uv_rwlock_rdunlock(&g_uvMonRwLock);

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

    uv_rwlock_wrlock(&pInfo->rwLock);
    pInfo->nCount++;
    uv_rwlock_wrunlock(&pInfo->rwLock);

    return 0;
}

int MonDiffStatistical(const char* pName, long long newVal)
{
    PMONITOR_INFO pInfo;
    
    if(pName == NULL || strlen(pName) == 0)
    {
        return -ERR_INPUT_PARAMS;
    }

    uv_rwlock_rdlock(&g_uvMonRwLock);
    HASH_FIND_STR(g_MonTbl, pName, pInfo);
    uv_rwlock_rdunlock(&g_uvMonRwLock);

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

    uv_rwlock_wrlock(&pInfo->rwLock);

    if(pInfo->nCstInfo.curVaule != 0)
    {
        long long diffValue = newVal - pInfo->nCstInfo.curVaule;
       
        pInfo->nCstInfo.tolValue += diffValue;
        pInfo->nCstInfo.tolCount++;

        if(pInfo->nCstInfo.tolCount > 10)
        {
            pInfo->nCstInfo.cVal.avgValue = pInfo->nCstInfo.tolValue / pInfo->nCstInfo.tolCount;

            if(pInfo->nCstInfo.cVal.maxValue < diffValue)
            {
                pInfo->nCstInfo.cVal.maxValue = diffValue;
            }

            if(pInfo->nCstInfo.cVal.minValue > diffValue)
            {
                pInfo->nCstInfo.cVal.minValue = diffValue;
            }
        }
    }

    pInfo->nCstInfo.curVaule = newVal;
    uv_rwlock_wrunlock(&pInfo->rwLock);

    //fprintf(stdout, "%s value %lld diffValue %lld\n", pName, newVal, diffValue);
    return 0;
}

int MonUpgradeStatistical(const char* pName, long newVal)
{
    PMONITOR_INFO pInfo;

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

    uv_rwlock_rdlock(&g_uvMonRwLock);
    HASH_FIND_STR(g_MonTbl, pName, pInfo);
    uv_rwlock_rdunlock(&g_uvMonRwLock);

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

    uv_rwlock_wrlock(&pInfo->rwLock);

    pInfo->nCstInfo.curVaule = newVal;
    pInfo->nCstInfo.tolValue += newVal;
    pInfo->nCstInfo.tolCount++;
    pInfo->nCstInfo.cVal.avgValue = pInfo->nCstInfo.tolValue / pInfo->nCstInfo.tolCount;

    if(pInfo->nCstInfo.cVal.maxValue < newVal)
    {
        pInfo->nCstInfo.cVal.maxValue = newVal;
    }

    if(pInfo->nCstInfo.cVal.minValue > newVal)
    {
        pInfo->nCstInfo.cVal.minValue = newVal;
    }

    uv_rwlock_wrunlock(&pInfo->rwLock);

    //fprintf(stdout, "%s value %ld\n", pName, newVal);
    return 0;
}

int MonItemLogout(const char* pName)
{
    PMONITOR_INFO pInfo;

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

    uv_rwlock_rdlock(&g_uvMonRwLock);
    HASH_FIND_STR(g_MonTbl, pName, pInfo);
    uv_rwlock_rdunlock(&g_uvMonRwLock);

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

    if(pInfo->nCount + pInfo->nCstInfo.tolCount == 0)
    {
        LOG_EX(LOG_Debug, "%s Statistical Unchanged\n", pInfo->pMonName);
        return 0;
    }

    uv_rwlock_rdlock(&pInfo->rwLock);

    LOG_EX(LOG_Debug, "%s Statistical Information:\n", pInfo->pMonName);

    if(pInfo->nCount)
    {
        LOG_EX(LOG_Debug, "        Total Count = %u\n", pInfo->nCount);
    }

    if(pInfo->nCstInfo.tolCount > 0)
    {
        LOG_EX(LOG_Debug, "        Max Value   = %lld\n", pInfo->nCstInfo.cVal.maxValue);
        LOG_EX(LOG_Debug, "        Min Value   = %lld\n", pInfo->nCstInfo.cVal.minValue);
        LOG_EX(LOG_Debug, "        Avg Value   = %lld\n", pInfo->nCstInfo.cVal.avgValue);
        LOG_EX(LOG_Debug, "    ---- Statistical by total %lld of %u times\n", 
               pInfo->nCstInfo.tolValue, pInfo->nCstInfo.tolCount);
    }

    uv_rwlock_rdunlock(&pInfo->rwLock);

    return 0;
}
#endif
