#include <eter/__py_tinker_method.h>
#include <eter/__py_tinker_func.h>

namespace py_tinker {

extern "C"
{
extern PyObject* PyCppInstance_New(PyObject* type, PyObject* args);

extern PyTypeObject PyCppClass_Type;

struct PyCppClass_SObject
{
	PyObject_HEAD
	PyObject* cl_bases;
	PyObject* cl_dict;
	PyObject* cl_name;	

	IFunction* cl_new_cpp_inst;
};


PyObject* PyCppClass_NewInstance(PyObject* self, PyObject* args)
{
	PyCppClass_SObject* cls = (PyCppClass_SObject*)self;

	return (*cls->cl_new_cpp_inst)(self, args);
}

void PyCppClass_DeleteInstance(PyObject* self, void* cpp_inst)
{
	PyCppClass_SObject* cls = (PyCppClass_SObject*)self;
	//cls->cl_del_cpp_inst(cpp_inst);
}


PyObject* PyCppClass_New(const char* name, IFunction* new_cpp_inst)
{	
	PyCppClass_SObject* cls = (PyCppClass_SObject*)PyType_GenericAlloc(&PyCppClass_Type, 0);

	Py_INCREF(Py_None);	
	cls->cl_bases	= Py_None;
	cls->cl_dict	= PyDict_New();
	cls->cl_name	= PyString_FromString(name);
	cls->cl_new_cpp_inst = new_cpp_inst;

	return (PyObject*)cls;	
}

static void
PyCppClass_Dealloc(PyCppClass_SObject* cls)
{			
	if (cls->cl_new_cpp_inst)
	{
		delete cls->cl_new_cpp_inst;
		cls->cl_new_cpp_inst = NULL;
	}

	Py_DECREF(cls->cl_bases);
	Py_DECREF(cls->cl_dict);
	Py_DECREF(cls->cl_name);
	cls->ob_type->tp_free(cls);	
}

int PyCppClass_SetAttrString(PyObject* self, const char* name, PyObject* value)
{	
	PyCppClass_SObject* cls = (PyCppClass_SObject*)self;	 
	int ret = PyDict_SetItem(cls->cl_dict, PyString_FromString(name), value);
	Py_DECREF(value);
	return ret;
}

PyObject* PyCppClass_GetAttr(PyObject* self, PyObject* key)
{
	PyCppClass_SObject* cls = (PyCppClass_SObject*)self;
	
	const char* skey = PyString_AsString(key);
	if (skey[0] == '_' && skey[1] == '_') 
	{
		if (strcmp(skey, "__dict__") == 0)
		{
			if (PyEval_GetRestricted()) 
			{
				PyErr_SetString(PyExc_RuntimeError, "py_tinker.cpp_class not accessible in restricted mode");
				return NULL;
			}
			Py_INCREF(cls->cl_dict);
			return cls->cl_dict;
		}
		else if (strcmp(skey, "__name__") == 0)
		{
			Py_INCREF(cls->cl_name);
			return cls->cl_name;
		}
	}

	PyObject* v = PyDict_GetItem(cls->cl_dict, key);
	if (NULL == v)
		return NULL;

	descrgetfunc descr_get = PY_TINKER_TP_DESCR_GET(v->ob_type);
	if (descr_get)
		return descr_get(v, NULL, self);		
	
	Py_INCREF(v);
	return v;
	
}

static PyObject *
PyCppClass_Repr(PyCppClass_SObject* cls)
{
	char repr[256];
	snprintf(repr, sizeof(repr)-1, "<%s %s at 0x%p>", cls->ob_type->tp_name, PyString_AsString(cls->cl_name), cls);
	return PyString_FromString(repr);
}

static PyObject *
PyCppClass_Call(PyObject* self, PyObject* args, PyObject* kw)
{
	return PyCppInstance_New(self, args);
}



PyTypeObject PyCppClass_Type = {
    PyObject_HEAD_INIT(&PyType_Type)
    0,
    "py_tinker.cpp.class",
    sizeof(PyCppClass_Type),
    0,
    (destructor)PyCppClass_Dealloc,       /* tp_dealloc */
    0,                                  /* tp_print */
    0,                                  /* tp_getattr */
    0,                                  /* tp_setattr */
    0,                                  /* tp_compare */
    (reprfunc)PyCppClass_Repr,     /* tp_repr */
    0,                                  /* tp_as_number */
    0,                                  /* tp_as_sequence */
    0,                                  /* tp_as_mapping */
    0,                                  /* tp_hash */
    PyCppClass_Call,                              /* tp_call */
    0,                                  /* tp_str */
    (getattrofunc)PyCppClass_GetAttr,   /* 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 */
    0,                 /* 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




















