<%SPL::CodeGen::implementationPrologue($model);%>

@include "../pyspltuple.cgt"
<%
 # Select the Python wrapper function
 my $pyoutstyle = splpy_tuplestyle($model->getOutputPortAt(0));
 my $pywrapfunc= $pystyle_fn . '_in__' . $pyoutstyle . '_out';
 my %cpp_tuple_types;
%>

#if SPLPY_OP_STATE_HANDLER == 1
#include "splpy_sh.h"
#endif

#define SPLPY_TUPLE_MAP(f, v, r, occ) \
    streamsx::topology::Splpy::pyTupleMap(f, v, r)

MY_OPERATOR::MY_OPERATOR() :
   funcop_(NULL),
   pyInStyleObj_(NULL),
   pyOutNames_0(NULL),
   occ_(-1)
{
    const char * wrapfn = "<%=$pywrapfunc%>";


<%
# If occ parameter is positive then pass-by-ref is possible
# Generate code to allow pass by ref but only use when
# not connected to a PE output port.

 my $oc = $model->getParameterByName("outputConnections");

 if ($oc) {
    my $occ = $oc->getValueAt(0)->getSPLExpression();
    if ($occ > 0) {
        my $pybyrefwrapfunc = $pystyle_fn . '_in__object_out';
%>

#undef SPLPY_TUPLE_MAP
#define SPLPY_TUPLE_MAP(f, v, r, occ) \
    streamsx::topology::Splpy::pyTupleMapByRef(f, v, r, occ)

    if (!this->getOutputPortAt(0).isConnectedToAPEOutputPort()) {
       // pass by reference
       wrapfn = "<%=$pybyrefwrapfunc%>";
       occ_ = <%=$occ%>;
    }
<%
    } 
 }
%>

    funcop_ = new SplpyFuncOp(this, SPLPY_CALLABLE_STATE_HANDLER, wrapfn);

@include "../pyspltuple_constructor.cgt"

<%if ($pyoutstyle eq 'dict') {%>
  {
  SplpyGIL lock;
  pyOutNames_0 = Splpy::pyAttributeNames(getOutputPortAt(0));
  }
<%}%>

#if SPLPY_OP_STATE_HANDLER == 1
   this->getContext().registerStateHandler(*this);
#endif
}

MY_OPERATOR::~MY_OPERATOR() 
{
  {
    SplpyGIL lock;
      Py_CLEAR(pyInStyleObj_);
      Py_CLEAR(pyOutNames_0);
  }

  delete funcop_;
}

void MY_OPERATOR::prepareToShutdown() 
{
    funcop_->prepareToShutdown();
}

void MY_OPERATOR::process(Tuple const & tuple, uint32_t port)
{
  OPort0Type otuple;
  {
#if SPLPY_OP_STATE_HANDLER == 1
    SPL::AutoMutex am(mutex_);
#elif SPLPY_CALLABLE_STATEFUL == 1
    SPL::AutoPortMutex am(mutex_, *this);
#endif
  
    try {

@include "../pyspltuple2value.cgt"

<%if ($pyoutstyle eq 'dict') {%>
       {
           SplpyGIL lock;
           PyObject * ret = streamsx::topology::Splpy::pyTupleMap(funcop_->callable(), value);
           if (ret == NULL)
             return;
           if (PyTuple_Check(ret)) {
               fromPyTupleToSPLTuple(ret, otuple);
               Py_DECREF(ret);
           } else if (PyDict_Check(ret)) {
               fromPyDictToSPLTuple(ret, otuple);
               Py_DECREF(ret);
           } else {
              throw SplpyGeneral::generalException("submit",
                "Fatal error: Value submitted must be a Python tuple or dict.");
           }
        } // GIL
  
<% } else { %>

        if (!SPLPY_TUPLE_MAP(funcop_->callable(), value,
             otuple.get_<%=$model->getOutputPortAt(0)->getAttributeAt(0)->getName()%>(), occ_))
            return;

<%}%>
    } catch (const streamsx::topology::SplpyExceptionInfo& excInfo) {
       SPLPY_OP_HANDLE_EXCEPTION_INFO_GIL(excInfo);
       return;
    }
  }
  submit(otuple, 0);
}

void MY_OPERATOR::process(Punctuation const & punct, uint32_t port)
{
   forwardWindowPunctuation(punct);
}

<%
if ($pyoutstyle eq 'dict') {
  # In this case we don't want the function that
  # converts the Python tuple to an SPL tuple to
  # copy attributes from the input port
  my $iport;

  my $oport = $model->getOutputPortAt(0);
  my $oport_submission = 0;
  my $otupleType = $oport->getSPLTupleType();
  my @onames = SPL::CodeGen::Type::getAttributeNames($otupleType);
  my @otypes = SPL::CodeGen::Type::getAttributeTypes($otupleType);

%>
// Create member function that converts Python tuple to SPL tuple
@include  "../../opt/python/codegen/py_pyTupleTosplTuple.cgt"
<%}%>

<%SPL::CodeGen::implementationEpilogue($model);%>
