/*  python.c      MIXED MODEL
 * 
 *  june 99
 * $Id: opy_nmesh.c,v 1.5 2003/06/13 04:21:46 ianwill Exp $
 *
 * ***** 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.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */

#include "Python.h"
#include "BPY_macros.h"
#include "b_interface.h"
#include "BPY_tools.h"
#include "BPY_main.h"

#include "opy_datablock.h"
#include "opy_nmesh.h"

#include "MEM_guardedalloc.h"
#include "BIF_editmesh.h" /* vertexnormals_mesh() */
#include "BDR_editface.h" /* make_tfaces */

#include "BKE_mesh.h"
#include "BKE_main.h"
#include "BKE_global.h"
#include "BKE_library.h"
#include "BKE_displist.h"
#include "BKE_screen.h"
#include "BKE_object.h"
#include "BPY_objtypes.h"
#include "BLI_blenlib.h"
#include "BIF_space.h"

#include "opy_vector.h"

#include "b_interface.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/* PROTOS */

static int convert_NMeshToMesh(Mesh *mesh, NMesh *nmesh);
static int unlink_existingMeshdata(Mesh *mesh);
void initNMesh(void);
PyObject *init_py_nmesh(void);
int BPY_check_sequence_consistency(PyObject *seq, PyTypeObject *against);

/* TYPE OBJECTS */

PyTypeObject NMesh_Type;
PyTypeObject NMFace_Type;
PyTypeObject NMVert_Type;
PyTypeObject NMCol_Type;

/* DEFINES */


#define COL_R (b)
#define COL_G (g)
#define COL_B (r)
#define COL_A (a)

#define COLOR_CONVERT(col,comp) (col##->COL_##)

/* GLOBALS */

static PyObject *g_nmeshmodule = NULL;

/*****************************/
/*	    Mesh Color Object    */
/*****************************/

static void NMCol_dealloc(PyObject *self) {
	PyMem_DEL(self);
}

static NMCol *newcol (char r, char g, char b, char a) {
	NMCol *mc= (NMCol *) PyObject_NEW(NMCol, &NMCol_Type);
	
	mc->r= r;
	mc->g= g;
	mc->b= b;
	mc->a= a;

	return mc;	
}

static char NMeshmodule_Col_doc[]=
"([r, g, b, a]) - Get a new mesh color\n\
\n\
[r=255, g=255, b=255, a=255] Specify the color components";

static PyObject *NMeshmodule_Col(PyObject *self, PyObject *args) {
	int r=255, g=255, b=255, a=255;
	
/*
if(PyArg_ParseTuple(args, "fff|f", &fr, &fg, &fb, &fa))
		return (PyObject *) newcol(255.0 * fr, 255.0 * fg, 255.0 * fb, 255.0 * fa);
		*/
	if(PyArg_ParseTuple(args, "|iiii", &r, &g, &b, &a))
		return (PyObject *) newcol(r, g, b, a);
	return NULL;		
}

static PyObject *NMCol_getattr(PyObject *self, char *name) {
	NMCol *mc= (NMCol *) self;
		
	if (strcmp(name, "r")==0) return Py_BuildValue("i", mc->r);
	else if (strcmp(name, "g")==0) return Py_BuildValue("i", mc->g);
	else if (strcmp(name, "b")==0) return Py_BuildValue("i", mc->b);
	else if (strcmp(name, "a")==0) return Py_BuildValue("i", mc->a);

	PyErr_SetString(PyExc_AttributeError, name);
	return NULL;
}

static int NMCol_setattr(PyObject *self, char *name, PyObject *v) {
	NMCol *mc= (NMCol *) self;
	int ival;
	
	if(!PyArg_Parse(v, "i", &ival)) return -1;
	
	CLAMP(ival, 0, 255);
	
	if (strcmp(name, "r")==0) mc->r= ival;
	else if (strcmp(name, "g")==0) mc->g= ival;
	else if (strcmp(name, "b")==0) mc->b= ival;
	else if (strcmp(name, "a")==0) mc->a= ival;
	else return -1;
	
	return 0;
}

PyObject *NMCol_repr(NMCol *self) 
{
	static char s[256];
	sprintf (s, "[NMCol - <%d, %d, %d, %d>]", self->r, self->g, self->b, self->a);
	return Py_BuildValue("s", s);
}

PyTypeObject NMCol_Type = {
	PyObject_HEAD_INIT(NULL)
	0,								/*ob_size*/
	"NMCol",						/*tp_name*/
	sizeof(NMCol),					/*tp_basicsize*/
	0,								/*tp_itemsize*/
	/* methods */
	(destructor) NMCol_dealloc,		/*tp_dealloc*/
	(printfunc) 0,		/*tp_print*/
	(getattrfunc) NMCol_getattr,	/*tp_getattr*/
	(setattrfunc) NMCol_setattr,	/*tp_setattr*/
	0,								/*tp_compare*/
	(reprfunc) NMCol_repr,								/*tp_repr*/
	0,								/*tp_as_number*/
	0,								/*tp_as_sequence*/
	0,								/*tp_as_mapping*/
	0,								/*tp_hash*/
	0,								/*tp_as_number*/
	0,								/*tp_as_sequence*/
	0,								/*tp_as_mapping*/
	0,								/*tp_hash*/
};


/*****************************/
/*    NMesh Python Object    */
/*****************************/


static void NMFace_dealloc(PyObject *self) {
	NMFace *mf= (NMFace *) self;
	
	Py_DECREF(mf->v);
	Py_DECREF(mf->uv);
	Py_DECREF(mf->col);
	
	PyMem_DEL(self);

}

static NMFace *newNMFace(PyObject *vertexlist) {
	NMFace *mf= PyObject_NEW(NMFace, &NMFace_Type);

	mf->v= vertexlist;
	mf->uv= PyList_New(0);
	mf->tpage= NULL;
	mf->mode = TF_DYNAMIC + TF_TEX;
	mf->flag= TF_SELECT;
	mf->transp= TF_SOLID;
	mf->col= PyList_New(0);
	
	mf->smooth= 0;
	mf->mat_nr= 0;
	
	return mf;
}

static char NMeshmodule_Face_doc[]=
"(vertexlist = None) - Get a new face, and pass optional vertex list";
static PyObject *NMeshmodule_Face(PyObject *self, PyObject *args) {
	PyObject *vertlist = NULL;
	BPY_TRY(PyArg_ParseTuple(args, "|O!", &PyList_Type, &vertlist));	

	if (!vertlist) {
		vertlist = PyList_New(0);
	}
	return (PyObject *) newNMFace(vertlist);
}

/* XXX this code will be used later...
static PyObject *Method_getmode(PyObject *self, PyObject *args) {
	PyObject *dict, *list;
	PyObject *constants, *values, *c;
	int flag;
	int i, n;

	list = PyList_New(0);
	dict = PyObject_GetAttrString(g_nmeshmodule, "Const");

	if (!dict) return 0;

	constants = PyDict_Keys(dict);
	values = PyDict_Values(dict);
	
	n = PySequence_Length(constants);
	for (i = 0; i < n; i++)
	{
		flag = PyInt_AsLong(PySequence_GetItem(values, i));
		if (flag & ((NMFace*) self)->mode)
		{
			c = PySequence_GetItem(constants, i);
			PyList_Append(list, c) 
		}	
	}
	return list;
}
*/

