#include <stdio.h>      /*標準輸入輸出定義*/
#include <stdlib.h>     /*標準函數庫定義*/
#include <string.h>
#include <stdint.h>
#include <time.h>

#include <unistd.h>
#include <sys/time.h>
#include <sys/timeb.h>

#include "../Define/define.h"
#include "../ShareMemory/shmMem.h"
#include "../Config.h"
#include "../Log/log.h"
#include "Module_InternalComm.h"
#include "internalComm.h"

//------------------------------------------------------------------------------
static struct SysConfigData *pSysConfig = NULL;
static struct SysInfoData *pSysInfo = NULL;
static struct AlarmCodeData *pAlarmCode = NULL;
static struct InfoCodeData *pInfoCode = NULL;
static struct FaultCodeData *pFaultCode = NULL;
static struct OCPP16Data *ShmOCPP16Data = NULL;

static int Uart5Fd = 0;

static struct timeval  _ac_charging_comp;
static struct timeval  _ac_preparing;
static struct timeb    _ac_startChargingTime;
static struct timeb    _ac_endChargingTime;

static Ac_Status acStatus;
static Ac_Led_Status ledStatus;
static Ac_Alarm_code acAlarmCode;
static Ac_Charging_energy acChargingEnergy;
static Ac_Charging_current acChargingCurrent;

static int _alarm_code[] = {
    AC_OVP,
    AC_UVP,
    AC_OCP,
    AC_OTP,
    AC_GMI_FAULT,
    AC_CP_ERROR,
    AC_AC_LEAKAGE,
    AC_DC_LEAKAGE,
    AC_SYSTEM_SELFTEST_FAULT,
    AC_HANDSHAKE_TIMEOUT,
    AC_EMC_STOP,
    AC_RELAY_WELDING,
    AC_GF_MODULE_FAULT,
    AC_SHUTTER_FAULT,
    AC_LOCKER_FAULT,
    AC_POWER_DROP,
    AC_CIRCUIT_SHORT,
    AC_ROTARY_SWITCH_FAULT,
    AC_RELAY_DRIVE_FAULT
};

//------------------------------------------------------------------------------
static void OcppStopTransation(uint8_t gunIndex)
{
    struct ChargingInfoData *pAcChargingInfo = (struct ChargingInfoData *)GetAcChargingInfoData(0);

    if (strcmp((char *)pAcChargingInfo->StartUserId, "") == EQUAL) {
        strcpy((char *)ShmOCPP16Data->StopTransaction[gunIndex].IdTag,
               (char *)ShmOCPP16Data->StopTransaction[gunIndex].IdTag);
    } else {
        strcpy((char *)ShmOCPP16Data->StopTransaction[gunIndex].IdTag,
               (char *)pAcChargingInfo->StartUserId);
    }

    log_info("AC IdTag = %s \n", ShmOCPP16Data->StopTransaction[gunIndex].IdTag);
    ShmOCPP16Data->CpMsg.bits[gunIndex].StopTransactionReq = YES;
}

static void SetCpDuty(uint8_t _value)
{
    Config_Ac_Duty(Uart5Fd, ADDR_AC_PLUG, _value);
}

static uint8_t GetChargingCurrent(void)
{
    return Query_Charging_Current(Uart5Fd, ADDR_AC_PLUG, &acChargingCurrent);
}

static uint8_t GetChargingEnergy(void)
{
    return Query_Charging_Energy(Uart5Fd, ADDR_AC_PLUG, &acChargingEnergy);
}

