/* 
 *
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * This program is free software; you can Redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA	02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * This is a new part of Blender.
 *
 * Contributor(s): Willian P. Germano
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
*/

#include "sceneRadio.h" /* includes Python.h */
#include "radio.h"
#include <BKE_object.h> /* disable_where_script() */

#include "gen_utils.h"
#include "constant.h"

/* bitflags */
#define EXPP_RADIO_flag_SHOWLIM 1
#define EXPP_RADIO_flag_Z 2
/* shorts */
#define EXPP_RADIO_hemires_MIN 100
#define EXPP_RADIO_hemires_MAX 1000
#define EXPP_RADIO_maxiter_MIN 0
#define EXPP_RADIO_maxiter_MAX 10000
#define EXPP_RADIO_subshootp_MIN 0
#define EXPP_RADIO_subshootp_MAX 10
#define EXPP_RADIO_subshoote_MIN 0
#define EXPP_RADIO_subshoote_MAX 10
#define EXPP_RADIO_nodelim_MIN 0
#define EXPP_RADIO_nodelim_MAX 50
#define EXPP_RADIO_maxsublamp_MIN 1
#define EXPP_RADIO_maxsublamp_MAX 250
#define EXPP_RADIO_pama_MIN 10
#define EXPP_RADIO_pama_MAX 1000
#define EXPP_RADIO_pami_MIN 10
#define EXPP_RADIO_pami_MAX 1000
#define EXPP_RADIO_elma_MIN 1
#define EXPP_RADIO_elma_MAX 500
#define EXPP_RADIO_elmi_MIN 1
#define EXPP_RADIO_elmi_MAX 500
/* ints */
#define EXPP_RADIO_maxnode_MIN 1
#define EXPP_RADIO_maxnode_MAX 250000
/* floats */
#define EXPP_RADIO_convergence_MIN 0.0
#define EXPP_RADIO_convergence_MAX 0.1
#define EXPP_RADIO_radfac_MIN 0.001
#define EXPP_RADIO_radfac_MAX 250.0
#define EXPP_RADIO_gamma_MIN 0.2
#define EXPP_RADIO_gamma_MAX 10.0
/* drawtypes */
#define EXPP_RADIO_drawtype_WIRE 0
#define EXPP_RADIO_drawtype_SOLID 1
#define EXPP_RADIO_drawtype_GOURAUD 2

static int EXPP_check_scene(Scene *scene)
{
	if (scene != G.scene) {
		PyErr_SetString(PyExc_EnvironmentError,
		"\nradiosity only works on the current scene, check scene.makeCurrent().");
		return 0;
	}
	else if (!scene->radio) {
		PyErr_SetString(PyExc_EnvironmentError,
			"\nradiosity data was deleted from scene!");
		return 0;
	}

	return 1;
}

static PyObject *Radio_collectMeshes(BPy_Radio *self);
static PyObject *Radio_go(BPy_Radio *self);
static PyObject *Radio_freeData(BPy_Radio *self);
static PyObject *Radio_replaceMeshes(BPy_Radio *self);
static PyObject *Radio_addMesh(BPy_Radio *self);
static PyObject *Radio_filterFaces(BPy_Radio *self);
static PyObject *Radio_filterElems(BPy_Radio *self);
static PyObject *Radio_limitSubdivide(BPy_Radio *self);
static PyObject *Radio_subdividePatches(BPy_Radio *self);
static PyObject *Radio_subdivideElems(BPy_Radio *self);
static PyObject *Radio_removeDoubles(BPy_Radio *self);

static void Radio_dealloc (BPy_Radio *self);
static PyObject *Radio_repr (BPy_Radio *self);

static PyObject *EXPP_create_ret_PyInt(int value)
{
	PyObject *pyval = PyInt_FromLong(value);

	if (!pyval)
		PyErr_SetString(PyExc_MemoryError, "couldn't create py int!");

	return pyval;
}

static PyObject *EXPP_create_ret_PyFloat(float value)
{
	PyObject *pyval = PyFloat_FromDouble((double)value);

	if (!pyval)
		PyErr_SetString(PyExc_MemoryError, "couldn't create py int!");

	return pyval;
}

