// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2023 chargebyte GmbH
// Copyright (C) 2023 Contributors to EVerest

#include <cbv2g/din/din_msgDefDatatypes.h>

#include <inttypes.h>
#include <string.h>

#include "din_server.hpp"
#include "log.hpp"
#include "tools.hpp"
#include "v2g_ctx.hpp"
#include "v2g_server.hpp"

#define SASCHEDULETUPLEID 1

/**
 * @brief load_din_physical_value This function copies the physical values from source to destination struct.
 * @param phy_value_dest is the destination struct.
 * @param phy_value_source is the source struct.
 */
void load_din_physical_value(struct din_PhysicalValueType* const phy_value_dest,
                             struct iso2_PhysicalValueType* const phy_value_source) {
    phy_value_dest->Multiplier = phy_value_source->Multiplier;
    phy_value_dest->Value = phy_value_source->Value;
    phy_value_dest->Unit_isUsed = 1;

    switch (phy_value_source->Unit) {
    case iso2_unitSymbolType_h:
        phy_value_dest->Unit = din_unitSymbolType_h;
        break;
    case iso2_unitSymbolType_m:
        phy_value_dest->Unit = din_unitSymbolType_m;
        break;
    case iso2_unitSymbolType_s:
        phy_value_dest->Unit = din_unitSymbolType_s;
        break;
    case iso2_unitSymbolType_A:
        phy_value_dest->Unit = din_unitSymbolType_A;
        break;
    case iso2_unitSymbolType_V:
        phy_value_dest->Unit = din_unitSymbolType_V;
        break;
    case iso2_unitSymbolType_W:
        phy_value_dest->Unit = din_unitSymbolType_W;
        break;
    case iso2_unitSymbolType_Wh:
        phy_value_dest->Unit = din_unitSymbolType_Wh;
        break;
    default:
        phy_value_dest->Unit = din_unitSymbolType_h;
        break;
    }
}

/*!
 * \brief din_validate_state This function checks whether the received message is expected and valid at this
 * point in the communication sequence state machine. The current V2G msg type must be set with the current V2G msg
 * state. \param state is the current state of the charging session \param current_v2g_msg is the current handled V2G
 * message \param state of the actual session. \return Returns a din_ResponseCode with sequence error if current_v2g_msg
 * is not expected, otherwise OK.
 */
static din_responseCodeType din_validate_state(int state, enum V2gMsgTypeId current_v2g_msg) {
    /* if the request type has enabled (= set) bit in expected_requests, then this
     * message is ok at this stage; otherwise reject it.
     */
    return (din_states[state].allowed_requests & (1 << current_v2g_msg)) ? din_responseCodeType_OK
                                                                         : din_responseCodeType_FAILED_SequenceError;
}

namespace utils {
/*!
 * \brief din_validate_response_code This function checks if an external error has occurred (sequence error, user
 * abort)... ). \param din_response_code is a pointer to the current response code. The value will be modified if an
 * external error has occurred. \param conn the structure with the external error information. \return Returns the next
 * v2g-event.
 */
v2g_event din_validate_response_code(din_responseCodeType* const din_response_code, struct v2g_connection const* conn) {
    enum v2g_event nextEvent = V2G_EVENT_NO_EVENT;
    din_responseCodeType response_code_tmp;

    if (conn->ctx->is_connection_terminated == true) {
        dlog(DLOG_LEVEL_ERROR, "Connection is terminated. Abort charging");
        return V2G_EVENT_TERMINATE_CONNECTION;
    }

    /* If MQTT user abort or emergency shutdown has occurred */
    if ((conn->ctx->stop_hlc == true) || (conn->ctx->intl_emergency_shutdown == true)) {
        *din_response_code = din_responseCodeType_FAILED;
    }

    /* [V2G-DC-390]: at this point we must check whether the given request is valid at this step;
     * the idea is that we catch this error in each function below to respond with a valid
     * encoded message; note, that the handler functions below must not access v2g_session in
     * error path, since it might not be set, yet!
     */
    response_code_tmp = din_validate_state(conn->ctx->state, conn->ctx->current_v2g_msg);
    *din_response_code = (response_code_tmp >= din_responseCodeType_FAILED) ? response_code_tmp : *din_response_code;

    /* [V2G-DC-391]: check whether the session id matches the expected one of the active session */
    *din_response_code = ((conn->ctx->current_v2g_msg != V2G_SESSION_SETUP_MSG) &&
                          (conn->ctx->evse_v2g_data.session_id != conn->ctx->ev_v2g_data.received_session_id))
                             ? din_responseCodeType_FAILED_UnknownSession
                             : *din_response_code;

    if ((conn->ctx->terminate_connection_on_failed_response == true) &&
        (*din_response_code >= din_responseCodeType_FAILED)) {
        nextEvent = V2G_EVENT_SEND_AND_TERMINATE; // [V2G-DC-665]
    }

    /* log failed response code message */
    if (*din_response_code >= din_responseCodeType_FAILED &&
        *din_response_code <= din_responseCodeType_FAILED_WrongEnergyTransferType) {
        dlog(DLOG_LEVEL_ERROR, "Failed response code detected for message \"%s\", error: %s",
             v2g_msg_type[conn->ctx->current_v2g_msg], dinResponse[*din_response_code]);
    }

    return nextEvent;
}
} // namespace utils

/*!
 * \brief publish_DIN_DcEvStatus This function is a helper function to publish EVStatusType.
 * \param ctx is a pointer to the V2G context.
 * \param din_ev_status the structure the holds the EV Status elements.
 */
static void publish_DIN_DcEvStatus(struct v2g_context* ctx, const struct din_DC_EVStatusType& din_ev_status) {
    if ((ctx->ev_v2g_data.din_dc_ev_status.EVErrorCode != din_ev_status.EVErrorCode) ||
        (ctx->ev_v2g_data.din_dc_ev_status.EVReady != din_ev_status.EVReady) ||
        (ctx->ev_v2g_data.din_dc_ev_status.EVRESSSOC != din_ev_status.EVRESSSOC)) {
        ctx->ev_v2g_data.din_dc_ev_status.EVErrorCode = din_ev_status.EVErrorCode;
        ctx->ev_v2g_data.din_dc_ev_status.EVReady = din_ev_status.EVReady;
        ctx->ev_v2g_data.din_dc_ev_status.EVRESSSOC = din_ev_status.EVRESSSOC;

        types::iso15118::DcEvStatus ev_status;
        ev_status.dc_ev_error_code = static_cast<types::iso15118::DcEvErrorCode>(din_ev_status.EVErrorCode);
        ev_status.dc_ev_ready = din_ev_status.EVReady;
        ev_status.dc_ev_ress_soc = static_cast<float>(din_ev_status.EVRESSSOC);
        ctx->p_charger->publish_dc_ev_status(ev_status);
    }
}

//=============================================
//             Publishing request msg
//=============================================

/*!
 * \brief publish_din_service_discovery_req This function publishes the din_ServiceDiscoveryReqType message to the MQTT
 * interface. \param ctx is the V2G context. \param din_ServiceDiscoveryReqType is the request message.
 */
static void
publish_din_service_discovery_req(struct v2g_context* ctx,
                                  struct din_ServiceDiscoveryReqType const* const v2g_service_discovery_req) {
    // V2G values that can be published: ServiceCategory, ServiceScope
}

/*!
 * \brief publish_din_service_payment_selection_req This function publishes the din_ServicePaymentSelectionReqType
 * message to the MQTT interface.
 * \param ctx is the V2G context.
 * \param v2g_payment_service_selection_req is the request message.
 */
static void publish_din_service_payment_selection_req(
    struct v2g_context* ctx, struct din_ServicePaymentSelectionReqType const* const v2g_payment_service_selection_req) {
    // V2G values that can be published: selected_payment_option, SelectedServiceList
}

/*!
 * \brief publish_din_charge_parameter_discovery_req This function publishes the din_ChargeParameterDiscoveryReqType
 * message to the MQTT interface.
 * \param ctx is the V2G context.
 * \param v2g_charge_parameter_discovery_req is the request message.
 */
