<%def name="struct(cls)">
   ## When called, generate the values for the module representing the c value
   ## of the given type.

  type t

  let c_struct : t structure typ = structure "${cls.api_name.lower}"
  let n = field c_struct "n" int
  let _ = field c_struct "ref_count" int
  let items = field c_struct "items"
    ${ocaml_api.c_type(cls.element_type, cls)}
  let () = seal c_struct

  let c_type = ptr c_struct

  let create = foreign ~from:c_lib "${cls.c_create(capi)}"
    (int @-> raisable c_type)
  let dec_ref = foreign ~from:c_lib "${cls.c_dec_ref(capi)}"
    (c_type @-> raisable void)
</%def>

<%def name="decl_struct(cls)">
module ${ocaml_api.struct_name(cls)} = struct
  ${struct(cls)}
end
</%def>

<%def name="sig_wrapper(cls)">
  type t = ${ocaml_api.type_public_name(cls)}

   % if ocaml_api.wrap_requires_context(cls):
  val wrap :
       ?dec_ref:bool
    -> analysis_context
    -> ${ocaml_api.c_value_type(cls)}
    -> t
   % else:
  val wrap :
       ?dec_ref:bool
    -> ${ocaml_api.c_value_type(cls)}
    -> t
   % endif

   % if cls.conversion_requires_context:
  val unwrap : analysis_context -> t -> ${ocaml_api.c_value_type(cls)}
   % else:
  val unwrap : t -> ${ocaml_api.c_value_type(cls)}
   % endif
</%def>

<%def name="struct_wrapper(cls)">
  type t = ${ocaml_api.type_public_name(cls)}

   % if ocaml_api.wrap_requires_context(cls):
  let wrap ?(dec_ref=true) (context : analysis_context) c_value_ptr =
   % else:
  let wrap ?(dec_ref=true) c_value_ptr =
   % endif
    let c_value = !@ c_value_ptr in
    let length = getf c_value ${ocaml_api.struct_name(cls)}.n in
    let items = c_value @. ${ocaml_api.struct_name(cls)}.items in
    let f i =
      % if cls.element_type.is_ada_record:
      (* we want to allocate a fresh value for a record, otherwize, the c value
       * will still point to the memory at array location *)
      let fresh =
        allocate ${ocaml_api.c_type(cls.element_type)} (!@ (items +@ i))
      in
      (* Do not dec_ref the item here since this is the responsability of
         the array *)
      ${ocaml_api.wrap_value('!@ fresh', cls.element_type, "context",
                             dec_ref="false")}
      % else:
      (* Do not dec_ref the item here since this is the responsability of
         the array *)
      ${ocaml_api.wrap_value('!@ (items +@ i)', cls.element_type, "context",
                             dec_ref="false")}
      % endif
    in
    let result = List.init length f in
    if dec_ref then ${ocaml_api.struct_name(cls)}.dec_ref c_value_ptr;
    result

   % if cls.conversion_requires_context:
  let unwrap (context : analysis_context) value =
   % else:
  let unwrap value =
   % endif
    let result = ${ocaml_api.struct_name(cls)}.create (List.length value) in
    let items = result |-> ${ocaml_api.struct_name(cls)}.items in
   % if cls.conversion_requires_context:
    let c_context = context.c_value in
   % endif
    let f i v =
      items +@ i <-@
        ${ocaml_api.unwrap_value('v', cls.element_type, 'c_context')}
    in
    List.iteri f value;
    result

</%def>

<%def name="decl_wrapper(cls)">
module ${ocaml_api.module_name(cls)} : sig
   ${sig_wrapper(cls)}
end = struct
   ${struct_wrapper(cls)}
end
</%def>