static char NMFace_append_doc[]= "(vert) - appends Vertex 'vert' to face vertex list";

static PyObject *NMFace_append(PyObject *self, PyObject *args)
{
	PyObject *vert;
	NMFace *f= (NMFace *) self;

	BPY_TRY(PyArg_ParseTuple(args, "O!", &NMVert_Type, &vert));
	PyList_Append(f->v, vert);
	RETURN_INC(Py_None);
}


#undef MethodDef
#define MethodDef(func) {#func, NMFace_##func, METH_VARARGS, NMFace_##func##_doc}

static struct PyMethodDef NMFace_methods[] = {
	MethodDef(append),
	{NULL, NULL}
};

static PyObject *NMFace_getattr(PyObject *self, char *name) {
	NMFace *mf= (NMFace *) self;
		
	if(strcmp(name, "v")==0) 
		return Py_BuildValue("O", mf->v);
	else if (strcmp(name, "col")==0) 
		return Py_BuildValue("O", mf->col);
	else if (strcmp(name, "mat")==0) // emulation XXX
		return Py_BuildValue("i", mf->mat_nr);
	else if (strcmp(name, "materialIndex")==0) 
		return Py_BuildValue("i", mf->mat_nr);
	else if (strcmp(name, "smooth")==0)
		return Py_BuildValue("i", mf->smooth);
	else if (strcmp(name, "image")==0) {
		if (mf->tpage)
			return Py_BuildValue("O", (PyObject *) mf->tpage);
		else 
			RETURN_INC(Py_None);
	}
	else if (strcmp(name, "mode")==0) 
		return Py_BuildValue("i", mf->mode);
	else if (strcmp(name, "flag")==0) 
		return Py_BuildValue("i", mf->flag);
	else if (strcmp(name, "transp")==0)
		return Py_BuildValue("i", mf->transp);
	else if (strcmp(name, "uv")==0)
		return Py_BuildValue("O", mf->uv);
		
	return Py_FindMethod(NMFace_methods, (PyObject*)self, name);
/*
	PyErr_SetString(PyExc_AttributeError, name);
	return NULL;
*/
}

static int NMFace_setattr(PyObject *self, char *name, PyObject *v) {
	NMFace *mf= (NMFace *) self;
	int ival;
	PyObject *tmp;
	
	if (STREQ(name, "v")) {
		if(PySequence_Check(v)) {
			Py_DECREF(mf->v);
			mf->v= BPY_incr_ret(v);

			return 0;
		}
	} else if (STREQ(name, "col")) {
		if(PySequence_Check(v)) {
			Py_DECREF(mf->col);
			mf->col= BPY_incr_ret(v);

			return 0;
		}
	} else if (STREQ(name, "mat") || STREQ(name, "materialIndex")) {
		PyArg_Parse(v, "i", &ival);

		mf->mat_nr= ival;
		
		return 0;
	} else if (STREQ(name, "smooth")) {
		PyArg_Parse(v, "i", &ival);

		mf->smooth= ival?1:0;
		
		return 0;
	} else if (STREQ(name, "uv")) {
		if(PySequence_Check(v)) {
			Py_DECREF(mf->uv);
			mf->uv= BPY_incr_ret(v);
			return 0;
		}	
	} else if (STREQ(name, "flag")) {
			PyArg_Parse(v, "i", &ival);
			mf->flag = ival;
			return 0;
	} else if (STREQ(name, "mode")) {
			PyArg_Parse(v, "i", &ival);
			mf->mode = ival;
			return 0;
	} else if (STREQ(name, "transp")) {
			PyArg_Parse(v, "i", &ival);
			mf->transp = ival;
			return 0;
	} else if (STREQ(name, "image")) {
		PyArg_Parse(v, "O", &tmp);
		if (tmp == Py_None) {
			mf->tpage = 0;
			return 0;
		}
		if (!DataBlock_isType((DataBlock *) tmp, ID_IM))
		{
			PyErr_SetString(PyExc_TypeError, "expects Image Datablock type");
			return -1;
		}
		mf->tpage = (DataBlock *) tmp;
		return 0;
	}
	
	PyErr_SetString(PyExc_AttributeError, name);
	return -1;
}

static PyObject *NMFace_repr (PyObject *self)
{
	return PyString_FromString("[NMFace]");
}

static int NMFace_len(NMFace *self) 
{
	return PySequence_Length(self->v);
}

static PyObject *NMFace_item(NMFace *self, int i)
{
	return PySequence_GetItem(self->v, i); // new ref
}

static PyObject *NMFace_slice(NMFace *self, int begin, int end)
{
	return PyList_GetSlice(self->v, begin, end); // new ref
}

static PySequenceMethods NMFace_SeqMethods = {
	(inquiry)			NMFace_len,			/* sq_length	*/
	(binaryfunc)		0,					/* sq_concat	*/
	(intargfunc)		0,					/* sq_repeat	*/
	(intargfunc)		NMFace_item,		/* sq_item		*/
	(intintargfunc)		NMFace_slice,		/* sq_slice		*/
	(intobjargproc)		0,	/* sq_ass_item	*/
	(intintobjargproc)	0,	/* sq_ass_slice	*/
};


PyTypeObject NMFace_Type = {
	PyObject_HEAD_INIT(NULL)
	0,							/*ob_size*/
	"NMFace",						/*tp_name*/
	sizeof(NMFace),			/*tp_basicsize*/
	0,							/*tp_itemsize*/
	/* methods */
	(destructor) NMFace_dealloc,	/*tp_dealloc*/
	(printfunc) 0,		/*tp_print*/
	(getattrfunc) NMFace_getattr,	/*tp_getattr*/
	(setattrfunc) NMFace_setattr,/*tp_setattr*/
	0,							/*tp_compare*/
	(reprfunc) NMFace_repr,		/*tp_repr*/
	0,							/*tp_as_number*/
	&NMFace_SeqMethods,							/*tp_as_sequence*/
	0,							/*tp_as_mapping*/
	0,							/*tp_hash*/
};


static NMVert *newvert(float *co) {
	NMVert *mv= PyObject_NEW(NMVert, &NMVert_Type);

	VECCOPY(mv->co, co);
	mv->no[0]= mv->no[1]= mv->no[2]= 0.0;
	mv->uvco[0]= mv->uvco[1]= mv->uvco[2]= 0.0;
	
	return mv;
}

static char NMeshmodule_Vert_doc[]=
"([x, y, z]) - Get a new vertice\n\
\n\
[x, y, z] Specify new coordinates";

static PyObject *NMeshmodule_Vert(PyObject *self, PyObject *args) {
	float co[3]= {0.0, 0.0, 0.0};
	
	BPY_TRY(PyArg_ParseTuple(args, "|fff", &co[0], &co[1], &co[2]));
	
	return (PyObject *) newvert(co);
}

