#include "mlogger.h"
#include "ccu_config.h"
#include "cJSON_Utils.h"
#include "ocpp16_configkey.h"
#include "ocpp16_authorization.h"
#include "ocpp16_ChargingProfiles.h"
#include "ocpp16_OfflineChargingRecords.h"

ocpp16_configkeys_t ocpp16_configkeys;

uint32_t Measurand_fromString(char* namelist)
{
    uint32_t result = 0;

    if (strstr(namelist, "Current.Export") != NULL) result|=  1<<Measurand_Current_Export;
    if (strstr(namelist, "Current.Import") != NULL) result|= 1<<Measurand_Current_Import;
    if (strstr(namelist, "Current.Offered") != NULL) result|= 1<<Measurand_Current_Offered;

    if (strstr(namelist, "Energy.Active.Export.Register") != NULL) result|=1<<Measurand_Energy_Active_Export_Register;
    if (strstr(namelist, "Energy.Active.Import.Register") != NULL) result|=1<<Measurand_Energy_Active_Import_Register;
    if (strstr(namelist, "Energy.Reactive.Export.Register") != NULL) result|=1<<Measurand_Energy_Reactive_Export_Register;
    if (strstr(namelist, "Energy.Reactive.Import.Register") != NULL) result|=1<<Measurand_Energy_Reactive_Import_Register;

    if (strstr(namelist, "Energy.Active.Export.Interval")) result|=1<<Measurand_Energy_Active_Export_Interval;
    if (strstr(namelist, "Energy.Active.Import.Interval")) result|=1<<Measurand_Energy_Active_Import_Interval;
    if (strstr(namelist, "Energy.Reactive.Export.Interval")) result|=1<<Measurand_Energy_Reactive_Export_Interval;
    if (strstr(namelist, "Energy.Reactive.Import.Interval")) result|=1<<Measurand_Energy_Reactive_Import_Interval;

    if (strstr(namelist, "Frequency")) result|=1<<Measurand_Frequency;

    if (strstr(namelist, "Power.Active.Export")) result|=1<<Measurand_Power_Active_Export;
    if (strstr(namelist, "Power.Active.Import")) result|=1<<Measurand_Power_Active_Import;  
    if (strstr(namelist, "Power.Factor")) result|=1<<Measurand_Power_Factor;
    if (strstr(namelist, "Power.Offered")) result|=1<<Measurand_Power_Offered;
    if (strstr(namelist, "Power.Reactive.Export")) result|=1<<Measurand_Power_Reactive_Export;
    if (strstr(namelist, "Power.Reactive.Import")) result|=1<<Measurand_Power_Reactive_Import;

    if (strstr(namelist, "RPM")) result|=1<<Measurand_RPM;
    if (strstr(namelist, "SoC")) result|=1<<Measurand_SoC;
    if (strstr(namelist, "Temperature")) result|=1<<Measurand_Temperature;  
    if (strstr(namelist, "Voltage")) result|=1<<Measurand_Voltage; 
    
    return result;
}

