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

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

<%
my $tkdir = $model->getContext()->getToolkitDirectory();
my $pydir = $tkdir."/opt/python";

require $pydir."/codegen/splpy.pm";

# Initialize splpy.pm
splpyInit($model);

my $pyoutstyle = splpy_tuplestyle($model->getOutputPortAt(0));
my $pywrapfunc='source_' . $pyoutstyle;
my %cpp_tuple_types;
%>

MY_OPERATOR::MY_OPERATOR() :
    funcop_(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) {
%>

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

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

<%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() 
{
    delete funcop_;
}

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

void MY_OPERATOR::process(uint32_t idx)
{
#if SPLPY_OP_CR == 1
    SPL::ConsistentRegionContext *crc = static_cast<SPL::ConsistentRegionContext *>(getContext().getOptionalContext(CONSISTENT_REGION));
#endif

    PyObject *pyReturnVar = NULL;

    while(!getPE().getShutdownRequested()) {
        OPort0Type otuple;

        bool submitTuple = false;
        bool allDone = false;

#if SPLPY_OP_CR == 1
        ConsistentRegionPermit crp(crc);
#endif
        {
#if SPLPY_OP_STATE_HANDLER == 1
            SPL::AutoMutex am(mutex_);
#endif
            try {
                SplpyGIL lock;
                Py_CLEAR(pyReturnVar);
                pyReturnVar = PyObject_CallObject(funcop_->callable(), NULL);

                if (pyReturnVar == NULL) {
                    // Has the iteration completed?
                    if (PyErr_Occurred() == SplpyErrors::StopIteration)
                        allDone = true;
                    else
                        throw SplpyExceptionInfo::pythonError("source");
                }
                else if (SplpyGeneral::isNone(pyReturnVar)) {
                    Py_CLEAR(pyReturnVar);
                } else {
                    submitTuple = true;

<% if ($pyoutstyle eq 'pickle') { %>
                    if (occ_ > 0) {
                        // passing by reference
                        pyTupleByRef(otuple.get___spl_po(), pyReturnVar, occ_);
                        pyReturnVar = NULL;
                    } else {

                        // Use the pointer of the pickled bytes object
                        // as the blob data so we need to maintain the
                        // reference count across the submit.
                        // We decrement it on the next loop iteration
                        // which is when we natually regain the lock.
                        pySplValueUsingPyObject(otuple.get___spl_po(), pyReturnVar);
                    }
<%}%>
<% if ($pyoutstyle eq 'string') { %>
                    pySplValueFromPyObject(otuple.get_string(), pyReturnVar);
<%}%>
<%if ($pyoutstyle eq 'dict') {%>
                    if (PyTuple_Check(pyReturnVar)) {
                        fromPyTupleToSPLTuple(pyReturnVar, otuple);
                        //Py_DECREF(pyReturnVar); // causing trouble with multiple sources in same PE, why Py_DECREF when there is Py_CLEAR in the loop?
                    } else if (PyDict_Check(pyReturnVar)) {
                        fromPyDictToSPLTuple(pyReturnVar, otuple);
                        //Py_DECREF(pyReturnVar); // causing trouble with multiple sources in same PE, why Py_DECREF when there is Py_CLEAR in the loop?
                    } else {
                        throw SplpyGeneral::generalException("submit",
                "Fatal error: Value submitted must be a Python tuple or dict.");
                   }
<%}%>
               }
           } catch (const streamsx::topology::SplpyExceptionInfo& excInfo) {
                SPLPY_OP_HANDLE_EXCEPTION_INFO_GIL(excInfo);
                continue;
           }
        }

        if (submitTuple) {
            submit(otuple, 0);
        } else if (allDone) {

#if SPLPY_OP_CR == 1
            // Wait until the region becomes consistent
            // before completing. If a reset occurred
            // then we need to continue the iterator which
            // might have been reset, and hence more tuples to submit.
            if (!crc->makeConsistent())
                continue;
#endif
            break;
        }
    }
}

<%
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);%>
