##
## Copyright 2020 Free Software Foundation, Inc.
##
## This file is part of GNU Radio
##
## SPDX-License-Identifier: GPL-3.0-or-later
##
##
<%
    namespace = header_info['namespace']
    modname = header_info['module_name']
    header_filename = header_info['filename']
    header_file_hash = header_info['md5hash']
%>\
${license}

/***********************************************************************************/
/* This file is automatically generated using bindtool and can be manually edited  */
/* The following lines can be configured to regenerate this file during cmake      */
/* If manual edits are made, the following tags should be modified accordingly.    */
/* BINDTOOL_GEN_AUTOMATIC(${'1' if flag_automatic else '0'})                                                       */
/* BINDTOOL_USE_PYGCCXML(${'1' if flag_pygccxml else '0'})                                                        */
/* BINDTOOL_HEADER_FILE(${header_filename})                                        */
/* BINDTOOL_HEADER_FILE_HASH(${header_file_hash})                     */
/***********************************************************************************/

#include <pybind11/complex.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

namespace py = pybind11;

#include <${prefix_include_root}/${basename}.h>
// pydoc.h is automatically generated in the build directory
#include <${basename}_pydoc.h>

void bind_${basename}(py::module& m)
{
${render_namespace(namespace=namespace,modname=[modname],modvar='m')}
}

<%def name='render_constructor()' >
</%def>

<%def name='render_function(fcn,fcn_list,cls_name,filter_val,isfree=False,modvar="",doc_prefix="")' >
<%
fcn_args = fcn['arguments']
fcn_name = fcn['name']
has_static = fcn['has_static'] if 'has_static' in fcn else False
matcher = lambda x,name: x['name'] == name
matched_list = [f for f in fcn_list if matcher(f,fcn_name)]
overloaded = len(matched_list) > 1
overloaded_str = ''
index_str = ''
if overloaded:
  index_into_list = matched_list.index(fcn)
  index_str = ','+str(index_into_list)
  overloaded_str = '({} ({}::*)({}))'.format(fcn['return_type'],cls_name,', '.join([f['dtype'] for f in fcn_args]))
%>\
% if fcn['name'] != filter_val:
%       if fcn['return_type'] == 'PyObject *': 
<%
func = '\n\
            [](std::shared_ptr<'+cls_name+'> p) {\n\
               return PyLong_AsLongLong(p->'+fcn_name+'());\n\
        }'
%>
%       elif fcn['return_type'] == 'QWidget *':
<%
func = '\n\
            []('+cls_name+'& self) { return reinterpret_cast<uintptr_t>(self.'+fcn_name+'()); }'
%>
%       else: ## No PyObject pointer 
<%
func = overloaded_str+'&'+cls_name+'::'+fcn_name
%>        
%       endif
        ${modvar if isfree else ""}${".def_static" if has_static and not isfree else ".def"}("${fcn['name']}",${func},       
% for arg in fcn_args:
            py::arg("${arg['name']}")${" = " + arg['default'] if arg['default'] else ''},
% endfor ## args 
            D(${doc_prefix}${cls_name},${fcn['name']}${index_str})
        )${';' if isfree else ""}
% endif ## Not a filtered function name
</%def>

<%def name='render_free_function(fcn,fcn_list,namespace_name,filter_val,modvar="",doc_prefix="")' >
<%
fcn_args = fcn['arguments']
fcn_name = fcn['name']
has_static = fcn['has_static'] if 'has_static' in fcn else False
matcher = lambda x,name: x['name'] == name
matched_list = [f for f in fcn_list if matcher(f,fcn_name)]
overloaded = len(matched_list) > 1
overloaded_str = ''
index_str = ''
if overloaded:
  index_into_list = matched_list.index(fcn)
  index_str = ','+str(index_into_list)
  overloaded_str = '({} ({}::*)({}))'.format(fcn['return_type'],'',', '.join([f['dtype'] for f in fcn_args]))
%>\
% if fcn['name'] != filter_val:
        ${modvar}.def("${fcn['name']}",${overloaded_str}&${namespace_name}::${fcn['name']},
% for arg in fcn_args:
            py::arg("${arg['name']}")${" = " + arg['default'] if arg['default'] else ''},
% endfor ## args 
            D(${doc_prefix}${fcn['name']}${index_str})
        );
% endif ## Not a filtered function name
</%def>