const char* Measurand_toString(char* stream, uint32_t measurand)
{
    int idx=0;
    stream[idx] = '\0';
    if (measurand & (1<<Measurand_Current_Export)) { idx += sprintf(&stream[idx], "Current.Export, "); }
    if (measurand & (1<<Measurand_Current_Import)) { idx += sprintf(&stream[idx], "Current.Import, "); }
    if (measurand & (1<<Measurand_Current_Offered)) { idx += sprintf(&stream[idx], "Current.Offered, "); }

    if (measurand & (1<<Measurand_Energy_Active_Export_Register)) { idx += sprintf(&stream[idx], "Energy.Active.Export.Register, "); }
    if (measurand & (1<<Measurand_Energy_Active_Import_Register)) { idx += sprintf(&stream[idx], "Energy.Active.Import.Register, "); }
    if (measurand & (1<<Measurand_Energy_Reactive_Export_Register)) { idx += sprintf(&stream[idx], "Energy.Reactive.Export.Register, "); }
    if (measurand & (1<<Measurand_Energy_Reactive_Import_Register)) { idx += sprintf(&stream[idx], "Energy.Reactive.Import.Register, "); }

    if (measurand & (1<<Measurand_Energy_Active_Export_Interval)) { idx += sprintf(&stream[idx], "Energy.Active.Export.Interval, "); }
    if (measurand & (1<<Measurand_Energy_Active_Import_Interval)) { idx += sprintf(&stream[idx], "Energy.Active.Import.Interval, "); }
    if (measurand & (1<<Measurand_Energy_Reactive_Export_Interval)) { idx += sprintf(&stream[idx], "Energy.Reactive.Export.Interval, "); }
    if (measurand & (1<<Measurand_Energy_Reactive_Import_Interval)) { idx += sprintf(&stream[idx], "Energy.Reactive.Import.Interval, "); }

    if (measurand & (1<<Measurand_Frequency)) { idx += sprintf(&stream[idx], "Frequency, "); }

    if (measurand & (1<<Measurand_Power_Active_Export)) { idx += sprintf(&stream[idx], "Power.Active.Export, "); }
    if (measurand & (1<<Measurand_Power_Active_Import)) { idx += sprintf(&stream[idx], "Power.Active.Import, "); }

    if (measurand & (1<<Measurand_Power_Factor)) { idx += sprintf(&stream[idx], "Power.Factor, "); }
    if (measurand & (1<<Measurand_Power_Offered)) { idx += sprintf(&stream[idx], "Power.Offered, "); }
    if (measurand & (1<<Measurand_Power_Reactive_Export)) { idx += sprintf(&stream[idx], "Power.Reactive.Export, "); }
    if (measurand & (1<<Measurand_Power_Reactive_Import)) { idx += sprintf(&stream[idx], "Power.Reactive.Import, "); }

    if (measurand & (1<<Measurand_RPM)) { idx += sprintf(&stream[idx], "RPM, "); }
    if (measurand & (1<<Measurand_SoC)) { idx += sprintf(&stream[idx], "SoC, "); }
    if (measurand & (1<<Measurand_Temperature)) { idx += sprintf(&stream[idx], "Temperature, "); }
    if (measurand & (1<<Measurand_Voltage)) { idx += sprintf(&stream[idx], "Voltage, "); }

    if (stream[idx-2] == ',') stream[idx-2] = '\0';
    if (stream[idx-1] == ',') stream[idx-1] = '\0';
    return stream;
}

uint32_t ChargingScheduleAllowedChargingRateUnit_fromString(char* string)
{
    uint32_t result = 0;
    if (strstr(string, "Power")) { result |= 1<<ChargingRateUnitType_W; }
    if (strstr(string, "Current")) { result |= 1<<ChargingRateUnitType_A; }
    return result;
}

char* ChargingScheduleAllowedChargingRateUnit_toString(char*stream, uint32_t type)
{
    int idx=0;
    stream[idx] = '\0';
    if (type & (1<<ChargingRateUnitType_W)) { idx += sprintf(&stream[idx], "Power, "); }
    if (type & (1<<ChargingRateUnitType_A)) { idx += sprintf(&stream[idx], "Current, "); }
    if (stream[idx-2] == ',') stream[idx-2] = '\0';
    if (stream[idx-1] == ',') stream[idx-1] = '\0';
    return stream;
}


