// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2022-2023 chargebyte GmbH
// Copyright (C) 2022-2023 Contributors to EVerest
#include "ISO15118_chargerImpl.hpp"
#include "log.hpp"
#include "v2g_ctx.hpp"
#include <algorithm>

const std::string CERTS_SUB_DIR = "certs"; // relativ path of the certs

using namespace std::chrono_literals;
using BidiMode = types::iso15118::SaeJ2847BidiMode;

namespace module {
namespace charger {

void ISO15118_chargerImpl::init() {
    if (!v2g_ctx) {
        dlog(DLOG_LEVEL_ERROR, "v2g_ctx not created");
        return;
    }

    /* Configure if_name and auth_mode */
    v2g_ctx->if_name = mod->config.device.data();
    dlog(DLOG_LEVEL_DEBUG, "if_name %s", v2g_ctx->if_name);
    v2g_ctx->proxy_if_name = mod->config.proxy_device.empty() ? nullptr : mod->config.proxy_device.data();

    /* Configure tls_security */
    if (mod->config.tls_security == "force") {
        v2g_ctx->tls_security = TLS_SECURITY_FORCE;
        dlog(DLOG_LEVEL_DEBUG, "tls_security force");
    } else if (mod->config.tls_security == "allow") {
        v2g_ctx->tls_security = TLS_SECURITY_ALLOW;
        dlog(DLOG_LEVEL_DEBUG, "tls_security allow");
    } else {
        v2g_ctx->tls_security = TLS_SECURITY_PROHIBIT;
        dlog(DLOG_LEVEL_DEBUG, "tls_security prohibit");
    }

    v2g_ctx->network_read_timeout_tls = mod->config.tls_timeout;

    v2g_ctx->certs_path = mod->info.paths.etc / CERTS_SUB_DIR;

    // Subscribe all vars
    mod->r_iso2->subscribe_supported_app_protocols_secc([this](const auto value) {
        if (auto merged = merge_supported_app_protocols(value, ProtocolSource::Iso2)) {
            publish_supported_app_protocols_secc(*merged);
        }
    });
    mod->r_iso2->subscribe_require_auth_eim([this]() {
        if (not mod->selected_iso20()) {
            publish_require_auth_eim(nullptr);
        }
    });
    mod->r_iso20->subscribe_require_auth_eim([this]() {
        if (mod->selected_iso20()) {
            publish_require_auth_eim(nullptr);
        }
    });

    mod->r_iso2->subscribe_require_auth_pnc([this](const auto value) {
        if (not mod->selected_iso20()) {
            publish_require_auth_pnc(value);
        }
    });
    mod->r_iso20->subscribe_require_auth_pnc([this](const auto value) {
        if (mod->selected_iso20()) {
            publish_require_auth_pnc(value);
        }
    });

    mod->r_iso2->subscribe_ac_close_contactor([this]() {
        if (not mod->selected_iso20()) {
            publish_ac_close_contactor(nullptr);
        }
    });
    mod->r_iso20->subscribe_ac_close_contactor([this]() {
        if (mod->selected_iso20()) {
            publish_ac_close_contactor(nullptr);
        }
    });

    mod->r_iso2->subscribe_ac_open_contactor([this]() {
        if (not mod->selected_iso20()) {
            publish_ac_open_contactor(nullptr);
        }
    });
    mod->r_iso20->subscribe_supported_app_protocols_secc([this](const auto value) {
        if (auto merged = merge_supported_app_protocols(value, ProtocolSource::Iso20)) {
            publish_supported_app_protocols_secc(*merged);
        }
    });
    mod->r_iso20->subscribe_ac_open_contactor([this]() {
        if (mod->selected_iso20()) {
            publish_ac_open_contactor(nullptr);
        }
    });

    mod->r_iso2->subscribe_start_cable_check([this]() {
        if (not mod->selected_iso20()) {
            publish_start_cable_check(nullptr);
        }
    });
    mod->r_iso20->subscribe_start_cable_check([this]() {
        if (mod->selected_iso20()) {
            publish_start_cable_check(nullptr);
        }
    });

    mod->r_iso2->subscribe_start_pre_charge([this]() {
        if (not mod->selected_iso20()) {
            publish_start_pre_charge(nullptr);
        }
    });
    mod->r_iso20->subscribe_start_pre_charge([this]() {
        if (mod->selected_iso20()) {
            publish_start_pre_charge(nullptr);
        }
    });

    mod->r_iso2->subscribe_dc_open_contactor([this]() {
        if (not mod->selected_iso20()) {
            publish_dc_open_contactor(nullptr);
        }
    });
    mod->r_iso20->subscribe_dc_open_contactor([this]() {
        if (mod->selected_iso20()) {
            publish_dc_open_contactor(nullptr);
        }
    });

    mod->r_iso2->subscribe_v2g_setup_finished([this]() {
        if (not mod->selected_iso20()) {
            publish_v2g_setup_finished(nullptr);
        }
    });
    mod->r_iso20->subscribe_v2g_setup_finished([this]() {
        if (mod->selected_iso20()) {
            publish_v2g_setup_finished(nullptr);
        }
    });

    mod->r_iso2->subscribe_current_demand_started([this]() {
        if (not mod->selected_iso20()) {
            publish_current_demand_started(nullptr);
        }
    });
    mod->r_iso20->subscribe_current_demand_started([this]() {
        if (mod->selected_iso20()) {
            publish_current_demand_started(nullptr);
        }
    });

    mod->r_iso2->subscribe_current_demand_finished([this]() {
        if (not mod->selected_iso20()) {
            publish_current_demand_finished(nullptr);
        }
    });
    mod->r_iso20->subscribe_current_demand_finished([this]() {
        if (mod->selected_iso20()) {
            publish_current_demand_finished(nullptr);
        }
    });

    mod->r_iso2->subscribe_sae_bidi_mode_active([this]() {
        if (not mod->selected_iso20()) {
            publish_sae_bidi_mode_active(nullptr);
        }
    });
    mod->r_iso20->subscribe_sae_bidi_mode_active([this]() {
        if (mod->selected_iso20()) {
            publish_sae_bidi_mode_active(nullptr);
        }
    });

    mod->r_iso2->subscribe_evcc_id([this](const auto id) {
        if (not mod->selected_iso20()) {
            publish_evcc_id(id);
        }
    });
    mod->r_iso20->subscribe_evcc_id([this](const auto id) {
        if (mod->selected_iso20()) {
            publish_evcc_id(id);
        }
    });

    mod->r_iso2->subscribe_selected_payment_option([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_selected_payment_option(o);
        }
    });
    mod->r_iso20->subscribe_selected_payment_option([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_selected_payment_option(o);
        }
    });