static void NMVert_dealloc(PyObject *self) {
	PyMem_DEL(self);
}

static PyObject *NMVert_getattr(PyObject *self, char *name) {
	NMVert *mv= (NMVert *) self;

	if (STREQ(name, "co") || STREQ(name, "loc")) return newVectorObject(mv->co, 3);
	else if (STREQ(name, "no")) return newVectorObject(mv->no, 3);		
	else if (STREQ(name, "uvco")) return newVectorObject(mv->uvco, 3);		
	else if (STREQ(name, "index")) return PyInt_FromLong(mv->index);		
	
	PyErr_SetString(PyExc_AttributeError, name);
	return NULL;
}

static int NMVert_setattr(PyObject *self, char *name, PyObject *v) {
	NMVert *mv= (NMVert *) self;
	int i;
	
	if (STREQ(name,"index")) {
		PyArg_Parse(v, "i", &i);
		mv->index= i;
		return 0;
	} else if (STREQ(name, "uvco")) {
		if (!PyArg_ParseTuple(v, "ff|f", &(mv->uvco[0]), &(mv->uvco[1]), &(mv->uvco[2]))) {
			PyErr_SetString(PyExc_AttributeError, "Vector tuple or triple expected");
			return -1;
		}	
		return 0;
/*
PyErr_SetString(PyExc_AttributeError, "Use slice assignment: uvco[i]");
		return -1;
		*/
	}	
	
	PyErr_SetString(PyExc_AttributeError, name);
	return -1;
}


static int NMVert_len(NMVert *self) {
	return 3;
}

static PyObject *NMVert_item(NMVert *self, int i)
{
	if (i < 0 || i >= 3) {
		PyErr_SetString(PyExc_IndexError, "array index out of range");
		return NULL;
	}
	return Py_BuildValue("f", self->co[i]);
}

static PyObject *NMVert_slice(NMVert *self, int begin, int end)
{
	PyObject *list;
	int count;
	
	if (begin<0) begin= 0;
	if (end>3) end= 3;
	if (begin>end) begin= end;
		
	list= PyList_New(end-begin);

	for (count= begin; count<end; count++)
		PyList_SetItem(list, count-begin, PyFloat_FromDouble(self->co[count]));
	
	return list;
}

static int NMVert_ass_item(NMVert *self, int i, PyObject *ob)
{
	if (i < 0 || i >= 3) {
		PyErr_SetString(PyExc_IndexError, "array assignment index out of range");
		return -1;
	}

	if (!PyNumber_Check(ob)) {
		PyErr_SetString(PyExc_IndexError, "NMVert member must be a number");
		return -1;
	}
	
	self->co[i]= PyFloat_AsDouble(ob);
/* 	if(!PyArg_Parse(ob, "f", &)) return -1; */
	
	return 0;
}

/** I guess this hurts...
  * sorry, couldn't resist (strubi) */

static int NMVert_ass_slice(NMVert *self, int begin, int end, PyObject *seq)
{
	int count;
	
	if (begin<0) begin= 0;
	if (end>3) end= 3;
	if (begin>end) begin= end;

	if (!PySequence_Check(seq)) {
		PyErr_SetString(PyExc_TypeError, "illegal argument type for built-in operation");
		return -1;		
	}

	if (PySequence_Length(seq)!=(end-begin)) {
		PyErr_SetString(PyExc_TypeError, "size mismatch in slice assignment");
		return -1;
	}
	
	for (count= begin; count<end; count++) {
		PyObject *ob= PySequence_GetItem(seq, count);
		if (!PyArg_Parse(ob, "f", &self->co[count])) {
			Py_DECREF(ob);
			return -1;
		}
		Py_DECREF(ob);
	}
		
	return 0;
}

static PySequenceMethods NMVert_SeqMethods = {
	(inquiry)			NMVert_len,			/* sq_length	*/
	(binaryfunc)		0,					/* sq_concat	*/
	(intargfunc)		0,					/* sq_repeat	*/
	(intargfunc)		NMVert_item,		/* sq_item		*/
	(intintargfunc)		NMVert_slice,		/* sq_slice		*/
	(intobjargproc)		NMVert_ass_item,	/* sq_ass_item	*/
	(intintobjargproc)	NMVert_ass_slice,	/* sq_ass_slice	*/
};

PyTypeObject NMVert_Type = {
	PyObject_HEAD_INIT(NULL)
	0,                             /*ob_size*/
	"NMVert",                      /*tp_name*/
	sizeof(NMVert),                /*tp_basicsize*/
	0,                             /*tp_itemsize*/
	/* methods */
	(destructor) NMVert_dealloc,   /*tp_dealloc*/
	(printfunc) 0,	               /*tp_print*/
	(getattrfunc) NMVert_getattr,  /*tp_getattr*/
	(setattrfunc) NMVert_setattr,  /*tp_setattr*/
	0,                             /*tp_compare*/
	(reprfunc) 0,                  /*tp_repr*/
	0,                             /*tp_as_number*/
	&NMVert_SeqMethods,            /*tp_as_sequence*/
};


static void NMesh_dealloc(PyObject *self) {
	NMesh *me= (NMesh *) self;

	Py_DECREF(me->name);
	Py_DECREF(me->verts);
	Py_DECREF(me->faces);
	
	PyMem_DEL(self);
}


static char NMesh_getSelectedFaces_doc[] = "(flag = None) - returns list of selected Faces\n\
If flag = 1, return indices instead";
static PyObject *NMesh_getSelectedFaces(PyObject *self, PyObject *args)
{
	NMesh *nm= (NMesh *) self;
	Mesh *me = nm->mesh;
	int flag = 0;

	TFace *tf;
	int i;
	PyObject *l= PyList_New(0);

	if (me == NULL) return NULL;

	tf = me->tface;
	if (tf == 0) {
		return l;
	}

	if (!PyArg_ParseTuple(args, "|i", &flag)) 
		return NULL;
	if (flag) {
		for (i =0 ; i < me->totface; i++) {
			if (tf[i].flag & TF_SELECT ) {
				PyList_Append(l, PyInt_FromLong(i));
			}
		}		
	} else {
		for (i =0 ; i < me->totface; i++) {
			if (tf[i].flag & TF_SELECT ) {
				PyList_Append(l, PyList_GetItem(nm->faces, i));
			}
		}		
	}
	return l;
}


static char NMesh_getActiveFace_doc[] = "returns the index of the active face ";
static PyObject *NMesh_getActiveFace(PyObject *self, PyObject *args)
{
	if (((NMesh *)self)->sel_face < 0)
		RETURN_INC(Py_None);
	return Py_BuildValue("i", ((NMesh *)self)->sel_face);
}

static char NMesh_hasVertexUV_doc[] = "(flag = None) - returns 1 if Mesh has per vertex UVs ('Sticky')\n\
The optional argument sets the Sticky flag";

