#include "mongoc-opts-helpers-private.h"
#include "mongoc-opts-private.h"
#include "mongoc-error.h"
#include "mongoc-util-private.h"
#include "mongoc-client-private.h"

{{ header_comment }}
{% for struct_type, description in opts_structs.items() %}
{% if description.is_shared or not description.generate_code %}{% continue %}{% endif %}
{% set struct_name = struct_type.split('_t', -1)[0] %}


bool
_{{ struct_name }}_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   {{ struct_type }} *{{ struct_name }},
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

{% for path, opt_name, info in paths(description) %}
{% if info['type'] == 'bool' %}
   {{ struct_name }}->{{ path }} = {{ description.default(opt_name, 'false') }};
{% elif info['type'].startswith('int') or info['type'].startswith('uint') %}
   {{ struct_name }}->{{ path }} = {{ description.default(opt_name, 0) }};
{% elif info['type'] == 'bson_validate_flags_t' %}
   {{ struct_name }}->{{ path }} = {{ description.default(opt_name, 'BSON_VALIDATE_NONE') }};
{% elif info['type'] == 'mongoc_write_bypass_document_validation_t' %}
   {{ struct_name }}->{{ path }} =
      {{ description.default(opt_name, 'MONGOC_BYPASS_DOCUMENT_VALIDATION_DEFAULT') }};
{% elif info['type'] == 'bson_value_t' %}
   memset (&{{ struct_name }}->{{ path }}, 0, sizeof (bson_value_t));
{% elif info['type'] in ('document', 'array') %}
   bson_init (&{{ struct_name }}->{{ path }});
{% elif info['type'] == 'utf8' %}
   {% set dflt = description.default(opt_name, "") -%}
   {{ struct_name }}->{{ path }} = {{ "NULL" if dflt is none else (dflt|tojson) }};
{% elif info['type'] == 'timestamp' %}
   memset (&{{ struct_name }}->{{ path }}, 0, sizeof (mongoc_timestamp_t));
{% else %}
   {{ struct_name }}->{{ path }} = {{ description.default(opt_name, 'NULL') }};
{% endif %}
{% if info['check_set'] %}
   {{ struct_name }}->{{ opt_name }}_is_set = false;
{% endif %}
{% endfor %}
   bson_init (&{{ struct_name }}->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
{% for path, opt_name, info in paths(description) %}
{% if info.get('internal') %}
{% continue %}
{% endif %}
{% if info['type'] == 'utf8' %}
{% set the_type = 'const char *' %}
{% elif info['type'] in ('document', 'array') %}
{% set the_type = 'bson_t' %}
{% else %}
{% set the_type = info['type'] %}
{% endif %}
{% set the_name = info.get('field', opt_name) %}
{% set the_converter = info.get('convert', '_mongoc_convert_' + info['type'] ) %}
      {% if loop.index > 1 %}else {% endif %}if (!strcmp (bson_iter_key (&iter), "{{ opt_name }}")) {
         if (!{{ the_converter }} (
               client,
               &iter,
               &{{ struct_name }}->{{ path }},
               error)) {
            return false;
         }
{% if the_converter == '_mongoc_convert_write_concern' %}

         {{ struct_name }}->{{ path_to(struct_type, "write_concern_owned") }} = true;
{% endif %}
{% if info['check_set'] %}

         {{ struct_name }}->{{ the_name }}_is_set = true;
{% endif %}
      }
{% endfor %}
      else {
{% if description.allow_extra %}
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &{{ struct_name }}->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
{% else %}
         bson_set_error (error,
                         MONGOC_ERROR_COMMAND,
                         MONGOC_ERROR_COMMAND_INVALID_ARG,
                         "Invalid option '%s'",
                         bson_iter_key (&iter));
         return false;
{% endif %}
      }
   }

   return true;
}

void
_{{ struct_name }}_cleanup ({{ struct_type }} *{{ struct_name }})
{
{% for path, opt_name, info in paths(description) %}
{% if info['type'] == 'mongoc_write_concern_t *' %}
   if ({{ struct_name }}->{{ path_to(struct_type, "write_concern_owned") }}) {
      mongoc_write_concern_destroy ({{ struct_name }}->{{ path }});
   }
{% elif info['type'] == 'bson_value_t' %}
   bson_value_destroy (&{{ struct_name }}->{{ path }});
{% elif info['type'] in ('document', 'array') %}
   bson_destroy (&{{ struct_name }}->{{ path }});
{% elif info['type'] == 'mongoc_read_concern_t *' %}
   mongoc_read_concern_destroy ({{ struct_name }}->{{ path }});
{% endif %}
{% endfor %}
   bson_destroy (&{{struct_name }}->extra);
}{% endfor %}
