#include "ocpp16_encode.h"
#include "uuid.h"

int ocpp16_encode_Call(uint8_t *stream, CidString37_t reqUUID, const char *method, uint8_t *payload, int length)
{
    uint32_t msgType = 2;
    int idx = 0;
    idx += sprintf((char *)&stream[idx], "%s", "[");
    idx += sprintf((char *)&stream[idx], "%d, ", msgType);
    idx += sprintf((char *)&stream[idx], "\"%s\", ", makeUUID(reqUUID));
    idx += sprintf((char *)&stream[idx], "\"%s\", ", method);

    if (payload)
    {
        idx += snprintf((char *)&stream[idx], length + 2, "%s", (char *)payload);
    }
    else
    {
        idx += sprintf((char *)&stream[idx], "%s", "{");
        idx += sprintf((char *)&stream[idx], "%s", "}");
    }

    idx += sprintf((char *)&stream[idx], "%s", "]");
    return idx;
}

int ocpp16_encode_AuthorizeReq(uint8_t *stream, CidString37_t msgUUID, AuthorizeReq_t *req)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"idTag\":\"%s\" ", req->idTag);
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_Call(stream, msgUUID, "Authorize", payplad, idx);
}

int ocpp16_encode_BootNotificationReq(uint8_t *stream, CidString37_t msgUUID, BootNotificationReq_t *req)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"chargePointModel\":\"%s\", ", req->chargePointModel);
    if (strlen(req->chargeBoxSerialNumber) > 0) idx += sprintf((char *)&payplad[idx], "\"chargeBoxSerialNumber\":\"%s\", ", req->chargeBoxSerialNumber);
    if (strlen(req->chargePointSerialNumber) > 0) idx += sprintf((char *)&payplad[idx], "\"chargePointSerialNumber\":\"%s\", ", req->chargePointSerialNumber);
    if (strlen(req->firmwareVersion) > 0) idx += sprintf((char *)&payplad[idx], "\"firmwareVersion\":\"%s\", ", req->firmwareVersion);
    if (strlen(req->iccid) > 0) idx += sprintf((char *)&payplad[idx], "\"iccid\":\"%s\", ", req->iccid);
    if (strlen(req->imsi) > 0) idx += sprintf((char *)&payplad[idx], "\"imsi\":\"%s\", ", req->imsi);
    if (strlen(req->imsi) > 0) idx += sprintf((char *)&payplad[idx], "\"meterSerialNumber\":\"%s\", ", req->meterSerialNumber);
    if (strlen(req->meterSerialNumber) > 0) idx += sprintf((char *)&payplad[idx], "\"meterType\":\"%s\"", req->meterType);
    idx += sprintf((char *)&payplad[idx], "\"chargePointVendor\":\"%s\"", req->chargePointVendor);
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_Call(stream, msgUUID, "BootNotification", payplad, idx);
}

int ocpp16_encode_DataTransferData(uint8_t *stream, const char *name, DataTransferData_t *req)
{
    int idx = 0, i;
    idx += sprintf((char *)&stream[idx], "\"%s\":{", name);

    idx += sprintf((char *)&stream[idx], "\"type\":\"%s\", ", req->type);
    idx += sprintf((char *)&stream[idx], "\"timeZone\":\"%s\", ", req->timeZone);
    idx += sprintf((char *)&stream[idx], "\"minAllowBalance\":\"%s\", ", req->minAllowBalance);

    idx += sprintf((char *)&stream[idx], "\"%s\":[ ", "price");
    for (i = 0; i < req->price.count; i++)
    {
        idx += sprintf((char *)&stream[idx], "%s", "{");
        idx += sprintf((char *)&stream[idx], "\"startTime\":\"%s\", ", req->price.items[i].startTime);
        idx += sprintf((char *)&stream[idx], "\"stopTime\":\"%s\", ", req->price.items[i].stopTime);
        idx += sprintf((char *)&stream[idx], "\"electrPrice\":\"%s\", ", req->price.items[i].electrPrice);
        idx += sprintf((char *)&stream[idx], "\"servicePrice\":\"%s\", ", req->price.items[i].servicePrice);
        idx += sprintf((char *)&stream[idx], "\"durationPrice\":\"%s\"", req->price.items[i].durationPrice);

        if (i==req->price.count-1)
            idx += sprintf((char *)&stream[idx], "%s", "}");
        else
            idx += sprintf((char *)&stream[idx], "%s", "}, ");
    }
    idx += sprintf((char *)&stream[idx], "%s", "]");
    idx += sprintf((char *)&stream[idx], "%s", "}");
}

int ocpp16_encode_DataTransferReq(uint8_t *stream, CidString37_t msgUUID, DataTransferReq_t *req)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"vendorId\":\"%s\", ", req->vendorId);
    idx += sprintf((char *)&payplad[idx], "\"messageId\":\"%s\", ", req->messageId);
    idx += ocpp16_encode_DataTransferData(&payplad[idx], "data", &req->data);
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_Call(stream, msgUUID, "DataTransfer", payplad, idx);
}