static PyObject *NMesh_hasVertexUV(PyObject *self, PyObject *args)
{
	NMesh *me= (NMesh *) self;
	int flag;

	if (args) {
		if (PyArg_ParseTuple(args, "i", &flag)) {
			if(flag) me->flags |= NMESH_HASVERTUV;
			else me->flags &= ~NMESH_HASVERTUV;
		}
	}
	PyErr_Clear();
	if (me->flags & NMESH_HASVERTUV)
		return BPY_incr_ret(Py_True);
	else
		return BPY_incr_ret(Py_False);
}

static char NMesh_hasFaceUV_doc[] = "(flag = None) - returns 1 if Mesh has textured faces\n\
The optional argument sets the textured faces flag";

static PyObject *NMesh_hasFaceUV(PyObject *self, PyObject *args)
{
	NMesh *me= (NMesh *) self;
	int flag = -1;

	BPY_TRY(PyArg_ParseTuple(args, "|i", &flag));

	switch (flag) {
	case 0:
		me->flags |= NMESH_HASFACEUV;
		break;
	case 1: 
		me->flags &= ~NMESH_HASFACEUV;
		break;
	default:
		break;
	}

	if (me->flags & NMESH_HASFACEUV)
		return BPY_incr_ret(Py_True);
	else
		return BPY_incr_ret(Py_False);
}


static char NMesh_hasVertexColours_doc[] = "(flag = None) - returns 1 if Mesh has vertex colours.\n\
The optional argument sets the vertex colour flag";

static PyObject *NMesh_hasVertexColours(PyObject *self, PyObject *args)
{
	NMesh *me= (NMesh *) self;
	int flag = -1;

	BPY_TRY(PyArg_ParseTuple(args, "|i", &flag));

	switch (flag) {
	case 0:
		me->flags &= ~NMESH_HASMCOL;
		break;
	case 1: 
		me->flags |= NMESH_HASMCOL;
		break;
	default:
		break;
	}

	if (me->flags & NMESH_HASMCOL)
		return BPY_incr_ret(Py_True);
	else
		return BPY_incr_ret(Py_False);

}


static char NMesh_update_doc[] = "updates the Mesh";
static PyObject *NMesh_update(PyObject *self, PyObject *args)
{
	NMesh *nmesh= (NMesh *) self;
	Mesh *mesh = nmesh->mesh;
	
	if (mesh) {
		unlink_existingMeshdata(mesh);
		convert_NMeshToMesh(mesh, nmesh);
		mesh_update(mesh);
	} else {  
		nmesh->mesh = Mesh_fromNMesh(nmesh);
	}

	nmesh_updateMaterials(nmesh);
/** This is another ugly fix due to the weird material handling of blender.
  * it makes sure that object material lists get updated (by their length)
  * according to their data material lists, otherwise blender crashes.
  * It just stupidly runs through all objects...BAD BAD BAD.
  */
	test_object_materials((ID *)mesh);

	if (!during_script())
		allqueue(REDRAWVIEW3D, 0);
	return PyInt_FromLong(1);

}


Mesh *Mesh_fromNMesh(NMesh *nmesh)
{
	Mesh *mesh= NULL;
	mesh = mesh_new(); // new empty mesh Bobject
	if (!mesh) {
		PyErr_SetString(PyExc_RuntimeError, "FATAL: could not create mesh object");
		return NULL;
	}
	
	convert_NMeshToMesh(mesh, nmesh);
	mesh_update(mesh);
	return mesh;
}

#ifdef EXPERIMENTAL

static char NMesh_asMesh_doc[] = "returns free Mesh datablock object from NMesh";
static PyObject *NMesh_asMesh(PyObject *self, PyObject *args)
{
	char *name= NULL;
	Mesh *mesh= NULL;
	NMesh *nmesh;
	int recalc_normals= 1;

	nmesh = (NMesh *) self;
	
	BPY_TRY(PyArg_ParseTuple(args, "|si", &name, &recalc_normals));
	
	if (!PySequence_Check(nmesh->verts))
		return BPY_err_ret_ob(PyExc_AttributeError, 
			"nmesh vertices are not a sequence");
	if (!PySequence_Check(nmesh->faces))
		return BPY_err_ret_ob(PyExc_AttributeError, 
			"nmesh faces are not a sequence");
	if (!PySequence_Check(nmesh->materials))
		return BPY_err_ret_ob(PyExc_AttributeError, 
			"nmesh materials are not a sequence");
	if (!BPY_check_sequence_consistency(nmesh->verts, &NMVert_Type))
		return BPY_err_ret_ob(PyExc_AttributeError, 
			"nmesh vertices must be NMVerts");
	if (!BPY_check_sequence_consistency(nmesh->faces, &NMFace_Type))
		return BPY_err_ret_ob(PyExc_AttributeError, 
			"nmesh faces must be NMFaces");

	mesh = Mesh_fromNMesh(nmesh);
	return DataBlock_fromData(mesh);
}

#endif
static char NMesh_link_doc[] = "(object) - Links NMesh data with Object 'object'";

PyObject * NMesh_link(PyObject *self, PyObject *args) 
{
	return DataBlock_link(self, args);
}

#undef MethodDef
#define MethodDef(func) {#func, NMesh_##func, METH_VARARGS, NMesh_##func##_doc}

static struct PyMethodDef NMesh_methods[] = {
	MethodDef(hasVertexColours),
	MethodDef(hasFaceUV),
	MethodDef(hasVertexUV),
	MethodDef(getActiveFace),
	MethodDef(getSelectedFaces),
	MethodDef(update),
#ifdef EXPERIMENTAL	
	MethodDef(asMesh),
#endif	
	{NULL, NULL}
};

static PyObject *NMesh_getattr(PyObject *self, char *name) {
	NMesh *me= (NMesh *) self;
	
	if (STREQ(name, "name")) 
		return BPY_incr_ret(me->name);
	
	else if (STREQ(name, "block_type"))
	  return PyString_FromString("NMesh");

	else if (STREQ(name, "materials"))
		return BPY_incr_ret(me->materials);

	else if (STREQ(name, "verts"))
		return BPY_incr_ret(me->verts);
		
	else if (STREQ(name, "users")) {
		if (me->mesh) {
			return PyInt_FromLong(me->mesh->id.us); 
		} else { // it's a free mesh:
			return Py_BuildValue("i", 0); 
		}
	}

	else if (STREQ(name, "faces"))
		return BPY_incr_ret(me->faces);

	return Py_FindMethod(NMesh_methods, (PyObject*)self, name);
	
	PyErr_SetString(PyExc_AttributeError, name);
	return NULL;
}

static int NMesh_setattr(PyObject *self, char *name, PyObject *v) {
	NMesh *me= (NMesh *) self;

	if (STREQ3(name, "verts", "faces", "materials")) {
		if(PySequence_Check(v)) {
			if(STREQ(name, "materials")) {
				Py_DECREF(me->materials);
				me->materials= BPY_incr_ret(v);
			} else if (STREQ(name, "verts")) {
				Py_DECREF(me->verts);
				me->verts= BPY_incr_ret(v);
			} else {
				Py_DECREF(me->faces);
				me->faces= BPY_incr_ret(v);				
			}
		} else {
			PyErr_SetString(PyExc_AttributeError, "expected a sequence");
			return -1;
		}
	} else {
		PyErr_SetString(PyExc_AttributeError, name);
		return -1;
	}

	return 0;
}