    mod->r_iso2->subscribe_requested_energy_transfer_mode([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_requested_energy_transfer_mode(o);
        }
    });
    mod->r_iso20->subscribe_requested_energy_transfer_mode([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_requested_energy_transfer_mode(o);
        }
    });

    mod->r_iso2->subscribe_departure_time([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_departure_time(o);
        }
    });
    mod->r_iso20->subscribe_departure_time([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_departure_time(o);
        }
    });

    mod->r_iso2->subscribe_ac_eamount([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_ac_eamount(o);
        }
    });
    mod->r_iso20->subscribe_ac_eamount([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_ac_eamount(o);
        }
    });

    mod->r_iso2->subscribe_ac_ev_max_voltage([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_ac_ev_max_voltage(o);
        }
    });
    mod->r_iso20->subscribe_ac_ev_max_voltage([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_ac_ev_max_voltage(o);
        }
    });

    mod->r_iso2->subscribe_ac_ev_max_current([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_ac_ev_max_current(o);
        }
    });
    mod->r_iso20->subscribe_ac_ev_max_current([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_ac_ev_max_current(o);
        }
    });

    mod->r_iso2->subscribe_ac_ev_min_current([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_ac_ev_min_current(o);
        }
    });
    mod->r_iso20->subscribe_ac_ev_min_current([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_ac_ev_min_current(o);
        }
    });

    mod->r_iso20->subscribe_ac_ev_power_limits([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_ac_ev_power_limits(o);
        }
    });

    mod->r_iso20->subscribe_ac_ev_present_powers([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_ac_ev_present_powers(o);
        }
    });

    mod->r_iso20->subscribe_ac_ev_dynamic_control_mode([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_ac_ev_dynamic_control_mode(o);
        }
    });

    mod->r_iso2->subscribe_dc_ev_energy_capacity([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_dc_ev_energy_capacity(o);
        }
    });
    mod->r_iso20->subscribe_dc_ev_energy_capacity([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_dc_ev_energy_capacity(o);
        }
    });

    mod->r_iso2->subscribe_dc_ev_energy_request([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_dc_ev_energy_request(o);
        }
    });
    mod->r_iso20->subscribe_dc_ev_energy_request([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_dc_ev_energy_request(o);
        }
    });

    mod->r_iso2->subscribe_dc_full_soc([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_dc_full_soc(o);
        }
    });
    mod->r_iso20->subscribe_dc_full_soc([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_dc_full_soc(o);
        }
    });

    mod->r_iso2->subscribe_dc_bulk_soc([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_dc_bulk_soc(o);
        }
    });
    mod->r_iso20->subscribe_dc_bulk_soc([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_dc_bulk_soc(o);
        }
    });

    mod->r_iso2->subscribe_dc_ev_status([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_dc_ev_status(o);
        }
    });
    mod->r_iso20->subscribe_dc_ev_status([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_dc_ev_status(o);
        }
    });

    mod->r_iso2->subscribe_dc_bulk_charging_complete([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_dc_bulk_charging_complete(o);
        }
    });
    mod->r_iso20->subscribe_dc_bulk_charging_complete([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_dc_bulk_charging_complete(o);
        }
    });

    mod->r_iso2->subscribe_dc_charging_complete([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_dc_charging_complete(o);
        }
    });
    mod->r_iso20->subscribe_dc_charging_complete([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_dc_charging_complete(o);
        }
    });

    mod->r_iso2->subscribe_dc_ev_target_voltage_current([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_dc_ev_target_voltage_current(o);
        }
    });
    mod->r_iso20->subscribe_dc_ev_target_voltage_current([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_dc_ev_target_voltage_current(o);
        }
    });

    mod->r_iso2->subscribe_dc_ev_maximum_limits([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_dc_ev_maximum_limits(o);
        }
    });
    mod->r_iso20->subscribe_dc_ev_maximum_limits([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_dc_ev_maximum_limits(o);
        }
    });

    mod->r_iso2->subscribe_dc_ev_remaining_time([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_dc_ev_remaining_time(o);
        }
    });
    mod->r_iso20->subscribe_dc_ev_remaining_time([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_dc_ev_remaining_time(o);
        }
    });

    mod->r_iso2->subscribe_hlc_session_failed([this](const auto reason) {
        if (not mod->selected_iso20()) {
            publish_hlc_session_failed(reason);
        }
    });
    mod->r_iso20->subscribe_hlc_session_failed([this](const auto reason) {
        if (mod->selected_iso20()) {
            publish_hlc_session_failed(reason);
        }
    });

    mod->r_iso2->subscribe_dlink_terminate([this]() {
        if (not mod->selected_iso20()) {
            publish_dlink_terminate(nullptr);
        }
    });
    mod->r_iso20->subscribe_dlink_terminate([this]() {
        if (mod->selected_iso20()) {
            publish_dlink_terminate(nullptr);
        }
    });

    mod->r_iso2->subscribe_dlink_error([this]() {
        if (not mod->selected_iso20()) {
            publish_dlink_error(nullptr);
        }
    });
    mod->r_iso20->subscribe_dlink_error([this]() {
        if (mod->selected_iso20()) {
            publish_dlink_error(nullptr);
        }
    });

    mod->r_iso2->subscribe_dlink_pause([this]() {
        if (not mod->selected_iso20()) {
            publish_dlink_pause(nullptr);
        }
    });
    mod->r_iso20->subscribe_dlink_pause([this]() {
        if (mod->selected_iso20()) {
            publish_dlink_pause(nullptr);
        }
    });

    mod->r_iso2->subscribe_ev_app_protocol([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_ev_app_protocol(o);
        }
    });
    mod->r_iso20->subscribe_ev_app_protocol([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_ev_app_protocol(o);
        }
    });

    mod->r_iso2->subscribe_v2g_messages([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_v2g_messages(o);
        }
    });
    mod->r_iso20->subscribe_v2g_messages([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_v2g_messages(o);
        }
    });

    mod->r_iso2->subscribe_selected_protocol([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_selected_protocol(o);
        }
    });
    mod->r_iso20->subscribe_selected_protocol([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_selected_protocol(o);
        }
    });

    mod->r_iso2->subscribe_selected_service_parameters([this](const auto& param) {
        if (not mod->selected_iso20()) {
            publish_selected_service_parameters(param);
        }
    });

    mod->r_iso20->subscribe_selected_service_parameters([this](const auto& param) {
        if (mod->selected_iso20()) {
            publish_selected_service_parameters(param);
        }
    });

    mod->r_iso2->subscribe_display_parameters([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_display_parameters(o);
        }
    });
    mod->r_iso20->subscribe_display_parameters([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_display_parameters(o);
        }
    });

    mod->r_iso20->subscribe_d20_dc_dynamic_charge_mode([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_d20_dc_dynamic_charge_mode(o);
        }
    });

    mod->r_iso2->subscribe_dc_ev_present_voltage([this](const auto o) {
        if (not mod->selected_iso20()) {
            publish_dc_ev_present_voltage(o);
        }
    });

    mod->r_iso20->subscribe_dc_ev_present_voltage([this](const auto o) {
        if (mod->selected_iso20()) {
            publish_dc_ev_present_voltage(o);
        }
    });

    mod->r_iso2->subscribe_meter_info_requested([this]() {
        if (not mod->selected_iso20()) {
            publish_meter_info_requested(nullptr);
        }
    });

    mod->r_iso20->subscribe_meter_info_requested([this]() {
        if (mod->selected_iso20()) {
            publish_meter_info_requested(nullptr);
        }
    });
}