static const char *DiagnosticsStatus_toString(DiagnosticsStatus_t status)
{
    switch (status)
    {
    case DiagnosticsStatus_Idle:
        return "Idle";
    case DiagnosticsStatus_Uploaded:
        return "Uploaded";
    case DiagnosticsStatus_UploadFailed:
        return "UploadFailed";
    case DiagnosticsStatus_Uploading:
        return "Uploading";
    default:
        return "Idle";
    }
}

int ocpp16_encode_DiagnosticsStatusNotificationReq(uint8_t *stream, CidString37_t msgUUID, DiagnosticsStatusNotificationReq_t *req)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", DiagnosticsStatus_toString(req->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_Call(stream, msgUUID, "DiagnosticsStatusNotification", payplad, idx);
}

static const char *FirmwareStatus_toString(FirmwareStatus_t status)
{
    switch (status)
    {
    case FirmwareStatus_Downloaded:
        return "Downloaded";
    case FirmwareStatus_DownloadFailed:
        return "DownloadFailed";
    case FirmwareStatus_Downloading:
        return "Downloading";
    case FirmwareStatus_Idle:
        return "Idle";
    case FirmwareStatus_InstallationFailed:
        return "InstallationFailed";
    case FirmwareStatus_Installing:
        return "Installing";
    case FirmwareStatus_Installed:
        return "Installed";
    default:
        return "Idle";
    }
}

int ocpp16_encode_FirmwareStatusNotificationReq(uint8_t *stream, CidString37_t msgUUID, FirmwareStatusNotificationReq_t *req)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", FirmwareStatus_toString(req->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_Call(stream, msgUUID, "FirmwareStatusNotification", payplad, idx);
}

int ocpp16_encode_HeartbeatReq(uint8_t *stream, CidString37_t msgUUID, HeartbeatReq_t *req)
{
    return ocpp16_encode_Call(stream, msgUUID, "Heartbeat", NULL, 0);
}

static const char *ValueFormat_toString(ValueFormat_t status)
{
    switch (status)
    {
    case ValueFormat_Raw:
        return "Raw";
    case ValueFormat_SignedData:
        return "SignedData";
    default:
        return "Raw";
    }
}

static const char *ReadingContext_toString(ReadingContext_t status)
{
    switch (status)
    {
    case ReadingContext_Interruption_Begin:
        return "Interruption.Begin";
    case ReadingContext_Interruption_End:
        return "Interruption.End";
    case ReadingContext_Other:
        return "Other";
    case ReadingContext_Sample_Clock:
        return "Sample.Clock";
    case ReadingContext_Sample_Periodic:
        return "Sample.Periodic";
    case ReadingContext_Transaction_Begin:
        return "Transaction.Begin";
    case ReadingContext_Transaction_End:
        return "Transaction.End";
    case ReadingContext_Trigger:
        return "Trigger";
    default:
        return "Other";
    }
}