static PyObject *Radio_get_hemires(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyInt((int)self->scene->radio->hemires);
}

static PyObject *Radio_get_maxiter(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyInt((int)self->scene->radio->maxiter);
}

static PyObject *Radio_get_subshootp(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyInt((int)self->scene->radio->subshootp);
}

static PyObject *Radio_get_subshoote(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyInt((int)self->scene->radio->subshoote);
}

static PyObject *Radio_get_nodelim(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyInt((int)self->scene->radio->nodelim);
}

static PyObject *Radio_get_maxsublamp(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyInt((int)self->scene->radio->maxsublamp);
}

static PyObject *Radio_get_pama(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyInt((int)self->scene->radio->pama);
}

static PyObject *Radio_get_pami(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyInt((int)self->scene->radio->pami);
}

static PyObject *Radio_get_elma(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyInt((int)self->scene->radio->elma);
}

static PyObject *Radio_get_elmi(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyInt((int)self->scene->radio->elmi);
}

static PyObject *Radio_get_drawtype(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyInt((int)self->scene->radio->drawtype);
}

static PyObject *Radio_get_flag(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyInt((int)self->scene->radio->flag);
}

static PyObject *Radio_get_maxnode(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyInt((int)self->scene->radio->maxnode);
}

static PyObject *Radio_get_convergence(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyFloat(self->scene->radio->convergence);
}

static PyObject *Radio_get_radfac(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyFloat(self->scene->radio->radfac);
}

static PyObject *Radio_get_gamma(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_create_ret_PyFloat(self->scene->radio->gamma);
}

static PyObject *EXPP_unpack_set_int(PyObject *args, int *ptr,
	int min, int max)
{
	int value;

	if (!PyArg_ParseTuple(args, "i", &value))
		return EXPP_ReturnPyObjError (PyExc_TypeError,
			"expected int argument");

	*ptr = EXPP_ClampInt(value, min, max);

	return EXPP_incr_ret (Py_None);
}

/* could merge with set_int, but is cleaner this way */
static PyObject *EXPP_unpack_set_short(PyObject *args, short *ptr,
	short min, short max)
{
	int value;

	if (!PyArg_ParseTuple(args, "i", &value))
		return EXPP_ReturnPyObjError (PyExc_TypeError,
			"expected int argument");

	*ptr = (short)EXPP_ClampInt(value, min, max);

	return EXPP_incr_ret (Py_None);
}

static PyObject *EXPP_unpack_set_float(PyObject *args, float *ptr,
	float min, float max)
{
	float value;

	if (!PyArg_ParseTuple(args, "f", &value))
		return EXPP_ReturnPyObjError (PyExc_TypeError,
			"expected float argument");

	*ptr = EXPP_ClampFloat(value, min, max);

	return EXPP_incr_ret (Py_None);
}

static PyObject *Radio_set_hemires(BPy_Radio *self, PyObject *args)
{
	PyObject *ret;

	if (!EXPP_check_scene(self->scene)) return NULL;
	ret = EXPP_unpack_set_short(args, &self->scene->radio->hemires,
		EXPP_RADIO_hemires_MIN, EXPP_RADIO_hemires_MAX);

	if (ret) rad_setlimits();

	return ret;
}

static PyObject *Radio_set_maxiter(BPy_Radio *self, PyObject *args)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_unpack_set_short(args, &self->scene->radio->maxiter,
		EXPP_RADIO_maxiter_MIN, EXPP_RADIO_maxiter_MAX);
}

static PyObject *Radio_set_subshootp(BPy_Radio *self, PyObject *args)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_unpack_set_short(args, &self->scene->radio->subshootp,
		EXPP_RADIO_subshootp_MIN, EXPP_RADIO_subshootp_MAX);
}

static PyObject *Radio_set_subshoote(BPy_Radio *self, PyObject *args)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_unpack_set_short(args, &self->scene->radio->subshoote,
		EXPP_RADIO_subshoote_MIN, EXPP_RADIO_subshoote_MAX);
}