static void publish_din_charge_parameter_discovery_req(
    struct v2g_context* ctx,
    struct din_ChargeParameterDiscoveryReqType const* const v2g_charge_parameter_discovery_req) {
    // V2G values that can be published: DC_EVChargeParameter, MaxEntriesSAScheduleTuple
    types::iso15118::EnergyTransferMode transfer_mode{};

    switch (v2g_charge_parameter_discovery_req->EVRequestedEnergyTransferType) {
    case din_EVRequestedEnergyTransferType_AC_single_phase_core:
        transfer_mode = types::iso15118::EnergyTransferMode::AC_single_phase_core;
        break;
    case din_EVRequestedEnergyTransferType_AC_three_phase_core:
        transfer_mode = types::iso15118::EnergyTransferMode::AC_three_phase_core;
        break;
    case din_EVRequestedEnergyTransferType_DC_core:
        transfer_mode = types::iso15118::EnergyTransferMode::DC_core;
        break;
    case din_EVRequestedEnergyTransferType_DC_extended:
        transfer_mode = types::iso15118::EnergyTransferMode::DC_extended;
        break;
    case din_EVRequestedEnergyTransferType_DC_combo_core:
        transfer_mode = types::iso15118::EnergyTransferMode::DC_combo_core;
        break;
    case din_EVRequestedEnergyTransferType_DC_unique:
        transfer_mode = types::iso15118::EnergyTransferMode::DC_unique;
        break;
    default:
        dlog(DLOG_LEVEL_WARNING, "Unable to convert RequestedEnergyTransferType to EnergyTransferMode: %d",
             v2g_charge_parameter_discovery_req->EVRequestedEnergyTransferType);
    }

    ctx->p_charger->publish_requested_energy_transfer_mode(transfer_mode);
    if (v2g_charge_parameter_discovery_req->DC_EVChargeParameter_isUsed == (unsigned int)1) {

        if (v2g_charge_parameter_discovery_req->DC_EVChargeParameter.EVEnergyCapacity_isUsed == (unsigned int)1) {
            ctx->p_charger->publish_dc_ev_energy_capacity(calc_physical_value(
                v2g_charge_parameter_discovery_req->DC_EVChargeParameter.EVEnergyCapacity.Value,
                v2g_charge_parameter_discovery_req->DC_EVChargeParameter.EVEnergyCapacity.Multiplier));
        }
        if (v2g_charge_parameter_discovery_req->DC_EVChargeParameter.EVEnergyRequest_isUsed == (unsigned int)1) {
            ctx->p_charger->publish_dc_ev_energy_request(calc_physical_value(
                v2g_charge_parameter_discovery_req->DC_EVChargeParameter.EVEnergyRequest.Value,
                v2g_charge_parameter_discovery_req->DC_EVChargeParameter.EVEnergyRequest.Multiplier));
        }
        if (v2g_charge_parameter_discovery_req->DC_EVChargeParameter.FullSOC_isUsed == (unsigned int)1) {
            ctx->p_charger->publish_dc_full_soc(v2g_charge_parameter_discovery_req->DC_EVChargeParameter.FullSOC);
        }
        if (v2g_charge_parameter_discovery_req->DC_EVChargeParameter.BulkSOC_isUsed == (unsigned int)1) {
            ctx->p_charger->publish_dc_bulk_soc(v2g_charge_parameter_discovery_req->DC_EVChargeParameter.BulkSOC);
        }

        float evMaximumCurrentLimit = calc_physical_value(
            v2g_charge_parameter_discovery_req->DC_EVChargeParameter.EVMaximumCurrentLimit.Value,
            v2g_charge_parameter_discovery_req->DC_EVChargeParameter.EVMaximumCurrentLimit.Multiplier);
        float evMaximumPowerLimit = calc_physical_value(
            v2g_charge_parameter_discovery_req->DC_EVChargeParameter.EVMaximumPowerLimit.Value,
            v2g_charge_parameter_discovery_req->DC_EVChargeParameter.EVMaximumPowerLimit.Multiplier);
        float evMaximumVoltageLimit = calc_physical_value(
            v2g_charge_parameter_discovery_req->DC_EVChargeParameter.EVMaximumVoltageLimit.Value,
            v2g_charge_parameter_discovery_req->DC_EVChargeParameter.EVMaximumVoltageLimit.Multiplier);
        publish_dc_ev_maximum_limits(
            ctx, evMaximumCurrentLimit, (unsigned int)1, evMaximumPowerLimit,
            v2g_charge_parameter_discovery_req->DC_EVChargeParameter.EVMaximumPowerLimit_isUsed, evMaximumVoltageLimit,
            (unsigned int)1);
        publish_DIN_DcEvStatus(ctx, v2g_charge_parameter_discovery_req->DC_EVChargeParameter.DC_EVStatus);
    }
}

/*!
 * \brief publish_din_power_delivery_req This function publishes the din_PowerDeliveryReqType message to the MQTT
 * interface. \param ctx is the V2G context. \param din_power_delivery_req is the request message.
 */
static void publish_din_power_delivery_req(struct v2g_context* ctx,
                                           struct din_PowerDeliveryReqType const* const v2g_power_delivery_req) {
    // V2G values that can be published: ReadyToChargeState
    if (v2g_power_delivery_req->DC_EVPowerDeliveryParameter_isUsed == (unsigned int)1) {
        ctx->p_charger->publish_dc_charging_complete(
            v2g_power_delivery_req->DC_EVPowerDeliveryParameter.ChargingComplete);
        if (v2g_power_delivery_req->DC_EVPowerDeliveryParameter.BulkChargingComplete_isUsed == (unsigned int)1) {
            ctx->p_charger->publish_dc_bulk_charging_complete(
                v2g_power_delivery_req->DC_EVPowerDeliveryParameter.BulkChargingComplete);
        }
        publish_DIN_DcEvStatus(ctx, v2g_power_delivery_req->DC_EVPowerDeliveryParameter.DC_EVStatus);
    }
}

/*!
 * \brief publish_din_precharge_req This function publishes the din_PreChargeReqType message to the MQTT interface.
 * \param ctx is the V2G context.
 * \param v2g_precharge_req is the request message.
 */
static void publish_din_precharge_req(struct v2g_context* ctx,
                                      struct din_PreChargeReqType const* const v2g_precharge_req) {
    publish_dc_ev_target_voltage_current(
        ctx,
        calc_physical_value(v2g_precharge_req->EVTargetVoltage.Value, v2g_precharge_req->EVTargetVoltage.Multiplier),
        calc_physical_value(v2g_precharge_req->EVTargetCurrent.Value, v2g_precharge_req->EVTargetCurrent.Multiplier));
    publish_DIN_DcEvStatus(ctx, v2g_precharge_req->DC_EVStatus);
}

/*!
 * \brief publish_din_current_demand_req This function publishes the din_CurrentDemandReqType message to the MQTT
 * interface. \param ctx is the V2G context. \param v2g_current_demand_req is the request message.
 */
static void publish_din_current_demand_req(struct v2g_context* ctx,
                                           struct din_CurrentDemandReqType const* const v2g_current_demand_req) {
    if ((v2g_current_demand_req->BulkChargingComplete_isUsed == (unsigned int)1) &&
        (ctx->ev_v2g_data.bulk_charging_complete != v2g_current_demand_req->BulkChargingComplete)) {
        ctx->p_charger->publish_dc_bulk_charging_complete(v2g_current_demand_req->BulkChargingComplete);
        ctx->ev_v2g_data.bulk_charging_complete = v2g_current_demand_req->BulkChargingComplete;
    }
    if (ctx->ev_v2g_data.charging_complete != v2g_current_demand_req->ChargingComplete) {
        ctx->p_charger->publish_dc_charging_complete(v2g_current_demand_req->ChargingComplete);
        ctx->ev_v2g_data.charging_complete = v2g_current_demand_req->ChargingComplete;
    }

    publish_DIN_DcEvStatus(ctx, v2g_current_demand_req->DC_EVStatus);

    publish_dc_ev_target_voltage_current(ctx,
                                         calc_physical_value(v2g_current_demand_req->EVTargetVoltage.Value,
                                                             v2g_current_demand_req->EVTargetVoltage.Multiplier),
                                         calc_physical_value(v2g_current_demand_req->EVTargetCurrent.Value,
                                                             v2g_current_demand_req->EVTargetCurrent.Multiplier));

    float evMaximumCurrentLimit = calc_physical_value(v2g_current_demand_req->EVMaximumCurrentLimit.Value,
                                                      v2g_current_demand_req->EVMaximumCurrentLimit.Multiplier);
    float evMaximumPowerLimit = calc_physical_value(v2g_current_demand_req->EVMaximumPowerLimit.Value,
                                                    v2g_current_demand_req->EVMaximumPowerLimit.Multiplier);
    float evMaximumVoltageLimit = calc_physical_value(v2g_current_demand_req->EVMaximumVoltageLimit.Value,
                                                      v2g_current_demand_req->EVMaximumVoltageLimit.Multiplier);
    publish_dc_ev_maximum_limits(ctx, evMaximumCurrentLimit, v2g_current_demand_req->EVMaximumCurrentLimit_isUsed,
                                 evMaximumPowerLimit, v2g_current_demand_req->EVMaximumPowerLimit_isUsed,
                                 evMaximumVoltageLimit, v2g_current_demand_req->EVMaximumVoltageLimit_isUsed);

    float v2g_dc_ev_remaining_time_to_full_soc =
        calc_physical_value(v2g_current_demand_req->RemainingTimeToFullSoC.Value,
                            v2g_current_demand_req->RemainingTimeToFullSoC.Multiplier);
    float v2g_dc_ev_remaining_time_to_bulk_soc =
        calc_physical_value(v2g_current_demand_req->RemainingTimeToBulkSoC.Value,
                            v2g_current_demand_req->RemainingTimeToBulkSoC.Multiplier);

    publish_dc_ev_remaining_time(
        ctx, v2g_dc_ev_remaining_time_to_full_soc, v2g_current_demand_req->RemainingTimeToFullSoC_isUsed,
        v2g_dc_ev_remaining_time_to_bulk_soc, v2g_current_demand_req->RemainingTimeToBulkSoC_isUsed);
}