static void ChangeStartOrStopDateTime(uint8_t isStart)
{
    char cmdBuf[32];
    struct timeb csuTime;
    struct tm *tmCSU;
    struct ChargingInfoData *pAcChargingInfo = (struct ChargingInfoData *)GetAcChargingInfoData(0);

    ftime(&csuTime);
    tmCSU = localtime(&csuTime.time);

    sprintf(cmdBuf, "%04d-%02d-%02d %02d:%02d:%02d",
            tmCSU->tm_year + 1900,
            tmCSU->tm_mon + 1,
            tmCSU->tm_mday,
            tmCSU->tm_hour,
            tmCSU->tm_min,
            tmCSU->tm_sec);

    if (isStart) {
        strcpy((char *)pAcChargingInfo->StartDateTime, cmdBuf);
    } else {
        strcpy((char *)pAcChargingInfo->StopDateTime, cmdBuf);
    }
}

static void OcppStartTransation(uint8_t gunIndex)
{
    struct ChargingInfoData *pAcChargingInfo = (struct ChargingInfoData *)GetAcChargingInfoData(0);

    if (strcmp((char *)pAcChargingInfo->StartUserId, "") == EQUAL) {
        strcpy((char *)ShmOCPP16Data->StartTransaction[gunIndex].IdTag,
               (char *)ShmOCPP16Data->StartTransaction[gunIndex].IdTag);
    } else {
        strcpy((char *)ShmOCPP16Data->StartTransaction[gunIndex].IdTag,
               (char *)pAcChargingInfo->StartUserId);
    }

    log_info("AC IdTag = %s \n", ShmOCPP16Data->StartTransaction[gunIndex].IdTag);
    ShmOCPP16Data->CpMsg.bits[gunIndex].StartTransactionReq = YES;
}

static void SetLegacyReq(uint8_t _switch)
{
    Config_Legacy_Req(Uart5Fd, ADDR_AC_PLUG, _switch);
}

static void ChangeLedStatus(void)
{
    struct ChargingInfoData *pAcChargingInfo = (struct ChargingInfoData *)GetAcChargingInfoData(0);

    if (pAcChargingInfo->SystemStatus == S_IDLE) {
        ledStatus.ActionMode = 1;
    } else if (pAcChargingInfo->SystemStatus == S_PREPARNING) {
        ledStatus.ActionMode = 3;
    } else if (pAcChargingInfo->SystemStatus == S_CHARGING) {
        ledStatus.ActionMode = 4;
    }

    Config_LED_Status(Uart5Fd, ADDR_AC_PLUG, &ledStatus);
}

static uint8_t isModeChange(void)
{
    uint8_t result = NO;
    struct ChargingInfoData *pAcChargingInfo = (struct ChargingInfoData *)GetAcChargingInfoData(0);

    if (pAcChargingInfo->SystemStatus != pAcChargingInfo->PreviousSystemStatus) {
        result = YES;
        pAcChargingInfo->PreviousSystemStatus = pAcChargingInfo->SystemStatus;
    }

    return result;
}

static bool OcppRemoteStop(uint8_t gunIndex)
{
    bool result = ShmOCPP16Data->CsMsg.bits[gunIndex].RemoteStopTransactionReq;

    if (ShmOCPP16Data->CsMsg.bits[gunIndex].RemoteStopTransactionReq == YES) {
        strcpy((char *)ShmOCPP16Data->StopTransaction[gunIndex].StopReason, "Remote");
        ShmOCPP16Data->CsMsg.bits[gunIndex].RemoteStopTransactionReq = NO;
    }

    return result;
}

