/* This is a set of functions used by test_extension_interface.py to test the
 * Greenlet C API.
 */

#include "../greenlet.h"

#ifndef Py_RETURN_NONE
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
#endif

#define TEST_MODULE_NAME "_test_extension"

static PyObject *
test_switch(PyObject *self, PyObject *greenlet)
{
	PyObject *result = NULL;

	if (greenlet == NULL || !PyGreenlet_Check(greenlet)) {
		PyErr_BadArgument();
		return NULL;
	}

	result = PyGreenlet_Switch((PyGreenlet *) greenlet, NULL, NULL);
	if (result == NULL) {
		if (!PyErr_Occurred()) {
			PyErr_SetString(
				PyExc_AssertionError,
				"greenlet.switch() failed for some reason.");
		}
		return NULL;
	}
	Py_INCREF(result);
	return result;
}

static PyObject *
test_switch_kwargs(PyObject *self, PyObject *args, PyObject *kwargs)
{
	PyGreenlet *g = NULL;
	PyObject *result = NULL;

	PyArg_ParseTuple(args, "O!", &PyGreenlet_Type, &g);

	if (g == NULL || !PyGreenlet_Check(g)) {
		PyErr_BadArgument();
		return NULL;
	}

	result = PyGreenlet_Switch(g, NULL, kwargs);
	if (result == NULL) {
		if (!PyErr_Occurred()) {
			PyErr_SetString(
				PyExc_AssertionError,
				"greenlet.switch() failed for some reason.");
		}
		return NULL;
	}
	Py_XINCREF(result);
	return result;
}

static PyObject *
test_getcurrent(PyObject *self)
{
	PyGreenlet *g = PyGreenlet_GetCurrent();
	if (g == NULL || !PyGreenlet_Check(g) || !PyGreenlet_ACTIVE(g)) {
		PyErr_SetString(
			PyExc_AssertionError,
			"getcurrent() returned an invalid greenlet");
		Py_XDECREF(g);
		return NULL;
	}
	Py_DECREF(g);
	Py_RETURN_NONE;
}

static PyObject *
test_setparent(PyObject *self, PyObject *arg)
{
	PyGreenlet *current;
	PyGreenlet *greenlet = NULL;

	if (arg == NULL || !PyGreenlet_Check(arg))
	{
		PyErr_BadArgument();
		return NULL;
	}
	if ((current = PyGreenlet_GetCurrent()) == NULL) {
		return NULL;
	}
	greenlet = (PyGreenlet *) arg;
	if (PyGreenlet_SetParent(greenlet, current)) {
		Py_DECREF(current);
		return NULL;
	}
	Py_DECREF(current);
	if (PyGreenlet_Switch(greenlet, NULL, NULL) == NULL) {
		return NULL;
	}
	Py_RETURN_NONE;
}

static PyObject *
test_new_greenlet(PyObject *self, PyObject *callable)
{
	PyObject *result = NULL;
	PyGreenlet *greenlet = PyGreenlet_New(callable, NULL);

	if (!greenlet) {
		return NULL;
	}

	result = PyGreenlet_Switch(greenlet, NULL, NULL);
	if (result == NULL) {
		return NULL;
	}

	Py_INCREF(result);
	return result;
}

static PyObject *
test_raise_dead_greenlet(PyObject *self)
{
	PyErr_SetString(PyExc_GreenletExit, "test GreenletExit exception.");
	return NULL;
}

static PyObject *
test_raise_greenlet_error(PyObject *self)
{
	PyErr_SetString(PyExc_GreenletError, "test greenlet.error exception");
	return NULL;
}

static PyObject *
test_throw(PyObject *self, PyGreenlet *g)
{
	const char msg[] = "take that sucka!";
	PyObject *msg_obj = Py_BuildValue("s", msg);
	PyGreenlet_Throw(g, PyExc_ValueError, msg_obj, NULL);
	Py_DECREF(msg_obj);
	Py_RETURN_NONE;
}

static PyMethodDef test_methods[] = {
	{"test_switch", (PyCFunction) test_switch, METH_O,
	 "Switch to the provided greenlet sending provided arguments, and \n"
	 "return the results."},
	{"test_switch_kwargs", (PyCFunction) test_switch_kwargs,
	 METH_VARARGS | METH_KEYWORDS,
	 "Switch to the provided greenlet sending the provided keyword args."},
	{"test_getcurrent", (PyCFunction) test_getcurrent, METH_NOARGS,
	 "Test PyGreenlet_GetCurrent()"},
	{"test_setparent", (PyCFunction) test_setparent, METH_O,
	 "Se the parent of the provided greenlet and switch to it."},
	{"test_new_greenlet", (PyCFunction) test_new_greenlet, METH_O,
	 "Test PyGreenlet_New()"},
	{"test_raise_dead_greenlet", (PyCFunction) test_raise_dead_greenlet,
	 METH_NOARGS, "Just raise greenlet.GreenletExit"},
	{"test_raise_greenlet_error", (PyCFunction) test_raise_greenlet_error,
	 METH_NOARGS, "Just raise greenlet.error"},
	{"test_throw", (PyCFunction) test_throw, METH_O,
	 "Throw a ValueError at the provided greenlet"},
	{NULL, NULL, 0, NULL}
};

#if PY_MAJOR_VERSION >= 3
#define INITERROR return NULL

static struct PyModuleDef moduledef = {
	PyModuleDef_HEAD_INIT,
	TEST_MODULE_NAME,
	NULL,
	0,
	test_methods,
	NULL,
	NULL,
	NULL,
	NULL
};

PyMODINIT_FUNC
PyInit__test_extension(void)
#else
#define INITERROR return
PyMODINIT_FUNC
init_test_extension(void)
#endif
{
	PyObject *module = NULL;

#if PY_MAJOR_VERSION >= 3
	module = PyModule_Create(&moduledef);
#else
	module = Py_InitModule(TEST_MODULE_NAME, test_methods);
#endif

	if (module == NULL) {
		INITERROR;
	}

	PyGreenlet_Import();

#if PY_MAJOR_VERSION >= 3
	return module;
#endif
}