//=============================================
//             Request Handling
//=============================================

namespace states {

/*!
 * \brief handle_iso_session_setup This function handles the din_session_setup msg pair. It analyzes the request msg and
 * fills the response msg. The request and response msg based on the open V2G structures. This structures must be
 * provided within the \c conn structure. [V2G-DC-436]
 * \param conn holds the structure with the V2G msg pair.
 * \return Returns the next V2G-event.
 */
enum v2g_event handle_din_session_setup(struct v2g_connection* conn) {
    struct din_SessionSetupReqType* req = &conn->exi_in.dinEXIDocument->V2G_Message.Body.SessionSetupReq;
    struct din_SessionSetupResType* res = &conn->exi_out.dinEXIDocument->V2G_Message.Body.SessionSetupRes;
    enum v2g_event nextEvent = V2G_EVENT_NO_EVENT;

    /* format EVCC ID */
    const auto mac_addr = to_mac_address_str(&req->EVCCID.bytes[0], req->EVCCID.bytesLen);

    conn->ctx->p_charger->publish_evcc_id(mac_addr); // publish EVCC ID

    dlog(DLOG_LEVEL_INFO, "SessionSetupReq.EVCCID: %s",
         (mac_addr.empty()) ? "(zero length provided)" : mac_addr.c_str());

    /* Now fill the evse response message */
    res->ResponseCode = din_responseCodeType_OK_NewSessionEstablished; // [V2G-DC-393]

    /* Check and init session id */
    /* If the customer doesen't select a session id, generate one */
    srand((unsigned int)time(NULL));
    if (conn->ctx->evse_v2g_data.session_id == (uint64_t)0) {
        generate_random_data(&conn->ctx->evse_v2g_data.session_id, 8);
        dlog(DLOG_LEVEL_INFO, "No session_id found. Generating random session id.");
    }

    dlog(DLOG_LEVEL_INFO, "Created new session with id 0x%016" PRIx64, be64toh(conn->ctx->evse_v2g_data.session_id));

    res->EVSEID.bytesLen = std::min((int)conn->ctx->evse_v2g_data.evse_id.bytesLen, iso2_EVSEID_CHARACTER_SIZE);
    memcpy(res->EVSEID.bytes, conn->ctx->evse_v2g_data.evse_id.bytes, res->EVSEID.bytesLen);

    res->DateTimeNow_isUsed = conn->ctx->evse_v2g_data.date_time_now_is_used;
    res->DateTimeNow = time(NULL);

    /* Check the current response code and check if no external error has occurred */
    nextEvent = utils::din_validate_response_code(&res->ResponseCode, conn);

    /* Set next expected req msg */
    conn->ctx->state = WAIT_FOR_SERVICEDISCOVERY; // [V2G-DC-438]

    return nextEvent;
}

/*!
 * \brief handle_din_service_discovery This function handles the din service discovery msg pair. It analyzes the request
 * msg and fills the response msg. The request and response msg based on the open V2G structures. This structures must
 * be provided within the \c conn structure. [V2G-DC-440]
 * \param conn is the structure with the V2G msg pair.
 * \return Returns the next V2G-event.
 */
enum v2g_event handle_din_service_discovery(struct v2g_connection* conn) {
    struct din_ServiceDiscoveryReqType* req = &conn->exi_in.dinEXIDocument->V2G_Message.Body.ServiceDiscoveryReq;
    struct din_ServiceDiscoveryResType* res = &conn->exi_out.dinEXIDocument->V2G_Message.Body.ServiceDiscoveryRes;
    enum v2g_event nextEvent = V2G_EVENT_NO_EVENT;

    /* At first, publish the received EV request message to the MQTT interface */
    publish_din_service_discovery_req(conn->ctx, req);

    /* Now fill the evse response message */
    res->ResponseCode = din_responseCodeType_OK; // [V2G-DC-388]

    if ((conn->ctx->evse_v2g_data.charge_service.SupportedEnergyTransferMode.EnergyTransferMode.array[0] !=
         iso2_EnergyTransferModeType_DC_core) &&
        (conn->ctx->evse_v2g_data.charge_service.SupportedEnergyTransferMode.EnergyTransferMode.array[0]) !=
            iso2_EnergyTransferModeType_DC_extended) {
        conn->ctx->evse_v2g_data.charge_service.SupportedEnergyTransferMode.EnergyTransferMode.array[0] =
            iso2_EnergyTransferModeType_DC_extended;
        dlog(DLOG_LEVEL_WARNING, "Selected EnergyTransferType is not supported in DIN 70121. Correcting value of field "
                                 "SupportedEnergyTransferType0 to 'DC_extended'");
    }

    res->ChargeService.ServiceTag.ServiceID =
        conn->ctx->evse_v2g_data.charge_service.ServiceID;               // ID of the charge service
    res->ChargeService.ServiceTag.ServiceName_isUsed = (unsigned int)0;  // [V2G-DC-628] Shall not be used in DIN 70121
    res->ChargeService.ServiceTag.ServiceScope_isUsed = (unsigned int)0; // [V2G-DC-629] Shall not be used in DIN 70121
    res->ChargeService.ServiceTag.ServiceCategory = din_serviceCategoryType_EVCharging; // Is fixed
    res->ChargeService.FreeService = conn->ctx->evse_v2g_data.charge_service.FreeService;
    res->ChargeService.EnergyTransferType =
        (din_EVSESupportedEnergyTransferType)
            conn->ctx->evse_v2g_data.charge_service.SupportedEnergyTransferMode.EnergyTransferMode.array[0];

    res->ServiceList_isUsed = 0; // Shall not be used in DIN 70121

    // In the scope of DIN 70121 only ExternalPayment shall be used.
    res->PaymentOptions.PaymentOption.array[0] = din_paymentOptionType_ExternalPayment;
    res->PaymentOptions.PaymentOption.arrayLen = 1;

    /* Check the current response code and check if no external error has occurred */
    nextEvent = utils::din_validate_response_code(&res->ResponseCode, conn);

    /* Set next expected req msg */
    conn->ctx->state = WAIT_FOR_PAYMENTSERVICESELECTION; // [V2G-DC-441]

    return nextEvent;
}

} // namespace states

/*!
 * \brief handle_din_service_discovery This function handles the din service payment selection msg pair. It analyzes the
 * request msg and fills the response msg. The request and response msg based on the open V2G structures. This
 * structures must be provided within the \c conn structure. [V2G-DC-443]
 * \param conn is the structure with the V2G msg pair.
 * \return Returns the next V2G-event.
 */
static enum v2g_event handle_din_service_payment_selection(struct v2g_connection* conn) {
    struct din_ServicePaymentSelectionReqType* req =
        &conn->exi_in.dinEXIDocument->V2G_Message.Body.ServicePaymentSelectionReq;
    struct din_ServicePaymentSelectionResType* res =
        &conn->exi_out.dinEXIDocument->V2G_Message.Body.ServicePaymentSelectionRes;
    enum v2g_event nextEvent = V2G_EVENT_NO_EVENT;

    /* At first, publish the received EV request message to the customer MQTT interface */
    publish_din_service_payment_selection_req(conn->ctx, req);

    /* Now fill the evse response message */
    res->ResponseCode = din_responseCodeType_OK; // [V2G-DC-388]

    res->ResponseCode = (req->SelectedPaymentOption != din_paymentOptionType_ExternalPayment)
                            ? din_responseCodeType_FAILED_PaymentSelectionInvalid
                            : res->ResponseCode; // [V2G-DC-395]