static PyObject *Radio_set_nodelim(BPy_Radio *self, PyObject *args)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_unpack_set_short(args, &self->scene->radio->nodelim,
		EXPP_RADIO_nodelim_MIN, EXPP_RADIO_nodelim_MAX);
}

static PyObject *Radio_set_maxsublamp(BPy_Radio *self, PyObject *args)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_unpack_set_short(args, &self->scene->radio->maxsublamp,
		EXPP_RADIO_maxsublamp_MIN, EXPP_RADIO_maxsublamp_MAX);
}

static PyObject *Radio_set_pama(BPy_Radio *self, PyObject *args)
{
	PyObject *ret;

	if (!EXPP_check_scene(self->scene)) return NULL;
	ret = EXPP_unpack_set_short(args, &self->scene->radio->pama,
		EXPP_RADIO_pama_MIN, EXPP_RADIO_pama_MAX);

	if (ret) rad_setlimits();

	return ret;
}

static PyObject *Radio_set_pami(BPy_Radio *self, PyObject *args)
{
	PyObject *ret;

	if (!EXPP_check_scene(self->scene)) return NULL;
	ret = EXPP_unpack_set_short(args, &self->scene->radio->pami,
		EXPP_RADIO_pami_MIN, EXPP_RADIO_pami_MAX);

	if (ret) rad_setlimits();

	return ret;
}

static PyObject *Radio_set_elma(BPy_Radio *self, PyObject *args)
{
	PyObject *ret;

	if (!EXPP_check_scene(self->scene)) return NULL;
	ret = EXPP_unpack_set_short(args, &self->scene->radio->elma,
		EXPP_RADIO_elma_MIN, EXPP_RADIO_elma_MAX);

	if (ret) rad_setlimits();

	return ret;
}

static PyObject *Radio_set_elmi(BPy_Radio *self, PyObject *args)
{
	PyObject *ret;

	if (!EXPP_check_scene(self->scene)) return NULL;
	ret = EXPP_unpack_set_short(args, &self->scene->radio->elmi,
		EXPP_RADIO_elmi_MIN, EXPP_RADIO_elmi_MAX);

	if (ret) rad_setlimits();

	return ret;
}

static PyObject *Radio_set_drawtype(BPy_Radio *self, PyObject *args)
{
	PyObject *pyob = NULL;
	char *str = NULL;
	short dt = EXPP_RADIO_drawtype_WIRE;

	if (!EXPP_check_scene(self->scene)) return NULL;

	if (!PyArg_ParseTuple (args, "O", &pyob))
		return EXPP_ReturnPyObjError(PyExc_TypeError,
			"expected int or string and another optional int as arguments");

	if (PyString_Check(pyob)) {
		str = PyString_AsString(pyob);
		if (!str)
			return EXPP_ReturnPyObjError (PyExc_MemoryError,
				"couldn't create py string!");
		else if (!strcmp(str, "Wire")) dt = EXPP_RADIO_drawtype_WIRE;
		else if (!strcmp(str, "Solid")) dt = EXPP_RADIO_drawtype_SOLID;
		else if (!strcmp(str, "Gouraud")) dt = EXPP_RADIO_drawtype_GOURAUD;
		else
			return EXPP_ReturnPyObjError (PyExc_AttributeError,
				"unknown drawtype string");
	}
	else if (PyInt_Check(pyob)) {
		dt = (short)EXPP_ClampInt(PyInt_AsLong(pyob),
			EXPP_RADIO_drawtype_WIRE, EXPP_RADIO_drawtype_GOURAUD);
	}
	else
		return EXPP_ReturnPyObjError (PyExc_TypeError,
			"expected int or string as argument");

	self->scene->radio->drawtype = dt;

	set_radglobal(); /* needed to update 3d view(s) */

	return EXPP_incr_ret (Py_None);
}

