// Author: Wim Lavrijsen   November 2010

// Bindings
#include "PyROOT.h"
#include "TPyFitFunction.h"
#include "ObjectProxy.h"
#include "MethodProxy.h"
#include "TPyBufferFactory.h"

// Standard
#include <stdexcept>

//______________________________________________________________________________
//                       Python wrapper for Fit functions
//                       ================================
//


//- data ---------------------------------------------------------------------
ClassImp(TPyMultiGenFunction)
ClassImp(TPyMultiGradFunction)


//- helper functions ---------------------------------------------------------
static PyObject* GetOverriddenPyMethod( PyObject* pyself, const char* method )
{
// Retrieve an overriden method on pyself
   PyObject* pymethod = 0;

   if ( pyself && pyself != Py_None ) {
      pymethod = PyObject_GetAttrString( (PyObject*)pyself, const_cast< char* >( method ) );
      if ( ! PyROOT::MethodProxy_CheckExact( pymethod ) )
         return pymethod;

      Py_XDECREF( pymethod );
      pymethod = 0;
   }

   return pymethod;
}

static PyObject* DispatchCall( PyObject* pyself, const char* method, PyObject* pymethod = NULL,
   PyObject* arg1 = NULL, PyObject* arg2 = NULL, PyObject* arg3 = NULL )
{
// Forward <method> to python (need to refactor this with TPySelector).
   PyObject* result = 0;

// get the named method and check for python side overload by not accepting the
// binding's methodproxy
   if ( ! pymethod )
      pymethod = GetOverriddenPyMethod( pyself, method );

   if ( pymethod ) {
      result = PyObject_CallFunctionObjArgs( pymethod, arg1, arg2, arg3, NULL );
   } else {
   // means the method has not been overridden ... simply accept its not there
      result = 0;
      PyErr_Format( PyExc_AttributeError,
         "method %s needs implementing in derived class", const_cast< char* >( method ) );
   }

   Py_XDECREF( pymethod );

   return result;
}


//- constructors/destructor --------------------------------------------------
TPyMultiGenFunction::TPyMultiGenFunction( PyObject* self ) : fPySelf( 0 )
{
// Construct a TPyMultiGenFunction derived with <self> as the underlying
   if ( self ) {
   // steal reference as this is us, as seen from python
      fPySelf = self;
   } else {
      Py_INCREF( Py_None );        // using None allows clearer diagnostics
      fPySelf = Py_None;
   }
}

//____________________________________________________________________________
TPyMultiGenFunction::~TPyMultiGenFunction()
{
// Destructor. Only deref if still holding on to Py_None (circular otherwise).
   if ( fPySelf == Py_None ) {
      Py_DECREF( fPySelf );
   }
}


//- public functions ---------------------------------------------------------
unsigned int TPyMultiGenFunction::NDim() const
{
// Simply forward the call to python self.
   PyObject* pyresult = DispatchCall( fPySelf, "NDim" );

   if ( ! pyresult ) {
      PyErr_Print();
      throw std::runtime_error( "Failure in TPyMultiGenFunction::NDim" );
   }

   unsigned int cppresult = (unsigned int)PyLong_AsLong( pyresult );
   Py_XDECREF( pyresult );

   return cppresult;
}

//____________________________________________________________________________
double TPyMultiGenFunction::DoEval( const double* x ) const
{
// Simply forward the call to python self.
   PyObject* xbuf = PyROOT::TPyBufferFactory::Instance()->PyBuffer_FromMemory( (Double_t*)x );
   PyObject* pyresult = DispatchCall( fPySelf, "DoEval", NULL, xbuf );
   Py_DECREF( xbuf );

   if ( ! pyresult ) {
      PyErr_Print();
      throw std::runtime_error( "Failure in TPyMultiGenFunction::DoEval" );
   }

   double cppresult = (double)PyFloat_AsDouble( pyresult );
   Py_XDECREF( pyresult );

   return cppresult;
}