static void CheckAlarmOccur(void)
{
    bool isErr = false;
    uint8_t count = 0;
    struct ChargingInfoData *pAcChargingInfo = (struct ChargingInfoData *)GetAcChargingInfoData(0);

    for (count = 0; count < sizeof(_alarm_code) / sizeof(_alarm_code[0]); count++) {
        if (acAlarmCode.AcAlarmCode & _alarm_code[count]) {
            isErr = true;
            switch (_alarm_code[count]) {
            case AC_OVP: pAlarmCode->AlarmEvents.bits.AcSystemInputOVP = YES; break;
            case AC_UVP: pAlarmCode->AlarmEvents.bits.AcSystemInputUVP = YES; break;
            case AC_OCP: pAlarmCode->AlarmEvents.bits.SystemAcOutputOCP = YES; break;
            case AC_OTP: pAlarmCode->AlarmEvents.bits.SystemAmbientOTP = YES; break;
            case AC_GMI_FAULT: pAlarmCode->AlarmEvents.bits.AcGroundfaultFail = YES; break;
            case AC_CP_ERROR: pInfoCode->InfoEvents.bits.PilotFault = YES; break;
            case AC_AC_LEAKAGE: pAlarmCode->AlarmEvents.bits.RcdTrip = YES; break;
            case AC_DC_LEAKAGE: pAlarmCode->AlarmEvents.bits.RcdTrip = YES; break;
            case AC_SYSTEM_SELFTEST_FAULT: pAlarmCode->AlarmEvents.bits.McuSelftestFail = YES; break;
            case AC_HANDSHAKE_TIMEOUT: break;
            //case AC_EMC_STOP: pAlarmCode->AlarmEvents.bits.EmergencyStopTrip = YES; break;
            case AC_RELAY_WELDING: pFaultCode->FaultEvents.bits.AcOutputRelayWelding = YES; break;
            case AC_GF_MODULE_FAULT: pFaultCode->FaultEvents.bits.RcdSelfTestFail = YES; break;
            case AC_SHUTTER_FAULT: break;
            case AC_LOCKER_FAULT: pFaultCode->FaultEvents.bits.AcConnectorLockFail = YES; break;
            case AC_POWER_DROP: pAlarmCode->AlarmEvents.bits.SystemL1InputDrop = YES; break;
            case AC_CIRCUIT_SHORT: pAlarmCode->AlarmEvents.bits.CircuitShort = YES; break;
            case AC_ROTARY_SWITCH_FAULT: break;
            case AC_RELAY_DRIVE_FAULT: pFaultCode->FaultEvents.bits.AcOutputRelayDrivingFault = YES; break;
            }
        } else {
            switch (_alarm_code[count]) {
            case AC_OVP: pAlarmCode->AlarmEvents.bits.AcSystemInputOVP = NO; break;
            case AC_UVP: pAlarmCode->AlarmEvents.bits.AcSystemInputUVP = NO; break;
            case AC_OCP: pAlarmCode->AlarmEvents.bits.SystemAcOutputOCP = NO; break;
            case AC_OTP: pAlarmCode->AlarmEvents.bits.SystemAmbientOTP = NO; break;
            case AC_GMI_FAULT: pAlarmCode->AlarmEvents.bits.AcGroundfaultFail = NO; break;
            case AC_CP_ERROR: pInfoCode->InfoEvents.bits.PilotFault = NO; break;
            case AC_AC_LEAKAGE: pAlarmCode->AlarmEvents.bits.RcdTrip = NO; break;
            case AC_DC_LEAKAGE: pAlarmCode->AlarmEvents.bits.RcdTrip = NO; break;
            case AC_SYSTEM_SELFTEST_FAULT: pAlarmCode->AlarmEvents.bits.McuSelftestFail = NO; break;
            case AC_HANDSHAKE_TIMEOUT: break;
            //case AC_EMC_STOP: pAlarmCode->AlarmEvents.bits.EmergencyStopTrip = NO; break;
            case AC_RELAY_WELDING: pFaultCode->FaultEvents.bits.AcOutputRelayWelding = NO; break;
            case AC_GF_MODULE_FAULT: pFaultCode->FaultEvents.bits.RcdSelfTestFail = NO; break;
            case AC_SHUTTER_FAULT: break;
            case AC_LOCKER_FAULT: pFaultCode->FaultEvents.bits.AcConnectorLockFail = NO; break;
            case AC_POWER_DROP: pAlarmCode->AlarmEvents.bits.SystemL1InputDrop = NO; break;
            case AC_CIRCUIT_SHORT: pAlarmCode->AlarmEvents.bits.CircuitShort = NO; break;
            case AC_ROTARY_SWITCH_FAULT:  break;
            case AC_RELAY_DRIVE_FAULT: pFaultCode->FaultEvents.bits.AcOutputRelayDrivingFault = NO; break;
            }
        }
    }

    pAcChargingInfo->IsErrorOccur = isErr;
}