static PyObject *Radio_set_flag(BPy_Radio *self, PyObject *args)
{
	int i, imode = 0;
	char *mode[2] = {NULL, NULL};

	if (!EXPP_check_scene(self->scene)) return NULL;

	if (!PyArg_ParseTuple(args, "|ss", &mode[0], &mode[1]))
		return EXPP_ReturnPyObjError (PyExc_TypeError,
			"expected string arguments (or nothing)");

	for (i = 0; i < 2; i++) {
		if (!mode[i]) break;
		else if (!strcmp(mode[i], "ShowLimits")) imode |= EXPP_RADIO_flag_SHOWLIM;
		else if (!strcmp(mode[i], "Z")) imode |= EXPP_RADIO_flag_Z;
		else return EXPP_ReturnPyObjError (PyExc_AttributeError,
			"unknown mode string");
	}

	self->scene->radio->flag = (short)EXPP_ClampInt(imode, 0, 3);

	set_radglobal(); /* needed to update 3d view(s) */

	return EXPP_incr_ret(Py_None);
}

static PyObject *Radio_set_maxnode(BPy_Radio *self, PyObject *args)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_unpack_set_int(args, &self->scene->radio->maxnode,
		EXPP_RADIO_maxnode_MIN, EXPP_RADIO_maxnode_MAX);
}

static PyObject *Radio_set_convergence(BPy_Radio *self, PyObject *args)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_unpack_set_float(args, &self->scene->radio->convergence,
		EXPP_RADIO_convergence_MIN, EXPP_RADIO_convergence_MAX);
}

static PyObject *Radio_set_radfac(BPy_Radio *self, PyObject *args)
{
	PyObject *ret;

	if (!EXPP_check_scene(self->scene)) return NULL;
	ret = EXPP_unpack_set_float(args, &self->scene->radio->radfac,
		EXPP_RADIO_radfac_MIN, EXPP_RADIO_radfac_MAX);

	if (ret) {
		set_radglobal();
		if (rad_phase() & RAD_PHASE_FACES) make_face_tab();
		else make_node_display();
	}

	return ret;
}

static PyObject *Radio_set_gamma(BPy_Radio *self, PyObject *args)
{
	if (!EXPP_check_scene(self->scene)) return NULL;
	return EXPP_unpack_set_float(args, &self->scene->radio->gamma,
		EXPP_RADIO_gamma_MIN, EXPP_RADIO_gamma_MAX);
}