static const char *Measurand_toString(Measurand_t status)
{
    switch (status)
    {
    case Measurand_Current_Export:
        return "Current.Export";
    case Measurand_Current_Import:
        return "Current.Import";
    case Measurand_Current_Offered:
        return "Current.Offered";
    case Measurand_Energy_Active_Export_Register:
        return "Energy.Active.Export.Register";
    case Measurand_Energy_Active_Import_Register:
        return "Energy.Active.Import.Register";
    case Measurand_Energy_Reactive_Export_Register:
        return "Energy.Reactive.Export.Register";
    case Measurand_Energy_Reactive_Import_Register:
        return "Reactive.Import.Register";

    case Measurand_Energy_Active_Export_Interval:
        return "Active.Export.Interval";
    case Measurand_Energy_Active_Import_Interval:
        return "Active.Import.Interval";
    case Measurand_Energy_Reactive_Export_Interval:
        return "Reactive.Export.Interval";
    case Measurand_Energy_Reactive_Import_Interval:
        return "Reactive.Import.Interval";
    case Measurand_Frequency:
        return "Frequency";
    case Measurand_Power_Active_Export:
        return "Power.Active.Export";
    case Measurand_Power_Active_Import:
        return "Power.Active.Import";

    case Measurand_Power_Factor:
        return "Power.Factor";
    case Measurand_Power_Offered:
        return "Power.Offered";
    case Measurand_Power_Reactive_Export:
        return "Reactive.Export";
    case Measurand_Power_Reactive_Import:
        return "Reactive.Import";
    case Measurand_RPM:
        return "RPM";
    case Measurand_SoC:
        return "SoC";
    case Measurand_Temperature:
        return "Temperature";
    case Measurand_Voltage:
        return "Voltage";
    default:
        return "SoC";
    }
}
static const char *Phase_toString(Phase_t status)
{
    switch (status)
    {
    case Phase_L1:
        return "L1";
    case Phase_L2:
        return "L2";
    case Phase_L3:
        return "L3";
    case Phase_N:
        return "N";
    case Phase_L1_N:
        return "L1-N";
    case Phase_L2_N:
        return "L2-N";
    case Phase_L3_N:
        return "L3-N";
    case Phase_L1_L2:
        return "L1-L2";
    case Phase_L2_L3:
        return "L2-L3";
    case Phase_L3_L1:
        return "L3-L1";
    default:
        return "N";
    }
}
static const char *Location_toString(Location_t status)
{
    switch (status)
    {
    case Location_Body:
        return "Body";
    case Location_Cable:
        return "Cable";
    case Location_EV:
        return "EV";
    case Location_Inlet:
        return "Inlet";
    case Location_Outlet:
        return "Outlet";
    default:
        return "EV";
    }
}
static const char *UnitOfMeasure_toString(UnitOfMeasure_t status)
{
    switch (status)
    {
    case UnitOfMeasure_Wh:
        return "Wh";
    case UnitOfMeasure_kWh:
        return "kWh";
    case UnitOfMeasure_varh:
        return "varh";
    case UnitOfMeasure_kvarh:
        return "kvarh";
    case UnitOfMeasure_W:
        return "W";
    case UnitOfMeasure_kW:
        return "kW";
    case UnitOfMeasure_VA:
        return "VA";
    case UnitOfMeasure_kVA:
        return "kVA";
    case UnitOfMeasure_var:
        return "var";
    case UnitOfMeasure_kvar:
        return "kvar";
    case UnitOfMeasure_A:
        return "A";
    case UnitOfMeasure_V:
        return "V";
    case UnitOfMeasure_Celsius:
        return "Celsius";
    case UnitOfMeasure_Fahrenheit:
        return "Fahrenheit";
    case UnitOfMeasure_K:
        return "K";
    case UnitOfMeasure_Percent:
        return "Percent";
    default:
        return "kWh";
    }
}
static int ocpp16_encode_MeterValue(uint8_t *stream, const char *name, MeterValue_t *req)
{
    int idx = 0, i, j;
    idx += sprintf((char *)&stream[idx], "\"%s\":[", name);
    for (i = 0; i < req->count; i++)
    {
        idx += sprintf((char *)&stream[idx], "%s", "{");
        idx += sprintf((char *)&stream[idx], "\"timestamp\":\"%s\", ", req->items[i].timestamp);

        idx += sprintf((char *)&stream[idx], "\"%s\":[", "sampledValue");
        for (j = 0; j < req->items[i].sampledvalue.count; j++)
        {
            idx += sprintf((char *)&stream[idx], "%s", "{");
            idx += sprintf((char *)&stream[idx], "\"value\":\"%s\", ", req->items[i].sampledvalue.items[j].value);
            
            if (req->items[i].sampledvalue.items[j].context!=0)
            idx += sprintf((char *)&stream[idx], "\"context\":\"%s\", ", ReadingContext_toString(req->items[i].sampledvalue.items[j].context));

            if (req->items[i].sampledvalue.items[j].format!=0)
            idx += sprintf((char *)&stream[idx], "\"format\":\"%s\", ", ValueFormat_toString(req->items[i].sampledvalue.items[j].format));

            if (req->items[i].sampledvalue.items[j].measurand!=0)
            idx += sprintf((char *)&stream[idx], "\"measurand\":\"%s\", ", Measurand_toString(req->items[i].sampledvalue.items[j].measurand));

            if (req->items[i].sampledvalue.items[j].phase!=0)
            idx += sprintf((char *)&stream[idx], "\"phase\":\"%s\", ", Phase_toString(req->items[i].sampledvalue.items[j].phase));

            if (req->items[i].sampledvalue.items[j].location!=0)
            idx += sprintf((char *)&stream[idx], "\"location\":\"%s\", ", Location_toString(req->items[i].sampledvalue.items[j].location));

            idx += sprintf((char *)&stream[idx], "\"unit\":\"%s\"", UnitOfMeasure_toString(req->items[i].sampledvalue.items[j].unit));
            if (j==req->items[i].sampledvalue.count-1)
                idx += sprintf((char *)&stream[idx], "%s", "}");
            else
                idx += sprintf((char *)&stream[idx], "%s", "}, ");
        }
        idx += sprintf((char *)&stream[idx], "%s", "]");
        if (i==req->count-1)
            idx += sprintf((char *)&stream[idx], "%s", "}");
        else
            idx += sprintf((char *)&stream[idx], "%s", "}, ");
    }
    idx += sprintf((char *)&stream[idx], "%s", "]");
    return idx;
}