void ISO15118_chargerImpl::ready() {
}

std::optional<types::iso15118::SupportedAppProtocols>
ISO15118_chargerImpl::merge_supported_app_protocols(const types::iso15118::SupportedAppProtocols& value,
                                                    ProtocolSource source) {
    std::lock_guard<std::mutex> lock(supported_app_protocols_mutex);
    bool changed = false;

    for (const auto protocol : value.app_protocols) {
        if (std::find(supported_app_protocols.app_protocols.begin(), supported_app_protocols.app_protocols.end(),
                      protocol) == supported_app_protocols.app_protocols.end()) {
            supported_app_protocols.app_protocols.push_back(protocol);
            changed = true;
        }
        protocol_sources[protocol] = source;
    }

    return changed ? std::make_optional(supported_app_protocols) : std::nullopt;
}

void ISO15118_chargerImpl::handle_setup(types::iso15118::EVSEID& evse_id,
                                        types::iso15118::SaeJ2847BidiMode& sae_j2847_mode, bool& debug_mode) {
    mod->r_iso20->call_setup(evse_id, sae_j2847_mode, debug_mode);
    mod->r_iso2->call_setup(evse_id, sae_j2847_mode, debug_mode);
}

void ISO15118_chargerImpl::handle_set_charging_parameters(types::iso15118::SetupPhysicalValues& physical_values) {
    mod->r_iso20->call_set_charging_parameters(physical_values);
    mod->r_iso2->call_set_charging_parameters(physical_values);
}

