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

{%- set class_name = interface.name %}
{%- set proxy_name = interface.name ~ "Proxy" %}
{%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %}

{%- macro alloc_params(struct, params, message, description) %}
  mojo::internal::SerializationContext serialization_context;
  serialization_context.TakeHandlesFromMessage({{message}});
  bool success = true;
{%-   for param in struct.packed.packed_fields_in_ordinal_order %}
  {{param.field.kind|cpp_wrapper_call_type}} p_{{param.field.name}}{};
{%-   endfor %}
  {{struct.name}}DataView input_data_view({{params}}, &serialization_context);
  {{struct_macros.deserialize(struct, "input_data_view", "p_%s", "success")}}
  if (!success) {
    ReportValidationErrorForMessage(
        {{message}},
        mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,
        "{{description}} deserializer");
    return false;
  }
{%- endmacro %}

{%- macro pass_params(parameters) %}
{%-   for param in parameters %}
std::move(p_{{param.name}})
{%-     if not loop.last %}, {% endif %}
{%-   endfor %}
{%- endmacro %}

{#--- Begin #}
const char {{class_name}}::Name_[] = "{{namespace}}.{{class_name}}";
{%-  if interface.uuid %}
constexpr base::Token {{class_name}}::Uuid_;
{%-  endif %}

{#--- Constants #}
{%-  for constant in interface.constants %}
{%-   if constant.kind|is_string_kind %}
const char {{interface.name}}::{{constant.name}}[] = {{constant|constant_value}};
{%-   else %}
constexpr {{constant.kind|cpp_pod_type}} {{interface.name}}::{{constant.name}};
{%-   endif %}
{%- endfor %}


{%- for method in interface.methods %}
{%-   if method.sync %}
bool {{class_name}}::{{method.name}}({{interface_macros.declare_sync_method_params("", method)}}) {
  NOTREACHED();
  return false;
}
{%-   endif %}
{%- endfor %}

{#--- ForwardToCallback definition #}
{%- for method in interface.methods -%}
{%-   if method.response_parameters != None %}
{%-     if method.sync %}
class {{class_name}}_{{method.name}}_HandleSyncResponse
    : public mojo::MessageReceiver {
 public:
  {{class_name}}_{{method.name}}_HandleSyncResponse(
      bool* result
{%-       for param in method.response_parameters -%}
      , {{param.kind|cpp_wrapper_call_type}}* out_{{param.name}}
{%-       endfor %})
      : result_(result)
{%-       for param in method.response_parameters -%}
        , out_{{param.name}}_(out_{{param.name}})
{%-       endfor %} {
    DCHECK(!*result_);
  }
  bool Accept(mojo::Message* message) override;
 private:
  bool* result_;
{%-       for param in method.response_parameters %}
  {{param.kind|cpp_wrapper_call_type}}* out_{{param.name}}_;
{%-       endfor -%}
  DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_HandleSyncResponse);
};
{%-     endif %}

class {{class_name}}_{{method.name}}_ForwardToCallback
    : public mojo::MessageReceiver {
 public:
  {{class_name}}_{{method.name}}_ForwardToCallback(
{%-     if use_once_callback %}
      {{class_name}}::{{method.name}}Callback callback
{%-     else %}
      const {{class_name}}::{{method.name}}Callback& callback
{%-     endif %}
      ) : callback_(std::move(callback)) {
  }
  bool Accept(mojo::Message* message) override;
 private:
  {{class_name}}::{{method.name}}Callback callback_;
  DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ForwardToCallback);
};
{%-   endif %}
{%- endfor %}

{{proxy_name}}::{{proxy_name}}(mojo::MessageReceiverWithResponder* receiver)
    : receiver_(receiver) {
}

{#--- Proxy definitions #}

{%- for method in interface.methods %}
{%-   set message_name =
          "internal::k%s_%s_Name"|format(interface.name, method.name) %}
{%-   set params_struct = method.param_struct %}
{%-   set params_description =
          "%s.%s request"|format(interface.name, method.name) %}
{%-   set message_typename = "%s_%s_Message"|format(proxy_name, method.name) %}

{%-   if method|method_supports_lazy_serialization %}
{{interface_macros.define_message_type(
    interface, message_typename, message_name, False, method, method.parameters,
    params_struct, params_description, use_once_callback)}}
{%-   endif %}

{%-   if method.sync %}
bool {{proxy_name}}::{{method.name}}(
    {{interface_macros.declare_sync_method_params("param_", method)}}) {
#if BUILDFLAG(MOJO_TRACE_ENABLED)
  TRACE_EVENT0("mojom", "{{namespace_as_string}}::{{class_name}}::{{method.name}}");
#endif
  const bool kExpectsResponse = true;
  const bool kIsSync = true;
{%-   if method|method_supports_lazy_serialization %}
  const bool kSerialize = receiver_->PrefersSerializedMessages();
  auto message = {{message_typename}}::Build(
      kSerialize, kExpectsResponse, kIsSync
{%-     for param in method.parameters -%}
      , std::move(param_{{param.name}})
{%-     endfor %});
{%-   else %}
  {{interface_macros.build_message_flags(False, "kIsSync", "kExpectsResponse",
                                         "kFlags")}}
  {{interface_macros.build_serialized_message(
      message_name, "param_%s", params_struct, params_description, "kFlags",
      "message")}}
{%-   endif %}

#if defined(ENABLE_IPC_FUZZER)
  message.set_interface_name({{class_name}}::Name_);
  message.set_method_name("{{method.name}}");
#endif

  bool result = false;
  std::unique_ptr<mojo::MessageReceiver> responder(
      new {{class_name}}_{{method.name}}_HandleSyncResponse(
          &result
{%-     for param in method.response_parameters -%}
          , out_param_{{param.name}}
{%-     endfor %}));
  ignore_result(receiver_->AcceptWithResponder(&message, std::move(responder)));
  return result;
}
{%-   endif %}

void {{proxy_name}}::{{method.name}}(
    {{interface_macros.declare_request_params("in_", method, use_once_callback)}}) {
#if BUILDFLAG(MOJO_TRACE_ENABLED)
  TRACE_EVENT0("mojom", "{{namespace_as_string}}::{{class_name}}::{{method.name}}");
#endif
{%- if method.response_parameters != None %}
  const bool kExpectsResponse = true;
{%- else %}
  const bool kExpectsResponse = false;
{%- endif %}
  const bool kIsSync = false;
{%-   if method|method_supports_lazy_serialization %}
  const bool kSerialize = receiver_->PrefersSerializedMessages();
  auto message = {{message_typename}}::Build(
      kSerialize, kExpectsResponse, kIsSync
{%-     for param in method.parameters -%}
      , std::move(in_{{param.name}})
{%-     endfor %});
{%-   else %}
  {{interface_macros.build_message_flags(False, "kIsSync", "kExpectsResponse",
                                         "kFlags")}}
  {{interface_macros.build_serialized_message(
      message_name, "in_%s", params_struct, params_description, "kFlags",
      "message")}}
{%-   endif %}

#if defined(ENABLE_IPC_FUZZER)
  message.set_interface_name({{class_name}}::Name_);
  message.set_method_name("{{method.name}}");
#endif

{%- if method.response_parameters != None %}
  std::unique_ptr<mojo::MessageReceiver> responder(
      new {{class_name}}_{{method.name}}_ForwardToCallback(
          std::move(callback)));
  ignore_result(receiver_->AcceptWithResponder(&message, std::move(responder)));
{%- else %}
  // This return value may be ignored as false implies the Connector has
  // encountered an error, which will be visible through other means.
  ignore_result(receiver_->Accept(&message));
{%- endif %}
}
{%- endfor %}

{#--- ProxyToResponder definition #}
{%- for method in interface.methods -%}
{%-   if method.response_parameters != None %}
{%-     set message_name =
            "internal::k%s_%s_Name"|format(interface.name, method.name) %}
{%-     set response_params_struct = method.response_param_struct %}
{%-     set params_description =
            "%s.%s response"|format(interface.name, method.name) %}
{%-     set response_message_typename =
            "%s_%s_Response_Message"|format(interface.name, method.name) %}
class {{class_name}}_{{method.name}}_ProxyToResponder {
 public:
  static {{class_name}}::{{method.name}}Callback CreateCallback(
      uint64_t request_id,
      bool is_sync,
      std::unique_ptr<mojo::MessageReceiverWithStatus> responder) {
    std::unique_ptr<{{class_name}}_{{method.name}}_ProxyToResponder> proxy(
        new {{class_name}}_{{method.name}}_ProxyToResponder(
            request_id, is_sync, std::move(responder)));
{%- if use_once_callback %}
    return base::BindOnce(&{{class_name}}_{{method.name}}_ProxyToResponder::Run,
                          std::move(proxy));
{%- else %}
    return base::Bind(&{{class_name}}_{{method.name}}_ProxyToResponder::Run,
                      base::Passed(&proxy));
{%- endif %}
  }

  ~{{class_name}}_{{method.name}}_ProxyToResponder() {
#if DCHECK_IS_ON()
    if (responder_) {
      // If we're being destroyed without being run, we want to ensure the
      // binding endpoint has been closed. This checks for that asynchronously.
      // We pass a bound generated callback to handle the response so that any
      // resulting DCHECK stack will have useful interface type information.
      responder_->IsConnectedAsync(base::BindOnce(&OnIsConnectedComplete));
    }
#endif
    // If the Callback was dropped then deleting the responder will close
    // the pipe so the calling application knows to stop waiting for a reply.
    responder_ = nullptr;
  }

 private:
  {{class_name}}_{{method.name}}_ProxyToResponder(
      uint64_t request_id,
      bool is_sync,
      std::unique_ptr<mojo::MessageReceiverWithStatus> responder)
      : request_id_(request_id),
        is_sync_(is_sync),
        responder_(std::move(responder)) {
  }

#if DCHECK_IS_ON()
  static void OnIsConnectedComplete(bool connected) {
    DCHECK(!connected)
        << "{{class_name}}::{{method.name}}Callback was destroyed without "
        << "first either being run or its corresponding binding being closed. "
        << "It is an error to drop response callbacks which still correspond "
        << "to an open interface pipe.";
  }
#endif

  void Run(
      {{interface_macros.declare_params("in_", method.response_parameters)}});

  uint64_t request_id_;
  bool is_sync_;
  std::unique_ptr<mojo::MessageReceiverWithStatus> responder_;

  DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ProxyToResponder);
};

{%-     if method|method_supports_lazy_serialization %}
{{interface_macros.define_message_type(
    interface, response_message_typename, message_name, True, method,
    method.response_parameters, response_params_struct, params_description,
    use_once_callback)}}
{%-     endif %}

bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept(
    mojo::Message* message) {
#if BUILDFLAG(MOJO_TRACE_ENABLED)
  TRACE_EVENT1("mojom", "{{namespace_as_string}}::{{class_name}}::{{method.name}}Callback",
               "message", message->name());
#endif
  mojo::internal::MessageDispatchContext dispatch_context(message);
{%-     if method|method_supports_lazy_serialization %}
  if (!message->is_serialized()) {
    auto context =
        message->TakeUnserializedContext<{{response_message_typename}}>();
    if (!context) {
      // The Message was not of the expected type. It may be a valid message
      // which was build using a different variant of these bindings. Force
      // serialization before dispatch in this case.
      message->SerializeIfNecessary();
    } else {
      if (!callback_.is_null())
        context->Dispatch(&callback_);
      return true;
    }
  }
{%-     endif %}

  DCHECK(message->is_serialized());
  internal::{{class_name}}_{{method.name}}_ResponseParams_Data* params =
      reinterpret_cast<
          internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>(
              message->mutable_payload());

{%-     set desc = class_name~"::"~method.name~" response" %}
  {{alloc_params(method.response_param_struct, "params", "message", desc)}}
  if (!callback_.is_null())
    std::move(callback_).Run({{pass_params(method.response_parameters)}});
  return true;
}

void {{class_name}}_{{method.name}}_ProxyToResponder::Run(
    {{interface_macros.declare_params("in_", method.response_parameters)}}) {
{%-   if method|method_supports_lazy_serialization %}
  const bool kSerialize = responder_->PrefersSerializedMessages();
  auto message = {{response_message_typename}}::Build(kSerialize, is_sync_
{%-     for param in method.response_parameters -%}
      , std::move(in_{{param.name}})
{%-     endfor %});
{%-   else %}
  {{interface_macros.build_message_flags(True, "is_sync_", "false", "kFlags")}}
  {{interface_macros.build_serialized_message(
      message_name, "in_%s", response_params_struct,
      response_params_description, "kFlags", "message")}}
{%-   endif %}

#if BUILDFLAG(MOJO_TRACE_ENABLED)
  TRACE_EVENT1("mojom", "(Impl){{namespace_as_string}}::{{class_name}}::{{method.name}}Callback",
               "message", message.name());
#endif

#if defined(ENABLE_IPC_FUZZER)
  message.set_interface_name({{class_name}}::Name_);
  message.set_method_name("{{method.name}}");
#endif

  message.set_request_id(request_id_);
  ignore_result(responder_->Accept(&message));
  // TODO(darin): Accept() returning false indicates a malformed message, and
  // that may be good reason to close the connection. However, we don't have a
  // way to do that from here. We should add a way.
  responder_ = nullptr;
}
{%-   endif -%}

{%-   if method.sync %}
bool {{class_name}}_{{method.name}}_HandleSyncResponse::Accept(
    mojo::Message* message) {
{%-     if method|method_supports_lazy_serialization %}
  if (!message->is_serialized()) {
    auto context =
        message->TakeUnserializedContext<{{response_message_typename}}>();
    if (!context) {
      // The Message was not of the expected type. It may be a valid message
      // which was built using a different variant of these bindings. Force
      // serialization before dispatch in this case.
      message->SerializeIfNecessary();
    } else {
      context->HandleSyncResponse(
{%-       for param in method.response_parameters %}
          out_{{param.name}}_
{%-         if not loop.last -%}, {% endif -%}
{%-       endfor %});
      *result_ = true;
      mojo::internal::SyncMessageResponseSetup::SetCurrentSyncResponseMessage(
          message);
      return true;
    }
  }
{%-     endif %}

  DCHECK(message->is_serialized());
  internal::{{class_name}}_{{method.name}}_ResponseParams_Data* params =
      reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>(
          message->mutable_payload());

{%-       set desc = class_name~"::"~method.name~" response" %}
  {{alloc_params(method.response_param_struct, "params", "message", desc)}}

{%-       for param in method.response_parameters %}
  *out_{{param.name}}_ = std::move(p_{{param.name}});
{%-       endfor %}
  mojo::internal::SyncMessageResponseSetup::SetCurrentSyncResponseMessage(
      message);
  *result_ = true;
  return true;
}
{%- endif %}

{%- endfor %}

{#--- StubDispatch definition #}

// static
bool {{class_name}}StubDispatch::Accept(
    {{interface.name}}* impl,
    mojo::Message* message) {
{%- if interface.methods %}
  switch (message->header()->name) {
{%-   for method in interface.methods %}
    case internal::k{{class_name}}_{{method.name}}_Name: {
#if BUILDFLAG(MOJO_TRACE_ENABLED)
  TRACE_EVENT1("mojom", "(Impl){{namespace_as_string}}::{{class_name}}::{{method.name}}",
               "message", message->name());
#endif
{%-     if method.response_parameters == None %}
      mojo::internal::MessageDispatchContext context(message);
{%-       if method|method_supports_lazy_serialization %}
      if (!message->is_serialized()) {
        auto context = message->TakeUnserializedContext<
            {{proxy_name}}_{{method.name}}_Message>();
        if (!context) {
          // The Message was not of the expected type. It may be a valid message
          // which was serialized using a different variant of these bindings.
          // Force serialization before dispatch in this case.
          message->SerializeIfNecessary();
        } else {
          context->Dispatch(impl);
          return true;
        }
      }
{%-       endif %}

      DCHECK(message->is_serialized());
      internal::{{class_name}}_{{method.name}}_Params_Data* params =
          reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>(
              message->mutable_payload());

{%-       set desc = class_name~"::"~method.name %}
      {{alloc_params(method.param_struct, "params", "message", desc)|
          indent(4)}}
      // A null |impl| means no implementation was bound.
      assert(impl);
      impl->{{method.name}}({{pass_params(method.parameters)}});
      return true;
{%-     else %}
      break;
{%-     endif %}
    }
{%-   endfor %}
  }
{%- endif %}
  return false;
}

// static
bool {{class_name}}StubDispatch::AcceptWithResponder(
    {{interface.name}}* impl,
    mojo::Message* message,
    std::unique_ptr<mojo::MessageReceiverWithStatus> responder) {
{%- if interface.methods %}
  switch (message->header()->name) {
{%-   for method in interface.methods %}
    case internal::k{{class_name}}_{{method.name}}_Name: {
#if BUILDFLAG(MOJO_TRACE_ENABLED)
  TRACE_EVENT1("mojom", "(Impl){{namespace_as_string}}::{{class_name}}::{{method.name}}",
               "message", message->name());
#endif
{%-     if method.response_parameters != None %}
      mojo::internal::MessageDispatchContext context(message);
{%-       if method|method_supports_lazy_serialization %}
      if (!message->is_serialized()) {
        auto context = message->TakeUnserializedContext<
            {{proxy_name}}_{{method.name}}_Message>();
        if (!context) {
          // The Message was not of the expected type. It may be a valid message
          // which was built using a different variant of these bindings. Force
          // serialization before dispatch in this case.
          message->SerializeIfNecessary();
        } else {
          {{class_name}}::{{method.name}}Callback callback =
              {{class_name}}_{{method.name}}_ProxyToResponder::CreateCallback(
                  message->request_id(),
                  message->has_flag(mojo::Message::kFlagIsSync),
                  std::move(responder));
          context->Dispatch(impl, std::move(callback));
          return true;
        }
      }
{%-       endif %}

      internal::{{class_name}}_{{method.name}}_Params_Data* params =
          reinterpret_cast<
              internal::{{class_name}}_{{method.name}}_Params_Data*>(
                  message->mutable_payload());

{%-       set desc = class_name~"::"~method.name %}
      {{alloc_params(method.param_struct, "params", "message", desc)|
          indent(4)}}
      {{class_name}}::{{method.name}}Callback callback =
          {{class_name}}_{{method.name}}_ProxyToResponder::CreateCallback(
              message->request_id(),
              message->has_flag(mojo::Message::kFlagIsSync),
              std::move(responder));
      // A null |impl| means no implementation was bound.
      assert(impl);
      impl->{{method.name}}(
{%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}std::move(callback));
      return true;
{%-     else %}
      break;
{%-     endif %}
    }
{%-   endfor %}
  }
{%- endif %}
  return false;
}

{#--- Request validator definitions #}

bool {{class_name}}RequestValidator::Accept(mojo::Message* message) {
  if (!message->is_serialized() ||
      mojo::internal::ControlMessageHandler::IsControlMessage(message)) {
    return true;
  }

  mojo::internal::ValidationContext validation_context(
    message->payload(), message->payload_num_bytes(),
    message->handles()->size(), message->payload_num_interface_ids(), message,
    "{{class_name}} RequestValidator");

  switch (message->header()->name) {
{%- for method in interface.methods %}
    case internal::k{{class_name}}_{{method.name}}_Name: {
{%-   if method.response_parameters != None %}
      if (!mojo::internal::ValidateMessageIsRequestExpectingResponse(
              message, &validation_context)) {
        return false;
      }
{%-   else %}
      if (!mojo::internal::ValidateMessageIsRequestWithoutResponse(
              message, &validation_context)) {
        return false;
      }
{%-   endif %}
      if (!mojo::internal::ValidateMessagePayload<
               internal::{{class_name}}_{{method.name}}_Params_Data>(
                  message, &validation_context)) {
        return false;
      }
      return true;
    }
{%- endfor %}
    default:
      break;
  }

  // Unrecognized message.
  ReportValidationError(
      &validation_context,
      mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
  return false;
}

{#--- Response validator definitions #}
{% if interface|has_callbacks %}
bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) {
  if (!message->is_serialized() ||
      mojo::internal::ControlMessageHandler::IsControlMessage(message)) {
    return true;
  }

  mojo::internal::ValidationContext validation_context(
    message->payload(), message->payload_num_bytes(),
    message->handles()->size(), message->payload_num_interface_ids(), message,
    "{{class_name}} ResponseValidator");

  if (!mojo::internal::ValidateMessageIsResponse(message, &validation_context))
    return false;
  switch (message->header()->name) {
{%- for method in interface.methods if method.response_parameters != None %}
    case internal::k{{class_name}}_{{method.name}}_Name: {
      if (!mojo::internal::ValidateMessagePayload<
               internal::{{class_name}}_{{method.name}}_ResponseParams_Data>(
                    message, &validation_context)) {
        return false;
      }
      return true;
    }
{%- endfor %}
    default:
      break;
  }

  // Unrecognized message.
  ReportValidationError(
      &validation_context,
      mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
  return false;
}
{%- endif -%}

{#--- Testing interceptor #}
{%- for method in interface.methods %}
void {{interface.name}}InterceptorForTesting::{{method.name}}({{interface_macros.declare_request_params("", method, use_once_callback)}}) {
  GetForwardingInterface()->{{method.name}}(
    {%- for param in method.parameters -%}
    std::move({{param.name}}){%- if not loop.last %}, {% endif %}
    {%- endfor %}
    {%-   if method.response_parameters != None -%}
    {%-     if method.parameters %}, {% endif -%}
    std::move(callback)
    {%-   endif -%}
  );
}
{%- endfor %}

{#--- Async wait helper for testing #}
{{interface.name}}AsyncWaiter::{{interface.name}}AsyncWaiter(
    {{interface.name}}* proxy) : proxy_(proxy) {}

{{interface.name}}AsyncWaiter::~{{interface.name}}AsyncWaiter() = default;

{%  for method in interface.methods if method.response_parameters != None -%}
void {{interface.name}}AsyncWaiter::{{method.name}}(
    {{interface_macros.declare_sync_method_params("", method)}}) {
  base::RunLoop loop;
  proxy_->{{method.name}}(
{%-   for param in method.parameters -%}
      std::move({{param.name}}),
{%-   endfor %}
{%-   if use_once_callback %}
      base::BindOnce(
{%-   else %}
      base::Bind(
{%-   endif %}
          [](base::RunLoop* loop
{%-   for param in method.response_parameters -%},
             {{param.kind|cpp_wrapper_call_type}}* out_{{param.name}}
{%    endfor -%}
{%-   for param in method.response_parameters -%},
             {{param.kind|cpp_wrapper_param_type}} {{param.name}}
{%-   endfor %}) {
{%-   for param in method.response_parameters -%}
            *out_{{param.name}} = std::move({{param.name}});
{%-   endfor %}
            loop->Quit();
          },
          &loop
{%-   for param in method.response_parameters -%},
          out_{{param.name}}
{%-   endfor %}));
  loop.Run();
}
{% endfor %}
