{% if action.is_request or action.is_send %}
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - {{year}} Pionix GmbH and Contributors to EVerest
// This code is generated using the generator in 'src/code_generator/common`, please do not edit manually

#include <ocpp/{{namespace}}/messages/{{action.name}}.hpp>

#include <ostream>
#include <string>
{% if uses_optional %}
#include <optional>
{% endif %}

using json = nlohmann::json;

namespace ocpp {
namespace {{namespace}} {
{% endif %}

{% for type in types %}
{% if type.name == action.class_name %}

{% if loop.last %}
    std::string {{ type.name }}::get_type() const {
        return "{{ type.name | remove_last('Request') }}";
    }

    void to_json(json& j, const {{ type.name }}& k) {
        // the required parts of the message
{% if type.properties|selectattr('required')|list|length %}
        j = json{
{%- endif %}
{%- for property in type.properties %}
{%- if property.required %}{"{{property.name}}",
    {%- if property.enum %}
 {{ conversions_namespace_prefix }}conversions::{{- property.type | snake_case}}_to_string(k.{{property.name}})
    {%- else %}
        {%- if property.type == 'ocpp::DateTime' %}
k.{{property.name}}.to_rfc3339()
        {% elif property.type.startswith('std::vector<') and property.type.endswith('Enum>') %}
            json::array()
        {%- else %}
k.{{property.name}}
        {%- endif %}
    {%- endif %}},
{%- endif %}
{%- endfor %}
{% if not type.properties|selectattr('required')|list|length %}
        j = json ({}, true);
{%- else %}
};
{%- endif %}
{%- for property in type.properties %}
{%- if property.required and property.type.startswith('std::vector<') and property.type.endswith('Enum>')%}
            for (const auto& val : k.{{property.name}}) {
                {%- if property.type.endswith('Enum>') %}
                j["{{property.name}}"].push_back({{ conversions_namespace_prefix }}conversions::{{- property.type.replace('std::vector<','').replace('>','') | snake_case}}_to_string(val));
                {%- else%}
                j["{{property.name}}"].push_back(val);
                {%- endif%}
            }
{%- endif%}
{%- endfor %}

        // the optional parts of the message
{% for property in type.properties %}
{% if not property.required %}
        if (k.{{property.name}}) {
            {% if property.type.startswith('std::vector<') %}
            {%- if type.properties|selectattr('required')|list|length %}
j["{{property.name}}"] = json::array();
            {%- else %}
{#only optional keys in json#}
{#TODO: add key to json when there are no required keys but multiple optional keys#}
if (j.empty()) {
                j = json{{'{{"'+property.name+'", json::array()}};'}}
            } else {
                j["{{property.name}}"] = json::array();
            }
{% endif %}
            for (const auto& val : k.{{property.name}}.value()) {
                {%- if property.type.endswith('Enum>') %}
                j["{{property.name}}"].push_back({{ conversions_namespace_prefix }}conversions::{{- property.type.replace('std::vector<','').replace('>','') | snake_case}}_to_string(val));
                {%- else%}
                j["{{property.name}}"].push_back(val);
                {%- endif%}
            }
{% else %}
{% if property.enum %}
j["{{property.name}}"] = {{ conversions_namespace_prefix }}conversions::{{- property.type | snake_case}}_to_string(k.{{property.name}}.value());
{% else %}
{% if property.type == 'ocpp::DateTime' %}
j["{{property.name}}"] = k.{{property.name}}.value().to_rfc3339();
{% else %}
j["{{property.name}}"] = k.{{property.name}}.value();
{% endif %}
{% endif %}
{% endif %}
        }
{% endif %}
{% endfor %}
{% if not type.properties|length %}
    (void)k; // no elements to unpack, silence unused parameter warning
{% endif %}
    }

    void from_json(const json& j, {{ type.name }}& k) {
        // the required parts of the message
{% for property in type.properties %}
{% if property.required %}
{% if property.type.startswith('std::vector<') %}
        for (const auto& val : j.at("{{property.name}}")) {
{%- if property.type.endswith('Enum>') %}
            k.{{property.name}}.push_back({{ conversions_namespace_prefix }}conversions::string_to_{{- property.type.replace('std::vector<','').replace('>','') | snake_case}}(val));
{% else %}
            k.{{property.name}}.push_back(val);
{% endif %}
        }
{% else %}
        k.{{property.name}} =
    {%- if property.enum %}
 {{ conversions_namespace_prefix }}conversions::string_to_{{- property.type | snake_case}}(j.at("{{property.name}}"))
    {%- else %}
        {%- if property.type == 'ocpp::DateTime' %}
 ocpp::DateTime(std::string(j.at("{{property.name}}")))
        {%- else %}
 j.at("{{property.name}}")
        {%- endif %}
    {%- endif %};
{% endif %}
{% endif %}
{%- endfor %}

        // the optional parts of the message
{% for property in type.properties %}
{% if not property.required %}
        if (j.contains("{{property.name}}")) {
{% if property.type.startswith('std::vector<') %}
            const json& arr = j.at("{{property.name}}");
            {{property.type}} vec;
            for (const auto& val : arr) {
                {%- if property.type.endswith('Enum>') %}
                vec.push_back({{ conversions_namespace_prefix }}conversions::string_to_{{- property.type.replace('std::vector<','').replace('>','') | snake_case}}(val));
                {%- else %}
                vec.push_back(val);
                {%- endif %}
            }
            k.{{property.name}}.emplace(vec);
{% else %}
{% if property.enum %}
            k.{{property.name}}.emplace({{ conversions_namespace_prefix }}conversions::string_to_{{- property.type.replace('std::vector<','').replace('>','') | snake_case}}(j.at("{{property.name}}")));
{% else %}
{% if property.type == 'ocpp::DateTime' %}
            k.{{property.name}}.emplace(ocpp::DateTime(std::string(j.at("{{property.name}}"))));
{% else %}
            k.{{property.name}}.emplace(j.at("{{property.name}}"));
{% endif %}
{% endif %}
{% endif %}
        }
{% endif %}
{% endfor %}
{% if not type.properties|length %}
    // no elements to unpack, silence unused parameter warning
    (void)j;
    (void)k;
{% endif %}
    }

/// \brief Writes the string representation of the given {{ type.name }} \p k to the given output stream \p os
/// \returns an output stream with the {{ type.name }} written to
std::ostream& operator<<(std::ostream& os, const {{ type.name }}& k) {
    os << json(k).dump(4);
    return os;
}

{% endif %}

{% endif %}
{% endfor %}

{% if not action.is_request or action.is_send %}
} // namespace {{namespace}}
} // namespace ocpp

{% endif %}