static PyMethodDef BPy_Radio_methods[] = {
	{"collectMeshes", (PyCFunction) Radio_collectMeshes, METH_NOARGS,
		"() - Convert selected meshes to patches."},
	{"go", (PyCFunction) Radio_go, METH_NOARGS,
		"() - Start radiosity calculations."},
	{"freeData", (PyCFunction) Radio_freeData, METH_NOARGS,
		"() - Free all memory used by radiosity."},
	{"addMesh", (PyCFunction) Radio_addMesh, METH_NOARGS,
		"() - Add a new mesh with the radio values as vertex colors to Blender."},
	{"replaceMeshes", (PyCFunction) Radio_replaceMeshes, METH_NOARGS,
		"() - Replace input meshes with the one created by radiosity simulation."},
	{"limitSubdivide", (PyCFunction) Radio_limitSubdivide, METH_NOARGS,
		"() - Subdivide patches."},
	{"filterFaces", (PyCFunction) Radio_filterFaces, METH_NOARGS,
		"() - Force an extra smoothing."},
	{"filterElems", (PyCFunction) Radio_filterElems, METH_NOARGS,
		"() - Filter elements to remove aliasing artifacts."},
	{"subdividePatches", (PyCFunction) Radio_subdividePatches, METH_NOARGS,
		"() - Pre-subdivision: detect high-energy patches and subdivide them."},
	{"subdivideElems", (PyCFunction) Radio_subdivideElems, METH_NOARGS,
		"() - Pre-subdivision: detect high-energy elements and subdivide them."},
	{"removeDoubles", (PyCFunction) Radio_removeDoubles, METH_NOARGS,
		"() - Join elements which differ less than the defined node limit."},
	{"getHemiRes", (PyCFunction) Radio_get_hemires, METH_NOARGS,
		"() - Get hemicube size."},
	{"setHemiRes", (PyCFunction) Radio_set_hemires, METH_VARARGS,
		"(int) - Set hemicube size, the range is [100, 1000]."},
	{"getMaxIter", (PyCFunction) Radio_get_maxiter, METH_NOARGS,
		"() - Get maximum number of radiosity rounds."},
	{"setMaxIter", (PyCFunction) Radio_set_maxiter, METH_VARARGS,
		"(i) - Set maximum number of radiosity rounds in [0, 10000]."},
	{"getSubShPatch", (PyCFunction) Radio_get_subshootp, METH_NOARGS,
		"() - Get max number of times environment is tested to detect patches."},
	{"setSubShPatch", (PyCFunction) Radio_set_subshootp, METH_VARARGS,
		"(i) - Set max number of times environment is tested to detect patches.\n\
	Range is [0, 10]."},
	{"getSubShElem", (PyCFunction) Radio_get_subshoote, METH_NOARGS,
		"() - Get number of times environment is tested to detect elements."},
	{"setSubShElem", (PyCFunction) Radio_set_subshoote, METH_VARARGS,
		"(i) - Set number of times environment is tested to detect elements.\n\
	Range is [0, 10]."},
	{"getElemLimit", (PyCFunction) Radio_get_nodelim, METH_NOARGS,
		"() - Get the range for removing doubles."},
	{"setElemLimit", (PyCFunction) Radio_set_nodelim, METH_VARARGS,
		"(i) - Set the range for removing doubles in [0, 50]."},
	{"getMaxSubdivSh", (PyCFunction) Radio_get_maxsublamp, METH_NOARGS,
		"() - Get max number of initial shoot patches evaluated."},
	{"setMaxSubdivSh", (PyCFunction) Radio_set_maxsublamp, METH_VARARGS,
		"(i) - Set max number of initial shoot patches evaluated in [1, 250]."},
	{"getPatchMax", (PyCFunction) Radio_get_pama, METH_NOARGS,
		"() - Get max size of a patch."},
	{"setPatchMax", (PyCFunction) Radio_set_pama, METH_VARARGS,
		"(i) - Set max size of a patch in [10, 1000]."},
	{"getPatchMin", (PyCFunction) Radio_get_pami, METH_NOARGS,
		"() - Get minimum size of a patch."},
	{"setPatchMin", (PyCFunction) Radio_set_pami, METH_VARARGS,
		"(i) - Set minimum size of a patch in [10, 1000]."},
	{"getElemMax", (PyCFunction) Radio_get_elma, METH_NOARGS,
		"() - Get max size of an element."},
	{"setElemMax", (PyCFunction) Radio_set_elma, METH_VARARGS,
		"(i) - Set max size of an element in [1, 100]."},
	{"getElemMin", (PyCFunction) Radio_get_elmi, METH_NOARGS,
		"() - Get minimum size of an element."},
	{"setElemMin", (PyCFunction) Radio_set_elmi, METH_VARARGS,
		"(i) - Set minimum size of an element in [1, 100]."},
	{"getMaxElems", (PyCFunction) Radio_get_maxnode, METH_NOARGS,
		"() - Get maximum number of elements."},
	{"setMaxElems", (PyCFunction) Radio_set_maxnode, METH_VARARGS,
		"(i) - Set maximum nunber of elements in [1, 250000]."},
	{"getConvergence", (PyCFunction) Radio_get_convergence, METH_NOARGS,
		"() - Get lower threshold of unshot energy."},
	{"setConvergence", (PyCFunction) Radio_set_convergence, METH_VARARGS,
		"(f) - Set lower threshold of unshot energy in [0.0, 1.0]."},
	{"getMult", (PyCFunction) Radio_get_radfac, METH_NOARGS,
		"() - Get energy value multiplier."},
	{"setMult", (PyCFunction) Radio_set_radfac, METH_VARARGS,
		"(f) - Set energy value multiplier in [0.001, 250.0]."},
	{"getGamma", (PyCFunction) Radio_get_gamma, METH_NOARGS,
		"() - Get change in the contrast of energy values."},
	{"setGamma", (PyCFunction) Radio_set_gamma, METH_VARARGS,
		"(f) - Set change in the contrast of energy values in [0.2, 10.0]."},
	{"getDrawType", (PyCFunction) Radio_get_drawtype, METH_NOARGS,
		"() - Get the draw type: Wire, Solid or Gouraud as an int value."},
	{"setDrawType", (PyCFunction) Radio_set_drawtype, METH_VARARGS,
		"(i or s) - Set the draw type: wire, solid (default) or gouraud."},
	{"getMode", (PyCFunction) Radio_get_flag, METH_NOARGS,
		"() - Get mode as int (or'ed bitflags), see Radio.Modes dict."},
	{"setMode", (PyCFunction) Radio_set_flag, METH_VARARGS,
		"(|ss) - Set mode flags as strings: 'ShowLimits', 'Z'."},
	{NULL, NULL, 0, NULL}
};