static void GetAcAlarmCode(void)
{
    if (Query_AC_Alarm_Code(Uart5Fd, ADDR_AC_PLUG, &acAlarmCode) == PASS) {
        CheckAlarmOccur();
    }
}

static void GetAcStatus(void)
{
    struct ChargingInfoData *pAcChargingInfo = (struct ChargingInfoData *)GetAcChargingInfoData(0);

    if (Query_AC_Status(Uart5Fd, ADDR_AC_PLUG, &acStatus) == PASS) {
        pSysConfig->AcRatingCurrent = acStatus.MaxCurrent;

        if (pSysConfig->AcMaxChargingCurrent == 0) {
            pSysConfig->AcMaxChargingCurrent = pSysConfig->AcRatingCurrent;
        }

        pAcChargingInfo->ConnectorPlugIn = acStatus.CpStatus;
        //  log_info("CpStatus = %d \n", acStatus.CpStatus);
        //              printf("CurLimit = %d \n", acStatus.CurLimit);
        //              printf("PilotVol_P = %d \n", acStatus.PilotVol_P);
        //              printf("PilotVol_N = %d \n", acStatus.PilotVol_N);
        //              printf("LockStatus = %d \n", acStatus.LockStatus);
        //              printf("RelayStatus = %d \n", acStatus.RelayStatus);
        //              printf("ShutterStatus = %d \n", acStatus.ShutterStatus);
        //              printf("MeterStatus = %d \n", acStatus.MeterStatus);
        //              printf("PpStatus = %d \n", acStatus.PpStatus);
        //              printf("MaxCurrent = %d \n", acStatus.MaxCurrent);
        //              printf("RotateSwitchStatus = %d \n", acStatus.RelayStatus);
        //              printf("============================== \n");
        //
        //              pAcChargingInfo->SystemStatus = acStatus.CpStatus;
    }
//  else
//      log_info("GetAcStatus return fail. \n");
}


static void ChangeToCsuMode(void)
{
    struct ChargingInfoData *pAcChargingInfo = (struct ChargingInfoData *)GetAcChargingInfoData(0);

    pAcChargingInfo->IsModeChagned = Config_CSU_Mode(Uart5Fd, ADDR_AC_PLUG);

//  if (pAcChargingInfo->IsModeChagned == PASS)
//  {
//      Config_Reset_MCU(Uart5Fd, ADDR_AC_PLUG);
//  }
}

static void GetAcModelName(void)
{
    memset(pSysConfig->AcModelName, 0, sizeof(pSysConfig->AcModelName));
    if (Query_Model_Name(Uart5Fd, ADDR_AC_PLUG, pSysConfig->AcModelName) == PASS) {
        log_info("ac model name = %s \n", pSysConfig->AcModelName);
    }
}

static void GetFwVersion_AC(void)
{
    Ver ver = {0};
    struct ChargingInfoData *pAcChargingInfo = (struct ChargingInfoData *)GetAcChargingInfoData(0);

    if (Query_FW_Ver(Uart5Fd, ADDR_AC_PLUG, &ver) == PASS) {
        pAcChargingInfo->SelfTest_Comp = YES;
        strcpy((char *)pAcChargingInfo->version, ver.Version_FW);
    }
}

