#include <eter/__py_tinker_method.h>

namespace py_tinker {

extern PyTypeObject PyCppMethod_Type;

struct PyCppMethod_SObject
{
	PyObject_HEAD
	PyObject*	mt_self;
	IMethod*	mt_cpp_method;

	PyObject* operator() (PyObject* self, PyObject* args)
	{
		PYTINKER_CALL(return (*mt_cpp_method)(self, args))
	}
};

PyObject* PyCppMethod_FromMethod(IMethod* cpp_method)
{
	PyCppMethod_SObject* ret_method = (PyCppMethod_SObject*)PyType_GenericAlloc(&PyCppMethod_Type, 0);	
	ret_method->mt_self = NULL;
	ret_method->mt_cpp_method = cpp_method;
	return (PyObject*)ret_method;	
}

void PyCppMethod_Destruct(PyCppMethod_SObject* method)
{
	if (method->mt_cpp_method)
	{
		delete method->mt_cpp_method;
		method->mt_cpp_method = NULL;
	}
}


static PyObject *
PyCppMethod_Call(PyObject* self, PyObject* args, PyObject* kw)
{	
	PyCppMethod_SObject* method = (PyCppMethod_SObject*)self;
	PYTINKER_CALL(return (*method)(method->mt_self, args))
}

extern "C"
{

PyObject* 
PyCppMethod_BuildBoundCppMethod(PyObject* method, PyObject* self)
{
	PyCppMethod_SObject* src_method = (PyCppMethod_SObject*)method;
	PyCppMethod_SObject* ret_method = (PyCppMethod_SObject*)PyType_GenericAlloc(src_method->ob_type, 0);
	Py_INCREF(self);
	ret_method->mt_self = self;
	ret_method->mt_cpp_method = src_method->mt_cpp_method;
	return (PyObject*)ret_method;	
}

static PyObject *
PyCppMethod_DescrGet(PyObject* self, PyObject* obj, PyObject* cls)
{
	PyCppMethod_SObject* method = (PyCppMethod_SObject*)self;
	if (method->mt_self)
	{
		Py_INCREF(self);
		return self;
	}

	// UnboundCppMethod
	if (!obj)
	{
		Py_INCREF(self);
		return self;
	}

    return PyCppMethod_BuildBoundCppMethod(self, obj);
}

static PyObject *
PyCppMethod_Repr(PyCppMethod_SObject* method)
{
	char repr[256];
	snprintf(repr, sizeof(repr)-1, "<%s %s %s at 0x%p>", method->mt_self ? "bound" : "unbound", method->ob_type->tp_name, method->mt_cpp_method->GetName().c_str(), method);
	return PyString_FromString(repr);
}

static void
PyCppMethod_Dealloc(PyCppMethod_SObject* method)
{
	if (method->mt_self)
		Py_DECREF(method->mt_self);
	else
		PyCppMethod_Destruct(method);
	
	method->ob_type->tp_free(method);	
}

PyTypeObject PyCppMethod_Type = {
    PyObject_HEAD_INIT(&PyType_Type)
    0,
    "py_tinker.cpp.method",
    sizeof(PyCppMethod_Type),
    0,
    (destructor)PyCppMethod_Dealloc,       /* tp_dealloc */
    0,                                  /* tp_print */
    0,                                  /* tp_getattr */
    0,                                  /* tp_setattr */
    0,                                  /* tp_compare */
    (reprfunc)PyCppMethod_Repr,    /* tp_repr */
    0,                                  /* tp_as_number */
    0,                                  /* tp_as_sequence */
    0,                                  /* tp_as_mapping */
    0,                                  /* tp_hash */
    PyCppMethod_Call,                              /* tp_call */
    0,                                  /* tp_str */
    0,						          /* tp_getattro */
    0,								   /* tp_setattro */
    0,                                  /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT /* | Py_TPFLAGS_HAVE_GC */,/* tp_flags */
    0,                                  /* tp_doc */
    0, // (traverseproc)func_traverse,          /* tp_traverse */
    0,                                  /* tp_clear */
    0,                                  /* tp_richcompare */
    0, //offsetof(PyFunctionObject, func_weakreflist), /* tp_weaklistoffset */
    0,                                  /* tp_iter */
    0,                                  /* tp_iternext */
    0,                                  /* tp_methods */
    0, // func_memberlist,              /* tp_members */
    0,                /* tp_getset */
    0,                                  /* tp_base */
    0,                                  /* tp_dict */
    PyCppMethod_DescrGet,                 /* tp_descr_get */
    0,                                  /* tp_descr_set */
    0, //offsetof(PyFunctionObject, func_dict),      /* tp_dictoffset */
    0,                                      /* tp_init */
    0,									/* tp_alloc */
    0,							/* tp_new */
    PyObject_Del,									/* tp_free */                                       
};


} // end of extern "C"


} // py_tinker




