static PyTypeObject Radio_Type = {
	PyObject_HEAD_INIT(NULL)
	0, /*ob_size*/
	"Blender Radiosity", /*tp_name*/
	sizeof(BPy_Radio), /*tp_basicsize*/
	0, /*tp_itemsize*/
	(destructor)Radio_dealloc, /*tp_dealloc*/
	0, /*tp_print*/
	0, /*tp_getattr*/
	0, /*tp_setattr*/
	0, /*tp_compare*/
	(reprfunc)Radio_repr, /*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*/
	"Blender radiosity", /* tp_doc */
	0, /* tp_traverse */
	0, /* tp_clear */
	0, /* tp_richcompare */
	0, /* tp_weaklistoffset */
	0, /* tp_iter */
	0, /* tp_iternext */
	BPy_Radio_methods, /* tp_methods */
	0, /* tp_members */
	0, /* tp_getset */
	0, /* tp_base */
	0, /* tp_dict */
	0, /* tp_descr_get */
	0, /* tp_descr_set */
	0, /* tp_dictoffset */
	0, /* tp_init */
	0, /* tp_alloc */
	0, /* tp_new */
	0,0,0,0,0,0,0,0, /* up to tp_del, so we don't get a warning */
};

static void Radio_dealloc (BPy_Radio *self)
{
		PyObject_DEL (self);
}

static PyObject *Radio_repr (BPy_Radio *self)
{
	if (self->radio)
		return PyString_FromFormat ("[Radiosity \"%s\"]", self->scene->id.name + 2);
	else
		return PyString_FromString ("NULL");
}

PyObject *Radio_CreatePyObject (struct Scene *scene)
{
	BPy_Radio *py_radio;

	if (scene != G.scene) {
		return EXPP_ReturnPyObjError (PyExc_EnvironmentError,
		"\nradiosity only works on the current scene, check scene.makeCurrent().");
	}

	py_radio = (BPy_Radio *) PyObject_NEW (BPy_Radio, &Radio_Type);

	if (!py_radio) return NULL;

	if (!scene->radio) add_radio(); /* adds to G.scene */

	py_radio->radio = scene->radio;
	py_radio->scene = scene;

	return ((PyObject *) py_radio);
}

int Radio_CheckPyObject (PyObject *pyob)
{
	return (pyob->ob_type == &Radio_Type);
}

static PyObject *Radio_collectMeshes(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;

	disable_where_script(1); /* used to avoid error popups */
	rad_collect_meshes();
	disable_where_script(0);

	return EXPP_incr_ret(Py_None);
}

static PyObject *Radio_freeData(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;

	delete_radio();

	return EXPP_incr_ret(Py_None);
}

static PyObject *Radio_go(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;

	if (rad_phase() == RAD_PHASE_PATCHES)	rad_go();
	else return EXPP_ReturnPyObjError(PyExc_RuntimeError,
		"you need to call radio.collectMeshes() first.");

	return EXPP_incr_ret(Py_None);
}

static PyObject *Radio_replaceMeshes(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;

	if (rad_phase() & RAD_PHASE_FACES) rad_replacemesh();
	else return EXPP_ReturnPyObjError(PyExc_RuntimeError,
		"you need to call radio.collectMeshes() and radio.go() first.");

	return EXPP_incr_ret(Py_None);
}