int ocpp16_encode_MeterValuesReq(uint8_t *stream, CidString37_t msgUUID, MeterValuesReq_t *req)
{
    int idx = 0;
    uint8_t payplad[4096];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"connectorId\":%d, ", req->connectorId);
    idx += sprintf((char *)&payplad[idx], "\"transactionId\":%d, ", req->transactionId);
    idx += ocpp16_encode_MeterValue((char *)&payplad[idx], "meterValue", &req->meterValue);
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_Call(stream, msgUUID, "MeterValues", payplad, idx);
}

int ocpp16_encode_StartTransactionReq(uint8_t *stream, CidString37_t msgUUID, StartTransactionReq_t *req)
{
    int idx = 0;
    char strbuff[128];
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"connectorId\":%d, ", req->connectorId);
    idx += sprintf((char *)&payplad[idx], "\"idTag\":\"%s\", ", req->idTag);
    idx += sprintf((char *)&payplad[idx], "\"meterStart\":%d, ", req->meterStart);
    if (req->reservationId != 0) idx += sprintf((char *)&payplad[idx], "\"reservationId\":%d, ", req->reservationId);
	idx += sprintf((char *)&payplad[idx], "\"timestamp\":\"%s\"", format_UTCTime(strbuff, req->timestamp));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_Call(stream, msgUUID, "StartTransaction", payplad, idx);
}

static const char *ChargePointErrorCode_toString(ChargePointErrorCode_t status)
{
    switch (status)
    {
    case ChargePointErrorCode_ConnectorLockFailure:
        return "ConnectorLockFailure";
    case ChargePointErrorCode_EVCommunicationError:
        return "EVCommunicationError";
    case ChargePointErrorCode_GroundFailure:
        return "GroundFailure";
    case ChargePointErrorCode_HighTemperature:
        return "HighTemperature";

    case ChargePointErrorCode_InternalError:
        return "InternalError";
    case ChargePointErrorCode_LocalListConflict:
        return "LocalListConflict";
    case ChargePointErrorCode_NoError:
        return "NoError";
    case ChargePointErrorCode_OtherError:
        return "OtherError";

    case ChargePointErrorCode_OverCurrentFailure:
        return "OverCurrentFailure";
    case ChargePointErrorCode_OverVoltage:
        return "OverVoltage";
    case ChargePointErrorCode_PowerMeterFailure:
        return "PowerMeterFailure";
    case ChargePointErrorCode_PowerSwitchFailure:
        return "PowerSwitchFailure";

    case ChargePointErrorCode_ReaderFailure:
        return "ReaderFailure";
    case ChargePointErrorCode_ResetFailure:
        return "ResetFailure";
    case ChargePointErrorCode_UnderVoltage:
        return "UnderVoltage";
    case ChargePointErrorCode_WeakSignal:
        return "WeakSignal";
    default:
        return "NoError";
    }
}

static const char *ChargePointStatus_toString(ChargePointStatus_t status)
{
    switch (status)
    {
    case ChargePointStatus_Available:
        return "Available";
    case ChargePointStatus_Preparing:
        return "Preparing";
    case ChargePointStatus_Charging:
        return "Charging";
    case ChargePointStatus_SuspendedEVSE:
        return "SuspendedEVSE";
    case ChargePointStatus_SuspendedEV:
        return "SuspendedEV";
    case ChargePointStatus_Finishing:
        return "Finishing";
    case ChargePointStatus_Reserved:
        return "Reserved";
    case ChargePointStatus_Unavailable:
        return "Unavailable";
    case ChargePointStatus_Faulted:
        return "Faulted";
    default:
        return "Faulted";
    }
}

int ocpp16_encode_StatusNotificationReq(uint8_t *stream, CidString37_t msgUUID, StatusNotificationReq_t *req)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"connectorId\":%d, ", req->connectorId);
    if (strlen(req->info) > 0) idx += sprintf((char *)&payplad[idx], "\"info\":\"%s\", ", req->info);
    if (strlen(req->timestamp) > 0) idx += sprintf((char *)&payplad[idx], "\"timestamp\":\"%s\", ", req->timestamp);
    if (strlen(req->vendorId) > 0) idx += sprintf((char *)&payplad[idx], "\"vendorId\":\"%s\", ", req->vendorId);
    if (strlen(req->vendorErrorCode) > 0) idx += sprintf((char *)&payplad[idx], "\"vendorErrorCode\":\"%s\", ", req->vendorErrorCode);
    idx += sprintf((char *)&payplad[idx], "\"errorCode\":\"%s\", ", ChargePointErrorCode_toString(req->errorCode));
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\" ", ChargePointStatus_toString(req->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_Call(stream, msgUUID, "StatusNotification", payplad, idx);
}