static void ocpp16_load_CoreProfile(cJSON* root, CoreProfile_t* profile)
{
    profile->AllowOfflineTxForUnknownId = cJSON_IsTrue(cJSON_GetObjectItem(root, "AllowOfflineTxForUnknownId"));
    profile->AuthorizationCacheEnabled = cJSON_IsTrue(cJSON_GetObjectItem(root, "AuthorizationCacheEnabled"));
    profile->AuthorizeRemoteTxRequests = cJSON_IsTrue(cJSON_GetObjectItem(root, "AuthorizeRemoteTxRequests"));
    profile->BlinkRepeat = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "BlinkRepeat"));
    profile->ClockAlignedDataInterval = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "ClockAlignedDataInterval"));

    profile->ConnectionTimeOut = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "ConnectionTimeOut"));
    sprintf(profile->ConnectorPhaseRotation, "%s", "0.RST,1.RST,2.RTS");
    profile->ConnectorPhaseRotationMaxLength = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "ConnectorPhaseRotationMaxLength"));
    profile->GetConfigurationMaxKeys = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "GetConfigurationMaxKeys"));
    profile->HeartbeatInterval = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "HeartbeatInterval"));

    profile->LightIntensity = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "LightIntensity"));
    profile->LocalAuthorizeOffline = cJSON_IsTrue(cJSON_GetObjectItem(root, "LocalAuthorizeOffline"));
    profile->LocalPreAuthorize = cJSON_IsTrue(cJSON_GetObjectItem(root, "LocalPreAuthorize"));
    profile->MaxEnergyOnInvalidId = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "MaxEnergyOnInvalidId"));

    profile->MeterValuesAlignedDataMaxLength = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "MeterValuesAlignedDataMaxLength"));
    
    profile->MeterValuesAlignedData = Measurand_fromString(cJSON_GetStringValue(cJSON_GetObjectItem(root, "MeterValuesAlignedData")));
    profile->MeterValuesSampledDataMaxLength = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "MeterValuesSampledDataMaxLength"));
    profile->MeterValuesSampledData = Measurand_fromString(cJSON_GetStringValue(cJSON_GetObjectItem(root, "MeterValuesSampledData")));
    profile->MeterValueSampleInterval = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "MeterValueSampleInterval"));
    profile->MinimumStatusDuration = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "MinimumStatusDuration"));

    profile->NumberOfConnectors = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "NumberOfConnectors"));
    profile->ResetRetries = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "ResetRetries"));  //9.1.22
    profile->StopTransactionOnEVSideDisconnect = cJSON_IsTrue(cJSON_GetObjectItem(root, "StopTransactionOnEVSideDisconnect"));
    profile->StopTransactionOnInvalidId = cJSON_IsTrue(cJSON_GetObjectItem(root, "StopTransactionOnInvalidId"));

    profile->StopTxnAlignedDataMaxLength = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "StopTxnAlignedDataMaxLength"));
    profile->StopTxnAlignedData = Measurand_fromString(cJSON_GetStringValue(cJSON_GetObjectItem(root, "StopTxnAlignedData")));
    profile->StopTxnSampledDataMaxLength = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "StopTxnSampledDataMaxLength"));
    profile->StopTxnSampledData = Measurand_fromString(cJSON_GetStringValue(cJSON_GetObjectItem(root, "StopTxnSampledData")));
    sprintf(profile->SupportedFeatureProfiles, "%s", "Core, FirmwareManagement, LocalAuthListManagement, Reservation, SmartCharging, RemoteTrigger");
    profile->SupportedFeatureProfilesMaxLength = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "SupportedFeatureProfilesMaxLength"));

    profile->TransactionMessageAttempts = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "TransactionMessageAttempts"));
    profile->TransactionMessageRetryInterval = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "TransactionMessageRetryInterval"));
    profile->UnlockConnectorOnEVSideDisconnect = cJSON_IsTrue(cJSON_GetObjectItem(root, "UnlockConnectorOnEVSideDisconnect"));//9.1.33
    profile->WebSocketPingInterval = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "WebSocketPingInterval"));

    sprintf(profile->AuthorizationKey, "%s", cJSON_GetStringValue(cJSON_GetObjectItem(root, "AuthorizationKey")));
    profile->AuthorizationCacheMaxLength = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "AuthorizationCacheMaxLength"));
}