<%def name='render_namespace(namespace, modname, modvar)'>
<%
    classes=namespace['classes'] if 'classes' in namespace else []
    free_functions=namespace['free_functions'] if 'free_functions' in namespace else []
    free_enums = namespace['enums'] if 'enums' in namespace else []
    subnamespaces = namespace['namespaces'] if 'namespaces' in namespace else []
    doc_prefix = '' if len(modname) == 1 else ','.join(modname[1:])+','
%>\
% for cls in classes:
% if classes:
    using ${cls['name']}    = ${namespace['name']}::${cls['name']};
% endif ##classes
% endfor ##classes
% for cls in classes:

<%
member_functions = cls['member_functions'] if 'member_functions' in cls else []
constructors = cls['constructors'] if 'constructors' in cls else []
class_enums = cls['enums'] if 'enums' in cls else []
class_variables = cls['variables'] if 'variables' in cls else []
try:
        def find_make_function(member_fcns):
            for mf in member_fcns:
                if mf['name'] == 'make':
                    return mf
            return None

        make_function = find_make_function(member_functions)
except:
        make_function = None

isablock = False
if 'bases' in cls:
  base_str = '::'.join(list(filter(lambda x: x != '::',cls['bases'])))
  bases_with_block = [s for s in cls['bases'] if 'block' in s]
  if bases_with_block:
    isablock = True
    # The goal is to make gr::sync_block have the entire chain back to basic_block
    #  if base_str == gr::sync_block, base_str == gr::sync_block,gr::block,gr::basic_block
    if (base_str.endswith('gr::sync_block') or 
          base_str.endswith('gr::sync_interpolator') or 
          base_str.endswith('gr::sync_decimator') or 
          base_str.endswith('gr::tagged_stream_block') or
          base_str.endswith('gr::uhd::rfnoc_block')):
        base_str += ", gr::block"
    if base_str.endswith('gr::block'):
        base_str += ", gr::basic_block"
%>
    py::class_<${cls['name']}\
% if 'bases' in cls:
, ${base_str},
% else: 
,
% endif\
 
        std::shared_ptr<${cls['name']}>>(${modvar}, "${cls['name']}", D(${doc_prefix}${cls['name']}))

% if make_function: ## override constructors with make function
<%
fcn = make_function
fcn_args = fcn['arguments']
%>\
        .def(py::init(&${cls['name']}::${make_function['name']}),
% for arg in fcn_args:
           py::arg("${arg['name']}")${" = " + arg['default'] if arg['default'] else ''},
% endfor 
           D(${doc_prefix}${cls['name']},${make_function['name']})
        )
        
% else:
% for fcn in constructors:
<%
fcn_args = fcn['arguments']
%>\
\
% if len(fcn_args) == 0:
        .def(py::init<>(),D(${doc_prefix}${cls['name']},${cls['name']},${loop.index}))
%else:
        .def(py::init<\
% for arg in fcn_args:
${arg['dtype']}${'>(),' if loop.index == len(fcn['arguments'])-1 else ',' }\
% endfor ## args
\
% for arg in fcn_args:
           py::arg("${arg['name']}")${" = " + arg['default'] if arg['default'] else ''},
% endfor 
           D(${doc_prefix}${cls['name']},${cls['name']},${loop.index})
        )
% endif
% endfor ## constructors
% endif ## make

% for fcn in member_functions:
${render_function(fcn=fcn,fcn_list=member_functions,cls_name=cls['name'],filter_val='make',doc_prefix=doc_prefix)}
% endfor ## member_functions
        ;
% endfor ## classes

% if free_enums:
% for en in free_enums:
<%
values = en['values']
%>\
    py::enum_<${namespace['name']}::${en['name']}>(${modvar},"${en["name"]}")
% for val in values:
        .value("${val[0]}", ${namespace['name']}::${en['name']}::${val[0]}) // ${val[1]}
% endfor 
        .export_values()
    ;

    py::implicitly_convertible<int, ${namespace['name']}::${en['name']}>();
% endfor
% endif

% if free_functions:
% for fcn in free_functions:
${render_free_function(fcn=fcn,fcn_list=free_functions,namespace_name=namespace['name'],filter_val="",modvar=modvar,doc_prefix=doc_prefix)}
% endfor ## free_functions
% endif ## free_functions
\
% for sns in subnamespaces:
<%  
  submod_name = sns['name'].split('::')[-1]
  next_modvar = modvar + '_' + submod_name
%>
        py::module ${next_modvar} = ${modvar}.def_submodule("${submod_name}");
${render_namespace(namespace=sns,modname=modname+[submod_name],modvar=next_modvar)}
% endfor

</%def>
