{%- import "struct_macros.tmpl" as struct_macros %}

{%- macro declare_params(prefix, parameters) %}
{%-   for param in parameters -%}
{{param.kind|cpp_wrapper_param_type}} {{prefix}}{{param.name}}
{%- if not loop.last %}, {% endif %}
{%-   endfor %}
{%- endmacro %}

{%- macro declare_callback(method, for_blink, use_once_callback) -%}
{%-   if use_once_callback -%}
base::OnceCallback<void(
{%-   else -%}
base::Callback<void(
{%-   endif -%}
{%-   for param in method.response_parameters -%}
{{param.kind|cpp_wrapper_param_type}}
{%-     if not loop.last %}, {% endif %}
{%-   endfor -%}
)>
{%- endmacro -%}

{%- macro declare_request_params(prefix, method, use_once_callback) -%}
{{declare_params(prefix, method.parameters)}}
{%-   if method.response_parameters != None -%}
{%-     if method.parameters %}, {% endif -%}
{%-     if use_once_callback -%}
{{method.name}}Callback callback
{%-     else -%}
const {{method.name}}Callback& callback
{%-     endif -%}
{%-   endif -%}
{%- endmacro -%}

{%- macro declare_sync_method_params(prefix, method) -%}
{{declare_params(prefix, method.parameters)}}
{%-   if method.response_parameters %}
{%-     if method.parameters %}, {% endif %}
{%-     for param in method.response_parameters -%}
{{param.kind|cpp_wrapper_call_type}}* out_{{prefix}}{{param.name}}
{%-       if not loop.last %}, {% endif %}
{%-     endfor %}
{%-   endif -%}
{%- endmacro -%}

{%- macro build_message_flags(is_response, is_sync_text, expects_response_text,
                              flags_name) %}
{%-   if is_response %}
  const uint32_t kFlags = mojo::Message::kFlagIsResponse |
      (({{is_sync_text}}) ? mojo::Message::kFlagIsSync : 0);
{%-   else %}
  const uint32_t kFlags =
      (({{expects_response_text}}) ? mojo::Message::kFlagExpectsResponse : 0) |
      (({{is_sync_text}}) ? mojo::Message::kFlagIsSync : 0);
{%-   endif %}
{%- endmacro %}

{%- macro build_serialized_message(message_name, param_name_prefix,
                                   params_struct, params_description,
                                   flags_text, message_object_name) %}
  mojo::Message {{message_object_name}}(
      {{message_name}}, {{flags_text}}, 0, 0, nullptr);
  auto* buffer = {{message_object_name}}.payload_buffer();
  {{params_struct|get_qualified_name_for_kind(internal=True)}}::BufferWriter
      params;
  mojo::internal::SerializationContext serialization_context;
  {{struct_macros.serialize(params_struct, params_description,
                            param_name_prefix, "params", "buffer",
                            "&serialization_context")}}
  {{message_object_name}}.AttachHandlesFromSerializationContext(
      &serialization_context);
{%- endmacro %}

{%- macro define_message_type(interface, message_typename, message_name,
                              is_response, method, parameters, params_struct,
                              params_description, use_once_callback) -%}
class {{message_typename}}
    : public mojo::internal::UnserializedMessageContext {
 public:
  static const mojo::internal::UnserializedMessageContext::Tag kMessageTag;

  explicit {{message_typename}}(
      uint32_t message_flags
{%-     for param in parameters %}
      , {{param.kind|cpp_wrapper_param_type}} param_{{param.name}}
{%-     endfor %}
  )
      : mojo::internal::UnserializedMessageContext(
          &kMessageTag,
          {{message_name}},
          message_flags)
{%-     for param in parameters -%}
{%-       if param.kind|is_interface_kind %}
      , param_{{param.name}}_(param_{{param.name}}.PassInterface())
{%-       else %}
      , param_{{param.name}}_(std::move(param_{{param.name}}))
{%-       endif %}
{%-     endfor -%} {}
  ~{{message_typename}}() override = default;

  static mojo::Message Build(
      bool serialize,
{%-   if not is_response %}
      bool expects_response,
{%-   endif %}
      bool is_sync
{%-   if parameters -%}
      ,
      {{declare_params("param_", parameters)}}
{%-   endif %}) {

    {{build_message_flags(is_response, "is_sync", "expects_response",
                          "kFlags")}}

    if (!serialize) {
      return mojo::Message(std::make_unique<{{message_typename}}>(
          kFlags
{%-     for param in parameters %}
          , std::move(param_{{param.name}})
{%-     endfor %}
          ));
    }

    DCHECK(serialize);
    {{build_serialized_message(message_name, "param_%s", params_struct,
                               params_description, "kFlags", "message")}}
    return message;
  }

{%      if not is_response %}
  void Dispatch({{interface.name}}* impl
{%-       if method.response_parameters != None -%}
{%-         if use_once_callback -%}
      , {{interface.name}}::{{method.name}}Callback callback
{%-         else -%}
      , const {{interface.name}}::{{method.name}}Callback& callback
{%-         endif -%}
{%-       endif -%}) {
    impl->{{method.name}}(
{%-       for param in parameters -%}
{%-         if param.kind|is_interface_kind %}
        {{param.kind|get_name_for_kind}}Ptr(std::move(param_{{param.name}}_))
{%-         else %}
        std::move(param_{{param.name}}_)
{%-         endif %}
        {%- if not loop.last -%}, {%- endif %}
{%-       endfor %}
{%-       if method.response_parameters != None %}
        {%- if parameters -%}, {% endif -%}std::move(callback)
{%-       endif -%});
  }
{%-     else %}
  void Dispatch({{interface.name}}::{{method.name}}Callback* callback) {
    std::move(*callback).Run(
{%-       for param in parameters -%}
{%-         if param.kind|is_interface_kind %}
        {{param.kind|get_name_for_kind}}Ptr(std::move(param_{{param.name}}_))
{%-         else %}
        std::move(param_{{param.name}}_)
{%-         endif %}
        {%- if not loop.last -%}, {% endif -%}
{%-       endfor -%});
  }

{%        if method.sync %}
  void HandleSyncResponse(
{%          for param in parameters %}
      {{param.kind|cpp_wrapper_call_type}}* out_{{param.name}}
      {%- if not loop.last -%}, {% endif -%}
{%-         endfor -%}) {
{%          for param in parameters -%}
{%-           if param.kind|is_interface_kind %}
    out_{{param.name}}->Bind(std::move(param_{{param.name}}_));
{%-           else %}
    *out_{{param.name}} = std::move(param_{{param.name}}_);
{%-           endif %}
{%          endfor %}
  }
{%-       endif -%}
{%-     endif %}

 private:
  // mojo::internal::UnserializedMessageContext:
  void Serialize(mojo::internal::SerializationContext* serialization_context,
                 mojo::internal::Buffer* buffer) override {
    {{params_struct|get_qualified_name_for_kind(internal=True)}}::BufferWriter
        params;
    {{struct_macros.serialize(params_struct, params_description, "param_%s_",
                              "params", "buffer", "serialization_context")}}
  }

{%-     for param in parameters %}
  {{param.kind|cpp_wrapper_type}} param_{{param.name}}_;
{%-     endfor %}

  DISALLOW_COPY_AND_ASSIGN({{message_typename}});
};

const mojo::internal::UnserializedMessageContext::Tag
{{message_typename}}::kMessageTag = {};
{%- endmacro -%}