PyTypeObject NMesh_Type = {
	PyObject_HEAD_INIT(NULL)
	0,								/*ob_size*/
	"NMesh",						/*tp_name*/
	sizeof(NMesh),					/*tp_basicsize*/
	0,								/*tp_itemsize*/
	/* methods */
	(destructor)	NMesh_dealloc,	/*tp_dealloc*/
	(printfunc)		0,	/*tp_print*/
	(getattrfunc)	NMesh_getattr,	/*tp_getattr*/
	(setattrfunc)	NMesh_setattr,	/*tp_setattr*/
};

static NMFace *nmface_from_data(NMesh *mesh, int vidxs[4], char mat_nr, char flag, TFace *tface, MCol *col) 
{
	NMFace *newf= PyObject_NEW(NMFace, &NMFace_Type);
	int i, len;

	if(vidxs[3]) len= 4;
	else if(vidxs[2]) len= 3;
	else len= 2;

	newf->v= PyList_New(len);

	for (i=0; i<len; i++)	
		PyList_SetItem(newf->v, i, BPY_incr_ret(PyList_GetItem(mesh->verts, vidxs[i])));

	if (tface) {
		newf->uv = PyList_New(len); // per-face UV coordinates
		for (i = 0; i < len; i++)
		{
			PyList_SetItem(newf->uv, i, Py_BuildValue("(ff)", tface->uv[i][0], tface->uv[i][1]));
		}
		if (tface->tpage)
			newf->tpage = (DataBlock *) DataBlock_fromData((void *) tface->tpage); /* pointer to image per face */
		else
			newf->tpage = 0;
		newf->mode = tface->mode;     /* draw mode */
		newf->flag = tface->flag;		/* select flag */
		newf->transp = tface->transp;  /* transparency flag */
		col = (MCol *) (tface->col);
	} else {
		newf->tpage = 0;
		newf->uv = PyList_New(0); 
	}	
	
	newf->mat_nr= mat_nr;
	newf->smooth= flag&ME_SMOOTH;

	if (col) {
		newf->col= PyList_New(4);
		for(i=0; i<4; i++, col++)
			PyList_SetItem(newf->col, i, 
				(PyObject *) newcol(col->b, col->g, col->r, col->a));
	} else {
		newf->col= PyList_New(0);
	}
	return newf;
}

static NMFace *nmface_from_shortdata(NMesh *mesh, MFace *face, TFace *tface, MCol *col) 
{
	int vidxs[4];
	vidxs[0]= face->v1;
	vidxs[1]= face->v2;
	vidxs[2]= face->v3;
	vidxs[3]= face->v4;
		
	return nmface_from_data(mesh, vidxs, face->mat_nr, face->flag, tface, col);
}

static NMFace *nmface_from_intdata(NMesh *mesh, MFaceInt *face, TFace *tface, MCol *col) 
{
	int vidxs[4];
	vidxs[0]= face->v1;
	vidxs[1]= face->v2;
	vidxs[2]= face->v3;
	vidxs[3]= face->v4;
		
	return nmface_from_data(mesh, vidxs, face->mat_nr, face->flag, tface, col);
}

static NMVert *nmvert_from_data(NMesh *me, MVert *vert, MSticky *st, float *co, int idx)
{			
	NMVert *mv= PyObject_NEW(NMVert, &NMVert_Type);
			
	VECCOPY (mv->co, co);
		
	mv->no[0]= vert->no[0]/32767.0;
	mv->no[1]= vert->no[1]/32767.0;
	mv->no[2]= vert->no[2]/32767.0;
	
	if (st) {
		mv->uvco[0]= st->co[0];
		mv->uvco[1]= st->co[1];
		mv->uvco[2]= 0.0;
		
	} else mv->uvco[0]= mv->uvco[1]= mv->uvco[2]= 0.0;

	mv->index= idx;
			
	return mv;
}

static int get_active_faceindex(Mesh *me)
{
	TFace *tf;
	int i;

	if (me == NULL) return -1;

	tf = me->tface;
	if (tf == 0) return -1;
	
	for (i =0 ; i < me->totface; i++) {
		if (tf[i].flag & TF_ACTIVE ) {
			return i;
		}
	}		
	return -1;
}

static PyObject *newNMesh_internal(Mesh *oldmesh, DispListMesh *dlm, float *extverts) 
{
	NMesh *me= PyObject_NEW(NMesh, &NMesh_Type);
	me->flags= 0;

	if (!oldmesh) {
		me->name= BPY_incr_ret(Py_None);
		me->materials= PyList_New(0);
		me->verts= PyList_New(0);
		me->faces= PyList_New(0);
		me->mesh= 0;
	} else {
		MVert *mverts;
		MSticky *msticky;
		MFaceInt *mfaceints;
		MFace *mfaces;
		TFace *tfaces;
		MCol *mcols;
		int i, totvert, totface;
		
		if (dlm) {
			me->name= BPY_incr_ret(Py_None);
			me->mesh= 0;

			msticky= NULL;
			mfaces= NULL;
			mverts= dlm->mvert;
			mfaceints= dlm->mface;
			tfaces= dlm->tface;
			mcols= dlm->mcol;
			
			totvert= dlm->totvert;
			totface= dlm->totface;
		} else {
			me->name= PyString_FromString(oldmesh->id.name+2);
			me->mesh= oldmesh;
			
			mfaceints= NULL;
			msticky= oldmesh->msticky;
			mverts= oldmesh->mvert;
			mfaces= oldmesh->mface;
			tfaces= oldmesh->tface;
			mcols= oldmesh->mcol;

			totvert= oldmesh->totvert;
			totface= oldmesh->totface;

			me->sel_face= get_active_faceindex(oldmesh);
		}

		if (msticky) me->flags |= NMESH_HASVERTUV;
		if (tfaces) me->flags |= NMESH_HASFACEUV;
		if (mcols) me->flags |= NMESH_HASMCOL;

		me->verts= PyList_New(totvert);
		for (i=0; i<totvert; i++) {
			MVert *oldmv= &mverts[i];
			MSticky *oldst= msticky?&msticky[i]:NULL;
			float *vco= extverts?&extverts[i*3]:oldmv->co;
			
			PyList_SetItem(me->verts, i, (PyObject *) nmvert_from_data(me, oldmv, oldst, vco, i));	
		}

		me->faces= PyList_New(totface);
		for (i=0; i<totface; i++) {
			TFace *oldtf= tfaces?&tfaces[i]:NULL;
			MCol *oldmc= mcols?&mcols[i*4]:NULL;

			if (mfaceints) {			
				MFaceInt *oldmf= &mfaceints[i];
				PyList_SetItem(me->faces, i, (PyObject *) nmface_from_intdata(me, oldmf, oldtf, oldmc));
			} else {
				MFace *oldmf= &mfaces[i];
				PyList_SetItem(me->faces, i, (PyObject *) nmface_from_shortdata(me, oldmf, oldtf, oldmc));
			}
		}
		me->materials = PyList_fromMaterialList(oldmesh->mat, oldmesh->totcol);
	}
	
	return (PyObject *) me;	
}