    res->ResponseCode = (req->SelectedServiceList.SelectedService.arrayLen == (uint16_t)1 &&
                         (req->SelectedServiceList.SelectedService.array[0].ServiceID ==
                          conn->ctx->evse_v2g_data.charge_service.ServiceID))
                            ? res->ResponseCode
                            : din_responseCodeType_FAILED_ServiceSelectionInvalid; // [V2G-DC-396] [V2G-DC-635] (List
                                                                                   // shall be limited to 1)

    /* Check the current response code and check if no external error has occurred */
    nextEvent = utils::din_validate_response_code(&res->ResponseCode, conn);

    /* Set next expected req msg */
    conn->ctx->state = WAIT_FOR_AUTHORIZATION; // [V2G-DC-444]

    return nextEvent;
}

/*!
 * \brief handle_din_service_discovery This function handles the din contract authentication msg pair. It analyzes the
 * request msg and fills the response msg. The request and response msg based on the open V2G structures. This
 * structures must be provided within the \c conn structure. [V2G-DC-494]
 * \param conn is the structure with the V2G msg pair.
 * \return Returns the next V2G-event.
 */
enum v2g_event states::handle_din_contract_authentication(struct v2g_connection* conn) {
    struct din_ContractAuthenticationResType* res =
        &conn->exi_out.dinEXIDocument->V2G_Message.Body.ContractAuthenticationRes;
    enum v2g_event nextEvent = V2G_EVENT_NO_EVENT;

    /* Fill the EVSE response message */
    if (conn->ctx->session.authorization_rejected == true) {
        res->ResponseCode = din_responseCodeType_FAILED;
    } else {
        res->ResponseCode = din_responseCodeType_OK; // [V2G-DC-388]
    }
    res->EVSEProcessing = (conn->ctx->evse_v2g_data.evse_processing[PHASE_AUTH] == (uint8_t)0)
                              ? din_EVSEProcessingType_Finished
                              : din_EVSEProcessingType_Ongoing;

    /* Check the current response code and check if no external error has occurred */
    nextEvent = utils::din_validate_response_code(&res->ResponseCode, conn);

    /* Set next expected req msg */
    conn->ctx->state = (res->EVSEProcessing == din_EVSEProcessingType_Ongoing)
                           ? WAIT_FOR_AUTHORIZATION
                           : WAIT_FOR_CHARGEPARAMETERDISCOVERY; // [V2G-DC-444], [V2G-DC-497]

    return nextEvent;
}

/*!
 * \brief handle_din_charge_parameter This function handles the din charge parameters msg pair. It analyzes the request
 * msg and fills the response msg. The request and response msg based on the open V2G structures. This structures must
 * be provided within the \c conn structure. [V2G-DC-445]
 * \param conn is the structure with the v2g msg pair.
 * \return Returns the next V2G-event.
 */
static enum v2g_event handle_din_charge_parameter(struct v2g_connection* conn) {
    struct din_ChargeParameterDiscoveryReqType* req =
        &conn->exi_in.dinEXIDocument->V2G_Message.Body.ChargeParameterDiscoveryReq;
    struct din_ChargeParameterDiscoveryResType* res =
        &conn->exi_out.dinEXIDocument->V2G_Message.Body.ChargeParameterDiscoveryRes;
    enum v2g_event nextEvent = V2G_EVENT_NO_EVENT;

    /* At first, publish the received EV request message to the customer MQTT interface */
    publish_din_charge_parameter_discovery_req(conn->ctx, req);

    /* Now fill the EVSE response message */
    res->ResponseCode = din_responseCodeType_OK; // [V2G-DC-388]
    res->AC_EVSEChargeParameter_isUsed = 0u;

    if (((req->EVRequestedEnergyTransferType != din_EVRequestedEnergyTransferType_DC_core) &&
         (req->EVRequestedEnergyTransferType != din_EVRequestedEnergyTransferType_DC_extended)) ||
        conn->ctx->evse_v2g_data.charge_service.SupportedEnergyTransferMode.EnergyTransferMode.array[0] !=
            (iso2_EnergyTransferModeType)req->EVRequestedEnergyTransferType) {
        res->ResponseCode = din_responseCodeType_FAILED_WrongEnergyTransferType; // [V2G-DC-397] Failed reponse code is
                                                                                 // logged at the end of the function
    } else {
        log_selected_energy_transfer_type((int)req->EVRequestedEnergyTransferType);
    }

    res->ResponseCode = (req->AC_EVChargeParameter_isUsed == (unsigned int)1)
                            ? din_responseCodeType_FAILED_WrongChargeParameter
                            : res->ResponseCode; // [V2G-DC-398]

    /* DC_EVSEChargeParameter */
    res->DC_EVSEChargeParameter.DC_EVSEStatus.EVSEIsolationStatus =
        (din_isolationLevelType)conn->ctx->evse_v2g_data.evse_isolation_status;
    res->DC_EVSEChargeParameter.DC_EVSEStatus.EVSEIsolationStatus_isUsed =
        conn->ctx->evse_v2g_data.evse_isolation_status_is_used;

    res->DC_EVSEChargeParameter.DC_EVSEStatus.EVSENotification =
        (din_EVSENotificationType)conn->ctx->evse_v2g_data.evse_notification;
    res->DC_EVSEChargeParameter.DC_EVSEStatus.EVSEStatusCode =
        (true == conn->ctx->intl_emergency_shutdown)
            ? din_DC_EVSEStatusCodeType_EVSE_EmergencyShutdown
            : (din_DC_EVSEStatusCodeType)conn->ctx->evse_v2g_data.evse_status_code[PHASE_PARAMETER];
    res->DC_EVSEChargeParameter.DC_EVSEStatus.NotificationMaxDelay = conn->ctx->evse_v2g_data.notification_max_delay;

    if (conn->ctx->evse_v2g_data.evse_current_regulation_tolerance_is_used) {
        load_din_physical_value(&res->DC_EVSEChargeParameter.EVSECurrentRegulationTolerance,
                                &conn->ctx->evse_v2g_data.evse_current_regulation_tolerance);
        res->DC_EVSEChargeParameter.EVSECurrentRegulationTolerance.Unit_isUsed = (unsigned int)1;
    }
    res->DC_EVSEChargeParameter.EVSECurrentRegulationTolerance_isUsed =
        conn->ctx->evse_v2g_data.evse_current_regulation_tolerance_is_used;

    load_din_physical_value(&res->DC_EVSEChargeParameter.EVSEEnergyToBeDelivered,
                            &conn->ctx->evse_v2g_data.evse_energy_to_be_delivered);
    res->DC_EVSEChargeParameter.EVSEEnergyToBeDelivered_isUsed =
        conn->ctx->evse_v2g_data.evse_energy_to_be_delivered_is_used;

    load_din_physical_value(&res->DC_EVSEChargeParameter.EVSEMaximumCurrentLimit,
                            &conn->ctx->evse_v2g_data.power_capabilities.max_current);

    load_din_physical_value(&res->DC_EVSEChargeParameter.EVSEMaximumPowerLimit,
                            &conn->ctx->evse_v2g_data.power_capabilities.max_power);
    res->DC_EVSEChargeParameter.EVSEMaximumPowerLimit_isUsed =
        conn->ctx->evse_v2g_data.evse_maximum_power_limit_is_used;

    load_din_physical_value(&res->DC_EVSEChargeParameter.EVSEMaximumVoltageLimit,
                            &conn->ctx->evse_v2g_data.power_capabilities.max_voltage);

    load_din_physical_value(&res->DC_EVSEChargeParameter.EVSEMinimumCurrentLimit,
                            &conn->ctx->evse_v2g_data.power_capabilities.min_current);

    load_din_physical_value(&res->DC_EVSEChargeParameter.EVSEMinimumVoltageLimit,
                            &conn->ctx->evse_v2g_data.power_capabilities.min_voltage);

    load_din_physical_value(&res->DC_EVSEChargeParameter.EVSEPeakCurrentRipple,
                            &conn->ctx->evse_v2g_data.evse_peak_current_ripple);
    res->DC_EVSEChargeParameter_isUsed = (unsigned int)1;

    // res->EVSEChargeParameter.noContent
    res->EVSEChargeParameter_isUsed = (unsigned int)0;
    res->EVSEProcessing = ((uint8_t)0 == conn->ctx->evse_v2g_data.evse_processing[PHASE_PARAMETER])
                              ? din_EVSEProcessingType_Finished
                              : din_EVSEProcessingType_Ongoing;