static const char *Reason_toString(Reason_t status)
{
    switch (status)
    {
    case Reason_DeAuthorized:
        return "DeAuthorized";
    case Reason_EmergencyStop:
        return "EmergencyStop";
    case Reason_EVDisconnected:
        return "EVDisconnected";
    case Reason_HardReset:
        return "HardReset";
    case Reason_Local:
        return "Local";
    case Reason_Other:
        return "Other";
    case Reason_PowerLoss:
        return "PowerLoss";
    case Reason_Reboot:
        return "Reboot";
    case Reason_Remote:
        return "Remote";
    case Reason_SoftReset:
        return "SoftReset";
    case Reason_UnlockCommand:
        return "UnlockCommand";
    default:
        return "Other";
    }
}

int ocpp16_encode_StopTransactionReq(uint8_t *stream, CidString37_t msgUUID, StopTransactionReq_t *req)
{
    int idx = 0;
    char strbuff[128];
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    if (strlen(req->idTag) > 0) idx += sprintf((char *)&payplad[idx], "\"idTag\":\"%s\", ", req->idTag);
    idx += sprintf((char *)&payplad[idx], "\"meterStop\":%d, ", req->meterStop);
    idx += sprintf((char *)&payplad[idx], "\"timestamp\":\"%s\", ", format_UTCTime(strbuff, req->timestamp));
    if (req->reason!=0) idx += sprintf((char *)&payplad[idx], "\"reason\":\"%s\", ", Reason_toString(req->reason));
    if (req->transactiondata_used == 1)
    {
        idx += sprintf((char *)&payplad[idx], "\"transactionId\":%d, ", req->transactionId);
        idx += ocpp16_encode_MeterValue((char *)&payplad[idx], "transactiondata", &req->transactiondata);
    }else{
        idx += sprintf((char *)&payplad[idx], "\"transactionId\":%d", req->transactionId);
    }
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_Call(stream, msgUUID, "StopTransaction", payplad, idx);
}

//-----------------------------------------------------------------------------------------------------

static const char *ErrorCode_toString(ErrorCode_t errorCode)
{
    switch (errorCode)
    {
    case ErrorCode_NotImplemented:
        return "NotImplemented";
    case ErrorCode_NotSupported:
        return "NotSupported";
    case ErrorCode_InternalError:
        return "InternalError";
    case ErrorCode_ProtocolError:
        return "ProtocolError";
    case ErrorCode_SecurityError:
        return "SecurityError";
    case ErrorCode_FormatViolation:
        return "FormatViolation";
    case ErrorCode_PropertyConstraintViolation:
        return "PropertyConstraintViolation";
    case ErrorCode_OccurenceConstraintViolation:
        return "OccurenceConstraintViolation";
    case ErrorCode_TypeConstraintViolation:
        return "TypeConstraintViolation";
    case ErrorCode_GenericError:
        return "GenericError";
    default:
        return "GenericError";
    }
}

static const char *ErrorCode_description(ErrorCode_t errorCode)
{
    switch (errorCode)
    {
    case ErrorCode_NotImplemented:
        return "Requested Action is recognized but not supported by the receiver";
    case ErrorCode_NotSupported:
        return "Requested Action is recognized but not supported by the receiver";
    case ErrorCode_InternalError:
        return "An internal error occurred and the receiver was not able to process the requested Action successfully";
    case ErrorCode_ProtocolError:
        return "Payload for Action is incomplete";
    case ErrorCode_SecurityError:
        return "During the processing of Action a security issue occurred preventing receiver from completing the Action successfully";
    case ErrorCode_FormatViolation:
        return "Payload for Action is syntactically incorrect or not conform the PDU structure for Action";
    case ErrorCode_PropertyConstraintViolation:
        return "Payload is syntactically correct but at least one field contains an invalid value";
    case ErrorCode_OccurenceConstraintViolation:
        return "Payload for Action is syntactically correct but at least one of the fields violates occurence constraints";
    case ErrorCode_TypeConstraintViolation:
        return "Payload for Action is syntactically correct but at least one of the fields violates data type constraints (e.g. “somestring”: 12)";
    case ErrorCode_GenericError:
        return "Any other error not covered by the previous ones";
    default:
        return "Any other error not covered by the previous ones";
    }
}

int ocpp16_encode_ErrorResult(uint8_t *stream, CidString37_t msgUUID, ErrorCode_t errorCode, const char *errorDetails)
{
    int idx = 0;
    uint32_t msgType = 4;
    if (errorCode < 0) errorCode = -errorCode;
    idx += sprintf((char *)&stream[idx], "%s", "[");
    idx += sprintf((char *)&stream[idx], "%d, ", msgType);
    idx += sprintf((char *)&stream[idx], "\"%s\", ", msgUUID);
    idx += sprintf((char *)&stream[idx], "\"%s\", ", ErrorCode_toString(errorCode));
    idx += sprintf((char *)&stream[idx], "\"%s\", ", ErrorCode_description(errorCode));

    if (errorDetails)
    {
        idx += sprintf((char *)&stream[idx], "%s", "{");
        idx += sprintf((char *)&stream[idx], "\"tcharge\":\"%s\"", errorDetails);
        idx += sprintf((char *)&stream[idx], "%s", "}");
    }
    else
    {
        idx += sprintf((char *)&stream[idx], "%s", "{");
        idx += sprintf((char *)&stream[idx], "%s", "}");
    }

    idx += sprintf((char *)&stream[idx], "%s", "]");
    return idx;
}

