{# useful macros -#}

{%- macro cls_name(cls) -%}
    {{ cls.name|replace('<', '_')|replace(' ', '_')|replace('>', '') }}
{%- endmacro -%}

{%- macro pointer(cls) -%}
    {%- if "Standard_Transient" in cls.rootclass or cls.name == "Standard_Transient" -%} ,opencascade::handle<{{cls.name}}>
    {%- elif cls.nonpublic_destructors|length>0 -%} , shared_ptr_nodelete<{{cls.name}}>
    {%- elif parent_has_nonpublic_destructor(cls) -%} , shared_ptr<{{cls.name}}>
    {%- else -%} , shared_ptr<{{cls.name}}>
    {%- endif -%}
{%- endmacro -%}

{%- macro super(cls,classes,typedefs) -%}
    {% for super in cls.superclass if super in classes or super in typedefs %}, {{super}} {% endfor %}
{%- endmacro -%}

{%- macro prototype(f) -%}
    {{f.return_type}} {{f.name}}({% for arg in f.args %}{{ arg.arg_type }} {{ arg.arg_name }}{{ "," if not loop.last }}{% endfor %}){% if f.const %} const {%endif%}
{%- endmacro -%}

{%- macro pybind_overload(c,m) -%}
    using return_type = {{m.return_type}};
    PYBIND11_OVERLOAD_PURE(return_type,{{c.name}},{{m.name}},{% for arg in m.args %}{{ arg.arg_name }}{{ "," if not loop.last }}{% endfor %})
{%- endmacro -%}

{%- macro argtypes(f) -%}
    {% for arg in f.args %}{{ arg.arg_type }}{{ "," if not loop.last }}{% endfor %}
{%- endmacro -%}

{%- macro argtypes_names(f) -%}
    {% for arg in f.args %}{{ arg.arg_type }} {{ arg.arg_name }}{{ "," if not loop.last }}{% endfor %}
{%- endmacro -%}

{%- macro argtypes_names_not_byref(f) -%}
    {% for arg in f.args if not is_byref(arg.arg_type) %}{{ type_from_byref_smart_ptr(arg.arg_type)+'&' if is_byref_smart_ptr(arg.arg_type) else arg.arg_type }} {{ arg.arg_name }}{{ "," if not loop.last }}{% endfor %}
{%- endmacro -%}

{%- macro init_outputs_byref(f) -%}
    {% for arg in f.args if is_byref(arg.arg_type) %}{{ arg.arg_type[:-1] }} {{ arg.arg_name }};
    {% endfor %}
    {% for arg in f.args if is_byref_smart_ptr(arg.arg_type) -%}
    {{ arg.arg_type[:-1] }} {{ arg.name }}_ptr; {{ arg.name }}_ptr = &{{arg.name}};
    {% endfor %}
{%- endmacro -%}

{%- macro argnames_wo_type(f) -%}
    {% for arg in f.args %}{% if is_byref_smart_ptr(arg.arg_type) %}{{ arg.arg_name }}_ptr{% else %}{{ arg.arg_name }}{% endif %}{{ "," if not loop.last }}{% endfor %}
{%- endmacro -%}

{%- macro argnames_byref(f) -%}
    {% for arg in f.args if is_byref(arg.arg_type) %}{{ arg.arg_name }}{{ "," if not loop.last }}{% endfor %}
{%- endmacro -%}

{%- macro argnames(f) -%}
    {% for arg in f.args %} {{ "," if loop.first }} py::arg("{{arg.arg_py_name if arg.arg_py_name!='' else 'arg'}}"){% if d %}=static_cast<{{arg.arg_type}}>({{arg.arg_default}}){% endif %}{{ "," if not loop.last }}{% endfor %}
{%- endmacro -%}

{%- macro argnames_template(template, f) -%}
    {%- for arg in f.args -%} {{ "," if loop.first }}
    {%- set ns = namespace(t=arg.arg_type) -%}
    {%- if arg.arg_type.startswith(template.name) -%}
        {% set ns.t = template.name + '<' +  template.type_params|map('get',1)|join(', ') + '>' + arg.arg_type.split(template.name)[-1] %}
    {%- endif -%}
    py::arg("{{arg.arg_py_name if arg.arg_py_name != '' else 'arg'}}"){% if arg.arg_default %}=static_cast<{{arg_type_with_template_params(template, arg.arg_type)}}>({{arg.arg_default}}){% endif %}{{ ", " if not loop.last }}
    {%- endfor -%}
{%- endmacro -%}

{%- macro arg_type_with_template_params(template, t) -%}
    {%- set ns = namespace(t=t) -%}
    {%- if t.startswith(template.name+'::') -%}
        {% set ns.t = 'typename ' + template.name + '<' +  template.type_params|map('get',1)|join(', ') + '>' + t.split(template.name)[-1] %}
    {%- endif -%}
    {{ ns.t }}
{%- endmacro -%}

{%- macro argnames_not_byref(f) -%}
    {% for arg in f.args if not is_byref(arg.arg_type) %} {{ "," if loop.first }} py::arg("{{arg.arg_py_name if arg.arg_py_name != '' else 'arg'}}"){% if arg.arg_default %}=static_cast<{{arg.arg_type}}>({{arg.arg_default}}){% endif %}{{ "," if not loop.last }}{% endfor %}
{%- endmacro -%}

{%- macro handle_results_ptr_byref(f) -%}
    {% for arg in f.args if is_byref_smart_ptr(arg.arg_type) -%}
    if ( {{ arg.arg_name }}_ptr.get() != &{{arg.arg_name}} ) copy_if_copy_constructible({{arg.arg_name}}, *{{ arg.arg_name}}_ptr);
    {% endfor -%}
{%- endmacro -%}

{%- macro method_pointer(cls,f) -%}
    ({{f.return_type}} ({{cls.name}}::*)({% for arg in f.args %} {{arg.arg_type}} {{ "," if not loop.last }} {% endfor %}) {{ "const" if f.const }}) static_cast<{{f.return_type}} ({{cls.name}}::*)({% for arg in f.args %} {{arg.arg_type}} {{ "," if not loop.last }} {% endfor %}) {{ "const" if f.const }}>(&{{cls.name}}::{{f.name}})
{%- endmacro -%}

{%- macro static_method_pointer(cls,f) -%}
    ({{f.return_type}} (*)({% for arg in f.args %} {{arg.arg_type}} {{ "," if not loop.last }} {% endfor %}) {{ "const" if f.const }}) static_cast<{{f.return_type}} (*)({% for arg in f.args %} {{arg.arg_type}} {{ "," if not loop.last }} {% endfor %}) {{ "const" if f.const }}>(&{{cls.name}}::{{f.name}})
{%- endmacro -%}

{%- macro function_pointer(f) -%}
    ({{f.return_type}} (*)({% for arg in f.args %} {{arg.arg_type}} {{ "," if not loop.last }} {% endfor %}))  static_cast<{{f.return_type}} (*)({% for arg in f.args %} {{arg.arg_type}} {{ "," if not loop.last }} {% endfor %})>(&{% if f.namespace %}{{f.namespace}}::{% endif %}{{f.name}})
{%- endmacro -%}

{%- macro template_args_typename(t) -%}
    <{% for type,name,default in t.type_params %}{% if type %}{{ type }}{% else %}typename{% endif %} {{name}}{% if default %}={{default}}{% endif %}{{ "," if not loop.last }}{% endfor %}>
{%- endmacro -%}

{%- macro template_args(t) -%}
    <{% for type,name,default in t.type_params %}{{name}}{{ "," if not loop.last }}{% endfor %}>
{%- endmacro -%}

{%- macro template_return_type(cls,f) -%}
    {% if f.return_type.startswith(cls.name+'::') %}typename {{cls.name}}{{ template_args(cls) }}::{{ f.return_type.split('::')[1]}}{% else %}{{f.return_type}}{% endif %}
{%- endmacro -%}

{%- macro template_method_pointer(cls,f) -%}
    ({{template_return_type(cls,f)}} ({{cls.name}}{{template_args(cls)}}::*)({% for _,t,d,_ in f.args %} {{arg_type_with_template_params(cls, t)}} {{ "," if not loop.last }} {% endfor %}) {{ "const" if f.const }}) &{{cls.name}}{{template_args(cls)}}::{{f.name}}
{%- endmacro -%}

{%- macro template_static_method_pointer(cls,f) -%}
    ({{template_return_type(cls,f)}} (*)({% for arg in f.args %} {{arg_type_with_template_params(cls, arg.arg_type)}} {{ "," if not loop.last }} {% endfor %}) {{ "const" if f.const }}) &{{cls.name}}{{template_args(cls)}}::{{f.name}}
{%- endmacro -%}

{%- macro template_pointer(cls) -%}
    {%- if "Standard_Transient" in cls.rootclass or cls.name == "Standard_Transient" -%} , opencascade::handle<{{cls.name}}{{template_args(cls)}}>
    {%- elif cls.nonpublic_destructors|length>0 -%} , shared_ptr_nodelete<{{cls.name}}{{template_args(cls)}}>
    {%- else -%} , shared_ptr<{{cls.name}}{{template_args(cls)}}>
    {%- endif -%}
{%- endmacro -%}

{%- macro trampoline_class(c) -%}
    class Py_{{c.name}} : public {{c.name}}{
    public:
        using {{c.name}}::{{c.name}};

        {% set methods = [] %}

        // public pure virtual
        {% for m in c.methods+c.methods_byref %}{% if m.pure_virtual %}
        {{ prototype(m) }} override { {{pybind_overload(c,m)}} };
        {% do methods.append(m.full_name) %}
        {% endif %}{% endfor %}

        {% for super in c.superclasses %}
        {% set p = all_classes.get(super,None) %}{% if p %}
        {% for m in p.methods %}{% if m.pure_virtual and m.name not in c.methods_dict and not m.full_name in methods %}
        {{ prototype(m) }} override { {{pybind_overload(p,m)}} };
        {% do methods.append(m.full_name) %}
        {% endif %}{% endfor %}
        {% endif %}
        {% endfor %}

        // protected pure virtual
        {% for m in c.protected_virtual_methods %}
        {{ prototype(m) }} override { {{pybind_overload(c,m)}} };
        {% do methods.append(m.full_name) %}
        {% endfor %}

        {% for super in c.superclasses %}
        {% set p = all_classes.get(super,None) %}{% if p %}
        {% for m in p.protected_virtual_methods %}{% if m.name not in c.protected_virtual_methods_dict and not m.full_name in methods %}
        {{ prototype(m) }} override { {{pybind_overload(p,m)}} };
        {% do methods.append(m.full_name) %}
        {% endif %}{% endfor %}
        {% endif %}
        {% endfor %}

        // private pure virtual
        {% for m in c.private_virtual_methods %}
        {{ prototype(m) }} override { {{pybind_overload(c,m)}} };
        {% endfor %}

        {% for super in c.superclasses %}
        {% set p = all_classes.get(super,None) %}{% if p %}
        {% for m in p.private_virtual_methods %}{% if m.name not in c.private_virtual_methods_dict and not m.full_name in methods %}
        {{ prototype(m) }} override { {{pybind_overload(p,m)}} };
        {% do methods.append(m.full_name) %}
        {% endif %}{% endfor %}
        {% endif %}
        {% endfor %}
    };
{%- endmacro -%}

{# end of macros macros -#}