    if ((unsigned int)1 == res->DC_EVSEChargeParameter.EVSEMaximumPowerLimit_isUsed) {
        res->SAScheduleList.SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].PMax =
            (SHRT_MAX < (conn->ctx->evse_v2g_data.power_capabilities.max_power.Value *
                         pow(10, conn->ctx->evse_v2g_data.power_capabilities.max_power.Multiplier)))
                ? SHRT_MAX
                : (conn->ctx->evse_v2g_data.power_capabilities.max_power.Value *
                   pow(10, conn->ctx->evse_v2g_data.power_capabilities.max_power.Multiplier));
    } else {
        res->SAScheduleList.SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].PMax = SHRT_MAX;
    }
    constexpr auto PAUSE_DURATION = 60 * 30;
    res->SAScheduleList.SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].RelativeTimeInterval.duration =
        conn->ctx->evse_v2g_data.no_energy_pause == NoEnergyPauseStatus::None ? SA_SCHEDULE_DURATION
                                                                              : PAUSE_DURATION; // Must cover 24 hours
    res->SAScheduleList.SAScheduleTuple.array[0]
        .PMaxSchedule.PMaxScheduleEntry.array[0]
        .RelativeTimeInterval.duration_isUsed = 1; // Must be used in DIN
    res->SAScheduleList.SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].RelativeTimeInterval.start = 0;
    res->SAScheduleList.SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].RelativeTimeInterval_isUsed =
        1;
    res->SAScheduleList.SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].TimeInterval_isUsed =
        0; // no content

    res->SAScheduleList.SAScheduleTuple.array[0].SalesTariff_isUsed =
        0; // In the scope of DIN 70121, this optional element shall not be used. [V2G-DC-554]
    res->SAScheduleList.SAScheduleTuple.array[0].SAScheduleTupleID = SASCHEDULETUPLEID;
    res->SAScheduleList.SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.arrayLen = 1;
    res->SAScheduleList.SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleID = 1;
    res->SAScheduleList.SAScheduleTuple.arrayLen = 1;
    res->SAScheduleList_isUsed = (unsigned int)1;

    // res->SASchedules.noContent
    res->SASchedules_isUsed = (unsigned int)0;

    constexpr auto physical_value_to_float = [](const din_PhysicalValueType& pv) {
        return calc_physical_value(pv.Value, pv.Multiplier);
    };

    const auto ev_maximum_current_limit = physical_value_to_float(req->DC_EVChargeParameter.EVMaximumCurrentLimit);
    const auto ev_maximum_voltage_limit = physical_value_to_float(req->DC_EVChargeParameter.EVMaximumVoltageLimit);
    const auto evse_minimum_current_limit =
        physical_value_to_float(res->DC_EVSEChargeParameter.EVSEMinimumCurrentLimit);
    const auto evse_minimum_voltage_limit =
        physical_value_to_float(res->DC_EVSEChargeParameter.EVSEMinimumVoltageLimit);

    if (ev_maximum_current_limit <= evse_minimum_current_limit ||
        ev_maximum_voltage_limit <= evse_minimum_voltage_limit) {
        res->ResponseCode = din_responseCodeType_FAILED_WrongChargeParameter;
        res->DC_EVSEChargeParameter.DC_EVSEStatus.EVSEStatusCode = din_DC_EVSEStatusCodeType_EVSE_Shutdown;
    }

    if (res->EVSEProcessing == din_EVSEProcessingType_Finished and
        conn->ctx->evse_v2g_data.no_energy_pause != NoEnergyPauseStatus::None) {
        res->DC_EVSEChargeParameter.DC_EVSEStatus.EVSENotification = din_EVSENotificationType_StopCharging;
        res->DC_EVSEChargeParameter.DC_EVSEStatus.NotificationMaxDelay = 0;
    }

    /* Check the current response code and check if no external error has occurred */
    nextEvent = utils::din_validate_response_code(&res->ResponseCode, conn);

    /* Set next expected req msg */
    if (res->EVSEProcessing == din_EVSEProcessingType_Finished) {
        if (res->DC_EVSEChargeParameter.DC_EVSEStatus.EVSEStatusCode != din_DC_EVSEStatusCodeType_EVSE_Ready) {
            dlog(DLOG_LEVEL_WARNING,
                 "EVSE wants to finish charge parameter phase, but status code is not set to 'ready' (1)");
        }
        if (conn->ctx->evse_v2g_data.no_energy_pause == NoEnergyPauseStatus::BeforeCableCheck or
            conn->ctx->evse_v2g_data.no_energy_pause == NoEnergyPauseStatus::AfterCableCheckPreCharge) {
            conn->ctx->state = WAIT_FOR_SESSIONSTOP; // IEC61851-23:2023 CC.5.3.2
        } else {
            conn->ctx->state = WAIT_FOR_CABLECHECK; // [V2G-DC-453]
        }

    } else {
        conn->ctx->state = WAIT_FOR_CHARGEPARAMETERDISCOVERY; // [V2G-DC-498]
    }

    return nextEvent;
}

/*!
 * \brief handle_din_service_discovery This function handles the din power delivery msg pair. It analyzes the request
 * msg and fills the response msg. The request and response msg based on the open V2G structures. This structures must
 * be provided within the \c conn structure. [V2G-DC-461]
 * \param conn is the structure with the V2G msg pair.
 * \return Returns the next V2G-event.
 */
static enum v2g_event handle_din_power_delivery(struct v2g_connection* conn) {
    struct din_PowerDeliveryReqType* req = &conn->exi_in.dinEXIDocument->V2G_Message.Body.PowerDeliveryReq;
    struct din_PowerDeliveryResType* res = &conn->exi_out.dinEXIDocument->V2G_Message.Body.PowerDeliveryRes;
    enum v2g_event nextEvent = V2G_EVENT_NO_EVENT;

    /* At first, publish the received EV request message to the MQTT interface */
    publish_din_power_delivery_req(conn->ctx, req);

    if (req->ReadyToChargeState == (int)0) {
        conn->ctx->p_charger->publish_current_demand_finished(nullptr);
        conn->ctx->p_charger->publish_dc_open_contactor(nullptr);
        conn->ctx->session.is_charging = false;
    } else {
        conn->ctx->p_charger->publish_v2g_setup_finished(nullptr);
    }

    /* Now fill the evse response message */
    res->ResponseCode = din_responseCodeType_OK; // [V2G-DC-388]

    res->AC_EVSEStatus_isUsed = (unsigned int)0;
    res->DC_EVSEStatus.EVSEIsolationStatus = (din_isolationLevelType)conn->ctx->evse_v2g_data.evse_isolation_status;
    res->DC_EVSEStatus.EVSEIsolationStatus_isUsed = conn->ctx->evse_v2g_data.evse_isolation_status_is_used;
    res->DC_EVSEStatus.EVSENotification = (din_EVSENotificationType)conn->ctx->evse_v2g_data.evse_notification;
    res->DC_EVSEStatus.NotificationMaxDelay = conn->ctx->evse_v2g_data.notification_max_delay;
    res->DC_EVSEStatus.EVSEStatusCode =
        (conn->ctx->intl_emergency_shutdown == true)
            ? din_DC_EVSEStatusCodeType_EVSE_EmergencyShutdown
            : (din_DC_EVSEStatusCodeType)conn->ctx->evse_v2g_data.evse_status_code[PHASE_CHARGE];
    res->DC_EVSEStatus_isUsed = (unsigned int)1;
    res->EVSEStatus_isUsed = (unsigned int)0;
    // res->EVSEStatus.noContent

    /* Check response code */
    if (req->ChargingProfile_isUsed == (unsigned int)1) {
        /* Check the selected SAScheduleTupleID */
        res->ResponseCode = (req->ChargingProfile.SAScheduleTupleID != (int16_t)SASCHEDULETUPLEID)
                                ? din_responseCodeType_FAILED_TariffSelectionInvalid
                                : res->ResponseCode; // [V2G-DC-400]

        for (uint16_t idx = 0; idx < req->ChargingProfile.ProfileEntry.arrayLen; idx++) {
            bool entry_found = (req->ChargingProfile.ProfileEntry.array[idx].ChargingProfileEntryStart <=
                                (uint32_t)SA_SCHEDULE_DURATION);

            if ((entry_found == false) || (req->ChargingProfile.ProfileEntry.array[idx].ChargingProfileEntryMaxPower >
                                           (conn->ctx->evse_v2g_data.evse_maximum_power_limit.Value *
                                            pow(10, conn->ctx->evse_v2g_data.evse_maximum_power_limit.Multiplier)))) {
                // res->ResponseCode = din_responseCodeType_FAILED_ChargingProfileInvalid; //[V2G-DC-399]. Currently
                // commented to increase compatibility with EV'S
                dlog(DLOG_LEVEL_WARNING, "EV's charging profile is invalid (ChargingProfileEntryMaxPower %d too high)!",
                     req->ChargingProfile.ProfileEntry.array[idx].ChargingProfileEntryMaxPower);
                break;
            }
        }
    }