static void ocpp16_load_LocalAuthListManagementProfile(cJSON* root, LocalAuthListManagemenProfile_t* profile)
{
    profile->LocalAuthListEnabled = cJSON_IsTrue(cJSON_GetObjectItem(root, "LocalAuthListEnabled"));
    profile->LocalAuthListMaxLength = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "LocalAuthListMaxLength"));
    profile->SendLocalListMaxLength = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "SendLocalListMaxLength"));
}

static void ocpp16_load_ReservationProfile(cJSON* root, ReservationProfile_t* profile)
{
    profile->ReserveConnectorZeroSupported = cJSON_IsTrue(cJSON_GetObjectItem(root, "ReserveConnectorZeroSupported"));
}

static void ocpp16_load_SmartChargingProfile(cJSON* root, SmartChargingProfile_t* profile)
{
    profile->ChargeProfileMaxStackLevel = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "ChargeProfileMaxStackLevel"));
    profile->ChargingScheduleAllowedChargingRateUnit = ChargingScheduleAllowedChargingRateUnit_fromString(cJSON_GetObjectItem(root, "ChargingScheduleAllowedChargingRateUnit")->valuestring);
    profile->ChargingScheduleMaxPeriods = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "ChargingScheduleMaxPeriods"));
    profile->ConnectorSwitch3to1PhaseSupported = cJSON_IsTrue(cJSON_GetObjectItem(root, "ConnectorSwitch3to1PhaseSupported"));
    profile->MaxChargingProfilesInstalled = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "MaxChargingProfilesInstalled"));
}

static void ocpp16_save_CoreProfile(cJSON* root, CoreProfile_t* profile)
{
    char buff[256];
    cJSON_AddBoolToObject(root, "AllowOfflineTxForUnknownId", profile->AllowOfflineTxForUnknownId);
    cJSON_AddBoolToObject(root, "AuthorizationCacheEnabled", profile->AuthorizationCacheEnabled);
    cJSON_AddBoolToObject(root, "AuthorizeRemoteTxRequests", profile->AuthorizeRemoteTxRequests);
    cJSON_AddNumberToObject(root, "BlinkRepeat", profile->BlinkRepeat);
    cJSON_AddNumberToObject(root, "ClockAlignedDataInterval", profile->ClockAlignedDataInterval);

    cJSON_AddNumberToObject(root, "ConnectionTimeOut", profile->ConnectionTimeOut);
    cJSON_AddStringToObject(root, "ConnectorPhaseRotation", profile->ConnectorPhaseRotation);
    cJSON_AddNumberToObject(root, "ConnectorPhaseRotationMaxLength", profile->ConnectorPhaseRotationMaxLength);
    cJSON_AddNumberToObject(root, "GetConfigurationMaxKeys", profile->GetConfigurationMaxKeys);
    cJSON_AddNumberToObject(root, "HeartbeatInterval", profile->HeartbeatInterval);

    cJSON_AddNumberToObject(root, "LightIntensity", profile->LightIntensity);
    cJSON_AddBoolToObject(root, "LocalAuthorizeOffline", profile->LocalAuthorizeOffline);
    cJSON_AddBoolToObject(root, "LocalPreAuthorize", profile->LocalPreAuthorize);
    cJSON_AddNumberToObject(root, "MaxEnergyOnInvalidId", profile->MaxEnergyOnInvalidId);

    cJSON_AddStringToObject(root, "MeterValuesAlignedData", Measurand_toString(buff, profile->MeterValuesAlignedData));
    cJSON_AddNumberToObject(root, "MeterValuesAlignedDataMaxLength", profile->MeterValuesAlignedDataMaxLength);
    cJSON_AddStringToObject(root, "MeterValuesSampledData", Measurand_toString(buff, profile->MeterValuesSampledData));
    cJSON_AddNumberToObject(root, "MeterValuesSampledDataMaxLength", profile->MeterValuesSampledDataMaxLength);
    cJSON_AddNumberToObject(root, "MeterValueSampleInterval", profile->MeterValueSampleInterval);
    cJSON_AddNumberToObject(root, "MinimumStatusDuration", profile->MinimumStatusDuration);

    cJSON_AddNumberToObject(root, "NumberOfConnectors", profile->NumberOfConnectors);
    cJSON_AddNumberToObject(root, "ResetRetries", profile->ResetRetries);
    cJSON_AddBoolToObject(root, "StopTransactionOnEVSideDisconnect", profile->StopTransactionOnEVSideDisconnect);
    cJSON_AddBoolToObject(root, "StopTransactionOnInvalidId", profile->StopTransactionOnInvalidId);

    cJSON_AddStringToObject(root, "StopTxnAlignedData", Measurand_toString(buff, profile->StopTxnAlignedData));
    cJSON_AddNumberToObject(root, "StopTxnAlignedDataMaxLength", profile->StopTxnAlignedDataMaxLength);
    cJSON_AddStringToObject(root, "StopTxnSampledData", Measurand_toString(buff, profile->StopTxnSampledData));
    cJSON_AddNumberToObject(root, "StopTxnSampledDataMaxLength", profile->StopTxnSampledDataMaxLength);
    cJSON_AddStringToObject(root, "SupportedFeatureProfiles", profile->SupportedFeatureProfiles);
    cJSON_AddNumberToObject(root, "SupportedFeatureProfilesMaxLength", profile->SupportedFeatureProfilesMaxLength);

    cJSON_AddNumberToObject(root, "TransactionMessageAttempts", profile->TransactionMessageAttempts);
    cJSON_AddNumberToObject(root, "TransactionMessageRetryInterval", profile->TransactionMessageRetryInterval);
    cJSON_AddBoolToObject(root, "UnlockConnectorOnEVSideDisconnect", profile->UnlockConnectorOnEVSideDisconnect);
    cJSON_AddNumberToObject(root, "WebSocketPingInterval", profile->WebSocketPingInterval);

    cJSON_AddStringToObject(root, "AuthorizationKey", profile->AuthorizationKey);
    cJSON_AddNumberToObject(root, "AuthorizationCacheMaxLength", profile->AuthorizationCacheMaxLength);
}

