#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/can.h>
#include    <linux/can/raw.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/define.h"
#include    "Config.h"

//------------------------------------------------------------------------------
#define Debug
#define OUTPUT_FLASH        0x01
#define OUTPUT_FILE         0x02

#define log_info(format, args...) StoreLogMsg("[%s:%d][%s][Info] "format, __FILE__, __LINE__, __FUNCTION__, ##args)
#define log_warn(format, args...) StoreLogMsg("[%s:%d][%s][Warn] "format, __FILE__, __LINE__, __FUNCTION__, ##args)
#define log_error(format, args...) StoreLogMsg("[%s:%d][%s][Error] "format, __FILE__, __LINE__, __FUNCTION__, ##args)

//------------------------------------------------------------------------------
struct SysConfigData            SysConfig;
struct SysConfigAndInfo         *ShmSysConfigAndInfo;
struct StatusCodeData           *ShmStatusCodeData;

//------------------------------------------------------------------------------
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);

    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 runShellCmd(const char *cmd)
{
    int result = FAIL;
    char buf[256];
    FILE *fp;

    fp = popen(cmd, "r");
    if (fp != NULL) {
        while (fgets(buf, sizeof(buf), fp) != NULL) {
            StoreLogMsg("%s\n", buf);
        }

        result = PASS;
    }
    pclose(fp);

    return result;
}

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

    //creat ShmSysConfigAndInfo
    if ((MeterSMId = shmget(ShmSysConfigAndInfoKey, sizeof(struct SysConfigAndInfo),  0777)) < 0) {
        log_error("shmget ShmSysConfigAndInfo NG\n");

        result = FAIL;
    } else if ((ShmSysConfigAndInfo = shmat(MeterSMId, NULL, 0)) == (void *) - 1) {
        log_error("shmat ShmSysConfigAndInfo NG\n");

        result = FAIL;
    }

    //creat ShmStatusCodeData
    if ((MeterSMId = shmget(ShmStatusCodeKey, sizeof(struct StatusCodeData),  0777)) < 0) {
        log_error("shmget ShmStatusCodeData NG\n");

        result = FAIL;
    } else if ((ShmStatusCodeData = shmat(MeterSMId, NULL, 0)) == (void *) - 1) {
        log_error("shmat ShmStatusCodeData NG\n");

        result = FAIL;
    }

    return result;
}

void helpOutput(void)
{
    printf("Usage: Module_FactoryConfig [OPTION]...\r\n\r\n");
    printf("Generate factory default configuration value\r\n\r\n");
    printf("OPTION:\r\n");
    printf("	-a Write to file(/mnt) & flash\r\n");
    printf("	-f Write to file(/mnt)\r\n");
    printf("	-m Write to flash\r\n");
}