    res->ResponseCode = ((req->ReadyToChargeState == (int)1) &&
                         (res->DC_EVSEStatus.EVSEStatusCode != din_DC_EVSEStatusCodeType_EVSE_Ready))
                            ? din_responseCodeType_FAILED_PowerDeliveryNotApplied
                            : res->ResponseCode; // [V2G-DC-401]

    /* Check the current response code and check if no external error has occurred */
    nextEvent = utils::din_validate_response_code(&res->ResponseCode, conn);

    /* Set next expected req msg */
    if ((req->ReadyToChargeState == (int)1) && (V2G_CURRENT_DEMAND_MSG != conn->ctx->last_v2g_msg)) {
        conn->ctx->state = WAIT_FOR_CURRENTDEMAND; // [V2G-DC-462]
    } else {
        /* abort charging session if EV is ready to charge after current demand phase */
        if (req->ReadyToChargeState == (int)1) {
            res->ResponseCode = din_responseCodeType_FAILED;
            nextEvent = V2G_EVENT_SEND_AND_TERMINATE;
        }
        conn->ctx->state = WAIT_FOR_WELDINGDETECTION_SESSIONSTOP; // [V2G-DC-459]
    }

    return nextEvent;
}

/*!
 * \brief handle_din_service_discovery This function handles the din cable check msg pair. It analyzes the request msg
 * and fills the response msg. The request and response msg based on the open V2G structures. This structures must be
 * provided within the \c conn structure. [V2G-DC-454]
 * \param conn is the structure with the V2G msg pair.
 * \return Returns the next V2G-event.
 */
static enum v2g_event handle_din_cable_check(struct v2g_connection* conn) {
    struct din_CableCheckReqType* req = &conn->exi_in.dinEXIDocument->V2G_Message.Body.CableCheckReq;
    struct din_CableCheckResType* res = &conn->exi_out.dinEXIDocument->V2G_Message.Body.CableCheckRes;
    enum v2g_event nextEvent = V2G_EVENT_NO_EVENT;

    /* At first, publish the received EV request message to the MQTT interface */
    publish_DIN_DcEvStatus(conn->ctx, req->DC_EVStatus);

    // TODO: Wait for CP state C [V2G-DC-547]

    /* Now fill the evse response message */
    res->ResponseCode = din_responseCodeType_OK; // [V2G-DC-388]

    res->DC_EVSEStatus.EVSEIsolationStatus = (din_isolationLevelType)conn->ctx->evse_v2g_data.evse_isolation_status;
    res->DC_EVSEStatus.EVSEIsolationStatus_isUsed = conn->ctx->evse_v2g_data.evse_isolation_status_is_used;
    res->DC_EVSEStatus.EVSENotification =
        static_cast<din_EVSENotificationType>(conn->ctx->evse_v2g_data.evse_notification);
    res->DC_EVSEStatus.NotificationMaxDelay = static_cast<uint32_t>(conn->ctx->evse_v2g_data.notification_max_delay);
    res->EVSEProcessing = (conn->ctx->evse_v2g_data.evse_processing[PHASE_ISOLATION] == (uint8_t)0)
                              ? din_EVSEProcessingType_Finished
                              : din_EVSEProcessingType_Ongoing;

    if (conn->ctx->intl_emergency_shutdown == true) {
        res->DC_EVSEStatus.EVSEStatusCode = din_DC_EVSEStatusCodeType_EVSE_EmergencyShutdown;
    } else if (res->EVSEProcessing == din_EVSEProcessingType_Finished) {
        res->DC_EVSEStatus.EVSEStatusCode = din_DC_EVSEStatusCodeType_EVSE_Ready;
    } else {
        res->DC_EVSEStatus.EVSEStatusCode =
            static_cast<din_DC_EVSEStatusCodeType>(conn->ctx->evse_v2g_data.evse_status_code[PHASE_ISOLATION]);
    }

    /* Check the current response code and check if no external error has occurred */
    nextEvent = utils::din_validate_response_code(&res->ResponseCode, conn);

    /* Set next expected req msg */
    if ((res->EVSEProcessing == din_EVSEProcessingType_Finished) &&
        (res->DC_EVSEStatus.EVSEIsolationStatus_isUsed == (unsigned int)1) &&
        ((res->DC_EVSEStatus.EVSEIsolationStatus == din_isolationLevelType_Valid) ||
         (res->DC_EVSEStatus.EVSEIsolationStatus == din_isolationLevelType_Warning)) &&
        (res->DC_EVSEStatus.EVSEStatusCode == din_DC_EVSEStatusCodeType_EVSE_Ready)) {
        conn->ctx->state = WAIT_FOR_PRECHARGE; // [V2G-DC-499]
    } else {
        if ((res->EVSEProcessing == din_EVSEProcessingType_Finished) &&
            ((res->DC_EVSEStatus.EVSEIsolationStatus_isUsed == (unsigned int)0) ||
             ((res->DC_EVSEStatus.EVSEIsolationStatus != din_isolationLevelType_Valid) &&
              (din_isolationLevelType_Warning != res->DC_EVSEStatus.EVSEIsolationStatus)))) {
            dlog(DLOG_LEVEL_WARNING, "EVSE wants to finish cable check phase, but either status code is not set to "
                                     "'ready' (1) or isolation status is not valid");
        }

        conn->ctx->state = WAIT_FOR_CABLECHECK; // [V2G-DC-455]
    }

    return nextEvent;
}

/*!
 * \brief handle_din_service_discovery This function handles the din pre charge msg pair. It analyzes the request msg
 * and fills the response msg. The request and response msg based on the open V2G structures. This structures must be
 * provided within the \c conn structure. [V2G-DC-457]
 * \param conn is the structure with the V2G msg pair.
 * \return Returns the next V2G-event.
 */
static enum v2g_event handle_din_pre_charge(struct v2g_connection* conn) {
    struct din_PreChargeReqType* req = &conn->exi_in.dinEXIDocument->V2G_Message.Body.PreChargeReq;
    struct din_PreChargeResType* res = &conn->exi_out.dinEXIDocument->V2G_Message.Body.PreChargeRes;
    enum v2g_event nextEvent = V2G_EVENT_NO_EVENT;

    /* At first, publish the received EV request message to the customer MQTT interface */
    publish_din_precharge_req(conn->ctx, req);

    /* Now fill the EVSE response message */
    res->ResponseCode = din_responseCodeType_OK; // [V2G-DC-388]

    res->DC_EVSEStatus.EVSEIsolationStatus = (din_isolationLevelType)conn->ctx->evse_v2g_data.evse_isolation_status;
    res->DC_EVSEStatus.EVSEIsolationStatus_isUsed = conn->ctx->evse_v2g_data.evse_isolation_status_is_used;
    res->DC_EVSEStatus.EVSENotification = (din_EVSENotificationType)conn->ctx->evse_v2g_data.evse_notification;
    res->DC_EVSEStatus.EVSEStatusCode =
        (conn->ctx->intl_emergency_shutdown == true)
            ? din_DC_EVSEStatusCodeType_EVSE_EmergencyShutdown
            : (din_DC_EVSEStatusCodeType)conn->ctx->evse_v2g_data.evse_status_code[PHASE_PRECHARGE];
    res->DC_EVSEStatus.NotificationMaxDelay = conn->ctx->evse_v2g_data.notification_max_delay;

    load_din_physical_value(&res->EVSEPresentVoltage, &conn->ctx->evse_v2g_data.evse_present_voltage);

    /* Check the current response code and check if no external error has occurred */
    nextEvent = utils::din_validate_response_code(&res->ResponseCode, conn);

    /* Set next expected req msg */
    conn->ctx->state = WAIT_FOR_PRECHARGE_POWERDELIVERY; // [V2G-DC-458]

    return nextEvent;
}

/*!
 * \brief handle_din_service_discovery This function handles the din current demand msg pair. It analyzes the request
 * msg and fills the response msg. The request and response msg based on the open V2G structures. This structures must
 * be provided within the \c conn structure. [V2G-DC-464]
 * \param conn is the structure with the V2G msg pair.
 * \return Returns the next V2G-event.
 */
static enum v2g_event handle_din_current_demand(struct v2g_connection* conn) {
    struct din_CurrentDemandReqType* req = &conn->exi_in.dinEXIDocument->V2G_Message.Body.CurrentDemandReq;
    struct din_CurrentDemandResType* res = &conn->exi_out.dinEXIDocument->V2G_Message.Body.CurrentDemandRes;
    enum v2g_event nextEvent = V2G_EVENT_NO_EVENT;

    /* At first, publish the received EV request message to the MQTT interface */
    publish_din_current_demand_req(conn->ctx, req);

    /* Now fill the evse response message */
    res->ResponseCode = din_responseCodeType_OK; // [V2G-DC-388]

