#include    <sys/time.h>
#include    <sys/timeb.h>
#include    <sys/types.h>
#include    <sys/stat.h>
#include    <sys/types.h>
#include    <sys/ioctl.h>
#include    <sys/socket.h>
#include    <sys/ipc.h>
#include    <sys/shm.h>
#include    <sys/shm.h>
#include    <sys/mman.h>
#include    <linux/wireless.h>
#include    <arpa/inet.h>
#include    <netinet/in.h>

#include    <unistd.h>
#include    <stdarg.h>
#include    <stdio.h>      /*標準輸入輸出定義*/
#include    <stdlib.h>     /*標準函數庫定義*/
#include    <unistd.h>     /*Unix 標準函數定義*/
#include    <fcntl.h>      /*檔控制定義*/
#include    <termios.h>    /*PPSIX 終端控制定義*/
#include    <errno.h>      /*錯誤號定義*/
#include    <errno.h>
#include    <string.h>
#include    <time.h>
#include    <ctype.h>
#include    <ifaddrs.h>
#include    "../../define.h"

#define ARRAY_SIZE(A)       (sizeof(A) / sizeof(A[0]))
#define PASS                1
#define FAIL                -1
#define YES                 1
#define NO                  0

struct SysConfigAndInfo         *ShmSysConfigAndInfo;
struct StatusCodeData           *ShmStatusCodeData;

sqlite3 *localDb; //DS60-120 add
#define DB_FILE             "/Storage/ChargeLog/localCgargingRecord.db" //DS60-120 add

void PRINTF_FUNC(char *string, ...);

int StoreLogMsg(const char *fmt, ...);
#define DEBUG_INFO(format, args...) StoreLogMsg("[%s:%d][%s][Info] "format, __FILE__, __LINE__, __FUNCTION__, ##args)
#define DEBUG_WARN(format, args...) StoreLogMsg("[%s:%d][%s][Warn] "format, __FILE__, __LINE__, __FUNCTION__, ##args)
#define DEBUG_ERROR(format, args...) StoreLogMsg("[%s:%d][%s][Error] "format, __FILE__, __LINE__, __FUNCTION__, ##args)
#define EVENT_INFO(format, args...) StoreEventLogMsg("[%s:%d][%s][Info] "format, __FILE__, __LINE__, __FUNCTION__, ##args) //DS60-120 add

int StoreLogMsg(const char *fmt, ...)
{
    char Buf[4096 + 256];
    char buffer[4096];
    va_list args;
    struct timeb  SeqEndTime;
    struct tm *tm;

    va_start(args, fmt);
    int rc = vsnprintf(buffer, sizeof(buffer), fmt, args);
    va_end(args);

    memset(Buf, 0, sizeof(Buf));
    ftime(&SeqEndTime);
    SeqEndTime.time = time(NULL);
    tm = localtime(&SeqEndTime.time);

    if (ShmSysConfigAndInfo->SysConfig.SwitchDebugFlag == YES) {
        sprintf(Buf, "%02d:%02d:%02d:%03d - %s",
                tm->tm_hour, tm->tm_min, tm->tm_sec, SeqEndTime.millitm, buffer);
        printf("%s \n", Buf);
    } else {
        sprintf(Buf, "echo \"%04d-%02d-%02d %02d:%02d:%02d:%03d - %s\" >> /Storage/SystemLog/[%04d.%02d]SystemLog",
                tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, SeqEndTime.millitm,
                buffer,
                tm->tm_year + 1900, tm->tm_mon + 1);
        system(Buf);
    }

    return rc;
}

