{%- from "dbus/dbus_marshalling.jinja" import marshall_type -%}
{%- set service = model.interfaces[0] -%}
{%- set dbus = service.dbus -%}
{%- set ident = dbus.name | replace(".", "_") -%}
// GENERATED FILE - DO NOT EDIT.
// Produced by clang-rpc-gen's QtDBus backend from an annotated C++ header.
#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <array>
#include <chrono>
#include <list>
#include <map>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <vector>

#include "rpc/dbus/Error.hh"
#include "rpc/dbus/QtCodec.hh"
{% if dbus.has_duration %}
#include "rpc/Duration.hh"
{% endif %}
#include "{{ dbus_header_filename }}"

{% for custom in dbus.custom_types %}
{{ marshall_type(custom, model) }}
{% endfor %}
{% if adapter_namespace %}
namespace {{ adapter_namespace }}
{
{% endif %}
{{ ident }}::{{ ident }}(::workrave::rpc::dbus::QtServer &server,
                         std::string path,
                         {{ service.cxx_qualified_class }} &implementation)
{% if service.signals %}
  : server_(server)
  , path_(std::move(path))
{% else %}
  : path_(std::move(path))
{% endif %}
  , implementation_(implementation)
{
{% if not service.signals %}
  (void)server;
{% endif %}
{% for custom in dbus.custom_types -%}
{%- if custom.kind == "enum" or custom.kind == "struct" %}
  qDBusRegisterMetaType<{{ model.types[custom.type_id].cxx.spelling }}>();
{% endif -%}
{%- endfor %}
{%- for signal in service.signals %}
  signal_connections_.emplace_back(implementation_.{{ signal.cxx_symbol }}().connect(
    [this]({% for field in signal.event_fields %}{% if not loop.first %}, {% endif %}{{ model.types[field.type_id].cxx.spelling }} {{ field.cxx_name }}{% endfor %}) {
      emit_{{ signal.rpc_name }}({% for field in signal.event_fields %}{% if not loop.first %}, {% endif %}{{ field.cxx_name }}{% endfor %});
    }));
{% endfor %}
}

std::string_view
{{ ident }}::name() const noexcept
{
  return "{{ dbus.name }}";
}

std::string_view
{{ ident }}::introspection() const noexcept
{
  static constexpr std::string_view xml =

  "  <interface name=\"{{ dbus.name }}\">\n"
{% for method in service.methods %}
  "\n"

  "    <method name=\"{{ method.rpc_name }}\">\n"
{% for param in method.params %}
  "\n"

  "      <arg type=\"{{ param.dbus_signature | default(model.types[param.type_id].dbus_signature, true) }}\" name=\"{{ param.proto_field }}\" direction=\"{{ param.direction | replace("_", "") }}\" />\n"
{% endfor -%}
{% if method.return_value is not none %}
  "\n"

  "      <arg type=\"{{ method.return_value.dbus_signature | default(model.types[method.return_value.type_id].dbus_signature, true) }}\" name=\"{{ method.return_value.proto_field }}\" direction=\"out\" />\n"
{% endif %}
  "\n"

  "    </method>\n"
{% endfor %}
  "\n"
{% for signal in service.signals %}
  "\n"

  "    <signal name=\"{{ signal.rpc_name }}\">\n"
{% for field in signal.event_fields %}
  "\n"

  "      <arg type=\"{{ field.dbus_signature | default(model.types[field.type_id].dbus_signature, true) }}\" name=\"{{ field.proto_name }}\" />\n"
{% endfor %}
  "\n"

  "    </signal>\n"
{% endfor %}
  "\n"

  "  </interface>\n";
  return xml;
}

bool
{{ ident }}::dispatch(const QDBusMessage &message, const QDBusConnection &connection)
{
  using Method = void ({{ ident }}::*)(const QDBusMessage &, const QDBusConnection &);
  struct Entry
  {
    std::string_view name;
    Method method;
  };
  static constexpr std::array<Entry, {{ service.methods | length }}> methods =
  { {
{% for method in service.methods %}
      {.name = "{{ method.rpc_name }}", .method = &{{ ident }}::dispatch_{{ method.rpc_name }}},
{% endfor %}
  } };

  const std::string method_name = message.member().toStdString();
  for (const auto &entry: methods)
    {
      if (entry.name == method_name)
        {
          (this->*entry.method)(message, connection);
          return true;
        }
    }
  return false;
}

{% for method in service.methods %}
void
{{ ident }}::dispatch_{{ method.rpc_name }}(const QDBusMessage &message, const QDBusConnection &connection)
{
{% for param in method.params %}
  {{ model.types[param.type_id].cxx.base_spelling }} p_{{ param.cxx_name }}{};
{% endfor %}
{% if method.return_value is not none %}
  {{ model.types[method.return_value.type_id].cxx.base_spelling }} p_{{ method.return_value.proto_field }}{};
{% endif %}

  const auto num_in_args = message.arguments().size();
  if (num_in_args != {{ method.dbus_num_in }})
    {
      throw ::workrave::rpc::dbus::Error(
        std::string(::workrave::rpc::dbus::error_names::invalid_args),
        "Incorrect number of input parameters for {{ dbus.name }}.{{ method.rpc_name }}");
    }

{% for param in method.params %}
{% if param.dbus_arg_index is not none %}
  p_{{ param.cxx_name }} = {% if param.dbus_cpp_type %}::workrave::rpc::dbus::checked_dbus_wire_cast<{{ model.types[param.type_id].cxx.base_spelling }}>(::workrave::rpc::dbus::QtCodec<{{ param.dbus_cpp_type }}>::decode(message.arguments().at({{ param.dbus_arg_index }}))){% else %}::workrave::rpc::dbus::QtCodec<{{ model.types[param.type_id].cxx.base_spelling }}>::decode(message.arguments().at({{ param.dbus_arg_index }})){% endif %};
{% endif %}
{% endfor %}

{% if method.return_value is not none %}
  p_{{ method.return_value.proto_field }} = implementation_.{{ method.cxx_symbol }}({% for param in method.params %}{% if not loop.first %}, {% endif %}{% if model.types[param.type_id].cxx.is_pointer %}&{% endif %}p_{{ param.cxx_name }}{% endfor %});
{% else %}
  implementation_.{{ method.cxx_symbol }}({% for param in method.params %}{% if not loop.first %}, {% endif %}{% if model.types[param.type_id].cxx.is_pointer %}&{% endif %}p_{{ param.cxx_name }}{% endfor %});
{% endif %}

  QDBusMessage reply = message.createReply();
{% if method.return_value is not none %}
  reply << ::workrave::rpc::dbus::QtCodec<{{ method.return_value.dbus_cpp_type | default(model.types[method.return_value.type_id].cxx.base_spelling, true) }}>::encode({% if method.return_value.dbus_cpp_type %}::workrave::rpc::dbus::checked_dbus_wire_cast<{{ method.return_value.dbus_cpp_type }}>(p_{{ method.return_value.proto_field }}){% else %}p_{{ method.return_value.proto_field }}{% endif %});
{% endif %}
{% for param in method.params %}
{% if param.direction == "out" %}
  reply << ::workrave::rpc::dbus::QtCodec<{{ param.dbus_cpp_type | default(model.types[param.type_id].cxx.base_spelling, true) }}>::encode({% if param.dbus_cpp_type %}::workrave::rpc::dbus::checked_dbus_wire_cast<{{ param.dbus_cpp_type }}>(p_{{ param.cxx_name }}){% else %}p_{{ param.cxx_name }}{% endif %});
{% endif %}
{% endfor %}

  if (!connection.send(reply))
    {
      throw ::workrave::rpc::dbus::Error(
        std::string(::workrave::rpc::dbus::error_names::failed),
        "Failed to send reply for {{ dbus.name }}.{{ method.rpc_name }}");
    }
}

{% endfor %}
{%- for signal in service.signals %}
void
{{ ident }}::emit_{{ signal.rpc_name }}({% for field in signal.event_fields %}{% if not loop.first %}, {% endif %}{{ model.types[field.type_id].cxx.spelling }} {{ field.cxx_name }}{% endfor %})
{
  QVariantList arguments;
{% for field in signal.event_fields %}
  arguments << ::workrave::rpc::dbus::QtCodec<{{ field.dbus_cpp_type | default(model.types[field.type_id].cxx.spelling, true) }}>::encode({% if field.dbus_cpp_type %}::workrave::rpc::dbus::checked_dbus_wire_cast<{{ field.dbus_cpp_type }}>({{ field.cxx_name }}){% else %}{{ field.cxx_name }}{% endif %});
{% endfor %}
  server_.emit_signal(path_, "{{ dbus.name }}", "{{ signal.rpc_name }}", arguments);
}

{%- endfor %}
{%- if adapter_namespace %}
} // namespace {{ adapter_namespace }}
{%- endif %}