    res->DC_EVSEStatus.EVSEIsolationStatus = (din_isolationLevelType)conn->ctx->evse_v2g_data.evse_isolation_status;
    res->DC_EVSEStatus.EVSEIsolationStatus_isUsed = conn->ctx->evse_v2g_data.evse_isolation_status_is_used;
    res->DC_EVSEStatus.EVSENotification = (din_EVSENotificationType)conn->ctx->evse_v2g_data.evse_notification;
    res->DC_EVSEStatus.EVSEStatusCode =
        (conn->ctx->intl_emergency_shutdown == true)
            ? din_DC_EVSEStatusCodeType_EVSE_EmergencyShutdown
            : (din_DC_EVSEStatusCodeType)conn->ctx->evse_v2g_data.evse_status_code[PHASE_CHARGE];
    res->DC_EVSEStatus.NotificationMaxDelay = (uint32_t)conn->ctx->evse_v2g_data.notification_max_delay;

    res->EVSECurrentLimitAchieved = conn->ctx->evse_v2g_data.evse_current_limit_achieved;

    res->EVSEMaximumCurrentLimit_isUsed = conn->ctx->evse_v2g_data.evse_maximum_current_limit_is_used;
    load_din_physical_value(&res->EVSEMaximumCurrentLimit, &conn->ctx->evse_v2g_data.evse_maximum_current_limit);

    res->EVSEMaximumPowerLimit_isUsed = conn->ctx->evse_v2g_data.evse_maximum_power_limit_is_used;
    load_din_physical_value(&res->EVSEMaximumPowerLimit, &conn->ctx->evse_v2g_data.evse_maximum_power_limit);

    res->EVSEMaximumVoltageLimit_isUsed = conn->ctx->evse_v2g_data.evse_maximum_voltage_limit_is_used;
    load_din_physical_value(&res->EVSEMaximumVoltageLimit, &conn->ctx->evse_v2g_data.evse_maximum_voltage_limit);

    res->EVSEPowerLimitAchieved = conn->ctx->evse_v2g_data.evse_power_limit_achieved;

    load_din_physical_value(&res->EVSEPresentCurrent, &conn->ctx->evse_v2g_data.evse_present_current);
    load_din_physical_value(&res->EVSEPresentVoltage, &conn->ctx->evse_v2g_data.evse_present_voltage);

    res->EVSEVoltageLimitAchieved = conn->ctx->evse_v2g_data.evse_voltage_limit_achieved;

    /* Check the current response code and check if no external error has occurred */
    nextEvent = utils::din_validate_response_code(&res->ResponseCode, conn);

    /* Set next expected req msg */
    conn->ctx->state = WAIT_FOR_CURRENTDEMAND_POWERDELIVERY; // [V2G-DC-465]

    return nextEvent;
}

/*!
 * \brief handle_din_service_discovery This function handles the din welding detection msg pair. It analyzes the request
 * msg and fills the response msg. The request and response msg based on the open V2G structures. This structures must
 * be provided within the \c conn structure. [V2G-DC-468]
 * \param conn is the structure with the V2G msg pair.
 * \return Returns the next V2G-event.
 */
static enum v2g_event handle_din_welding_detection(struct v2g_connection* conn) {
    struct din_WeldingDetectionReqType* req = &conn->exi_in.dinEXIDocument->V2G_Message.Body.WeldingDetectionReq;
    struct din_WeldingDetectionResType* res = &conn->exi_out.dinEXIDocument->V2G_Message.Body.WeldingDetectionRes;
    enum v2g_event nextEvent = V2G_EVENT_NO_EVENT;

    /* At first, publish the received EV request message to the MQTT interface */
    publish_DIN_DcEvStatus(conn->ctx, req->DC_EVStatus);

    /* TODO: Waiting for CP-State B [V2G-DC-556]. */

    /* Now fill the evse response message */
    res->ResponseCode = din_responseCodeType_OK; // [V2G-DC-388]

    res->DC_EVSEStatus.EVSEIsolationStatus = (din_isolationLevelType)conn->ctx->evse_v2g_data.evse_isolation_status;
    res->DC_EVSEStatus.EVSEIsolationStatus_isUsed = conn->ctx->evse_v2g_data.evse_isolation_status_is_used;
    res->DC_EVSEStatus.EVSENotification = (din_EVSENotificationType)conn->ctx->evse_v2g_data.evse_notification;
    res->DC_EVSEStatus.EVSEStatusCode =
        (conn->ctx->intl_emergency_shutdown == true)
            ? din_DC_EVSEStatusCodeType_EVSE_EmergencyShutdown
            : (din_DC_EVSEStatusCodeType)conn->ctx->evse_v2g_data.evse_status_code[PHASE_WELDING];
    res->DC_EVSEStatus.NotificationMaxDelay = (uint32_t)conn->ctx->evse_v2g_data.notification_max_delay;

    load_din_physical_value(&res->EVSEPresentVoltage, &conn->ctx->evse_v2g_data.evse_present_voltage);

    /* Check the current response code and check if no external error has occurred */
    nextEvent = utils::din_validate_response_code(&res->ResponseCode, conn);

    /* Set next expected req msg */
    conn->ctx->state = WAIT_FOR_WELDINGDETECTION_SESSIONSTOP; // [V2G-DC-469]

    return nextEvent;
}

/*!
 * \brief handle_din_session_stop This function handles the din session stop msg pair. It analyzes the request msg and
 * fills the response msg. The request and response msg based on the open V2G structures. This structures must be
 * provided within the \c conn structure. [V2G-DC-450]
 * \param conn is the structure with the V2G msg pair.
 * \return Returns the next V2G-event.
 */
static enum v2g_event handle_din_session_stop(struct v2g_connection* conn) {
    struct din_SessionStopResType* res = &conn->exi_out.dinEXIDocument->V2G_Message.Body.SessionStopRes;

    /* Now fill the EVSE response message */
    res->ResponseCode = din_responseCodeType_OK; // [V2G-DC-388]

    /* Check the current response code and check if no external error has occurred */
    utils::din_validate_response_code(&res->ResponseCode, conn);

    /* Setuo dlink action */
    conn->d_link_action = dLinkAction::D_LINK_ACTION_TERMINATE;

    /* Set next expected req msg */
    conn->ctx->state = WAIT_FOR_TERMINATED_SESSION; // [V2G-DC-451]

    /* Check the current response code and check if no external error has occurred */
    return V2G_EVENT_SEND_AND_TERMINATE; // Charging must be terminated after sending the response message.
}

enum v2g_event din_handle_request(v2g_connection* conn) {
    using namespace states;

    struct din_exiDocument* exi_in = conn->exi_in.dinEXIDocument;
    struct din_exiDocument* exi_out = conn->exi_out.dinEXIDocument;
    enum v2g_event next_v2g_event = V2G_EVENT_TERMINATE_CONNECTION; // ERROR_UNEXPECTED_REQUEST_MESSAGE;

    /* extract session id */
    conn->ctx->ev_v2g_data.received_session_id = v2g_session_id_from_exi(false, exi_in);

    /* init V2G structure (document, header, body) */
    init_din_exiDocument(exi_out);
    init_din_MessageHeaderType(&exi_out->V2G_Message.Header);

    exi_out->V2G_Message.Header.SessionID.bytesLen = 8;
    init_din_BodyType(&exi_out->V2G_Message.Body);