void ISO15118_chargerImpl::handle_session_setup(std::vector<types::iso15118::PaymentOption>& payment_options,
                                                bool& supported_certificate_service,
                                                bool& central_contract_validation_allowed) {
    mod->r_iso20->call_session_setup(payment_options, supported_certificate_service,
                                     central_contract_validation_allowed);
    mod->r_iso2->call_session_setup(payment_options, supported_certificate_service,
                                    central_contract_validation_allowed);
}

void ISO15118_chargerImpl::handle_bpt_setup(types::iso15118::BptSetup& bpt_config) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_bpt_setup(bpt_config);
    }
}

void ISO15118_chargerImpl::handle_set_powersupply_capabilities(types::power_supply_DC::Capabilities& capabilities) {
    mod->r_iso20->call_set_powersupply_capabilities(capabilities);
    mod->r_iso2->call_set_powersupply_capabilities(capabilities);
}

void ISO15118_chargerImpl::handle_authorization_response(
    types::authorization::AuthorizationStatus& authorization_status,
    types::authorization::CertificateStatus& certificate_status) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_authorization_response(authorization_status, certificate_status);
    } else {
        mod->r_iso2->call_authorization_response(authorization_status, certificate_status);
    }
}

void ISO15118_chargerImpl::handle_ac_contactor_closed(bool& status) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_ac_contactor_closed(status);
    } else {
        mod->r_iso2->call_ac_contactor_closed(status);
    }
}

