{%- macro marshall_type(custom, model) -%}
{%- set ty = model.types[custom.type_id] -%}
{%- if custom.kind == "enum" -%}
namespace workrave::rpc::dbus
{
template<>
struct QtCodec<{{ ty.cxx.spelling }}>
{
  static {{ ty.cxx.spelling }} decode(const QVariant &variant)
  {
    const std::string value = QtCodec<std::string>::decode(variant);
{% for value in custom.values %}
    if (value == "{{ value.canonical_name }}") return {{ value.cxx_symbol }};
{% endfor %}
    throw Error(std::string(error_names::invalid_args), "Unknown DBus enum value: " + value);
  }
  static void append(QDBusArgument &arg, const {{ ty.cxx.spelling }} &value)
  {
    std::string str;
    switch (value)
      {
{% for value in custom.values %}
      case {{ value.cxx_symbol }}: str = "{{ value.canonical_name }}"; break;
{% endfor %}
      default:
        throw Error(std::string(error_names::invalid_args), "Type error in enum");
      }
    arg << QString::fromStdString(str);
  }
  static QVariant encode(const {{ ty.cxx.spelling }} &value)
  {
    std::string str;
    switch (value)
      {
{% for value in custom.values %}
      case {{ value.cxx_symbol }}: str = "{{ value.canonical_name }}"; break;
{% endfor %}
      default:
        throw Error(std::string(error_names::invalid_args), "Type error in enum");
      }
    return QVariant::fromValue(QString::fromStdString(str));
  }
};
} // namespace workrave::rpc::dbus

// ADL requires these at global scope (or a namespace associated with one of
// the parameter types) to be found from Qt's own template code — nesting
// them inside workrave::rpc::dbus, where they'd only be found via {{ ty.cxx.spelling }}'s
// own namespace (which may not be workrave::rpc::dbus at all), silently breaks
// qDBusRegisterMetaType<{{ ty.cxx.spelling }}>() and any use as a sequence/map element.
[[maybe_unused]] static QDBusArgument &operator<<(QDBusArgument &arg, const {{ ty.cxx.spelling }} &data)
{
  workrave::rpc::dbus::QtCodec<{{ ty.cxx.spelling }}>::append(arg, data);
  return arg;
}

[[maybe_unused]] static const QDBusArgument &operator>>(const QDBusArgument &arg, {{ ty.cxx.spelling }} &data)
{
  QString value;
  arg >> value;
  data = workrave::rpc::dbus::QtCodec<{{ ty.cxx.spelling }}>::decode(QVariant::fromValue(value));
  return arg;
}
{%- elif custom.kind == "struct" -%}
namespace workrave::rpc::dbus
{
template<>
struct QtCodec<{{ ty.cxx.spelling }}>
{
  static {{ ty.cxx.spelling }} decode(const QVariant &variant)
  {
    const auto arg = variant.value<QDBusArgument>();
    {{ ty.cxx.spelling }} result{};
    arg.beginStructure();
{% for field in ty.shape.fields %}
    result.{{ field.cxx_name }} = QtCodec<{{ model.types[field.type_id].cxx.spelling }}>::decode(arg.asVariant());
{% endfor %}
    arg.endStructure();
    return result;
  }
  static void append(QDBusArgument &arg, const {{ ty.cxx.spelling }} &value)
  {
    arg.beginStructure();
{% for field in ty.shape.fields %}
    QtCodec<{{ model.types[field.type_id].cxx.spelling }}>::append(arg, value.{{ field.cxx_name }});
{% endfor %}
    arg.endStructure();
  }
  static QVariant encode(const {{ ty.cxx.spelling }} &value)
  {
    QDBusArgument arg;
    append(arg, value);
    return QVariant::fromValue(arg);
  }
};
} // namespace workrave::rpc::dbus

// See the enum case above for why these are at global scope, not nested in
// workrave::rpc::dbus: ADL needs to find them from {{ ty.cxx.spelling }}'s own associated
// namespace, not this library's.
[[maybe_unused]] static QDBusArgument &operator<<(QDBusArgument &arg, const {{ ty.cxx.spelling }} &data)
{
  workrave::rpc::dbus::QtCodec<{{ ty.cxx.spelling }}>::append(arg, data);
  return arg;
}

[[maybe_unused]] static const QDBusArgument &operator>>(const QDBusArgument &arg, {{ ty.cxx.spelling }} &data)
{
  data = workrave::rpc::dbus::QtCodec<{{ ty.cxx.spelling }}>::decode(QVariant::fromValue(arg));
  return arg;
}
{%- elif custom.kind == "vector" -%}
{%- set element = model.types[custom.element_type] -%}
namespace workrave::rpc::dbus
{
template<>
struct QtCodec<{{ ty.cxx.spelling }}>
{
  static {{ ty.cxx.spelling }} decode(const QVariant &variant)
  {
    const auto arg = variant.value<QDBusArgument>();
    {{ ty.cxx.spelling }} result;
    arg.beginArray();
    while (!arg.atEnd())
      {
        result.push_back(QtCodec<{{ element.cxx.spelling }}>::decode(arg.asVariant()));
      }
    arg.endArray();
    return result;
  }
  static void append(QDBusArgument &arg, const {{ ty.cxx.spelling }} &value)
  {
    arg.beginArray(qMetaTypeId<QVariant>());
    for (const auto &item : value)
      {
        QtCodec<{{ element.cxx.spelling }}>::append(arg, item);
      }
    arg.endArray();
  }
  static QVariant encode(const {{ ty.cxx.spelling }} &value)
  {
    QDBusArgument arg;
    append(arg, value);
    return QVariant::fromValue(arg);
  }
};
} // namespace workrave::rpc::dbus
{%- elif custom.kind == "duration" -%}
namespace workrave::rpc::dbus
{
template<>
struct QtCodec<{{ ty.cxx.spelling }}>
{
  static {{ ty.cxx.spelling }} decode(const QVariant &variant)
  {
    std::string text = QtCodec<std::string>::decode(variant);
    return std::chrono::duration_cast<{{ ty.cxx.spelling }}>(::rpc::parse_duration(text));
  }
};
} // namespace workrave::rpc::dbus
{%- elif custom.kind == "bitmask" -%}
{%- set enum_ty = model.types[custom.enum_type] -%}
namespace workrave::rpc::dbus
{
template<>
struct QtCodec<{{ ty.cxx.spelling }}>
{
  static {{ ty.cxx.spelling }} decode(const QVariant &variant)
  {
    std::list<{{ enum_ty.cxx.spelling }}> items = QtCodec<std::list<{{ enum_ty.cxx.spelling }}>>::decode(variant);
    {{ ty.cxx.spelling }} result;
    for (const auto &item : items)
      {
        result |= item;
      }
    return result;
  }
  static void append(QDBusArgument &arg, const {{ ty.cxx.spelling }} &value)
  {
    std::list<{{ enum_ty.cxx.spelling }}> items;
    for (unsigned int b = 0; b < workrave::utils::enum_traits<{{ enum_ty.cxx.spelling }}>::bits; ++b)
      {
        auto e = static_cast<{{ enum_ty.cxx.spelling }}>(1 << b);
        if (value.is_set(e))
          {
            items.push_back(e);
          }
      }
    QtCodec<std::list<{{ enum_ty.cxx.spelling }}>>::append(arg, items);
  }
  static QVariant encode(const {{ ty.cxx.spelling }} &value)
  {
    QDBusArgument arg;
    append(arg, value);
    return QVariant::fromValue(arg);
  }
};
} // namespace workrave::rpc::dbus
{%- endif -%}
{%- endmacro -%}