/**************************************************************************************/
/************This task will create Factory default confgiuration file *****************/
/***********and store it into mtdblock 10,11,12                               ****************/
/**************************************************************************************/
int main(int argc, char *argv[])
{
    uint8_t outType = 0;
    unsigned int i, Chk;
    uint8_t *ptr;
    int fd, wrd;

    ptr = malloc(MtdBlockSize);
    if (ptr == NULL) {
#ifdef SystemLogMessage
        StoreLogMsg("[FactoryConfig]main: malloc for SysConfigData NG");
#endif
        return 0;
    }
    memset(ptr, 0, MtdBlockSize);
    memset(&SysConfig, 0, sizeof(struct SysConfigData));


    // Initial Share Memory
    if (InitShareMemory() == FAIL) {
        log_error("InitShareMemory NG\n");

        //strcpy((char *)SysConfig.ModelName, "");
        //strcpy((char *)SysConfig.SerialNumber, "");
        strcpy((char *)SysConfig.ModelName, "DDYC182V0UE2AD");
        strcpy((char *)SysConfig.SerialNumber, "SERIALFORRD");
        sleep(5);
    } else {
        memcpy((char *)SysConfig.ModelName,
               ShmSysConfigAndInfo->SysConfig.ModelName,
               ARRAY_SIZE(ShmSysConfigAndInfo->SysConfig.ModelName));

        memcpy((char *)SysConfig.SerialNumber,
               ShmSysConfigAndInfo->SysConfig.SerialNumber,
               ARRAY_SIZE(ShmSysConfigAndInfo->SysConfig.SerialNumber));

        log_info("InitShareMemory OK.\n");
    }

    /*
     * TODO: Set factory default configuration
    */
    //********** System **********// udhcpc -i eth1 -s ./dhcp_script/eth1.script
    //
    //strcpy((char *)SysConfig.ModelName, "DDYC182V0UE2AD");
    //strcpy((char *)SysConfig.SerialNumber, "SERIALFORRD");

    memset(SysConfig.SystemId, 0x00, sizeof(SysConfig.SystemId));
    char Dash = '-';

    strcat((char *)SysConfig.SystemId, (char *)SysConfig.ModelName);
    strncat((char *)SysConfig.SystemId, &Dash, 1);
    strcat((char *)SysConfig.SystemId, (char *)SysConfig.SerialNumber);

    strcpy((char *)SysConfig.SystemDateTime, "");
    SysConfig.AuthorisationMode = AUTH_MODE_ENABLE;
    SysConfig.DefaultLanguage = 0;
    SysConfig.RfidCardNumEndian = 0;
    SysConfig.AcPlugInTimes = 0;
    SysConfig.GbPlugInTimes = 0;
    SysConfig.Ccs1PlugInTime = 0;
    SysConfig.Ccs2PlugInTimes = 0;
    SysConfig.ChademoPlugInTimes = 0;
    SysConfig.BillingData.isBilling = 0;
    SysConfig.isAPP = 1;
    SysConfig.isQRCode = 1;
    SysConfig.isRFID = 1;
    //********** Charging **********//
    SysConfig.MaxChargingEnergy = 0;
    SysConfig.MaxChargingCurrent = 500;     // 最大可輸出電流 (整樁)
    SysConfig.MaxChargingDuration = 0;
    SysConfig.AcMaxChargingCurrent = 0;
    SysConfig.PhaseLossPolicy = 0;
    /*+++ 20200908, vern, modify it +++*/
    /*for(uint8_t i = 0; i < 10; i++)
        strcpy((char *)SysConfig.LocalWhiteCard, "");*/
    memset(SysConfig.LocalWhiteCard, 0, sizeof(SysConfig.LocalWhiteCard));
    /*--- 20200908, vern, modify it ---*/
    strcpy((char *)SysConfig.UserId, "");
    //********** Network **********//
    strcpy((char *)SysConfig.FtpServer, "");
    SysConfig.Eth0Interface.EthDhcpClient = 0;
    strcpy((char *) SysConfig.Eth0Interface.EthIpAddress, "192.168.1.10");
    strcpy((char *) SysConfig.Eth0Interface.EthSubmaskAddress, "255.255.255.0");
    strcpy((char *) SysConfig.Eth0Interface.EthGatewayAddress, "192.168.1.254");
    SysConfig.Eth1Interface.EthDhcpClient = 0;
    strcpy((char *) SysConfig.Eth1Interface.EthIpAddress, "192.168.0.10");
    strcpy((char *) SysConfig.Eth1Interface.EthSubmaskAddress, "255.255.255.0");
    strcpy((char *) SysConfig.Eth1Interface.EthGatewayAddress, "192.168.0.254");
    if (SysConfig.ModelName[10] == 'W') {
        SysConfig.AthInterface.WifiMode = 2;
    } else {
        SysConfig.AthInterface.WifiMode = 0;
    }
    strcpy((char *) SysConfig.AthInterface.WifiSsid, "");
    strcpy((char *) SysConfig.AthInterface.WifiPassword, "");
    SysConfig.AthInterface.WifiRssi = 0;
    SysConfig.AthInterface.WifiDhcpServer = 0;
    SysConfig.AthInterface.WifiDhcpClient = 0;
    strcpy((char *) SysConfig.AthInterface.WifiMacAddress, "");
    strcpy((char *) SysConfig.AthInterface.WifiIpAddress, "");
    strcpy((char *) SysConfig.AthInterface.WifiSubmaskAddress, "");
    strcpy((char *) SysConfig.AthInterface.WifiGatewayAddress, "");
    SysConfig.AthInterface.WifiNetworkConn = 0;
    strcpy((char *) SysConfig.TelecomInterface.TelcomModelName, "");
    strcpy((char *) SysConfig.TelecomInterface.TelcomSoftwareVer, "");
    //strcpy((char *) SysConfig.TelecomInterface.TelcomApn, "Internet");
    strcpy((char *) SysConfig.TelecomInterface.TelcomApn, "");
    SysConfig.TelecomInterface.TelcomRssi = 0;
    strcpy((char *) SysConfig.TelecomInterface.TelcomChapPapId, " ");
    strcpy((char *) SysConfig.TelecomInterface.TelcomChapPapPwd, " ");
    strcpy((char *) SysConfig.TelecomInterface.TelcomModemImei, "");
    strcpy((char *) SysConfig.TelecomInterface.TelcomSimImsi, "");
    strcpy((char *) SysConfig.TelecomInterface.TelcomSimIccid, "");
    SysConfig.TelecomInterface.TelcomSimStatus = 0;
    SysConfig.TelecomInterface.TelcomModemMode = 0;
    strcpy((char *) SysConfig.TelecomInterface.TelcomIpAddress, "");
    SysConfig.TelecomInterface.TelcomNetworkConn = 0;
    strcpy((char *)SysConfig.chargePointVendor, "Phihong Technology");
    //********** Backend **********//
    SysConfig.BackendConnTimeout = 300; //300 seconds
    SysConfig.OfflinePolicy = 2;
    SysConfig.OfflineMaxChargeEnergy = 0;
    SysConfig.OfflineMaxChargeDuration = 0;
    strcpy((char *) SysConfig.OcppServerURL, "");
    strcpy((char *) SysConfig.ChargeBoxId, "");
    SysConfig.LedInfo.Intensity = 2;

    //copy default configuration to pointer
    memcpy(ptr, &SysConfig, sizeof(struct SysConfigData));

    //calculate CRC
    Chk = 0;
    for (i = ARRAY_SIZE(SysConfig.CsuBootLoadFwRev); i < (MtdBlockSize - 4); i++) {
        Chk += *(ptr + i);
    }
    memcpy(ptr + MtdBlockSize - 4, &Chk, 4);
    //Chk = 0;
    //for (i = 0; i < (MtdBlockSize - 4); i++) {
    //    Chk += *(ptr + i);
    //}
    //memcpy( ptr + MtdBlockSize - 4, &Chk, 4);

    /*
    * Parameter process
    */
    if (argc > 1) {
        char *arg = argv[1];
        switch (arg[0]) {
        case '-':
            switch (arg[1]) {
            case 'a':
                outType |= OUTPUT_FILE;
                outType |= OUTPUT_FLASH;
                break;
            case 'f':
                outType |= OUTPUT_FILE;
                break;
            case 'm':
                outType |= OUTPUT_FLASH;
                break;
            default:
                helpOutput();
                break;
            }
            break;
        default:
            helpOutput();
            break;
        }
    } else {
        helpOutput();
    }

    //if (argc == 4) {
    //    strcpy((char *)SysConfig.ModelName, argv[2]);
    //    strcpy((char *)SysConfig.SerialNumber, argv[3]);
    //} else {
    //    strcpy((char *)SysConfig.ModelName, "DDYC182V0UE2AD");
    //    strcpy((char *)SysConfig.SerialNumber, "SERIALFORRD");
    //}

    /*
     * Configuration bin file generate
    */
    fd = open("/mnt/FactoryDefaultConfig.bin", O_RDWR | O_CREAT | O_TRUNC);
    if (fd < 0) {

        StoreLogMsg("open /mnt/FactoryDefaultConfig.bin NG\n");

        free(ptr);
        return 0;
    }
    wrd = write(fd, ptr, MtdBlockSize);
    close(fd);
    if (wrd < MtdBlockSize) {
        StoreLogMsg("write /mnt/FactoryDefaultConfig.bin NG\n");

        free(ptr);
        return 0;
    }

    StoreLogMsg("FactoryConfig write to file in /mnt OK.\n");

    //if ((outType & OUTPUT_FILE) > 0) {
    //    fd = open("/mnt/FactoryDefaultConfig.bin", O_RDWR | O_CREAT);
    //    if (fd < 0) {
    //        StoreLogMsg("[FactoryConfig]main: open /mnt/FactoryDefaultConfig.bin NG");
    //        free(ptr);
    //        return 0;
    //    }
    //    wrd = write(fd, ptr, MtdBlockSize);
    //    close(fd);
    //    if (wrd < MtdBlockSize) {
    //        StoreLogMsg("write /mnt/FactoryDefaultConfig.bin NG\r\n");
    //        free(ptr);
    //        return 0;
    //    }
    //    StoreLogMsg("FactoryConfig write to file in /mnt OK.\r\n");
    //}

    /*
    * Flash memory write
    */
    if ((outType & OUTPUT_FLASH) > 0) {
        StoreLogMsg("Erase /dev/mtd10.\n");
        runShellCmd("flash_erase /dev/mtd10 0 0");
        StoreLogMsg("Write /dev/mtd10.\n");
        runShellCmd("nandwrite -p /dev/mtd10 /mnt/FactoryDefaultConfig.bin");

        StoreLogMsg("Erase /dev/mtd11.\n");
        runShellCmd("flash_erase /dev/mtd11 0 0");
        StoreLogMsg("Write /dev/mtd11.\n");
        runShellCmd("nandwrite -p /dev/mtd11 /mnt/FactoryDefaultConfig.bin");

        StoreLogMsg("Erase /dev/mtd12.\n");
        runShellCmd("flash_erase /dev/mtd12 0 0");
        StoreLogMsg("Write /dev/mtd12.\n");
        runShellCmd("nandwrite -p /dev/mtd12 /mnt/FactoryDefaultConfig.bin");

        system("rm -f /mnt/FactoryDefaultConfig.bin");

        StoreLogMsg("FactoryConfig write to flash OK\n");
#if 0
        // Save factory default setting value to flash factory default setting block
        fd = open("/dev/mtdblock12", O_RDWR);
        if (fd < 0) {
            StoreLogMsg("open /dev/mtdblock12 NG\r\n");
            free(ptr);
            return 0;
        }
        wrd = write(fd, ptr, MtdBlockSize);
        close(fd);
        if (wrd < MtdBlockSize) {
            StoreLogMsg("write /dev/mtdblock12 NG\r\n");
            free(ptr);
            return 0;
        }

        // Save factory default setting value to flash backup setting block
        fd = open("/dev/mtdblock11", O_RDWR);
        if (fd < 0) {
            StoreLogMsg("open /dev/mtdblock11 NG\r\n");
            free(ptr);
            return 0;
        }
        wrd = write(fd, ptr, MtdBlockSize);
        close(fd);
        if (wrd < MtdBlockSize) {
            StoreLogMsg("write /dev/mtdblock11 NG\r\n");
            free(ptr);
            return 0;
        }

        // Save factory default setting value to flash setting block
        fd = open("/dev/mtdblock10", O_RDWR);
        if (fd < 0) {
            StoreLogMsg("open /dev/mtdblock10 NG\r\n");
            free(ptr);
            return 0;
        }
        wrd = write(fd, ptr, MtdBlockSize);
        close(fd);
        if (wrd < MtdBlockSize) {
            StoreLogMsg("write /dev/mtdblock10 NG\r\n");
            free(ptr);
            return 0;
        }
        StoreLogMsg("FactoryConfig write to flash OK\r\n");
#endif //0
    }

    free(ptr);

    return FAIL;
}