void ISO15118_chargerImpl::handle_dlink_ready(bool& value) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_dlink_ready(value);
    } else {
        mod->r_iso2->call_dlink_ready(value);
    }
}

void ISO15118_chargerImpl::handle_cable_check_finished(bool& status) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_cable_check_finished(status);
    } else {
        mod->r_iso2->call_cable_check_finished(status);
    }
}

void ISO15118_chargerImpl::handle_receipt_is_required(bool& receipt_required) {
    mod->r_iso20->call_receipt_is_required(receipt_required);
    mod->r_iso2->call_receipt_is_required(receipt_required);
}

void ISO15118_chargerImpl::handle_stop_charging(bool& stop) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_stop_charging(stop);
    } else {
        mod->r_iso2->call_stop_charging(stop);
    }
}

void ISO15118_chargerImpl::handle_pause_charging(bool& pause) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_pause_charging(pause);
    } else {
        mod->r_iso2->call_pause_charging(pause);
    }
}

void ISO15118_chargerImpl::handle_no_energy_pause_charging(types::iso15118::NoEnergyPauseMode& mode) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_no_energy_pause_charging(mode);
    } else {
        mod->r_iso2->call_no_energy_pause_charging(mode);
    }
}

bool ISO15118_chargerImpl::handle_update_supported_app_protocols(
    types::iso15118::SupportedAppProtocols& updated_supported_app_protocols) {
    bool iso20_configured = false;

    if (updated_supported_app_protocols.app_protocols.empty()) {
        v2g_ctx->iso20_proxy_enabled = false;
        EVLOG_info << "No protocols configured, disabling ISO-20 proxy routing";
        // Forward empty updates to both implementations
        // TODO(FH): Currently only forwarded to the ISO-2 module, because ISO-20 module is not able to
        // disable supported protocols. Currently, this implementation gap has been resolved by deactivating
        // the ISO-20 proxy if the ISO-20 protocol has not been enabled. In that case, the ISO-2
        // proxy takes over the responsibility for responding to the SupportedAppReq.
        const bool ok_iso2 = mod->r_iso2->call_update_supported_app_protocols(updated_supported_app_protocols);
        mod->r_iso20->call_update_supported_app_protocols(updated_supported_app_protocols);
        return ok_iso2;
    }

    types::iso15118::SupportedAppProtocols iso2_payload;
    types::iso15118::SupportedAppProtocols iso20_payload;

    {
        /* Iterate though the list of supported_app_protocols and check whether the received protocol is intended for
        the r_iso2 or the r_iso20 instance */
        std::lock_guard<std::mutex> lock(supported_app_protocols_mutex);
        for (const auto protocol : updated_supported_app_protocols.app_protocols) {
            const auto source_it = protocol_sources.find(protocol);
            const bool known_protocol = std::find(this->supported_app_protocols.app_protocols.begin(),
                                                  this->supported_app_protocols.app_protocols.end(),
                                                  protocol) != this->supported_app_protocols.app_protocols.end();
            if (source_it == protocol_sources.end() || !known_protocol) {
                EVLOG_warning << "Received update for unsupported app protocol: "
                              << types::iso15118::supported_app_protocol_to_string(protocol);
                return false;
            }

            if (source_it->second == ProtocolSource::Iso20) {
                iso20_configured = true;
                iso20_payload.app_protocols.push_back(protocol);
            } else {
                iso2_payload.app_protocols.push_back(protocol);
            }
        }
    }

    /* update supported protocols */
    bool ok = true;
    if (!iso2_payload.app_protocols.empty()) {
        ok = mod->r_iso2->call_update_supported_app_protocols(iso2_payload) && ok;
    }
    if (!iso20_payload.app_protocols.empty()) {
        ok = mod->r_iso20->call_update_supported_app_protocols(iso20_payload) && ok;
    }

    /* route to ISO-20 proxy only when ISO15118-20 is explicitly configured. */
    v2g_ctx->iso20_proxy_enabled = iso20_configured;
    if (!iso20_configured) {
        EVLOG_debug << "Disabling ISO-20 proxy routing since no ISO15118-20 protocol is configured";
    }

    return ok;
}