PyObject *newNMesh(Mesh *oldmesh) 
{
	return newNMesh_internal(oldmesh, NULL, NULL);
}

static char NMeshmodule_New_doc[]=
"() - returns a new, empty NMesh mesh object\n";

static PyObject *NMeshmodule_New(PyObject *self, PyObject *args) 
{
	return newNMesh(NULL);
}

static char NMeshmodule_GetRaw_doc[]=
"([name]) - Get a raw mesh from Blender\n\
\n\
[name] Name of the mesh to be returned\n\
\n\
If name is not specified a new empty mesh is\n\
returned, otherwise Blender returns an existing\n\
mesh.";

static PyObject *NMeshmodule_GetRaw(PyObject *self, PyObject *args) 
{
	char *name=NULL;
	Mesh *oldmesh=NULL;
	
	BPY_TRY(PyArg_ParseTuple(args, "|s", &name));	

	if(name) {
		oldmesh = (Mesh *) getFromList(getMeshList(), name);

		if (!oldmesh) return BPY_incr_ret(Py_None);
	}
	return newNMesh(oldmesh);
}

static char NMeshmodule_GetRawFromObject_doc[]=
"(name) - Get the raw mesh used by a Blender object\n"
"\n"
"(name) Name of the object to get the mesh from\n"
"\n"
"This returns the mesh as used by the object, which\n"
"means it contains all deformations and modifications.";

static PyObject *NMeshmodule_GetRawFromObject(PyObject *self, PyObject *args) 
{
	char *name;
	Object *ob;
	PyObject *nmesh;
	
	BPY_TRY(PyArg_ParseTuple(args, "s", &name));
	
	ob= (Object*) getFromList(getObjectList(), name);
	if (!ob)
		return BPY_err_ret_ob(PyExc_AttributeError, name);
	else if (ob->type!=OB_MESH)
		return BPY_err_ret_ob(PyExc_AttributeError, "Object does not have Mesh data");
	else {
		Mesh *me= (Mesh*) ob->data;
		DispList *dl;
		
		if (mesh_uses_displist(me) && (dl= find_displist(&me->disp, DL_MESH)))
			nmesh = newNMesh_internal(me, dl->mesh, NULL);
		else if ((dl= find_displist(&ob->disp, DL_VERTS)))
			nmesh = newNMesh_internal(me, NULL, dl->verts);
		else
			nmesh = newNMesh(me);
	}
	((NMesh *) nmesh)->mesh = 0; // hack: to mark that (deformed) mesh is readonly,
                                 // so the update function will not try to write it.
	return nmesh;
}

static void mvert_from_data(MVert *mv, MSticky *st, NMVert *from) 
{
	VECCOPY (mv->co, from->co);
	mv->no[0]= from->no[0]*32767.0;
	mv->no[1]= from->no[1]*32767.0;
	mv->no[2]= from->no[2]*32767.0;
		
	mv->flag= 0;
	mv->mat_nr= 0;

	if (st) {
		st->co[0]= from->uvco[0];
		st->co[1]= from->uvco[1];
	}
}

/* TODO: this function is just a added hack. Don't look at the
 * RGBA/BRGA confusion, it just works, but will never work with
 * a restructured Blender */

static void assign_perFaceColors(TFace *tf, NMFace *from)
{
	MCol *col;
	int i;

	col = (MCol *) (tf->col);

	if (col) {
		int len= PySequence_Length(from->col);
		
		if(len>4) len= 4;
		
		for (i=0; i<len; i++, col++) {
			NMCol *mc= (NMCol *) PySequence_GetItem(from->col, i);
			if(!NMCol_Check(mc)) {
				Py_DECREF(mc);
				continue;
			}
			
			col->r= mc->b;
			col->b= mc->r;
			col->g= mc->g;
			col->a= mc->a;

			Py_DECREF(mc);
		}
	}
}

static int assignFaceUV(TFace *tf, NMFace *nmface)
{
	PyObject *fuv, *tmp;
	int i;

	fuv = nmface->uv;
	if (PySequence_Length(fuv) == 0)
		return 0;
	/* fuv = [(u_1, v_1), ... (u_n, v_n)] */
	for (i = 0; i < PySequence_Length(fuv); i++) {
		tmp = PyList_GetItem(fuv, i); /* stolen reference ! */
		if (!PyArg_ParseTuple(tmp, "ff", &(tf->uv[i][0]), &(tf->uv[i][1])))
			return 0;
	}
	if (nmface->tpage) /* image assigned ? */
	{
		tf->tpage = nmface->tpage->data; 
	}
	else
		tf->tpage = 0;

	tf->mode = nmface->mode; /* copy mode */
	tf->flag = nmface->flag; /* copy flag */
	tf->transp = nmface->transp; /* copy transp flag */

	/* assign vertex colours */
	assign_perFaceColors(tf, nmface);
	return 1;
}

static void mface_from_data(MFace *mf, TFace *tf, MCol *col, NMFace *from)
{
	NMVert *nmv;

	int i= PyList_Size(from->v);
	if(i>=1) {
		nmv= (NMVert *) PyList_GetItem(from->v, 0);
		if (NMVert_Check(nmv) && nmv->index!=-1) mf->v1= nmv->index;
		else mf->v1= 0;
	}
	if(i>=2) {
		nmv= (NMVert *) PyList_GetItem(from->v, 1);
		if (NMVert_Check(nmv) && nmv->index!=-1) mf->v2= nmv->index;
		else mf->v2= 0;
	}
	if(i>=3) {
		nmv= (NMVert *) PyList_GetItem(from->v, 2);
		if (NMVert_Check(nmv) && nmv->index!=-1) mf->v3= nmv->index;
		else mf->v3= 0;
	}
	if(i>=4) {
		nmv= (NMVert *) PyList_GetItem(from->v, 3);
		if (NMVert_Check(nmv) && nmv->index!=-1) mf->v4= nmv->index;
		else mf->v4= 0;
	}

	/*	this function is evil: 

			test_index_mface(mf, i);

		It rotates vertex indices, if there are illegal '0's (end marker)
		in the vertex index list.
		But it doesn't do that with vertex colours or texture coordinates...
	*/

	if (tf) {
		assignFaceUV(tf, from);
		if (PyErr_Occurred())
		{
			PyErr_Print();
			return;
		}

		test_index_face(mf, tf, i);
	} else {
		test_index_mface(mf, i);
	}

	mf->puno= 0;
	mf->mat_nr= from->mat_nr;
	mf->edcode= 0;
	if (from->smooth) 
		mf->flag= ME_SMOOTH;
	else
		mf->flag= 0;
	
	if (col) {
		int len= PySequence_Length(from->col);
		
		if(len>4) len= 4;
		
		for (i=0; i<len; i++, col++) {
			NMCol *mc= (NMCol *) PySequence_GetItem(from->col, i);
			if(!NMCol_Check(mc)) {
				Py_DECREF(mc);
				continue;
			}
			
			col->b= mc->r;
			col->g= mc->g;
			col->r= mc->b;
			col->a= mc->a;

			Py_DECREF(mc);
		}
	}
}


