{% from "macros.j2" import cls_name,pointer,super,argtypes,argnames,method_pointer,
static_method_pointer,function_pointer,template_args_typename,template_args,
template_method_pointer,template_static_method_pointer, template_pointer, argnames_template %}
#pragma once

// pybind 11 related includes
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

namespace py = pybind11;

// Standard Handle
#include <Standard_Handle.hxx>

// user-defined inclusion per module before includes
{% if module_settings['include_header_pre_top'] %}
{{ module_settings['include_header_pre_top'] }}
{% endif %}

// includes to resolve forward declarations
{% for h in module.headers %}{% for f in h.forwards %}{% if f.name in class_dict %}
#include <{{ class_dict[f.name].splitpath()[-1] }}>
{% endif %}{% endfor %}{% endfor %}

// module includes
{% for h in module.headers %}
#include <{{ h.short_name }}>
{% endfor %}

// user-defined pre
{% if include_pre %}
#include "{{ include_pre }}"
{% endif %}

// Class template handling functions
{% for t in module.class_templates %}

template {{template_args_typename(t)}}
void preregister_template_{{t.name}}(py::object &m, const char *name){
    py::class_<{{t.name}}{{template_args(t)}} {{template_pointer(t)}} {{super(t,all_classes,all_typedefs)}}>(m,name,R"#({{t.comment}})#");
}

template {{template_args_typename(t)}}
void register_template_{{t.name}}(py::object &m, const char *name){
    static_cast<py::class_<{{t.name}}{{template_args(t)}} {{template_pointer(t)}} {{super(t,all_classes,all_typedefs)}}>>(m.attr(name))
    {% for i, con in enumerate(t.constructors) %}
        {% if not i in module_settings['Templates'].get(t.name,{}).get('exclude_constructors',[])  %}
        .def(py::init< {{ argtypes(con) }} >() {{argnames_template(t, con)}} )
        {% endif %}
    {% endfor %}
    {% for m in t.methods if not references_inner(t.name,m)%}
        .def("{{m.name}}",
             {{ template_method_pointer(t,m) }},
             R"#({{m.comment}})#" {{argnames_template(t, m)}})
    {% endfor %}
    {% for m in t.static_methods %}
        .def_static("{{m.name}}_s",
                    {{ template_static_method_pointer(t,m) }},
                    R"#({{m.comment}})#" {{argnames_template(t, m)}})
    {% endfor %}
    {% for op in t.operators %}
    {% if op.name in operator_dict %}
    {% for py_op in operator_dict[op.name] %}
        .def("{{py_op}}",
             {{ template_method_pointer(t,op) }},
             py::is_operator(),
             R"#({{op.comment}})#" {{argnames_template(t, op)}})
    {% endfor %}
    {% endif %}
    {% endfor %}
    {% if "begin" in t.methods_dict and "end" in t.methods_dict %}
        .def("__iter__",[](const {{t.name}}{{template_args(t)}} &self)
             { return py::make_iterator<py::return_value_policy::copy>(self.begin(), self.end()); },
             py::keep_alive<0, 1>())
    {% elif "cbegin" in t.methods_dict and "cend" in t.methods_dict %}
        .def("__iter__",[](const {{t.name}}{{template_args(t)}} &self)
             { return py::make_iterator<py::return_value_policy::copy>(self.cbegin(), self.cend()); },
             py::keep_alive<0, 1>())
    {% endif %}
    {% if "Extent" in t.methods_dict %}
        .def("__len__",[](const {{t.name}}{{template_args(t)}} &self)
             { return self.Extent(); }
             )
    {% endif %}
    {% if "Size" in t.methods_dict %}
        .def("__len__",[](const {{t.name}}{{template_args(t)}} &self)
             { return self.Size(); }
             )
    {% endif %}
    {% if "IsEmpty" in t.methods_dict %}
        .def("__bool__",[](const {{t.name}}{{template_args(t)}} &self)
             { return self.IsEmpty(); }
             )
    {% endif %}
    ;
};
{% endfor %}


// user-defined post
{% if module_settings['include_body_template_post'] %}
{{ module_settings['include_body_template_post'] }}
{% endif %}

{% if include_post %}
#include "{{ include_post }}"
{% endif %}