int StoreEventLogMsg(const char *fmt, ...)
{
    char Buf[4096 + 256];
    char buffer[4096];
    time_t CurrentTime;
    struct tm *tm;
    struct timeval tv;
    va_list args;

    va_start(args, fmt);
    int rc = vsnprintf(buffer, sizeof(buffer), fmt, args);
    va_end(args);

    memset(Buf, 0, sizeof(Buf));
    CurrentTime = time(NULL);
    tm = localtime(&CurrentTime);
    gettimeofday(&tv, NULL); // get microseconds, 10^-6

    if ((ShmSysConfigAndInfo->SysConfig.ModelName != NULL) && (ShmSysConfigAndInfo->SysConfig.SerialNumber != NULL) && (strlen((char *)ShmSysConfigAndInfo->SysConfig.ModelName) >= 14)) {
        sprintf(Buf, "echo -n \"[%04d.%02d.%02d %02d:%02d:%02d.%06ld]%s\" >> /Storage/EventLog/[%04d.%02d]%s_%s_EventLog",
                tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec,
                buffer,
                tm->tm_year + 1900, tm->tm_mon + 1,
                ShmSysConfigAndInfo->SysConfig.ModelName,
                ShmSysConfigAndInfo->SysConfig.SerialNumber);
    } else {
        sprintf(Buf, "echo -n \"[%04d.%02d.%02d %02d:%02d:%02d.%06ld]%s\" >> /Storage/EventLog/[%04d.%02d]EventLog",
                tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec,
                buffer,
                tm->tm_year + 1900, tm->tm_mon + 1);
    }

#ifdef SystemLogMessage
    system(Buf);

#endif

#ifdef ConsloePrintLog
    printf("[%04d.%02d.%02d %02d:%02d:%02d.%06ld]%s", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec, buffer);
#endif

    return rc;
}

int DiffTimeb(struct timeb ST, struct timeb ET)
{
    //return milli-second
    unsigned int StartTime, StopTime;

    StartTime = (unsigned int)ST.time;
    StopTime = (unsigned int)ET.time;
    return (StopTime - StartTime) * 1000 + ET.millitm - ST.millitm;
}

void PRINTF_FUNC(char *string, ...)
{
    va_list args;
    char buffer[4096];
    va_start(args, string);
    vsnprintf(buffer, sizeof(buffer), string, args);
    va_end(args);

    DEBUG_INFO("%s \n", buffer);
}