void AcPlugTask(int uartFD)
{
    static float _beforeChargingTotalEnergy = 0.0;

    if (pSysConfig->AcConnectorCount <= 0) {
        return;
    }

    //share memory mapping
    pSysConfig = (struct SysConfigData *)GetShmSysConfigData();
    pSysInfo = (struct SysInfoData *)GetShmSysInfoData();
    pAlarmCode = (struct AlarmCodeData *)GetShmAlarmCodeData();
    pInfoCode = (struct InfoCodeData *)GetShmInfoCodeData();
    pFaultCode = (struct FaultCodeData *)GetShmFaultCodeData();
    ShmOCPP16Data = (struct OCPP16Data *)GetShmOCPP16Data();

    struct ChargingInfoData *pAcChargingInfo = (struct ChargingInfoData *)GetAcChargingInfoData(0);

    Uart5Fd = uartFD;

    //pAcChargingInfo->SelfTest_Comp = YES;
    //pAcChargingInfo->IsModeChagned = PASS;
    //---------------------------------------------
    if (pAcChargingInfo->SelfTest_Comp == NO) {
        pAcChargingInfo->IsModeChagned = NO;
        GetFwVersion_AC();
        GetAcModelName();
    } else if (pAcChargingInfo->SelfTest_Comp == YES) {
        if (pAcChargingInfo->IsModeChagned != PASS) {
            ChangeToCsuMode();
            return;
        }
        GetAcStatus();
        GetAcAlarmCode();

        uint8_t _status = S_NONE;

        if (pAcChargingInfo->SystemStatus == S_IDLE && pAcChargingInfo->IsErrorOccur) {
            _status = S_ALARM;
        } else if (acStatus.CpStatus == AC_SYS_A || pAcChargingInfo->IsErrorOccur) {
            if (pAcChargingInfo->SystemStatus == S_CHARGING) {
                _status = S_TERMINATING;
            } else if (pAcChargingInfo->SystemStatus >= S_TERMINATING) {
                if (GetTimeoutValue(_ac_charging_comp) >= 10000000 && acStatus.CpStatus == AC_SYS_A) {
                    _status = S_IDLE;
                }
            } else {
                _status = S_IDLE;
            }
        } else if (pAcChargingInfo->SystemStatus >= S_PREPARNING &&
                   pAcChargingInfo->SystemStatus < S_CHARGING) {
            if (acStatus.CpStatus == AC_SYS_C && acStatus.RelayStatus == YES) {
                _status = S_CHARGING;
            } else if (GetTimeoutValue(_ac_preparing) >= 30000000) {
                _status = S_IDLE;
            }
        } else if ((acStatus.CpStatus == AC_SYS_B || pAcChargingInfo->ConnectorPlugIn == AC_SYS_B) &&
                   pAcChargingInfo->IsAvailable &&
                   !pAcChargingInfo->IsErrorOccur &&
                   (pSysInfo->WaitForPlugit == YES /*||
                    pSysConfig->AuthorisationMode == AUTH_MODE_DISABLE*/)) {
            if (pAcChargingInfo->RemoteStartFlag == YES) {
                log_info("** AC Remote \n");
                pAcChargingInfo->RemoteStartFlag = NO;
                strcpy((char *)pAcChargingInfo->StartUserId, "");
                pSysInfo->WaitForPlugit = NO;
                _status = S_PREPARNING;
            } else if (pSysInfo->OrderCharging == NO_DEFINE) {
                log_info("** UserId = %s \n", pSysConfig->UserId);
                strcpy((char *)pAcChargingInfo->StartUserId, (char *)pSysConfig->UserId);
                log_info("** CardNumber = %s \n", pAcChargingInfo->StartUserId);
                strcpy((char *)pSysConfig->UserId, "");
                pSysInfo->WaitForPlugit = NO;
                _status = S_PREPARNING;
            }
        } else if (pAcChargingInfo->SystemStatus == S_CHARGING) {
            if (OcppRemoteStop(1)) {
                _status = S_TERMINATING;
            }
        }

        //printf("_status = %d \n", _status);

        if (_status != S_NONE && pAcChargingInfo->SystemStatus != _status) {
            pAcChargingInfo->SystemStatus = _status;
        }

        // 設定限制最大充電電流 >= 6 ~ <= 32
        switch (pAcChargingInfo->SystemStatus) {
        case S_IDLE:
        case S_ALARM: {
            if (isModeChange()) {
                pAcChargingInfo->PresentChargedEnergy = 0.0;
                pAcChargingInfo->PresentChargingVoltage = 0;
                pAcChargingInfo->ChargingFee = 0.0;
                strcpy((char *)pAcChargingInfo->StartDateTime, "");
                strcpy((char *)pAcChargingInfo->StopDateTime, "");
                _beforeChargingTotalEnergy = 0.0;
            }

            ChangeLedStatus();
        }
        break;
        case S_PREPARNING: {
            if (isModeChange()) {
                pSysInfo->SystemPage = _LCM_NONE;
                pSysInfo->CurGunSelectedByAc = DEFAULT_AC_INDEX;
                if (pSysInfo->OrderCharging != NO_DEFINE) {
                    pSysInfo->OrderCharging = NO_DEFINE;
                }
                gettimeofday(&_ac_preparing, NULL);
            }

            if (GetChargingEnergy() == PASS) {
                //pAcChargingInfo->PresentChargedEnergy = acChargingEnergy.Energy / 100;
                _beforeChargingTotalEnergy = acChargingEnergy.Energy;
            }

            SetLegacyReq(YES);
            ChangeLedStatus();
        }
        break;
        case S_CHARGING: {
            if (isModeChange()) {
                ftime(&_ac_startChargingTime);
                OcppStartTransation(1);
                ChangeStartOrStopDateTime(YES);
                pSysInfo->CurGunSelectedByAc = DEFAULT_AC_INDEX;
            }

            if (GetChargingEnergy() == PASS) {
                if ((acChargingEnergy.Energy - _beforeChargingTotalEnergy) > 0) {
                    pAcChargingInfo->PresentChargedEnergy += (acChargingEnergy.Energy - _beforeChargingTotalEnergy) / 100;
                    if (pSysConfig->BillingData.isBilling) {
                        pAcChargingInfo->ChargingFee += pAcChargingInfo->PresentChargedEnergy * pSysConfig->BillingData.Cur_fee;
                    }
                }

                _beforeChargingTotalEnergy = acChargingEnergy.Energy;
            }

            if (GetChargingCurrent() == PASS) {
                pAcChargingInfo->PresentChargingPower = (((float)(AC_DEFAULT_VOL * acChargingCurrent.OuputCurrentL1) / 10) / 1000);
            }

            ftime(&_ac_endChargingTime);
            pAcChargingInfo->PresentChargedDuration = DiffTimeb(_ac_startChargingTime, _ac_endChargingTime);
            pAcChargingInfo->PresentChargingVoltage = AC_DEFAULT_VOL;
            pAcChargingInfo->PresentChargingCurrent = ((float)acChargingCurrent.OuputCurrentL1 / 10);

            // 用以判斷是否有在輸出
            pAcChargingInfo->IsCharging = acStatus.RelayStatus;

            SetCpDuty(pSysConfig->AcMaxChargingCurrent);
            ChangeLedStatus();
        }
        break;
        case S_TERMINATING: {
            if (isModeChange()) {
                ChangeStartOrStopDateTime(NO);
                gettimeofday(&_ac_charging_comp, NULL);
            }

            SetLegacyReq(NO);
            if (acStatus.RelayStatus == NO) {
                pAcChargingInfo->SystemStatus = S_COMPLETE;
            }
        }
        break;
        case S_COMPLETE: {
            if (isModeChange()) {
                gettimeofday(&_ac_charging_comp, NULL);
                ftime(&_ac_endChargingTime);
                if (strcmp((char *)pAcChargingInfo->StartDateTime, "") != EQUAL) {
                    // AC 固定為第2把槍
                    OcppStopTransation(1);
                }

                ChangeStartOrStopDateTime(NO);
                pAcChargingInfo->PresentChargedDuration = DiffTimeb(_ac_startChargingTime, _ac_endChargingTime);
            }
        }
        break;
        }
    }
}