{#  TODO(yzshen): Make these templates more readable. #}
{%- import "send_validation_macros.tmpl" as send_validation_macros %}

{#  Serializes the specified struct.
    |struct| is the struct definition.
    |struct_display_name| is the display name for the struct that can be showed
    in error/log messages, for example, "FooStruct", "FooMethod request".
    |input_field_pattern| should be a pattern that contains one string
    placeholder, for example, "input->%s", "p_%s". The placeholder will be
    substituted with struct field names to refer to the input fields.
    |fragment| is the name of the MessageFragment to serialize into.
    |input_may_be_temp|: please see the comments of get_serialized_size.
    This macro is expanded to do serialization for both:
    - user-defined structs: the input is an instance of the corresponding struct
      wrapper class.
    - method parameters/response parameters: the input is a list of
      arguments. #}
{%- macro serialize(struct, struct_display_name, input_field_pattern, fragment,
        send_validation_compiler_setting, input_may_be_temp=False) -%}
  {{fragment}}.Allocate();
{%- for pf in struct.packed.packed_fields_in_ordinal_order %}

{%-   if pf|is_nullable_value_kind_packed_field %}
{%-     if pf|is_primary_nullable_value_kind_packed_field %}
{%-       set original_field = pf.original_field %}
{%-       set has_value_pf = pf %}
{%-       set value_pf = pf.linked_value_packed_field %}
{%-       set input_field = input_field_pattern|format(original_field.name) %}
{%-       set name = original_field.name %}
{%-       set kind = original_field.kind %}
{%-       set serializer_type = value_pf.field.kind|unmapped_type_for_serializer %}
  {{fragment}}->{{has_value_pf.field.name}} = {{input_field}}.has_value();

{%-       if kind|is_enum_kind %}
  if ({{input_field}}.has_value()) {
    {%- set serialization_args =
        [input_field + ".value()",
        "&" + fragment + "->" + value_pf.field.name]
    %}
    {{send_validation_macros.get_serialize_call(
      "default",
      serializer_type,
      serialization_args)|indent(4)}}
  } else {
    {{fragment}}->{{value_pf.field.name}} =
        static_cast<int32_t>({{serializer_type}}::kMinValue);
  }
{%-       else %}
  if ({{input_field}}.has_value()) {
    {{fragment}}->{{value_pf.field.name}} = {{input_field}}.value();
  }
{%-       endif %}
{%-     endif %}
{%-   else %}

{%-     set input_field = input_field_pattern|format(pf.field.name) %}
{%-     set name = pf.field.name %}
{%-     set kind = pf.field.kind %}
{%-     set serializer_type = kind|unmapped_type_for_serializer %}

{%-     if kind|is_object_kind or kind|is_any_handle_or_interface_kind %}
{%-       set original_input_field = input_field_pattern|format(name) %}
{%-       set input_field = "in_%s"|format(name) if input_may_be_temp
                                                 else original_input_field %}
{%-       if input_may_be_temp %}

  decltype({{original_input_field}}) in_{{name}} = {{original_input_field}};

{%-       endif %}
{%-     endif %}

{%-     if kind|is_object_kind %}
{%-       if kind|is_array_kind or kind|is_map_kind %}
  mojo::internal::MessageFragment<
      typename decltype({{fragment}}->{{name}})::BaseType>
      {{name}}_fragment({{fragment}}.message());
  constexpr const mojo::internal::ContainerValidateParams& {{name}}_validate_params =
      {{kind|get_container_validate_params_ctor_args|indent(6)}};
{%-         set serialization_args =
              [input_field,
              name + "_fragment",
              "&" + name + "_validate_params"]
%}
  {{send_validation_macros.get_serialize_call(
      send_validation_compiler_setting,
      serializer_type,
      serialization_args)|indent(2)}}

  {{fragment}}->{{name}}.Set(
      {{name}}_fragment.is_null() ? nullptr : {{name}}_fragment.data());
{%-       elif kind|is_union_kind %}
  mojo::internal::MessageFragment<decltype({{fragment}}->{{name}})>
      {{name}}_fragment({{fragment}}.message());
  {{name}}_fragment.Claim(&{{fragment}}->{{name}});

{%-         set serialization_args =
              [input_field,
              name + "_fragment",
              "true"]
%}
  {{send_validation_macros.get_serialize_call(
      send_validation_compiler_setting,
      serializer_type,
      serialization_args)|indent(2)}}
{%-       else %}
  mojo::internal::MessageFragment<
      typename decltype({{fragment}}->{{name}})::BaseType> {{name}}_fragment(
          {{fragment}}.message());

{%-         set serialization_args =
              [input_field,
              name + "_fragment"]
%}
  {{send_validation_macros.get_serialize_call(
      send_validation_compiler_setting,
      serializer_type,
      serialization_args)|indent(2)}}

  {{fragment}}->{{name}}.Set(
      {{name}}_fragment.is_null() ? nullptr : {{name}}_fragment.data());
{%-       endif %}
{%-       if not kind|is_nullable_kind%}

  {{send_validation_macros.get_validation_check(
      send_validation_compiler_setting,
      "%s->%s.is_null()" | format(fragment, name),
      "mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER",
      "null %s in %s"| format(name, struct_display_name))|indent(2)}}
{%-       endif %}

{%-     elif kind|is_any_handle_or_interface_kind %}
{%-       set serialization_args =
            [input_field,
            "&" + fragment + "->" + name,
            "&"+ fragment + ".message()"]
%}

  {{send_validation_macros.get_serialize_call(
      "default",
      serializer_type,
      serialization_args)|indent(2)}}

{%-       if not kind|is_nullable_kind %}
{%-         if kind|is_associated_kind %}
{%-           set error_code =
          "mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID"
%}
{%-         else %}
{%-           set error_code =
                "mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE"
%}
{%-         endif %}

  {{send_validation_macros.get_validation_check(
      send_validation_compiler_setting,
      "!mojo::internal::IsHandleOrInterfaceValid(%s->%s)" | format(fragment, name),
      error_code,
      "invalid %s in %s" | format(name, struct_display_name))|indent(2)}}
{%-       endif %}

{%-     elif kind|is_enum_kind %}
{%-       set serialization_args =
            [input_field,
            "&" + fragment + "->" + name]
%}

  {{send_validation_macros.get_serialize_call(
      "default",
      serializer_type,
      serialization_args)|indent(2)}}
{%-     else %}

  {{fragment}}->{{name}} = {{input_field}};
{%-     endif %}
{%-   endif %}
{%- endfor %}
{%- endmacro -%}

{#  Deserializes the specified struct.
    |struct| is the struct definition.
    |input| is the name of the input struct data view. It is expected to be
    non-null.
    |output_field_pattern| should be a pattern that contains one string
    placeholder, for example, "result->%s", "p_%s". The placeholder will be
    substituted with struct field names to refer to the output fields.
    |message| is the name of the Message object we're deserializing.
    |success| is the name of a bool variable to track success of the operation.
    This macro is expanded to do deserialization for both:
    - user-defined structs: the output is an instance of the corresponding
      struct wrapper class.
    - method parameters/response parameters: the output is a list of
      arguments. #}
{%- macro deserialize(struct, input, output_field_pattern, success) -%}
{%-   for pf in struct.packed.packed_fields_in_ordinal_order %}
{%-     if pf|is_nullable_value_kind_packed_field %}
{%-       if pf|is_primary_nullable_value_kind_packed_field %}
{%-         set original_field = pf.original_field %}
{%-         set output_field = output_field_pattern|format(original_field.name) %}
{%-         set name = original_field.name %}
{%-         set kind = original_field.kind %}
{%-         if kind|is_enum_kind %}
  if ({{success}} && !{{input}}.Read{{name|under_to_camel}}(&{{output_field}})) {
    {{success}} = false;
{%-         else %}
  if ({{success}}) {
    {{output_field}} = {{input}}.{{name}}();
{%-         endif %}
  }
{%-       endif %}
{%-     else %}
{%-       set output_field = output_field_pattern|format(pf.field.name) %}
{%-       set name = pf.field.name %}
{%-       set kind = pf.field.kind %}
{%-       if kind|is_object_kind or kind|is_enum_kind %}
  if ({{success}} && !{{input}}.Read{{name|under_to_camel}}(&{{output_field}}))
    {{success}} = false;
{%-       elif kind|is_any_handle_kind %}
  if ({{success}})
    {{output_field}} = {{input}}.Take{{name|under_to_camel}}();
{%-       elif kind|is_any_interface_kind %}
  if ({{success}}) {
    {{output_field}} =
        {{input}}.Take{{name|under_to_camel}}<decltype({{output_field}})>();
  }
{%-       else %}
  if ({{success}})
    {{output_field}} = {{input}}.{{name}}();
{%-       endif %}
{%-     endif %}
{%-   endfor %}
{%- endmacro %}

{%- macro assert_nullable_output_type_if_necessary(kind, name) -%}
{%-   if kind|is_nullable_kind and not kind|is_native_only_kind %}
static_assert(
    mojo::internal::IsValidUserTypeForOptionalValue<
        {{kind|unmapped_type_for_serializer}}, UserType>(),
    "Attempting to read the optional `{{name}}` field into a type which "
    "cannot represent a null value. Either wrap the destination object "
    "with std::optional, ensure that any corresponding "
    "{Struct/Union/Array/String}Traits define the necessary IsNull and "
    "SetToNull methods, or use `MaybeRead{{name|under_to_camel}}` instead "
    "of `Read{{name|under_to_camel}} if you're fine with null values being "
    "silently ignored in this case.");
{%-   endif %}
{%- endmacro %}
