#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;

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

#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)

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 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)
    {
        #ifdef SystemLogMessage
        DEBUG_ERROR("shmget ShmSysConfigAndInfo NG\n");
        #endif
        result = FAIL;
    }
    else if ((ShmSysConfigAndInfo = shmat(MeterSMId, NULL, 0)) == (void *) -1)
    {
        #ifdef SystemLogMessage
        DEBUG_ERROR("shmat ShmSysConfigAndInfo NG\n");
        #endif
        result = FAIL;
     }
    else
    {}

     //creat ShmStatusCodeData
     if ((MeterSMId = shmget(ShmStatusCodeKey, sizeof(struct StatusCodeData),  0777)) < 0)
    {
        #ifdef SystemLogMessage
        DEBUG_ERROR("shmget ShmStatusCodeData NG\n");
        #endif
        result = FAIL;
    }
    else if ((ShmStatusCodeData = shmat(MeterSMId, NULL, 0)) == (void *) -1)
    {
        #ifdef SystemLogMessage
        DEBUG_ERROR("shmat ShmStatusCodeData NG\n");
        #endif
        result = FAIL;
    }
    else
    {}

    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--;
        }
    }
}

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

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

    for(;;)
    {
        //check Fault Status
        for(ByteCount=0;ByteCount<4;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);
                        }
                        else
                        {
                            DEBUG_INFO("Fault Code = %s\n", EventCodeTmp);
                            ShmStatusCodeData->FaultCode.PreviousFaultVal[ByteCount] |= (1<<BitCount);
                            AddFaultCodeToBuf(EventCodeTmp);
                        }
                    }
                }
            }
        }

        //check Alarm Status
        for(ByteCount=0;ByteCount<13;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);
                        }
                        else
                        {
                            DEBUG_INFO("Alarm Code = %s\n", EventCodeTmp);
                            ShmStatusCodeData->AlarmCode.PreviousAlarmVal[ByteCount] |= (1<<BitCount);
                            AddFaultCodeToBuf(EventCodeTmp);
                        }
                    }
                }
            }
        }

        //check Info Status
        for(ByteCount=0;ByteCount<40;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);
                        }
                        else
                        {
                            DEBUG_INFO("Info Code = %s\n", EventCodeTmp);
                            ShmStatusCodeData->InfoCode.PreviousInfoVal[ByteCount] |= (1<<BitCount);
                            AddFaultCodeToBuf(EventCodeTmp);
                        }
                    }
                }
            }
        }
        usleep(500000);
    }

    return FAIL;
}