static void ocpp16_save_LocalAuthListManagementProfile(cJSON* root, LocalAuthListManagemenProfile_t* profile)
{
    cJSON_AddBoolToObject(root, "LocalAuthListEnabled", profile->LocalAuthListEnabled);
    cJSON_AddNumberToObject(root, "LocalAuthListMaxLength", profile->LocalAuthListMaxLength);
    cJSON_AddNumberToObject(root, "SendLocalListMaxLength", profile->SendLocalListMaxLength);
}

static void ocpp16_save_ReservationProfile(cJSON* root, ReservationProfile_t* profile)
{
    cJSON_AddBoolToObject(root, "ReserveConnectorZeroSupported", profile->ReserveConnectorZeroSupported);
}

static void ocpp16_save_SmartChargingProfile(cJSON* root, SmartChargingProfile_t* profile)
{
    char buff[32];
    cJSON_AddNumberToObject(root, "ChargeProfileMaxStackLevel", profile->ChargeProfileMaxStackLevel);
    cJSON_AddStringToObject(root, "ChargingScheduleAllowedChargingRateUnit", ChargingScheduleAllowedChargingRateUnit_toString(buff, profile->ChargingScheduleAllowedChargingRateUnit));
    cJSON_AddNumberToObject(root, "ChargingScheduleMaxPeriods", profile->ChargingScheduleMaxPeriods);
    cJSON_AddBoolToObject(root, "ConnectorSwitch3to1PhaseSupported", profile->ConnectorSwitch3to1PhaseSupported);
    cJSON_AddNumberToObject(root, "MaxChargingProfilesInstalled", profile->MaxChargingProfilesInstalled);
}

