#include <python/Python.h>

//TODO: using_native accessible to python scripts

typedef struct {
    PyObject_HEAD
    SimObject* obj;
    unsigned int count;
    unsigned int counter;
    bool using_native;
} SimSetLooper_SSIObject;


static int
SimSetLooper_init(SimSetLooper_SSIObject *self, PyObject *args, PyObject *kwds)
{
	//ignore kwords

	int objID;
	if(!PythonABI::PyArg_ParseTuple(args, "i:SimSetLooper_init", &objID))
	{
		PythonABI::PyErr_SetString(*PythonABI::PyExc_TypeError,
		"Usage: (int)object ID");
		return -1;
	}

	SimObject* obj = (SimObject*)FIXEDtsf_FindObject(objID);
	if(obj == NULL)
	{
		PythonABI::PyErr_SetString(TorqueObjectNotFoundError, "Object ID not found");
		return -1;
	}
	self->obj = obj;

	if(NamespaceClasses_instanceOf(obj, "SimSet") > 0)
	{
		self->using_native = true;
		self->count = ((SimSet*)obj)->mElementCount;
	}

	else
		self->count = atoi(tsf_BlCon__executefSimObj((ADDR*)obj, 1, "getCount"));

	self->counter = 0;

	return 0;
}

static PyObject*
SimSetLooper_isNative(PyObject *self, PyObject *args)
{
	SimSetLooper_SSIObject* selfR = (SimSetLooper_SSIObject*)self;

	//Too lazy to do a bool
	return PythonABI::PyLong_FromSsize_t(selfR->using_native);
}

PyObject* SimSetLooper_iter(PyObject* self)
{
	//https://stackoverflow.com/questions/53302105/exception-thrown-read-access-violation-bp-was-0xffffffffffffffff
	//https://docs.python.org/3/extending/newtypes_tutorial.html
	//definition of Custom_getfirst made me realize that self-returns have to increase reference

	//missing this IncRef wasted over a day of work:
	PythonABI::Py_IncRef(self);
	return self;
}

PyObject* SimSetLooper_next(SimSetLooper_SSIObject* self)
{
	if(self->counter >= self->count)
	{
		PythonABI::PyErr_SetNone(*PythonABI::PyExc_StopIteration);
		return NULL;
	}

	unsigned int iterObjID;
	if(self->using_native)
	{
		SimSet* self_obj = ((SimSet*)self->obj);
		SimObject* objChild = (SimObject*)(*self_obj->mArray)[self->counter];
		iterObjID = objChild->id;
	}

	else
	{
		static char buf[16];
		snprintf(buf, 16, "%u", self->counter);
		iterObjID = atoi(tsf_BlCon__executefSimObj((ADDR*)self->obj, 2, "getObject", buf));
	}

	self->counter++;

	return PythonABI::PyLong_FromLong(iterObjID);
}


static PyMethodDef SimSetLooper_methods[] = {
    {"isNative", SimSetLooper_isNative, METH_NOARGS,
     "Whether this object is using direct memory access to the SimSet (as if being done directly by the engine)"},

    {NULL, NULL, 0, NULL} //sentinel
};

//TODO: new vs. init??

static PyTypeObject SimSetLooper_SSIType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    "SimSetLooper",             /* tp_name */
    sizeof(SimSetLooper_SSIObject), /* tp_basicsize */
    0,                         /* tp_itemsize */
    0, //(destructor)SimSetLooper_dealloc, /* tp_dealloc */
    0,                         /* tp_print */
    0,                         /* tp_getattr */
    0,                         /* tp_setattr */
    0,                         /* tp_reserved */
    0,                         /* tp_repr */
    0,                         /* tp_as_number */
    0,                         /* tp_as_sequence */
    0,                         /* tp_as_mapping */
    0,                         /* tp_hash  */
    0,                         /* tp_call */
    0,                         /* tp_str */
    0,                         /* tp_getattro */
    0,                         /* tp_setattro */
    0,                         /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT |
        Py_TPFLAGS_BASETYPE,   /* tp_flags */
    "SimSetLooper objects",           /* tp_doc */
    0,                         /* tp_traverse */
    0,                         /* tp_clear */
    0,                         /* tp_richcompare */
    0,                         /* tp_weaklistoffset */
    (getiterfunc)SimSetLooper_iter,  /* tp_iter */
    (iternextfunc)SimSetLooper_next, /* tp_iternext */
    SimSetLooper_methods,   /* tp_methods */
    0, //SimSetLooper_members,   /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc)SimSetLooper_init,      /* tp_init */
    0,                         /* tp_alloc */
    0, //SimSetLooper_new,                 /* tp_new */
};