//- constructors/destructor --------------------------------------------------
TPyMultiGradFunction::TPyMultiGradFunction( PyObject* self )
{
// Construct a TPyMultiGradFunction derived with <self> as the underlying
   if ( self ) {
   // steal reference as this is us, as seen from python
      fPySelf = self;
   } else {
      Py_INCREF( Py_None );        // using None allows clearer diagnostics
      fPySelf = Py_None;
   }
}

//____________________________________________________________________________
TPyMultiGradFunction::~TPyMultiGradFunction()
{
// Destructor. Only deref if still holding on to Py_None (circular otherwise).
   if ( fPySelf == Py_None ) {
      Py_DECREF( fPySelf );
   }
}


//- public functions ---------------------------------------------------------
unsigned int TPyMultiGradFunction::NDim() const
{
// Simply forward the call to python self.
   PyObject* pyresult = DispatchCall( fPySelf, "NDim" );

   if ( ! pyresult ) {
      PyErr_Print();
      throw std::runtime_error( "Failure in TPyMultiGradFunction::NDim" );
   }

   unsigned int cppresult = (unsigned int)PyLong_AsLong( pyresult );
   Py_XDECREF( pyresult );

   return cppresult;
}

//____________________________________________________________________________
double TPyMultiGradFunction::DoEval( const double* x ) const
{
// Simply forward the call to python self.
   PyObject* xbuf = PyROOT::TPyBufferFactory::Instance()->PyBuffer_FromMemory( (Double_t*)x );
   PyObject* pyresult = DispatchCall( fPySelf, "DoEval", NULL, xbuf );
   Py_DECREF( xbuf );

   if ( ! pyresult ) {
      PyErr_Print();
      throw std::runtime_error( "Failure in TPyMultiGradFunction::DoEval" );
   }

   double cppresult = (double)PyFloat_AsDouble( pyresult );
   Py_XDECREF( pyresult );

   return cppresult;
}

//____________________________________________________________________________
void TPyMultiGradFunction::Gradient( const double* x, double* grad ) const {
// Simply forward the call to python self.
   PyObject* pymethod = GetOverriddenPyMethod( fPySelf, "Gradient" );

   if ( pymethod ) {
      PyObject* xbuf = PyROOT::TPyBufferFactory::Instance()->PyBuffer_FromMemory( (Double_t*)x );
      PyObject* gbuf = PyROOT::TPyBufferFactory::Instance()->PyBuffer_FromMemory( (Double_t*)grad );
      PyObject* pyresult = DispatchCall( fPySelf, "Gradient", pymethod, xbuf, gbuf );
      Py_DECREF( gbuf );
      Py_DECREF( xbuf );

      if ( ! pyresult ) {
         PyErr_Print();
         throw std::runtime_error( "Failure in TPyMultiGradFunction::Gradient" );
      }

      Py_DECREF( pyresult );

   } else
      return ROOT::Math::IMultiGradFunction::Gradient( x, grad );
}

//____________________________________________________________________________
void TPyMultiGradFunction::FdF( const double* x, double& f, double* df ) const
{
// Simply forward the call to python self.
   PyObject* pymethod = GetOverriddenPyMethod( fPySelf, "FdF" );

   if ( pymethod ) {
      PyObject* xbuf = PyROOT::TPyBufferFactory::Instance()->PyBuffer_FromMemory( (Double_t*)x );
      PyObject* pyf = PyList_New( 1 );
      PyList_SetItem( pyf, 0, PyFloat_FromDouble( f ) );
      PyObject* dfbuf = PyROOT::TPyBufferFactory::Instance()->PyBuffer_FromMemory( (Double_t*)df );

      PyObject* pyresult = DispatchCall( fPySelf, "FdF", pymethod, xbuf, pyf, dfbuf );
      f = PyFloat_AsDouble( PyList_GetItem( pyf, 0 ) );

      Py_DECREF( dfbuf );
      Py_DECREF( pyf );
      Py_DECREF( xbuf );

      if ( ! pyresult ) {
         PyErr_Print();
         throw std::runtime_error( "Failure in TPyMultiGradFunction::FdF" );
      }

      Py_DECREF( pyresult );

   } else
      return ROOT::Math::IMultiGradFunction::FdF( x, f, df );
}