static PyObject *Radio_addMesh(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;

	if (rad_phase() & RAD_PHASE_FACES) rad_addmesh();
	else return EXPP_ReturnPyObjError(PyExc_RuntimeError,
		"you need to call radio.collectMeshes() and radio.go() first.");

	return EXPP_incr_ret(Py_None);
}

static PyObject *Radio_filterFaces(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;

	if (rad_phase() & RAD_PHASE_FACES) filterFaces();
	else return EXPP_ReturnPyObjError(PyExc_RuntimeError,
		"you need to call radio.collectMeshes() and radio.go() first.");

	return EXPP_incr_ret(Py_None);
}

static PyObject *Radio_filterElems(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;

	if (rad_phase() & RAD_PHASE_FACES) {
		set_radglobal();
		filterNodes();
		make_face_tab();
	}
	else return EXPP_ReturnPyObjError(PyExc_RuntimeError,
		"you need to call radio.collectMeshes() and radio.go() first.");

	return EXPP_incr_ret(Py_None);
}

static PyObject *Radio_limitSubdivide(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;

	if (rad_phase() == RAD_PHASE_PATCHES) rad_limit_subdivide();
	else return EXPP_ReturnPyObjError(PyExc_RuntimeError,
		"you need to call this before calculating the radiosity simulation.");

	return EXPP_incr_ret(Py_None);
}

static PyObject *Radio_subdividePatches(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;

	if (rad_phase() == RAD_PHASE_PATCHES) rad_subdivshootpatch();
	else return EXPP_ReturnPyObjError(PyExc_RuntimeError,
		"you need to call this before calculating the radiosity simulation.");

	return EXPP_incr_ret(Py_None);
}

static PyObject *Radio_subdivideElems(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;

	if (rad_phase() == RAD_PHASE_PATCHES) rad_subdivshootelem();
	else return EXPP_ReturnPyObjError(PyExc_RuntimeError,
		"you need to call radio.collectMeshes() and radio.go() first.");

	return EXPP_incr_ret(Py_None);
}

static PyObject *Radio_removeDoubles(BPy_Radio *self)
{
	if (!EXPP_check_scene(self->scene)) return NULL;

	if (rad_phase() == RAD_PHASE_FACES) {
		set_radglobal();
		removeEqualNodes(self->scene->radio->nodelim);
		make_face_tab();
	}
	else return EXPP_ReturnPyObjError(PyExc_RuntimeError,
		"you need to call radio.collectMeshes() and radio.go() first.");

	return EXPP_incr_ret(Py_None);
}

static PyMethodDef M_Radio_methods[] = {{NULL, NULL, 0, NULL}};

PyObject *Radio_Init (void)
{
	PyObject *submodule, *Modes, *DrawTypes;

	if (PyType_Ready(&Radio_Type) < 0) return NULL;

	submodule = Py_InitModule3 ("Blender.Scene.Radio", M_Radio_methods,
		"The Blender Radiosity submodule");

	Modes = M_constant_New();
	DrawTypes = M_constant_New();

	if (Modes) {
		BPy_constant *d = (BPy_constant *)Modes;

		constant_insert(d, "ShowLimits", PyInt_FromLong(EXPP_RADIO_flag_SHOWLIM));
		constant_insert(d, "Z", PyInt_FromLong(EXPP_RADIO_flag_Z));

		PyModule_AddObject(submodule, "Modes", Modes);
	}

	if (DrawTypes) {
		BPy_constant *d = (BPy_constant *)DrawTypes;

		constant_insert(d, "Wire", PyInt_FromLong(EXPP_RADIO_drawtype_WIRE));
		constant_insert(d, "Solid", PyInt_FromLong(EXPP_RADIO_drawtype_SOLID));
		constant_insert(d, "Gouraud", PyInt_FromLong(EXPP_RADIO_drawtype_GOURAUD));

		PyModule_AddObject(submodule, "DrawTypes", DrawTypes);
	}

	return submodule;
}
