//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include <python.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "PythonEngine"
#pragma link "PythonGUIInputOutput"
#pragma resource "*.dfm"
TForm1 *Form1;

//---------------------------------------------------------------------------
// New Python type Point
//
// Constructors & Destructors

// We override the constructors
__fastcall TPyPoint::TPyPoint( TPythonType* APythonType )
  : TPyObject(APythonType)
{
  x = 0;
  y = 0;
}

// Don't call the Create constructor of TPyPoint, because
// we call the inherited constructor CreateWith that calls
// the Create constructor first, and because the constructors
// are virtual, TPyPoint.Create will be automatically be called.
__fastcall TPyPoint::TPyPoint( TPythonType* APythonType, PPyObject args )
  : TPyObject(APythonType, args)
{
  if (! Py::PyArg_ParseTuple( (Py::PyObject *)args, "ii:CreatePoint", &x, &y ))
    return;
}

// Type services
////////////////

// Basic services
PPyObject __fastcall TPyPoint::GetAttr(char * key)
{
  TPythonEngine* eng = GetPythonEngine();
  if ( strcmp(key, "x") == 0 )
    return eng->VariantAsPyObject( x );
    // Or  return eng->PyInt_FromLong( x );
  else if ( strcmp(key, "y" ) == 0 )
    return eng->VariantAsPyObject( y );
    // or  return PyInt_FromLong( y );
  else
    return inherited::GetAttr(key);
}

int __fastcall TPyPoint::SetAttr(char * key, PPyObject value)
{
  Py::PyObject * val = (Py::PyObject *)value;
  if ( strcmp(key, "x") == 0 ) {
    if ( Py::PyArg_Parse( val, "i:Point.SetAttr", &x ) )
      return 0;
    else
      return -1;
  } else if ( strcmp(key, "y") == 0 ) {
    if ( Py::PyArg_Parse( val, "i:Point.SetAttr", &y ) )
      return 0;
    else
      return -1;
  } else
    return inherited::SetAttr(key, value);
}

PPyObject __fastcall TPyPoint::Repr(void)
{
  TPythonEngine* eng = GetPythonEngine();
  return eng->VariantAsPyObject(Format("(%d, %d)",ARRAYOFCONST((x, y))));
    // or return eng->PyString_FromString( (char *)Format("(%d, %d)", ARRAYOFCONST((x, y))) );
}

// Interface methods
// They will be called directly by Python, so we extract the
// python arguments and we call the method that will really do
// the action.

PPyObject __cdecl DoOffsetBy( PPyObject self, PPyObject args )
{
  TPyPoint * p = (TPyPoint *)PythonToDelphi(self);
  int dx, dy;
  if (! Py::PyArg_ParseTuple( (Py::PyObject *)args, "ii:OffsetBy", &dx, &dy ))
    return NULL;
  p->OffsetBy(dx, dy);
  TPythonEngine* eng = GetPythonEngine();
  return eng->ReturnNone();
}

PPyObject __cdecl DoRaiseError( PPyObject self, PPyObject args )
{
  TPyPoint * p = (TPyPoint *)PythonToDelphi(self);
  p->RaiseError();
  return NULL; // we return NULL because RaiseError raised a Python error.
}

// Class method for registering the type interface methods
void __fastcall TPyPoint::RegisterMethods(TPythonType* PythonType)
{
  inherited::RegisterMethods(PythonType);
  PythonType->AddMethod( "OffsetBy", DoOffsetBy, "Point.OffsetBy( dx, dy )" );
  PythonType->AddMethod( "RaiseError", DoRaiseError, "Point.RaiseError()" );
}

// Methods of TPyPoint
// They do the real actions on the object
// It's better to split the functions that interface
// Delphi to Python and the functions that do the
// real implementation.

void TPyPoint::OffsetBy( int dx, int dy )
{
  x += dx;
  y += dy;
}

// Here's an example of how you can raise errors defined
// in the module linked to our type.
void TPyPoint::RaiseError(void)
{
  TPythonEngine* eng = GetPythonEngine();
  // This is a simple call:
  // GetModule->RaiseError( "PointError", "this is an example of raising an error !" );
  // This is an advanced call:
  // We provide the instance vars as a dictionary, so that we can intercept the
  // error with "except" and extract informations from the error object.
  // ArrayToPyDict needs a list of pairs: varName (string), varValue (anything)
  GetModule()->RaiseErrorObj( "EBadPoint", "this is an example of raising an error !",
                            eng->ArrayToPyDict( ARRAYOFCONST(("a", 1, "b", 2, "c", 3)) ) );
}

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  PythonEngine1->ExecStrings( Memo2->Lines );
}
//---------------------------------------------------------------------------
// We need to initialize the property PyObjectClass with
// the class of our Type object
void __fastcall TForm1::PythonType1Initialization(TObject *Sender)
{
  PythonType1->PyObjectClass = __classid(TPyPoint);
}
//---------------------------------------------------------------------------