void ocpp16_configkey_save(ocpp16_configkeys_t* configkeys, const char* filename)
{
    char cmdbuff[128];
    char strtime[128];
    // sprintf(cmdbuff, "mv %s %s_%s", filename, filename, format_UTCTime(strtime, time(NULL)));
    // system(cmdbuff);

    cJSON* root = cJSON_CreateObject();
    cJSON* profile1 = cJSON_CreateObject();
    cJSON* profile2 = cJSON_CreateObject();
    cJSON* profile3 = cJSON_CreateObject();
    cJSON* profile4 = cJSON_CreateObject();
    ocpp16_save_CoreProfile(profile1, &configkeys->coreProfile);
    ocpp16_save_LocalAuthListManagementProfile(profile2, &configkeys->localAuthListManagemenProfile);
    ocpp16_save_ReservationProfile(profile3, &configkeys->reservationProfile);
    ocpp16_save_SmartChargingProfile(profile4, &configkeys->smartChargingProfile);
    cJSON_AddItemToObject(root, "CoreProfile", profile1);
    cJSON_AddItemToObject(root, "LocalAuthListManagementProfile", profile2);
    cJSON_AddItemToObject(root, "ReservationProfile", profile3);
    cJSON_AddItemToObject(root, "SmartChargingProfile", profile4);
    char *fbuff = cJSON_Print(root);
    FILE *fp = fopen(filename, "wb");
    if (fp != NULL)
    {
        fwrite(fbuff, 1, strlen(fbuff), fp);
        fclose(fp);
    }else{
        printd("open(%s, wb) failed.\n", filename);
    }
    free(fbuff);
    cJSON_Delete(root);
}

void ocpp16_configkey_load(ocpp16_configkeys_t* configkeys, const char* filename)
{
    int i;
    FILE* file = fopen(filename, "rb");
    if (file)
    {
        fseek(file, 0, SEEK_END);
        int flen = ftell(file);
        fseek(file, 0, SEEK_SET);
        char* fbuff = malloc(flen+1);
        if (fbuff)
        {
            fread(fbuff, flen, 1, file);
            cJSON* root = cJSON_Parse(fbuff);
            if (root)
            {
                ocpp16_load_CoreProfile(cJSON_GetObjectItem(root, "CoreProfile"), &configkeys->coreProfile);
                ocpp16_load_LocalAuthListManagementProfile(cJSON_GetObjectItem(root, "LocalAuthListManagementProfile"), &configkeys->localAuthListManagemenProfile);
                ocpp16_load_ReservationProfile(cJSON_GetObjectItem(root, "ReservationProfile"), &configkeys->reservationProfile);
                ocpp16_load_SmartChargingProfile(cJSON_GetObjectItem(root, "SmartChargingProfile"), &configkeys->smartChargingProfile);
                cJSON_Delete(root);
            }else{
                printd("cJSON_Parse(%p) failed.\n", fbuff);
            }
            free(fbuff);
        }else{
            printd("malloc(%d) failed.\n", flen);
        }
        fclose(file);
    }else{
        printd("open(%s, rb) failed.\n", filename);
    }
}

void ocpp16_configkey_init(void)
{
    memset(&ocpp16_configkeys, 0, sizeof(ocpp16_configkeys_t));
    ocpp16_configkey_load(&ocpp16_configkeys, "/home/root/app_run/etc/ocpp16_configkey.json");
    {
        ocpp16_configkeys.coreProfile.NumberOfConnectors = ccu_config.GUN_Numb;
        printi("sync config.GUN_Numb(%d) to ocpp16_configkeys.coreProfile.NumberOfConnectors\n", ccu_config.GUN_Numb);
    }
}

void ocpp16_configkey_exit(void)
{
    ocpp16_configkey_save(&ocpp16_configkeys, "/home/root/app_run/etc/ocpp16_configkey.json");
    memset(&ocpp16_configkeys, 0, sizeof(ocpp16_configkeys_t));
}