int ocpp16_encode_CallResult(uint8_t *stream, CidString37_t reqUUID, uint8_t *payload, int length)
{
    uint32_t msgType = 3;
    int idx = 0;
    idx += sprintf((char *)&stream[idx], "%s", "[");
    idx += sprintf((char *)&stream[idx], "%d, ", msgType);
    idx += sprintf((char *)&stream[idx], "\"%s\", ", reqUUID);

    if (payload)
    {
        idx += snprintf((char *)&stream[idx], length + 2, "%s", (char *)payload);
    }
    else
    {
        idx += sprintf((char *)&stream[idx], "%s", "{");
        idx += sprintf((char *)&stream[idx], "%s", "}");
    }

    idx += sprintf((char *)&stream[idx], "%s", "]");
    return idx;
}

static const char *CancelReservationStatus_toString(CancelReservationStatus_t status)
{
    switch (status)
    {
    case CancelReservationStatus_Accepted:
        return "Accepted";
    case CancelReservationStatus_Rejected:
        return "Rejected";
    default:
        return "Rejected";
    }
}
int ocpp16_encode_CancelReservationConf(uint8_t *stream, CidString37_t msgUUID, CancelReservationConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", CancelReservationStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

static const char *AvailabilityStatus_toString(AvailabilityStatus_t status)
{
    switch (status)
    {
    case AvailabilityStatus_Accepted:
        return "Accepted";
    case AvailabilityStatus_Rejected:
        return "Rejected";
    case AvailabilityStatus_Scheduled:
        return "Scheduled";
    default:
        return "Rejected";
    }
}
int ocpp16_encode_ChangeAvailabilityConf(uint8_t *stream, CidString37_t msgUUID, ChangeAvailabilityConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", AvailabilityStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

static const char *ConfigurationStatus_toString(ConfigurationStatus_t status)
{
    switch (status)
    {
    case ConfigurationStatus_Accepted:
        return "Accepted";
    case ConfigurationStatus_Rejected:
        return "Rejected";
    case ConfigurationStatus_RebootRequired:
        return "RebootRequired";
    case ConfigurationStatus_NotSupported:
        return "NotSupported";
    default:
        return "Rejected";
    }
}
int ocpp16_encode_ChangeConfigurationConf(uint8_t *stream, CidString37_t msgUUID, ChangeConfigurationConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", ConfigurationStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

static const char *ClearCacheStatus_toString(ClearCacheStatus_t status)
{
    switch (status)
    {
    case ClearCacheStatus_Accepted:
        return "Accepted";
    case ClearCacheStatus_Rejected:
        return "Rejected";
    default:
        return "Rejected";
    }
}
int ocpp16_encode_ClearCacheConf(uint8_t *stream, CidString37_t msgUUID, ClearCacheConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", ClearCacheStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

static const char *ClearChargingProfileStatus_toString(ClearChargingProfileStatus_t status)
{
    switch (status)
    {
    case ClearChargingProfileStatus_Accepted:
        return "Accepted";
    case ClearChargingProfileStatus_Unknown:
        return "Unknown";
    default:
        return "Unknown";
    }
}
int ocpp16_encode_ClearChargingProfileConf(uint8_t *stream, CidString37_t msgUUID, ClearChargingProfileConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", ClearChargingProfileStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

static const char *DataTransferStatus_toString(DataTransferStatus_t status)
{
    switch (status)
    {
    case DataTransferStatus_Accepted:
        return "Accepted";
    case DataTransferStatus_Rejected:
        return "Rejected";
    case DataTransferStatus_UnknownMessageId:
        return "UnknownMessageId";
    case DataTransferStatus_UnknownVendorId:
        return "UnknownVendorId";
    default:
        return "Rejected";
    }
}
int ocpp16_encode_DataTransferConf(uint8_t *stream, CidString37_t msgUUID, DataTransferConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\", ", DataTransferStatus_toString(conf->status));
    idx += ocpp16_encode_DataTransferData((char *)&payplad[idx], "data", &conf->data);
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

static const char *ChargingRateUnitType_toString(ChargingRateUnitType_t status)
{
    switch (status)
    {
    case ChargingRateUnitType_W:
        return "W";
    case ChargingRateUnitType_A:
        return "A";
    default:
        return "NULL";
    }
}
int ocpp16_encode_ChargingSchedule(uint8_t *stream, const char *name, ChargingSchedule_t *conf)
{
    int idx = 0, i;
    idx += sprintf((char *)&stream[idx], "\"%s\":{", name);
    idx += sprintf((char *)&stream[idx], "\"duration\":%d, ", conf->duration);
    idx += sprintf((char *)&stream[idx], "\"startTime\":\"%s\", ", conf->startTime);
    idx += sprintf((char *)&stream[idx], "\"chargingRateUnit\":\"%s\", ", ChargingRateUnitType_toString(conf->chargingRateUnit));
    idx += sprintf((char *)&stream[idx], "\"%s\":[ ", "chargingSchedulePeriod");
    for (i = 0; i < conf->chargingSchedulePeriod.count; i++)
    {
        idx += sprintf((char *)&stream[idx], "%s", "{");
        idx += sprintf((char *)&stream[idx], "\"startPeriod\":%d, ", conf->chargingSchedulePeriod.items[i].startPeriod);
        idx += sprintf((char *)&stream[idx], "\"limit\":%f, ", conf->chargingSchedulePeriod.items[i].limit);
        idx += sprintf((char *)&stream[idx], "\"numberPhases\":%d", conf->chargingSchedulePeriod.items[i].numberPhases);

        if (i==conf->chargingSchedulePeriod.count-1)
            idx += sprintf((char *)&stream[idx], "%s", "}");
        else
            idx += sprintf((char *)&stream[idx], "%s", "}, ");
    }
    idx += sprintf((char *)&stream[idx], "%s", "]");
    idx += sprintf((char *)&stream[idx], "\"minChargingRate\":\"%f\"", conf->minChargingRate);
    idx += sprintf((char *)&stream[idx], "%s", "}");
    return idx;
}

static const char *GetCompositeScheduleStatus_toString(GetCompositeScheduleStatus_t status)
{
    switch (status)
    {
    case GetCompositeScheduleStatus_Accepted:
        return "Accepted";
    case GetCompositeScheduleStatus_Rejected:
        return "Rejected";
    default:
        return "Rejected";
    }
}
int ocpp16_encode_GetCompositeScheduleConf(uint8_t *stream, CidString37_t msgUUID, GetCompositeScheduleConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\", ", GetCompositeScheduleStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "\"connectorId\":%d, ", conf->connectorId);
    idx += sprintf((char *)&payplad[idx], "\"scheduleStart\":\"%s\", ", conf->scheduleStart);
    idx += ocpp16_encode_ChargingSchedule((char *)&payplad[idx], "chargingSchedule", &conf->chargingSchedule);
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}
static int isdigitstr(char *str)
{
    return (strspn(str, "0123456789")==strlen(str));
}
int ocpp16_encode_GetConfigurationConf(uint8_t *stream, CidString37_t msgUUID, GetConfigurationConf_t *conf)
{
    int idx = 0, i, j;
    uint8_t payplad[4096];
    idx += sprintf((char *)&payplad[idx], "%s", "{");

    idx += sprintf((char *)&payplad[idx], "\"%s\":[ \n", "configurationKey");
    for (i = 0; i < conf->configurationKey.count; i++)
    {
        idx += sprintf((char *)&payplad[idx], "%s", "{");
        idx += sprintf((char *)&payplad[idx], "\"key\":\"%s\", ", conf->configurationKey.items[i].key);
        if (conf->configurationKey.items[i].readonly == true)
        {
            idx += sprintf((char *)&payplad[idx], "\"readonly\":%s, ", "true");
        }else{
            idx += sprintf((char *)&payplad[idx], "\"readonly\":%s, ", "false");
        }
        if (strlen(conf->configurationKey.items[i].value)>0)
        {
            idx += sprintf((char *)&payplad[idx], "\"value\":\"%s\"", conf->configurationKey.items[i].value);
        }
        if (i==conf->configurationKey.count-1)
            idx += sprintf((char *)&payplad[idx], "%s", "}\n");
        else
            idx += sprintf((char *)&payplad[idx], "%s", "},\n");
    }
    idx += sprintf((char *)&payplad[idx], "%s", "]");

    if (conf->unknownKey.count > 0)
    {
        idx += sprintf((char *)&payplad[idx], ", \"%s\":[ ", "unknownKey");
        for (i = 0; i < conf->unknownKey.count; i++)
        {
            if (i==conf->unknownKey.count-1)
                idx += sprintf((char *)&payplad[idx], "\"%s\"", conf->unknownKey.items[i]);
            else
                idx += sprintf((char *)&payplad[idx], "\"%s\", ", conf->unknownKey.items[i]);
        }
        idx += sprintf((char *)&payplad[idx], "%s", "]");
    }

    idx += sprintf((char *)&payplad[idx], "%s", "}");

    printv("idx:%d\n", idx);

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

int ocpp16_encode_GetDiagnosticsConf(uint8_t *stream, CidString37_t msgUUID, GetDiagnosticsConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    if (strncmp(conf->fileName, "NULL", strlen(conf->fileName)) != 0)
        idx += sprintf((char *)&payplad[idx], "\"fileName\":\"%s\"", conf->fileName);
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

int ocpp16_encode_GetLocalListVersionConf(uint8_t *stream, CidString37_t msgUUID, GetLocalListVersionConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"listVersion\":%d", conf->listVersion);
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

static const char *RemoteStartStopStatus_toString(RemoteStartStopStatus_t status)
{
    switch (status)
    {
    case RemoteStartStopStatus_Accepted:
        return "Accepted";
    case RemoteStartStopStatus_Rejected:
        return "Rejected";
    default:
        return "Rejected";
    }
}
int ocpp16_encode_RemoteStartTransactionConf(uint8_t *stream, CidString37_t msgUUID, RemoteStartTransactionConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", RemoteStartStopStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

int ocpp16_encode_RemoteStopTransactionConf(uint8_t *stream, CidString37_t msgUUID, RemoteStopTransactionConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", RemoteStartStopStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

static const char *ReservationStatus_toString(ReservationStatus_t status)
{
    switch (status)
    {
    case ReservationStatus_Accepted:
        return "Accepted";
    case ReservationStatus_Faulted:
        return "Faulted";
    case ReservationStatus_Occupied:
        return "Occupied";
    case ReservationStatus_Rejected:
        return "Rejected";
    case ReservationStatus_Unavailable:
        return "Unavailable";
    default:
        return "Rejected";
    }
}
int ocpp16_encode_ReserveNowConf(uint8_t *stream, CidString37_t msgUUID, ReserveNowConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", ReservationStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

static const char *ResetStatus_toString(ResetStatus_t status)
{
    switch (status)
    {
    case ResetStatus_Accepted:
        return "Accepted";
    case ResetStatus_Rejected:
        return "Rejected";
    default:
        return "Rejected";
    }
}
int ocpp16_encode_ResetConf(uint8_t *stream, CidString37_t msgUUID, ResetConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", ResetStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

static const char *UpdateStatus_toString(UpdateStatus_t status)
{
    switch (status)
    {
    case UpdateStatus_Accepted:
        return "Accepted";
    case UpdateStatus_Failed:
        return "Failed";
    case UpdateStatus_NotSupported:
        return "NotSupported";
    case UpdateStatus_VersionMismatch:
        return "VersionMismatch";
    default:
        return "Failed";
    }
}
int ocpp16_encode_SendLocalListConf(uint8_t *stream, CidString37_t msgUUID, SendLocalListConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", UpdateStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

static const char *ChargingProfileStatus_toString(ChargingProfileStatus_t status)
{
    switch (status)
    {
    case ChargingProfileStatus_Accepted:
        return "Accepted";
    case ChargingProfileStatus_Rejected:
        return "Rejected";
    case ChargingProfileStatus_NotSupported:
        return "NotSupported";
    default:
        return "NotSupported";
    }
}
int ocpp16_encode_SetChargingProfileConf(uint8_t *stream, CidString37_t msgUUID, SetChargingProfileConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", ChargingProfileStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

static const char *TriggerMessageStatus_toString(TriggerMessageStatus_t status)
{
    switch (status)
    {
    case TriggerMessageStatus_Accepted:
        return "Accepted";
    case TriggerMessageStatus_Rejected:
        return "Rejected";
    case TriggerMessageStatus_NotImplemented:
        return "NotImplemented";
    default:
        return "NotImplemented";
    }
}
int ocpp16_encode_TriggerMessageConf(uint8_t *stream, CidString37_t msgUUID, TriggerMessageConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", TriggerMessageStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

static const char *UnlockStatus_toString(UnlockStatus_t status)
{
    switch (status)
    {
    case UnlockStatus_Unlocked:
        return "Unlocked";
    case UnlockStatus_UnlockFailed:
        return "UnlockFailed";
    case UnlockStatus_NotSupported:
        return "NotSupported";
    default:
        return "NotSupported";
    }
}
int ocpp16_encode_UnlockConnectorConf(uint8_t *stream, CidString37_t msgUUID, UnlockConnectorConf_t *conf)
{
    int idx = 0;
    uint8_t payplad[2048];
    idx += sprintf((char *)&payplad[idx], "%s", "{");
    idx += sprintf((char *)&payplad[idx], "\"status\":\"%s\"", UnlockStatus_toString(conf->status));
    idx += sprintf((char *)&payplad[idx], "%s", "}");

    return ocpp16_encode_CallResult(stream, msgUUID, payplad, idx);
}

int ocpp16_encode_UpdateFirmwareConf(uint8_t *stream, CidString37_t msgUUID, UpdateFirmwareConf_t *conf)
{
    return ocpp16_encode_CallResult(stream, msgUUID, NULL, 0);
}