/* check for a valid UV sequence */
static int check_validFaceUV(NMesh *nmesh)
{
	PyObject *faces;
	NMFace *nmface;
	int i, n;

	faces = nmesh->faces;
	for (i = 0; i < PySequence_Length(faces); i++) {
		nmface = (NMFace *) PyList_GetItem(faces, i);
		n = PySequence_Length(nmface->uv);
		if (n != PySequence_Length(nmface->v))
		{
			if (n > 0) 
				printf("Warning: different length of vertex and UV coordinate "
				       "list in face!\n");
			return 0;
		}	
	}
	return 1;
}

static int unlink_existingMeshdata(Mesh *mesh)
{
	freedisplist(&mesh->disp);
	unlink_mesh(mesh);
	if(mesh->mvert) MEM_freeN(mesh->mvert);
	if(mesh->mface) MEM_freeN(mesh->mface);
	if(mesh->mcol) MEM_freeN(mesh->mcol);
	if(mesh->msticky) MEM_freeN(mesh->msticky);
	if(mesh->mat) MEM_freeN(mesh->mat);
	if(mesh->tface) MEM_freeN(mesh->tface);
	return 1;
}

Material **nmesh_updateMaterials(NMesh *nmesh)
{
	Material **matlist;
	Mesh *mesh = nmesh->mesh;
	int len = PySequence_Length(nmesh->materials);

	if (!mesh) {
		printf("FATAL INTERNAL ERROR: illegal call to updateMaterials()\n");
		return 0;
	}

	if (len > 0) {
		matlist = newMaterialList_fromPyList(nmesh->materials);
		if (mesh->mat)
			MEM_freeN(mesh->mat);
		mesh->mat = matlist;
	} else {
		matlist = 0;
	}
	mesh->totcol = len;
	return matlist;
}

PyObject *NMesh_assignMaterials_toObject(NMesh *nmesh, Object *ob)
{
	DataBlock *block;
	Material *ma;
	int i;
	short old_matmask;

	old_matmask = ob->colbits; // HACK: save previous colbits
	ob->colbits = 0;           // make assign_material work on mesh linked material

	for (i= 0; i < PySequence_Length(nmesh->materials); i++) {
		block= (DataBlock *) PySequence_GetItem(nmesh->materials, i);
		
		if (DataBlock_isType(block, ID_MA)) {
			ma = (Material *) block->data;
			assign_material(ob, ma, i+1); // XXX don't use this function anymore
		} else {
			PyErr_SetString(PyExc_TypeError, 
			"Material type in attribute list 'materials' expected!");
			Py_DECREF(block);
			return NULL;
		}	
		
		Py_DECREF(block);
	}
	ob->colbits = old_matmask; // HACK

	ob->actcol = 1;
	RETURN_INC(Py_None);
}

static int convert_NMeshToMesh(Mesh *mesh, NMesh *nmesh)
{
	MFace *newmf;
	TFace *newtf;
	MVert *newmv;
	MSticky *newst;
	MCol *newmc;

	int i, j;

	mesh->mvert= NULL;
	mesh->mface= NULL;
	mesh->mcol= NULL;
	mesh->msticky= NULL;
	mesh->tface = NULL;
	mesh->mat= NULL;

	// material assignment moved to PutRaw
	mesh->totvert= PySequence_Length(nmesh->verts);
	if (mesh->totvert) {
		if (nmesh->flags&NMESH_HASVERTUV)
			mesh->msticky= MEM_callocN(sizeof(MSticky)*mesh->totvert, "msticky");

		mesh->mvert= MEM_callocN(sizeof(MVert)*mesh->totvert, "mverts");
	}

	if (mesh->totvert)
		mesh->totface= PySequence_Length(nmesh->faces);
	else
		mesh->totface= 0;


	if (mesh->totface) {

/* only create vertcol array if mesh has no texture faces */

/* TODO: get rid of double storage of vertex colours. In a mesh,
 * vertex colors can be stored the following ways:
 * - per (TFace*)->col
 * - per (Mesh*)->mcol
 * This is stupid, but will reside for the time being -- at least until
 * a redesign of the internal Mesh structure */

		if (!(nmesh->flags & NMESH_HASFACEUV) && (nmesh->flags&NMESH_HASMCOL))
			mesh->mcol= MEM_callocN(4*sizeof(MCol)*mesh->totface, "mcol");
			
		mesh->mface= MEM_callocN(sizeof(MFace)*mesh->totface, "mfaces");
	}

	/* This stuff here is to tag all the vertices referenced
	 * by faces, then untag the vertices which are actually
	 * in the vert list. Any vertices untagged will be ignored
	 * by the mface_from_data function. It comes from my
	 * screwed up decision to not make faces only store the
	 * index. - Zr
	 */
	for (i=0; i<mesh->totface; i++) {
		NMFace *mf= (NMFace *) PySequence_GetItem(nmesh->faces, i);
			
		j= PySequence_Length(mf->v);
		while (j--) {
			NMVert *mv= (NMVert *) PySequence_GetItem(mf->v, j);
			if (NMVert_Check(mv)) mv->index= -1;
			Py_DECREF(mv);
		}
		
		Py_DECREF(mf);
	}
	
	for (i=0; i<mesh->totvert; i++) {
		NMVert *mv= (NMVert *) PySequence_GetItem(nmesh->verts, i);
		mv->index= i;
		Py_DECREF(mv);
	}	
	
	newmv= mesh->mvert;
	newst= mesh->msticky;
	for (i=0; i<mesh->totvert; i++) {
		PyObject *mv=  PySequence_GetItem(nmesh->verts, i);
		mvert_from_data(newmv, newst, (NMVert *)mv);
		Py_DECREF(mv);
		
		newmv++;
		if (newst) newst++;
	}

/*  assign per face texture UVs */

	/* check face UV flag, then check whether there was one 
	 * UV coordinate assigned, if yes, make tfaces */
	if ((nmesh->flags & NMESH_HASFACEUV) || (check_validFaceUV(nmesh))) {
		make_tfaces(mesh); /* initialize TFaces */

		newmc= mesh->mcol;
		newmf= mesh->mface;
		newtf= mesh->tface;
		for (i=0; i<mesh->totface; i++) {
			PyObject *mf= PySequence_GetItem(nmesh->faces, i);
			mface_from_data(newmf, newtf, newmc, (NMFace *) mf);
			Py_DECREF(mf);
				
			newtf++;
			newmf++;
			if (newmc) newmc++;
		}

		nmesh->flags |= NMESH_HASFACEUV;
	} else {

		newmc= mesh->mcol;
		newmf= mesh->mface;
		for (i=0; i<mesh->totface; i++) {
			PyObject *mf= PySequence_GetItem(nmesh->faces, i);
			mface_from_data(newmf, 0, newmc, (NMFace *) mf);
			Py_DECREF(mf);
				
			newmf++;
			if (newmc) newmc += 4; /* there are 4 MCol's per face */
		}
	}
	return 1;
}