    // === Start request handling ===
    if (exi_in->V2G_Message.Body.CurrentDemandReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "Handling CurrentDemandReq");
        if (conn->ctx->last_v2g_msg == V2G_POWER_DELIVERY_MSG) {
            conn->ctx->p_charger->publish_current_demand_started(nullptr);
            conn->ctx->session.is_charging = true;
        }
        conn->ctx->current_v2g_msg = V2G_CURRENT_DEMAND_MSG;
        exi_out->V2G_Message.Body.CurrentDemandRes_isUsed = 1u;
        init_din_CurrentDemandResType(&exi_out->V2G_Message.Body.CurrentDemandRes);
        next_v2g_event = handle_din_current_demand(conn);
    } else if (exi_in->V2G_Message.Body.SessionSetupReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "Handling SessionSetupReq");
        conn->ctx->current_v2g_msg = V2G_SESSION_SETUP_MSG;
        exi_out->V2G_Message.Body.SessionSetupRes_isUsed = 1u;
        init_din_SessionSetupResType(&exi_out->V2G_Message.Body.SessionSetupRes);
        /* Handle v2g msg */
        next_v2g_event = handle_din_session_setup(conn);
    } else if (exi_in->V2G_Message.Body.ServiceDiscoveryReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "Handling ServiceDiscoveryReq");
        conn->ctx->current_v2g_msg = V2G_SERVICE_DISCOVERY_MSG;
        exi_out->V2G_Message.Body.ServiceDiscoveryRes_isUsed = 1u;
        init_din_ServiceDiscoveryResType(&exi_out->V2G_Message.Body.ServiceDiscoveryRes);
        next_v2g_event = handle_din_service_discovery(conn);
    } else if (exi_in->V2G_Message.Body.ServicePaymentSelectionReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "Handling PaymentServiceSelectionReq");
        conn->ctx->current_v2g_msg = V2G_PAYMENT_SERVICE_SELECTION_MSG;
        exi_out->V2G_Message.Body.ServicePaymentSelectionRes_isUsed = 1u;
        init_din_ServicePaymentSelectionResType(&exi_out->V2G_Message.Body.ServicePaymentSelectionRes);
        next_v2g_event = handle_din_service_payment_selection(conn);
    } else if (exi_in->V2G_Message.Body.ContractAuthenticationReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "Handling ContractAuthenticationReq");
        conn->ctx->current_v2g_msg = V2G_AUTHORIZATION_MSG;
        if (conn->ctx->last_v2g_msg != V2G_AUTHORIZATION_MSG) {
            dlog(DLOG_LEVEL_INFO, "Auth-phase started");
            conn->ctx->p_charger->publish_require_auth_eim(nullptr);
        }
        exi_out->V2G_Message.Body.ContractAuthenticationRes_isUsed = 1u;
        init_din_ContractAuthenticationResType(&exi_out->V2G_Message.Body.ContractAuthenticationRes);
        next_v2g_event = handle_din_contract_authentication(conn);
    } else if (exi_in->V2G_Message.Body.PaymentDetailsReq_isUsed) {
        dlog(DLOG_LEVEL_ERROR, "PaymentDetails request is not supported in DIN 70121");
        conn->ctx->current_v2g_msg = V2G_UNKNOWN_MSG;
        next_v2g_event = V2G_EVENT_IGNORE_MSG;
    } else if (exi_in->V2G_Message.Body.ServiceDetailReq_isUsed) {
        dlog(DLOG_LEVEL_ERROR, "ServiceDetail request is not supported in DIN 70121");
        conn->ctx->current_v2g_msg = V2G_UNKNOWN_MSG;
        next_v2g_event = V2G_EVENT_IGNORE_MSG;
    } else if (exi_in->V2G_Message.Body.ChargeParameterDiscoveryReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "Handling ChargeParameterDiscoveryReq");
        conn->ctx->current_v2g_msg = V2G_CHARGE_PARAMETER_DISCOVERY_MSG;
        if (conn->ctx->last_v2g_msg == V2G_AUTHORIZATION_MSG) {
            dlog(DLOG_LEVEL_INFO, "Parameter-phase started");
        }
        exi_out->V2G_Message.Body.ChargeParameterDiscoveryRes_isUsed = 1u;
        init_din_ChargeParameterDiscoveryResType(&exi_out->V2G_Message.Body.ChargeParameterDiscoveryRes);
        next_v2g_event = handle_din_charge_parameter(conn);
    } else if (exi_in->V2G_Message.Body.CableCheckReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "Handling CableCheckReq");
        conn->ctx->current_v2g_msg = V2G_CABLE_CHECK_MSG;
        if (conn->ctx->last_v2g_msg == V2G_CHARGE_PARAMETER_DISCOVERY_MSG) {
            conn->ctx->p_charger->publish_start_cable_check(nullptr);
            dlog(DLOG_LEVEL_INFO, "Isolation-phase started");
        }
        exi_out->V2G_Message.Body.CableCheckRes_isUsed = 1u;
        init_din_CableCheckResType(&exi_out->V2G_Message.Body.CableCheckRes);
        next_v2g_event = handle_din_cable_check(conn);
    } else if (exi_in->V2G_Message.Body.PreChargeReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "Handling PreChargeReq");
        conn->ctx->current_v2g_msg = V2G_PRE_CHARGE_MSG;
        if (conn->ctx->last_v2g_msg == V2G_CABLE_CHECK_MSG) {
            conn->ctx->p_charger->publish_start_pre_charge(nullptr);
            dlog(DLOG_LEVEL_INFO, "Precharge-phase started");
        }
        exi_out->V2G_Message.Body.PreChargeRes_isUsed = 1u;
        init_din_PreChargeResType(&exi_out->V2G_Message.Body.PreChargeRes);
        next_v2g_event = handle_din_pre_charge(conn);
    } else if (exi_in->V2G_Message.Body.PowerDeliveryReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "Handling PowerDeliveryReq");
        conn->ctx->current_v2g_msg = V2G_POWER_DELIVERY_MSG;
        if (conn->ctx->last_v2g_msg == V2G_PRE_CHARGE_MSG) {
            dlog(DLOG_LEVEL_INFO, "Charge-phase started");
        }
        exi_out->V2G_Message.Body.PowerDeliveryRes_isUsed = 1u;
        init_din_PowerDeliveryResType(&exi_out->V2G_Message.Body.PowerDeliveryRes);
        next_v2g_event = handle_din_power_delivery(conn);
    } else if (exi_in->V2G_Message.Body.ChargingStatusReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "ChargingStatus request is not supported in DIN 70121");
        conn->ctx->current_v2g_msg = V2G_UNKNOWN_MSG;
        exi_out->V2G_Message.Body.ChargingStatusRes_isUsed = 0u;
        init_din_ChargingStatusResType(&exi_out->V2G_Message.Body.ChargingStatusRes);
        next_v2g_event = V2G_EVENT_IGNORE_MSG;
    } else if (exi_in->V2G_Message.Body.MeteringReceiptReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "MeteringReceipt request is not supported in DIN 70121");
        conn->ctx->current_v2g_msg = V2G_UNKNOWN_MSG;
        exi_out->V2G_Message.Body.MeteringReceiptRes_isUsed = 0u;
        init_din_MeteringReceiptResType(&exi_out->V2G_Message.Body.MeteringReceiptRes);
        next_v2g_event = V2G_EVENT_IGNORE_MSG;
    } else if (exi_in->V2G_Message.Body.CertificateUpdateReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "CertificateUpdate request is not supported in DIN 70121");
        conn->ctx->current_v2g_msg = V2G_UNKNOWN_MSG;
        next_v2g_event = V2G_EVENT_IGNORE_MSG;
    } else if (exi_in->V2G_Message.Body.CertificateInstallationReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "CertificateInstallation request is not supported in DIN 70121");
        conn->ctx->current_v2g_msg = V2G_UNKNOWN_MSG;
        next_v2g_event = V2G_EVENT_IGNORE_MSG;
    } else if (exi_in->V2G_Message.Body.WeldingDetectionReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "Handling WeldingDetectionReq");
        conn->ctx->current_v2g_msg = V2G_WELDING_DETECTION_MSG;
        if (conn->ctx->last_v2g_msg == V2G_POWER_DELIVERY_MSG) {
            dlog(DLOG_LEVEL_INFO, "Welding-phase started");
        }
        exi_out->V2G_Message.Body.WeldingDetectionRes_isUsed = 1u;
        init_din_WeldingDetectionResType(&exi_out->V2G_Message.Body.WeldingDetectionRes);
        next_v2g_event = handle_din_welding_detection(conn);
    } else if (exi_in->V2G_Message.Body.SessionStopReq_isUsed) {
        dlog(DLOG_LEVEL_TRACE, "Handling SessionStopReq");
        conn->ctx->current_v2g_msg = V2G_SESSION_STOP_MSG;

        exi_out->V2G_Message.Body.SessionStopRes_isUsed = 1u;
        init_din_SessionStopResType(&exi_out->V2G_Message.Body.SessionStopRes);
        next_v2g_event = handle_din_session_stop(conn);
    } else {
        dlog(DLOG_LEVEL_ERROR, "Create_response_message: request type not found");
        next_v2g_event = V2G_EVENT_IGNORE_MSG;
    }

    if (next_v2g_event != V2G_EVENT_IGNORE_MSG) {
        /* Configure session id */
        memcpy(exi_out->V2G_Message.Header.SessionID.bytes, &conn->ctx->evse_v2g_data.session_id, sizeof(uint64_t));

        // TODO: Set byteLen
        exi_out->V2G_Message.Header.SessionID.bytesLen = din_sessionIDType_BYTES_SIZE;

        dlog(DLOG_LEVEL_TRACE, "Current state: %s", din_states[conn->ctx->state].description);
        conn->ctx->last_v2g_msg = conn->ctx->current_v2g_msg;
    }

    return next_v2g_event;
}