//=================================
// Common routine
//=================================
char *getTimeString(void)
{
    char *result = malloc(21);
    time_t timep;
    struct tm *p;
    time(&timep);
    p = gmtime(&timep);

    sprintf(result, "[%04d-%02d-%02d %02d:%02d:%02d]", (1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday, p->tm_hour, p->tm_hour, p->tm_sec);

    return result;
}

//==========================================
// Init all share memory
//==========================================
int InitShareMemory()
{
    int result = PASS;
    int MeterSMId;

    //creat ShmSysConfigAndInfo
    if ((MeterSMId = shmget(ShmSysConfigAndInfoKey, sizeof(struct SysConfigAndInfo),  0777)) < 0) {
        result = FAIL;
    } else if ((ShmSysConfigAndInfo = shmat(MeterSMId, NULL, 0)) == (void *) - 1) {
        result = FAIL;
    }

    //creat ShmStatusCodeData
    if ((MeterSMId = shmget(ShmStatusCodeKey, sizeof(struct StatusCodeData),  0777)) < 0) {
        result = FAIL;
    } else if ((ShmStatusCodeData = shmat(MeterSMId, NULL, 0)) == (void *) - 1) {
        result = FAIL;
    }

    return result;
}

//================================================
// Main process
//================================================
void AddFaultCodeToBuf(unsigned char *Code)
{
    if (ShmSysConfigAndInfo->SysWarningInfo.WarningCount < 10) {
        memcpy(&ShmSysConfigAndInfo->SysWarningInfo.WarningCode[ShmSysConfigAndInfo->SysWarningInfo.WarningCount][0], Code, 7);
        ShmSysConfigAndInfo->SysWarningInfo.WarningCount++;
    }
}

void RemoveFaultCodeToBuf(unsigned char *Code)
{
    unsigned char find = 0x01;
    char _code[7];
    sprintf(_code, "%s", Code);

    // 把相關的錯誤碼一次移除,避免重複顯示
    while (find) {
        find = 0x00;
        for (unsigned char i = 0; i < ShmSysConfigAndInfo->SysWarningInfo.WarningCount; i++) {
            if (find == 0x00) {
                if (memcmp(&ShmSysConfigAndInfo->SysWarningInfo.WarningCode[i][0], _code, 7) == 0) {
                    find = 0x01;
                }
            } else {
                memcpy(&ShmSysConfigAndInfo->SysWarningInfo.WarningCode[i - 1][0],
                       &ShmSysConfigAndInfo->SysWarningInfo.WarningCode[i][0], 7);
            }
        }

        if (find) {
            ShmSysConfigAndInfo->SysWarningInfo.WarningCount--;
        }
    }
}

//==========================================
// SQLite routne
//==========================================
int DB_Open(sqlite3 *db) //DS60-120 add
{
    int result = PASS;
    char *errMsg = NULL;
    char *createRecordSql = "CREATE TABLE IF NOT EXISTS event_record("
                            "idx integer primary key AUTOINCREMENT, "
                            "occurDatetime text NOT NULL, "
                            "statusCode text NOT NULL"
                            ");";

    if (sqlite3_open(DB_FILE, &db)) {
        result = FAIL;
        DEBUG_ERROR( "Can't open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
    } else {
        DEBUG_INFO( "Local event record database open successfully.\n");

        if (sqlite3_exec(db, createRecordSql, 0, 0, &errMsg) != SQLITE_OK) {
            result = FAIL;
            DEBUG_ERROR( "Create local event record table error message: %s\n", errMsg);
        } else {
            DEBUG_INFO( "Opened local event record table successfully\n");
        }

        sqlite3_close(db);
    }

    return result;
}

int DB_Insert_Record(sqlite3 *db, uint8_t *statusCode) //DS60-120 add
{
    int result = PASS;
    char *errMsg = NULL;
    char sqlStr[1024];

    sprintf(sqlStr, "insert into event_record(occurDatetime, statusCode) values(CURRENT_TIMESTAMP, '%s');", statusCode);

    if (sqlite3_open(DB_FILE, &db)) {
        result = FAIL;
        DEBUG_INFO( "Can't open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
    } else {
        DEBUG_INFO( "Local event record database open successfully.\n");
        if (sqlite3_exec(db, sqlStr, 0, 0, &errMsg) != SQLITE_OK) {
            result = FAIL;
            DEBUG_INFO( "Insert local event record error message: %s\n", errMsg);
        } else {
            DEBUG_INFO( "Insert local event record successfully\n");
        }

        sprintf(sqlStr, "delete from event_record where idx < (select idx from event_record order by idx desc limit 1)-2000;");
        if (sqlite3_exec(db, sqlStr, 0, 0, &errMsg) != SQLITE_OK) {
            result = FAIL;
            DEBUG_INFO( "delete local event_record error message: %s\n", errMsg);
        } else {
            DEBUG_INFO( "delete local event record successfully\n");
        }

        sqlite3_close(db);
    }

    return result;
}

int main(void)
{
    int ByteCount, BitCount;
    unsigned char tmp, EventCodeTmp[7];

    if (InitShareMemory() == FAIL) {
        DEBUG_ERROR("InitShareMemory NG\n");
        if (ShmStatusCodeData != NULL) {
            ShmStatusCodeData->AlarmCode.AlarmEvents.bits.FailToCreateShareMemory = 1;
        }
        sleep(5);
        return 0;
    }

    for (;;) {
        //check Fault Status
        for (ByteCount = 0; ByteCount < sizeof(ShmStatusCodeData->FaultCode.PreviousFaultVal); ByteCount++) {
            if (ShmStatusCodeData->FaultCode.FaultEvents.FaultVal[ByteCount] != ShmStatusCodeData->FaultCode.PreviousFaultVal[ByteCount]) {
                tmp = ShmStatusCodeData->FaultCode.FaultEvents.FaultVal[ByteCount]; //prevent be modified during following process
                for (BitCount = 0; BitCount < 8; BitCount++) {
                    if (((tmp >> BitCount) & 0x01) != ((ShmStatusCodeData->FaultCode.PreviousFaultVal[ByteCount] >> BitCount) & 0x01)) {
                        memset(EventCodeTmp, 0, sizeof(EventCodeTmp));
                        memcpy(EventCodeTmp, FaultStatusCode[ByteCount * 8 + BitCount], sizeof(EventCodeTmp) - 1);
                        if (((tmp >> BitCount) & 0x01) == 0) { //Recovered
                            //EventCodeTmp[0]=1;
                            DEBUG_INFO("Recovery Fault Code = %s\n", EventCodeTmp);
                            ShmStatusCodeData->FaultCode.PreviousFaultVal[ByteCount] &= ~(1 << BitCount);
                            RemoveFaultCodeToBuf(EventCodeTmp);
                            EventCodeTmp[0] = '1';//DS60-120 add
                        } else {
                            DEBUG_INFO("Fault Code = %s\n", EventCodeTmp);
                            ShmStatusCodeData->FaultCode.PreviousFaultVal[ByteCount] |= (1 << BitCount);
                            AddFaultCodeToBuf(EventCodeTmp);
                        }
                        EVENT_INFO("%s\n", EventCodeTmp); //DS60-120 add
                        DB_Insert_Record(localDb, EventCodeTmp); //DS60-120 add
                    }
                }
            }
        }

        //check Alarm Status
        for (ByteCount = 0; ByteCount < sizeof(ShmStatusCodeData->AlarmCode.PreviousAlarmVal); ByteCount++) {
            if (ShmStatusCodeData->AlarmCode.AlarmEvents.AlarmVal[ByteCount] != ShmStatusCodeData->AlarmCode.PreviousAlarmVal[ByteCount]) {
                tmp = ShmStatusCodeData->AlarmCode.AlarmEvents.AlarmVal[ByteCount]; //prevent be modified during following process
                for (BitCount = 0; BitCount < 8; BitCount++) {
                    if (((tmp >> BitCount) & 0x01) != ((ShmStatusCodeData->AlarmCode.PreviousAlarmVal[ByteCount] >> BitCount) & 0x01)) {
                        memset(EventCodeTmp, 0, sizeof(EventCodeTmp));
                        memcpy(EventCodeTmp, AlarmStatusCode[ByteCount * 8 + BitCount], sizeof(EventCodeTmp) - 1);
                        if (((tmp >> BitCount) & 0x01) == 0) { //Recovered
                            //EventCodeTmp[0]=1;
                            DEBUG_INFO("Recovery Alarm Code = %s\n", EventCodeTmp);
                            ShmStatusCodeData->AlarmCode.PreviousAlarmVal[ByteCount] &= ~(1 << BitCount);
                            RemoveFaultCodeToBuf(EventCodeTmp);
                            EventCodeTmp[0] = '1';//DS60-120 add
                        } else {
                            DEBUG_INFO("Alarm Code = %s\n", EventCodeTmp);
                            ShmStatusCodeData->AlarmCode.PreviousAlarmVal[ByteCount] |= (1 << BitCount);
                            AddFaultCodeToBuf(EventCodeTmp);
                        }
                        EVENT_INFO("%s\n", EventCodeTmp); //DS60-120 add
                        DB_Insert_Record(localDb, EventCodeTmp); //DS60-120 add
                    }
                }
            }
        }

        //check Info Status
        for (ByteCount = 0; ByteCount < sizeof(ShmStatusCodeData->InfoCode.PreviousInfoVal); ByteCount++) {
            if (ShmStatusCodeData->InfoCode.InfoEvents.InfoVal[ByteCount] != ShmStatusCodeData->InfoCode.PreviousInfoVal[ByteCount]) {
                tmp = ShmStatusCodeData->InfoCode.InfoEvents.InfoVal[ByteCount]; //prevent be modified during following process
                for (BitCount = 0; BitCount < 8; BitCount++) {
                    if (((tmp >> BitCount) & 0x01) != ((ShmStatusCodeData->InfoCode.PreviousInfoVal[ByteCount] >> BitCount) & 0x01)) {
                        memset(EventCodeTmp, 0, sizeof(EventCodeTmp));
                        memcpy(EventCodeTmp, InfoStatusCode[ByteCount * 8 + BitCount], sizeof(EventCodeTmp) - 1);
                        if (((tmp >> BitCount) & 0x01) == 0) { //Recovered
                            //EventCodeTmp[0]=1;
                            DEBUG_INFO("Recovery Info Code = %s\n", EventCodeTmp);
                            ShmStatusCodeData->InfoCode.PreviousInfoVal[ByteCount] &= ~(1 << BitCount);
                            RemoveFaultCodeToBuf(EventCodeTmp);
                            EventCodeTmp[0] = '1';//DS60-120 add
                        } else {
                            DEBUG_INFO("Info Code = %s\n", EventCodeTmp);
                            ShmStatusCodeData->InfoCode.PreviousInfoVal[ByteCount] |= (1 << BitCount);
                            AddFaultCodeToBuf(EventCodeTmp);
                        }
                        EVENT_INFO("%s\n", EventCodeTmp); //DS60-120 add
                        DB_Insert_Record(localDb, EventCodeTmp); //DS60-120 add
                    }
                }
            }
        }
        usleep(500000);
    }

    return FAIL;
}