void ISO15118_chargerImpl::handle_update_energy_transfer_modes(
    std::vector<types::iso15118::EnergyTransferMode>& supported_energy_transfer_modes) {
    mod->r_iso20->call_update_energy_transfer_modes(supported_energy_transfer_modes);
    mod->r_iso2->call_update_energy_transfer_modes(supported_energy_transfer_modes);
}

void ISO15118_chargerImpl::handle_update_ac_max_current(double& max_current) {
    mod->r_iso20->call_update_ac_max_current(max_current);
    mod->r_iso2->call_update_ac_max_current(max_current);
}

void ISO15118_chargerImpl::handle_update_ac_parameters(types::iso15118::AcParameters& ac_parameters) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_update_ac_parameters(ac_parameters);
    }
}

void ISO15118_chargerImpl::handle_update_ac_maximum_limits(types::iso15118::AcEvseMaximumPower& maximum_limits) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_update_ac_maximum_limits(maximum_limits);
    }
}

void ISO15118_chargerImpl::handle_update_ac_minimum_limits(types::iso15118::AcEvseMinimumPower& minimum_limits) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_update_ac_minimum_limits(minimum_limits);
    }
}

void ISO15118_chargerImpl::handle_update_ac_target_values(types::iso15118::AcTargetValues& target_values) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_update_ac_target_values(target_values);
    }
}

void ISO15118_chargerImpl::handle_update_ac_present_power(types::units::Power& present_power) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_update_ac_present_power(present_power);
    }
}

void ISO15118_chargerImpl::handle_update_dc_maximum_limits(types::iso15118::DcEvseMaximumLimits& maximum_limits) {
    mod->r_iso20->call_update_dc_maximum_limits(maximum_limits);
    mod->r_iso2->call_update_dc_maximum_limits(maximum_limits);
}

void ISO15118_chargerImpl::handle_update_dc_minimum_limits(types::iso15118::DcEvseMinimumLimits& minimum_limits) {
    mod->r_iso20->call_update_dc_minimum_limits(minimum_limits);
    mod->r_iso2->call_update_dc_minimum_limits(minimum_limits);
}

void ISO15118_chargerImpl::handle_update_isolation_status(types::iso15118::IsolationStatus& isolation_status) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_update_isolation_status(isolation_status);
    } else {
        mod->r_iso2->call_update_isolation_status(isolation_status);
    }
}

void ISO15118_chargerImpl::handle_update_dc_present_values(
    types::iso15118::DcEvsePresentVoltageCurrent& present_voltage_current) {
    mod->r_iso20->call_update_dc_present_values(present_voltage_current);
    mod->r_iso2->call_update_dc_present_values(present_voltage_current);
}

void ISO15118_chargerImpl::handle_update_meter_info(types::powermeter::Powermeter& powermeter) {
    mod->r_iso20->call_update_meter_info(powermeter);
    mod->r_iso2->call_update_meter_info(powermeter);
}

void ISO15118_chargerImpl::handle_send_error(types::iso15118::EvseError& error) {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_send_error(error);
    } else {
        mod->r_iso2->call_send_error(error);
    }
}

void ISO15118_chargerImpl::handle_reset_error() {
    if (mod->selected_iso20()) {
        mod->r_iso20->call_reset_error();
    } else {
        mod->r_iso2->call_reset_error();
    }
}

} // namespace charger
} // namespace module