static char NMeshmodule_PutRaw_doc[]=
"(mesh, [name, renormal]) - Return a raw mesh to Blender\n\
\n\
(mesh) The NMesh object to store\n\
[name] The mesh to replace\n\
[renormal=1] Flag to control vertex normal recalculation\n\
\n\
If the name of a mesh to replace is not given a new\n\
object is created and returned.";

static PyObject *NMeshmodule_PutRaw(PyObject *self, PyObject *args) 
{
	char *name= NULL;
	Mesh *mesh= NULL;
	Object *ob= NULL;
	NMesh *nmesh;
	int recalc_normals= 1;
	
	BPY_TRY(PyArg_ParseTuple(args, "O!|si", &NMesh_Type, &nmesh, &name, &recalc_normals));
	
	if (!PySequence_Check(nmesh->verts))
		return BPY_err_ret_ob(PyExc_AttributeError, "nmesh vertices are not a sequence");
	if (!PySequence_Check(nmesh->faces))
		return BPY_err_ret_ob(PyExc_AttributeError, "nmesh faces are not a sequence");
	if (!PySequence_Check(nmesh->materials))
		return BPY_err_ret_ob(PyExc_AttributeError, "nmesh materials are not a sequence");

	if (!BPY_check_sequence_consistency(nmesh->verts, &NMVert_Type))
		return BPY_err_ret_ob(PyExc_AttributeError, "nmesh vertices must be NMVerts");
	if (!BPY_check_sequence_consistency(nmesh->faces, &NMFace_Type))
		return BPY_err_ret_ob(PyExc_AttributeError, "nmesh faces must be NMFaces");
	
	if (name) 
		mesh= (Mesh *) getFromList(getMeshList(), name);
	/* returns new mesh if not found */
	
	if(!mesh || mesh->id.us==0) {
		ob= add_object(OB_MESH);
		if (!ob) {
			PyErr_SetString(PyExc_RuntimeError, "Fatal: could not create mesh object");
			return 0;
		}
		if (mesh)
			set_mesh(ob, mesh);
		else
			mesh= (Mesh *) ob->data;
	}
	if(name) new_id(getMeshList(), &mesh->id, name);
	
 	unlink_existingMeshdata(mesh);
	convert_NMeshToMesh(mesh, nmesh);
	nmesh->mesh = mesh;

	if(recalc_normals)
		vertexnormals_mesh(mesh, 0);

	mesh_update(mesh);
	
	if (!during_script())
		allqueue(REDRAWVIEW3D, 0);

	// OK...this requires some explanation:
	// Materials can be assigned two ways:
	// a) to the object data (in this case, the mesh)
	// b) to the Object
	//	
	// Case a) is wanted, if Mesh data should be shared among objects,
	// as well as its materials (up to 16)
	// Case b) is wanted, when Mesh data should be shared, but not the
	// materials. For example, you want several checker boards sharing their
	// mesh data, but having different colors. So you would assign material
	// index 0 to all even, index 1 to all odd faces and bind the materials
	// to the Object instead (MaterialButtons: [OB] button "link materials to object")
	//
	// This feature implies that pointers to materials can be stored in
	// an object or a mesh. The number of total materials MUST be
	// synchronized (ob->totcol <-> mesh->totcol). We avoid the dangerous
	// direct access by calling blenderkernel/material.c:assign_material().

	// The flags setting the material binding is found in ob->colbits, where 
	// each bit indicates the binding PER MATERIAL 

	if (ob) { // we created a new object
		NMesh_assignMaterials_toObject(nmesh, ob);
		return DataBlock_fromData(ob);
	} else {
		RETURN_INC(Py_None);
	}	
}

#undef MethodDef
#define MethodDef(func) {#func, NMeshmodule_##func, METH_VARARGS, NMeshmodule_##func##_doc}

static struct PyMethodDef NMeshmodule_methods[] = {
// These should be: Mesh.Col, Mesh.Vert, Mesh.Face in fure
// -- for ownership reasons
	MethodDef(Col),
	MethodDef(Vert),
	MethodDef(Face),
	MethodDef(New),
	MethodDef(GetRaw),
	MethodDef(GetRawFromObject),
	MethodDef(PutRaw),
	{NULL, NULL}
};
#undef BPY_ADDCONST
#define BPY_ADDCONST(dict, name) insertConst(dict, #name, PyInt_FromLong(TF_##name))

/* set constants for face drawing mode -- see drawmesh.c */

static void init_NMeshConst(PyObject *d)
{
	insertConst(d, "BILLBOARD", PyInt_FromLong(TF_BILLBOARD2));
	//BPY_ADDCONST(d, BILLBOARD);
	insertConst(d, "ALL", PyInt_FromLong(0xffff));
	BPY_ADDCONST(d, DYNAMIC);
	BPY_ADDCONST(d, INVISIBLE);
	insertConst(d, "HALO", PyInt_FromLong(TF_BILLBOARD));
	BPY_ADDCONST(d, LIGHT);
	BPY_ADDCONST(d, OBCOL);
	BPY_ADDCONST(d, SHADOW);
	BPY_ADDCONST(d, SHAREDVERT);
	BPY_ADDCONST(d, SHAREDCOL);
	BPY_ADDCONST(d, TEX);
	BPY_ADDCONST(d, TILES);
	BPY_ADDCONST(d, TWOSIDE);
/* transparent modes */
	BPY_ADDCONST(d, SOLID);
	BPY_ADDCONST(d, ADD);
	BPY_ADDCONST(d, ALPHA);
	BPY_ADDCONST(d, SUB);
/* TFACE flags */
	BPY_ADDCONST(d, SELECT);
	BPY_ADDCONST(d, HIDE);
	BPY_ADDCONST(d, ACTIVE);
}

PyObject *init_py_nmesh(void) 
{
	PyObject *d;
	PyObject *mod= Py_InitModule(SUBMODULE(NMesh), NMeshmodule_methods);
	PyObject *dict= PyModule_GetDict(mod);

	NMesh_Type.ob_type= &PyType_Type;	
	NMVert_Type.ob_type= &PyType_Type;	
	NMFace_Type.ob_type= &PyType_Type;	
	NMCol_Type.ob_type= &PyType_Type;	
	
	d = ConstObject_New();
	PyDict_SetItemString(dict, "Const" , d);
	init_NMeshConst(d);

	g_nmeshmodule = mod;
	return mod;
}

#ifdef SHAREDMODULE
void initNMesh(void) 
{
	init_py_nmesh();
}
#endif