//____________________________________________________________________________
double TPyMultiGradFunction::DoDerivative( const double * x, unsigned int icoord ) const
{
// Simply forward the call to python self.
   PyObject* xbuf = PyROOT::TPyBufferFactory::Instance()->PyBuffer_FromMemory( (Double_t*)x );
   PyObject* pycoord = PyLong_FromLong( icoord );

   PyObject* pyresult = DispatchCall( fPySelf, "DoDerivative", NULL, xbuf, pycoord );
   Py_DECREF( pycoord );
   Py_DECREF( xbuf );

   if ( ! pyresult ) {
      PyErr_Print();
      throw std::runtime_error( "Failure in TPyMultiGradFunction::DoDerivative" );
   }

   double cppresult = (double)PyFloat_AsDouble( pyresult );
   Py_XDECREF( pyresult );

   return cppresult;
}

 TPyFitFunction.cxx:1
 TPyFitFunction.cxx:2
 TPyFitFunction.cxx:3
 TPyFitFunction.cxx:4
 TPyFitFunction.cxx:5
 TPyFitFunction.cxx:6
 TPyFitFunction.cxx:7
 TPyFitFunction.cxx:8
 TPyFitFunction.cxx:9
 TPyFitFunction.cxx:10
 TPyFitFunction.cxx:11
 TPyFitFunction.cxx:12
 TPyFitFunction.cxx:13
 TPyFitFunction.cxx:14
 TPyFitFunction.cxx:15
 TPyFitFunction.cxx:16
 TPyFitFunction.cxx:17
 TPyFitFunction.cxx:18
 TPyFitFunction.cxx:19
 TPyFitFunction.cxx:20
 TPyFitFunction.cxx:21
 TPyFitFunction.cxx:22
 TPyFitFunction.cxx:23
 TPyFitFunction.cxx:24
 TPyFitFunction.cxx:25
 TPyFitFunction.cxx:26
 TPyFitFunction.cxx:27
 TPyFitFunction.cxx:28
 TPyFitFunction.cxx:29
 TPyFitFunction.cxx:30
 TPyFitFunction.cxx:31
 TPyFitFunction.cxx:32
 TPyFitFunction.cxx:33
 TPyFitFunction.cxx:34
 TPyFitFunction.cxx:35
 TPyFitFunction.cxx:36
 TPyFitFunction.cxx:37
 TPyFitFunction.cxx:38
 TPyFitFunction.cxx:39
 TPyFitFunction.cxx:40
 TPyFitFunction.cxx:41
 TPyFitFunction.cxx:42
 TPyFitFunction.cxx:43
 TPyFitFunction.cxx:44
 TPyFitFunction.cxx:45
 TPyFitFunction.cxx:46
 TPyFitFunction.cxx:47
 TPyFitFunction.cxx:48
 TPyFitFunction.cxx:49
 TPyFitFunction.cxx:50
 TPyFitFunction.cxx:51
 TPyFitFunction.cxx:52
 TPyFitFunction.cxx:53
 TPyFitFunction.cxx:54
 TPyFitFunction.cxx:55
 TPyFitFunction.cxx:56
 TPyFitFunction.cxx:57
 TPyFitFunction.cxx:58
 TPyFitFunction.cxx:59
 TPyFitFunction.cxx:60
 TPyFitFunction.cxx:61
 TPyFitFunction.cxx:62
 TPyFitFunction.cxx:63
 TPyFitFunction.cxx:64
 TPyFitFunction.cxx:65
 TPyFitFunction.cxx:66
 TPyFitFunction.cxx:67
 TPyFitFunction.cxx:68
 TPyFitFunction.cxx:69
 TPyFitFunction.cxx:70
 TPyFitFunction.cxx:71
 TPyFitFunction.cxx:72
 TPyFitFunction.cxx:73
 TPyFitFunction.cxx:74
 TPyFitFunction.cxx:75
 TPyFitFunction.cxx:76
 TPyFitFunction.cxx:77
 TPyFitFunction.cxx:78
 TPyFitFunction.cxx:79
 TPyFitFunction.cxx:80
 TPyFitFunction.cxx:81
 TPyFitFunction.cxx:82
 TPyFitFunction.cxx:83
 TPyFitFunction.cxx:84
 TPyFitFunction.cxx:85
 TPyFitFunction.cxx:86
 TPyFitFunction.cxx:87
 TPyFitFunction.cxx:88
 TPyFitFunction.cxx:89
 TPyFitFunction.cxx:90
 TPyFitFunction.cxx:91
 TPyFitFunction.cxx:92
 TPyFitFunction.cxx:93
 TPyFitFunction.cxx:94
 TPyFitFunction.cxx:95
 TPyFitFunction.cxx:96
 TPyFitFunction.cxx:97
 TPyFitFunction.cxx:98
 TPyFitFunction.cxx:99
 TPyFitFunction.cxx:100
 TPyFitFunction.cxx:101
 TPyFitFunction.cxx:102
 TPyFitFunction.cxx:103
 TPyFitFunction.cxx:104
 TPyFitFunction.cxx:105
 TPyFitFunction.cxx:106
 TPyFitFunction.cxx:107
 TPyFitFunction.cxx:108
 TPyFitFunction.cxx:109
 TPyFitFunction.cxx:110
 TPyFitFunction.cxx:111
 TPyFitFunction.cxx:112
 TPyFitFunction.cxx:113
 TPyFitFunction.cxx:114
 TPyFitFunction.cxx:115
 TPyFitFunction.cxx:116
 TPyFitFunction.cxx:117
 TPyFitFunction.cxx:118
 TPyFitFunction.cxx:119
 TPyFitFunction.cxx:120
 TPyFitFunction.cxx:121
 TPyFitFunction.cxx:122
 TPyFitFunction.cxx:123
 TPyFitFunction.cxx:124
 TPyFitFunction.cxx:125
 TPyFitFunction.cxx:126
 TPyFitFunction.cxx:127
 TPyFitFunction.cxx:128
 TPyFitFunction.cxx:129
 TPyFitFunction.cxx:130
 TPyFitFunction.cxx:131
 TPyFitFunction.cxx:132
 TPyFitFunction.cxx:133
 TPyFitFunction.cxx:134
 TPyFitFunction.cxx:135
 TPyFitFunction.cxx:136
 TPyFitFunction.cxx:137
 TPyFitFunction.cxx:138
 TPyFitFunction.cxx:139
 TPyFitFunction.cxx:140
 TPyFitFunction.cxx:141
 TPyFitFunction.cxx:142
 TPyFitFunction.cxx:143
 TPyFitFunction.cxx:144
 TPyFitFunction.cxx:145
 TPyFitFunction.cxx:146
 TPyFitFunction.cxx:147
 TPyFitFunction.cxx:148
 TPyFitFunction.cxx:149
 TPyFitFunction.cxx:150
 TPyFitFunction.cxx:151
 TPyFitFunction.cxx:152
 TPyFitFunction.cxx:153
 TPyFitFunction.cxx:154
 TPyFitFunction.cxx:155
 TPyFitFunction.cxx:156
 TPyFitFunction.cxx:157
 TPyFitFunction.cxx:158
 TPyFitFunction.cxx:159
 TPyFitFunction.cxx:160
 TPyFitFunction.cxx:161
 TPyFitFunction.cxx:162
 TPyFitFunction.cxx:163
 TPyFitFunction.cxx:164
 TPyFitFunction.cxx:165
 TPyFitFunction.cxx:166
 TPyFitFunction.cxx:167
 TPyFitFunction.cxx:168
 TPyFitFunction.cxx:169
 TPyFitFunction.cxx:170
 TPyFitFunction.cxx:171
 TPyFitFunction.cxx:172
 TPyFitFunction.cxx:173
 TPyFitFunction.cxx:174
 TPyFitFunction.cxx:175
 TPyFitFunction.cxx:176
 TPyFitFunction.cxx:177
 TPyFitFunction.cxx:178
 TPyFitFunction.cxx:179
 TPyFitFunction.cxx:180
 TPyFitFunction.cxx:181
 TPyFitFunction.cxx:182
 TPyFitFunction.cxx:183
 TPyFitFunction.cxx:184
 TPyFitFunction.cxx:185
 TPyFitFunction.cxx:186
 TPyFitFunction.cxx:187
 TPyFitFunction.cxx:188
 TPyFitFunction.cxx:189
 TPyFitFunction.cxx:190
 TPyFitFunction.cxx:191
 TPyFitFunction.cxx:192
 TPyFitFunction.cxx:193
 TPyFitFunction.cxx:194
 TPyFitFunction.cxx:195
 TPyFitFunction.cxx:196
 TPyFitFunction.cxx:197
 TPyFitFunction.cxx:198
 TPyFitFunction.cxx:199
 TPyFitFunction.cxx:200
 TPyFitFunction.cxx:201
 TPyFitFunction.cxx:202
 TPyFitFunction.cxx:203
 TPyFitFunction.cxx:204
 TPyFitFunction.cxx:205
 TPyFitFunction.cxx:206
 TPyFitFunction.cxx:207
 TPyFitFunction.cxx:208
 TPyFitFunction.cxx:209
 TPyFitFunction.cxx:210
 TPyFitFunction.cxx:211
 TPyFitFunction.cxx:212
 TPyFitFunction.cxx:213
 TPyFitFunction.cxx:214
 TPyFitFunction.cxx:215
 TPyFitFunction.cxx:216
 TPyFitFunction.cxx:217
 TPyFitFunction.cxx:218
 TPyFitFunction.cxx:219
 TPyFitFunction.cxx:220
 TPyFitFunction.cxx:221
 TPyFitFunction.cxx:222
 TPyFitFunction.cxx:223
 TPyFitFunction.cxx:224
 TPyFitFunction.cxx:225
 TPyFitFunction.cxx:226
 TPyFitFunction.cxx:227
 TPyFitFunction.cxx:228
 TPyFitFunction.cxx:229
 TPyFitFunction.cxx:230
 TPyFitFunction.cxx:231
 TPyFitFunction.cxx:232
 TPyFitFunction.cxx:233
 TPyFitFunction.cxx:234
 TPyFitFunction.cxx:235
 TPyFitFunction.cxx:236
 TPyFitFunction.cxx:237
 TPyFitFunction.cxx:238
 TPyFitFunction.cxx:239
 TPyFitFunction.cxx:240
 TPyFitFunction.cxx:241
 TPyFitFunction.cxx:242
 TPyFitFunction.cxx:243
 TPyFitFunction.cxx:244
 TPyFitFunction.cxx:245
 TPyFitFunction.cxx:246
 TPyFitFunction.cxx:247
 TPyFitFunction.cxx:248
 TPyFitFunction.cxx:249
 TPyFitFunction.cxx:250
 TPyFitFunction.cxx:251
 TPyFitFunction.cxx:252
 TPyFitFunction.cxx:253
 TPyFitFunction.cxx:254
 TPyFitFunction.cxx:255
 TPyFitFunction.cxx:256
 TPyFitFunction.cxx:257
 TPyFitFunction.cxx:258
 TPyFitFunction.cxx:259
 TPyFitFunction.cxx:260
 TPyFitFunction.cxx:261
 TPyFitFunction.cxx:262