/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Damien CALISTE, laboratoire L_Sim, (2001-2008)
  
	Adresse m�l :
	CALISTE, damien P caliste AT cea P fr.

	Ce logiciel est un programme informatique servant � visualiser des
	structures atomiques dans un rendu pseudo-3D. 

	Ce logiciel est r�gi par la licence CeCILL soumise au droit fran�ais et
	respectant les principes de diffusion des logiciels libres. Vous pouvez
	utiliser, modifier et/ou redistribuer ce programme sous les conditions
	de la licence CeCILL telle que diffus�e par le CEA, le CNRS et l'INRIA 
	sur le site "http://www.cecill.info".

	Le fait que vous puissiez acc�der � cet en-t�te signifie que vous avez 
	pris connaissance de la licence CeCILL, et que vous en avez accept� les
	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
*/

/*   LICENCE SUM UP
	Copyright CEA, contributors: Damien CALISTE, laboratoire L_Sim, (2001-2008)

	E-mail address:
	CALISTE, damien P caliste AT cea P fr.

	This software is a computer program whose purpose is to visualize atomic
	configurations in 3D.

	This software is governed by the CeCILL  license under French law and
	abiding by the rules of distribution of free software.  You can  use, 
	modify and/ or redistribute the software under the terms of the CeCILL
	license as circulated by CEA, CNRS and INRIA at the following URL
	"http://www.cecill.info". 

	The fact that you are presently reading this means that you have had
	knowledge of the CeCILL license and that you accept its terms. You can
	find a copy of this licence shipped with this software at
        Documentation/licence.en.txt.
*/

#include <Python.h>

#include <gtk/gtk.h>
#include <pygobject.h>
#include <pygtk/pygtk.h>

#include <visu_basic.h>
#include <visu_gtk.h>
#include <visu_elements.h>
#include <visu_data.h>
#include <visu_object.h>
#include <visu_extension.h>
#include <gtk_renderingWindowWidget.h>
#include <coreTools/toolPhysic.h>
#include <openGLFunctions/objectList.h>

#include "plane_py.h"


typedef struct _VisuElementPy
{
  PyObject_HEAD
  VisuElement *obj;
  gboolean ownByVSim;
} VisuElementPy;

static int visuElementPy_init(VisuElementPy *self, PyObject *args, PyObject *kwds);
static PyObject* visuElementPy_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
static void visuElementPy_free(VisuElementPy* self);
static PyObject* visuElementPy_set(PyObject *obj, PyObject *args,
				   PyObject *kwds);

/* We define here all the methods of the dtsets class. */
static PyMethodDef VisuElementPyMethods[] =
  {
    {"set", (PyCFunction)visuElementPy_set, METH_VARARGS | METH_KEYWORDS,
     "Change values for a VisuElement."},
/*     {"get", (PyCFunction)dtsets_get, METH_VARARGS, */
/*      "Return the valus of an attribute."}, */
    {NULL, NULL, 0, NULL}
  };
/* static PyMemberDef VisuElementPyMembers[] = */
/*   { */
/*     {"dt", T_OBJECT_EX, offsetof(Dtsets, dt), READONLY, */
/*      "Internal identifier for Fortran calls."}, */
/*     {NULL, 0, 0, 0, NULL} */
/*   }; */
static PyTypeObject VisuElementPyType = {
  PyObject_HEAD_INIT(NULL)
  0,                         /*ob_size*/
  "v_sim.VisuElementPy",              /*tp_name*/
  sizeof(VisuElementPy),        /*tp_basicsize*/
  0,                         /*tp_itemsize*/
  (destructor)visuElementPy_free, /*tp_dealloc*/
  0,                         /*tp_print*/
  0,                         /*tp_getattr*/
  0,                         /*tp_setattr*/
  0,                         /*tp_compare*/
  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*/
  "Ploum",          /* tp_doc */
  0,                         /* tp_traverse */
  0,                         /* tp_clear */
  0,                         /* tp_richcompare */
  0,                         /* tp_weaklistoffset */
  0,                         /* tp_iter */
  0,                         /* tp_iternext */
  VisuElementPyMethods,         /* 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 */
  (initproc)visuElementPy_init, /* tp_init */
  0,                         /* tp_alloc */
  visuElementPy_new,            /* tp_new */
/*   0, */
/*   0, */
/*   0, */
/*   0, */
/*   0, */
/*   0, */
/*   0, */
/*   0 */
};


typedef struct _VisuDataPy VisuDataPy;

typedef struct _VisuNodePy
{
  PyObject_HEAD
  guint nodeId;
  VisuDataPy *parent;
} VisuNodePy;

static int visuNodePy_init(VisuNodePy *self, PyObject *args, PyObject *kwds);
static PyObject* visuNodePy_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
static void visuNodePy_free(VisuNodePy* self);
static PyObject* visuNodePy_isValid(PyObject *obj);
static PyObject* visuNodePy_set(PyObject *obj, PyObject *args, PyObject *kwds);

/* We define here all the methods of the dtsets class. */
static PyMethodDef VisuNodePyMethods[] =
  {
    {"set", (PyCFunction)visuNodePy_set, METH_VARARGS | METH_KEYWORDS,
     "Change values for a VisuNode."},
    {"isValid", (PyCFunction)visuNodePy_isValid, METH_NOARGS,
     "Test if the given node still exist or has been deleted."},
/*     {"get", (PyCFunction)dtsets_get, METH_VARARGS, */
/*      "Return the valus of an attribute."}, */
    {NULL, NULL, 0, NULL}
  };
/* static PyMemberDef VisuNodePyMembers[] = */
/*   { */
/*     {"dt", T_OBJECT_EX, offsetof(Dtsets, dt), READONLY, */
/*      "Internal identifier for Fortran calls."}, */
/*     {NULL, 0, 0, 0, NULL} */
/*   }; */
static PyTypeObject VisuNodePyType = {
  PyObject_HEAD_INIT(NULL)
  0,                         /*ob_size*/
  "v_sim.VisuNodePy",              /*tp_name*/
  sizeof(VisuNodePy),        /*tp_basicsize*/
  0,                         /*tp_itemsize*/
  (destructor)visuNodePy_free, /*tp_dealloc*/
  0,                         /*tp_print*/
  0,                         /*tp_getattr*/
  0,                         /*tp_setattr*/
  0,                         /*tp_compare*/
  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*/
  "Ploum",          /* tp_doc */
  0,                         /* tp_traverse */
  0,                         /* tp_clear */
  0,                         /* tp_richcompare */
  0,                         /* tp_weaklistoffset */
  0,                         /* tp_iter */
  0,                         /* tp_iternext */
  VisuNodePyMethods,         /* 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 */
  (initproc)visuNodePy_init, /* tp_init */
  0,                         /* tp_alloc */
  visuNodePy_new,            /* tp_new */
/*   0, */
/*   0, */
/*   0, */
/*   0, */
/*   0, */
/*   0, */
/*   0, */
/*   0 */
};

struct _VisuDataPy
{
  PyObject_HEAD
  VisuData *obj;

  PyObject *planes;
  int planesId;
};

static int       visuDataPy_init(VisuDataPy *self, PyObject *args, PyObject *kwds);
static PyObject* visuDataPy_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
static void      visuDataPy_free(VisuDataPy* self);
static PyObject* visuDataPy_addFile(PyObject *self, PyObject *args, PyObject *kwds);
static PyObject* visuDataPy_setPopulation(PyObject *self, PyObject *args, PyObject *kwds);
static PyObject* visuDataPy_getPopulation(PyObject *self);
static PyObject* visuDataPy_createAllNodes(PyObject *self);
static PyObject* visuDataPy_setBox(PyObject *self, PyObject *args, PyObject *kwds);
static PyObject* visuDataPy_addNode(PyObject *self, PyObject *args, PyObject *kwds);
static PyObject* visuDataPy_delNodes(PyObject *self, PyObject *args);
static PyObject* visuDataPy_setVisuPlanes(PyObject *self, PyObject *planes);

/* We define here all the methods of the dtsets class. */
static PyMethodDef VisuDataPyMethods[] =
  {
    {"addFile", (PyCFunction)visuDataPy_addFile, METH_VARARGS | METH_KEYWORDS,
     "Associate a file name to a file type."},
    {"setPopulation", (PyCFunction)visuDataPy_setPopulation, METH_VARARGS | METH_KEYWORDS,
     "Allocate the size to store nodes."},
    {"getPopulation", (PyCFunction)visuDataPy_getPopulation, METH_NOARGS,
     "get a dictionnary with the description of the population."},
    {"createAllNodes", (PyCFunction)visuDataPy_createAllNodes, METH_NOARGS,
     "Create the OpenGL rendering for all the nodes."},
    {"setBox", (PyCFunction)visuDataPy_setBox, METH_VARARGS | METH_KEYWORDS,
     "Set the box size."},
    {"addNode", (PyCFunction)visuDataPy_addNode, METH_VARARGS | METH_KEYWORDS,
     "Set the coordinates of a new node for the given element."},
    {"delNodes", (PyCFunction)visuDataPy_delNodes, METH_VARARGS,
     "Delete the given nodes."},
    {"setVisuPlanes", (PyCFunction)visuDataPy_setVisuPlanes, METH_O,
     "Draw the given planes inside the box of the given data."},
    {NULL, NULL, 0, NULL}
  };
/* static PyMemberDef VisuDataPyMembers[] = */
/*   { */
/*     {"dt", T_OBJECT_EX, offsetof(Dtsets, dt), READONLY, */
/*      "Internal identifier for Fortran calls."}, */
/*     {NULL, 0, 0, 0, NULL} */
/*   }; */
static PyTypeObject VisuDataPyType = {
  PyObject_HEAD_INIT(NULL)
  0,                         /*ob_size*/
  "v_sim.VisuDataPy",        /*tp_name*/
  sizeof(VisuDataPy),        /*tp_basicsize*/
  0,                         /*tp_itemsize*/
  (destructor)visuDataPy_free, /*tp_dealloc*/
  0,                         /*tp_print*/
  0,                         /*tp_getattr*/
  0,                         /*tp_setattr*/
  0,                         /*tp_compare*/
  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*/
  "Ploum",          /* tp_doc */
  0,                         /* tp_traverse */
  0,                         /* tp_clear */
  0,                         /* tp_richcompare */
  0,                         /* tp_weaklistoffset */
  0,                         /* tp_iter */
  0,                         /* tp_iternext */
  VisuDataPyMethods,         /* 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 */
  (initproc)visuDataPy_init, /* tp_init */
  0,                         /* tp_alloc */
  visuDataPy_new,            /* tp_new */
/*   0, */
/*   0, */
/*   0, */
/*   0, */
/*   0, */
/*   0, */
/*   0, */
/*   0 */
};









static int visuElementPy_init(VisuElementPy *self _U_, PyObject *args _U_,
			      PyObject *kwds _U_)
{
  return 0;
}
static PyObject* visuElementPy_new(PyTypeObject *type, PyObject *args,
				   PyObject *kwds)
{
  VisuElementPy *self;
  char *name;
  gchar *eleName;
  PyObject *argsPy = NULL, *namePy;

  name = (char*)0;
  eleName = (gchar*)0;
  namePy = PyTuple_GetItem(args, 0);
  if (! PyObject_TypeCheck(namePy, &PyString_Type) &&
      ! PyObject_TypeCheck(namePy, &PyInt_Type))
    {
      PyErr_SetString(PyExc_TypeError, "first argument must be a string or a Z number.");
      return NULL;
    }
  else
    {
      if (PyObject_TypeCheck(namePy, &PyString_Type))
	name = PyString_AsString(namePy);
      else if (PyObject_TypeCheck(namePy, &PyInt_Type))
	{
	  if (!tool_physic_getSymbolFromZ(&eleName, (float*)0,
				       ((PyIntObject*)namePy)->ob_ival))
	    PyErr_SetString(PyExc_ValueError, "Unknown element.");
	  name = eleName;
	}
    }
  if (!name)
    return NULL;

  argsPy = PyTuple_GetSlice(args, 1, PyTuple_Size(args));
  
  self = (VisuElementPy*)type->tp_alloc(type, 0);
  if (self != NULL)
    {
      DBG_fprintf(stderr, "Visu Python: new VisuElement object %p.\n", self);
      self->obj = visu_element_getFromName(name);
      if (self->obj)
	self->ownByVSim = TRUE;
      else
	{
	  self->obj = visu_element_new(name);
	  self->ownByVSim = FALSE;
	}
    }
  else
    {
      Py_DECREF(argsPy);
      return NULL;
    }

  if (! visuElementPy_set((PyObject*)self, argsPy, kwds))
    {
      Py_DECREF(argsPy);
      return NULL;
    }

  Py_DECREF(argsPy);

  return (PyObject *)self;
}
static void visuElementPy_free(VisuElementPy* self)
{
  DBG_fprintf(stderr, "Visu Python: deallocate a VisuElement object %p.\n", self);

  if (!self->ownByVSim)
    visuElementFree(self->obj);

  self->ob_type->tp_free((PyObject*)self);
}
static PyObject* visuElementPy_set(PyObject *obj, PyObject *args,
				   PyObject *kwds)
{
  VisuElementPy *self;
  static char *kwlist[] = {"rgba", "material", "rendered", "hidden", NULL};
  PyObject *rgbPy = NULL, *matPy = NULL, *rendered = Py_True, *hidden = Py_True;
  float rgb[4] = {-1.f, -1.f, -1.f, -1.f};
  float mat[5] = {-0.2f, -1.f, -0.5f, -0.5f, -0.f};

  DBG_fprintf(stderr, "Visu Python: set values for VisuElement '%s' object %p.\n",
	      ((VisuElementPy*)obj)->obj->name, obj);

  if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O!O!O!O!", kwlist,
				    &PyTuple_Type, &rgbPy,
				    &PyTuple_Type, &matPy,
				    &PyBool_Type, &rendered,
				    &PyBool_Type, &hidden))
    return NULL;
  if (rgbPy)
    {
      if (! PyArg_ParseTuple(rgbPy, "ffff", rgb, rgb + 1, rgb + 2, rgb + 3))
	return NULL;
    }
  if (matPy)
    {
      if (! PyArg_ParseTuple(matPy, "fffff", mat, mat + 1, mat + 2, mat + 3, mat + 4))
	return NULL;
    }
  
  self = (VisuElementPy*)obj;

  if (rgb[0] >= 0.f && mat[0] >= 0.f)
    visuElementSet_allColorValues(self->obj, rgb, mat);
  else if (rgb[0] >= 0.f)
    visuElementSet_allRGBValues(self->obj, rgb);
  else if (mat[0] >= 0.f)
    visuElementSet_allToolMaterialIdsValues(self->obj, mat);

  visuElementSet_rendered(self->obj, (rendered == Py_True));
  visuElementSet_sensitiveToVisuPlanes(self->obj, (hidden == Py_True));

  Py_INCREF(Py_None);
  return Py_None;
}






static int visuNodePy_init(VisuNodePy *self _U_, PyObject *args _U_,
			   PyObject *kwds _U_)
{
  return 0;
}
static PyObject* visuNodePy_new(PyTypeObject *type, PyObject *args, PyObject *kwds _U_)
{
  VisuNodePy *self;
  guint id;
  PyObject *parentPy;

  if (! PyArg_ParseTuple(args, "O!I", &VisuDataPyType, &parentPy, &id))
    return NULL;

  self = (VisuNodePy*)type->tp_alloc(type, 0);
  if (self == NULL)
    return NULL;
  DBG_fprintf(stderr, "Visu Python: new VisuNode object %p.\n", self);

  Py_INCREF(parentPy);
  self->nodeId = id;
  self->parent = (VisuDataPy*)parentPy;

  return (PyObject *)self;
}
static void visuNodePy_free(VisuNodePy* self)
{
  DBG_fprintf(stderr, "Visu Python: deallocate a VisuNode object %p.\n", self);

  Py_DECREF(self->parent);
  self->ob_type->tp_free((PyObject*)self);
}
static PyObject* visuNodePy_set(PyObject *obj, PyObject *args,
				PyObject *kwds)
{
  VisuNodePy *self;
  static char *kwlist[] = {"coord", "translation", "rendered", "emit", NULL};
  PyObject *coordPy = NULL, *transPy = NULL, *rendered = Py_None, *emit = Py_False;
  gboolean coordSet = FALSE, transSet = FALSE;
  float coord[3] = {-1.f, -1.f, -1.f};
  float trans[3] = {-1.f, -1.f, -1.f};
  VisuNode *node;
  gboolean emitPos;

  DBG_fprintf(stderr, "Visu Python: set values for VisuNode %d object %p.\n",
	      ((VisuNodePy*)obj)->nodeId, obj);

  if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O!O!O!O!", kwlist,
				    &PyTuple_Type, &coordPy,
				    &PyTuple_Type, &transPy,
				    &PyBool_Type, &rendered,
				    &PyBool_Type, &emit))
    return NULL;
  if (coordPy)
    {
      if (! PyArg_ParseTuple(coordPy, "fff", coord, coord + 1, coord + 2))
	return NULL;
      coordSet = TRUE;
    }
  if (transPy)
    {
      if (! PyArg_ParseTuple(transPy, "fff", trans, trans + 1, trans + 2))
	return NULL;
      transSet = TRUE;
    }
  
  self = (VisuNodePy*)obj;

  node = visuDataGet_nodeFromNumber(self->parent->obj, self->nodeId);
  if (!node)
    {
      PyErr_SetString(PyExc_RuntimeError, "not a VisuNode anymore.");
      return NULL;
    }

  emitPos = FALSE;
  if (coordSet)
    {
      node->xyz[0] = coord[0];
      node->xyz[1] = coord[1];
      node->xyz[2] = coord[2];
      emitPos = (emit == Py_True);
    }
  if (transSet)
    {
      node->translation[0] = coord[0];
      node->translation[1] = coord[1];
      node->translation[2] = coord[2];
      emitPos = (emit == Py_True);
    }

  if (rendered != Py_None &&
      visuNodeSet_visibility(node, (rendered == Py_True)) && emit == Py_True)
    visuDataEmit_nodeRenderedChange(self->parent->obj);

  if (emitPos)
    visuDataEmit_nodePositionChanged(self->parent->obj);

  Py_INCREF(Py_None);
  return Py_None;
}
static PyObject* visuNodePy_isValid(PyObject *obj)
{
  VisuNodePy *self;
  VisuNode *node;

  self = (VisuNodePy*)obj;

  node = visuDataGet_nodeFromNumber(self->parent->obj, self->nodeId);
  if (node)
    {
      Py_INCREF(Py_True);
      return Py_True;
    }
  else
    {
      Py_INCREF(Py_False);
      return Py_False;
    }
}






static void _onAskForHide(VisuData *visuData, gboolean *redraw, VisuDataPy *self);
static void _onPositionChanged(VisuData *dataObj, VisuDataPy *self);

static int visuDataPy_init(VisuDataPy *self, PyObject *args _U_, PyObject *kwds _U_)
{
  self->planes   = (PyObject*)0;
  self->planesId = 0;
  return 0;
}
static PyObject* visuDataPy_new(PyTypeObject *type, PyObject *args, PyObject *kwds _U_)
{
  VisuDataPy *self;
  PyObject *parentPy = NULL;

  self = (VisuDataPy*)type->tp_alloc(type, 0);
  if (self != NULL)
    DBG_fprintf(stderr, "Visu Python: new VisuData object %p.\n", self);

  if (!PyArg_ParseTuple(args, "|O!", &VisuDataPyType, &parentPy))
    return NULL;

  if (parentPy)
    self->obj = visuDataNew_withVisuGlView(visuDataGet_openGLView(((VisuDataPy*)parentPy)->obj));
  else
    self->obj = visuDataNew();

  g_object_ref(G_OBJECT(self->obj));
  g_object_set_data(G_OBJECT(self->obj), "selfPython", self);
  g_signal_connect(G_OBJECT(self->obj), "AskForShowHide",
		   G_CALLBACK(_onAskForHide), self);
  g_signal_connect(G_OBJECT(self->obj), "PositionChanged",
		   G_CALLBACK(_onPositionChanged), self);

  return (PyObject *)self;
}
static void visuDataPy_free(VisuDataPy* self)
{
  DBG_fprintf(stderr, "Visu Python: deallocate a VisuData object %p.\n", self);
  g_object_unref(G_OBJECT(self->obj));
  self->ob_type->tp_free((PyObject*)self);
}

static PyObject* visuDataPy_addFile(PyObject *self, PyObject *args, PyObject *kwds)
{
  char *filename;
  int type;
  static char *kwlist[] = {"filename", "type", NULL};

  if (! PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, 
				    &filename, &type))
    return NULL;

  visuDataAdd_file(((VisuDataPy*)self)->obj, filename, type, (ToolFileFormat*)0);

  Py_INCREF(Py_None);
  return Py_None;
}
static PyObject* visuDataPy_getPopulation(PyObject *self)
{
  PyObject* dict, *nb;
  const char *ele;
  VisuNodeArray *nodes;
  guint i;

  dict = PyDict_New();
  if (!dict)
    return NULL;

  nodes = visu_data_getNodeArray(((VisuDataPy*)self)->obj);
  if (nodes)
    for (i = 0; i < nodes->ntype; i++)
      {
	nb = PyInt_FromLong((long)nodes->nStoredNodesPerEle[i]);
	ele = ((VisuDataPy*)self)->obj->fromIntToVisuElement[i]->name;
	PyDict_SetItemString(dict, ele, nb); 
      }

  Py_INCREF(dict);
  return dict;
}
static PyObject* visuDataPy_setPopulation(PyObject *self, PyObject *args, PyObject *kwds)
{
  static char *kwlist[] = {"population", "iSet", NULL};
  guint ntypes, i, iSet;
  VisuElement **visuElementUsed;
  guint *nbOfNodesPerVisuElement;
  PyObject *dict, *key, *value;
#if PY_MINOR_VERSION > 4
  Py_ssize_t pos = 0;
#else
  int pos = 0;
#endif

  iSet = 0;
  if (! PyArg_ParseTupleAndKeywords(args, kwds, "O!|I", kwlist, 
				    &PyDict_Type, &dict, &iSet))
    return NULL;

  ntypes = (guint)PyDict_Size(dict);
  if (ntypes == 0)
    {
      PyErr_SetString(PyExc_ValueError, "dictionary of elements must not be empty.");
      return NULL;
    }

  visuElementUsed = g_malloc(sizeof(VisuElement*) * ntypes);
  nbOfNodesPerVisuElement = g_malloc(sizeof(guint) * ntypes);

  visuDataFree_population(((VisuDataPy*)self)->obj);

  pos = 0;
  i = 0;
  while (PyDict_Next(dict, &pos, &key, &value))
    {
      if (! PyObject_TypeCheck(key, &VisuElementPyType))
	{
	  PyErr_SetString(PyExc_TypeError, "keys must be VisuElementPy.");
	  g_free(visuElementUsed);
	  g_free(nbOfNodesPerVisuElement);
	  return NULL;
	}
      if (! PyInt_Check(value) || ((PyIntObject*)value)->ob_ival <= 0)
	{
	  PyErr_SetString(PyExc_TypeError, "values must be positive integer.");
	  g_free(visuElementUsed);
	  g_free(nbOfNodesPerVisuElement);
	  return NULL;
	}
      Py_INCREF(key);
      visuElementUsed[i] = ((VisuElementPy*)key)->obj;
      nbOfNodesPerVisuElement[i] = (int) ((PyIntObject*)value)->ob_ival;
      i += 1;
    }
  if (i != ntypes)
    {
      PyErr_SetString(PyExc_RuntimeError, "can't browse the population dictionnary.");
      g_free(visuElementUsed);
      g_free(nbOfNodesPerVisuElement);
      return NULL;
    }
  visu_data_setPopulation(((VisuDataPy*)self)->obj, ntypes,
			 nbOfNodesPerVisuElement, visuElementUsed);
  visuDataSet_setId(((VisuDataPy*)self)->obj, iSet);

  g_free(visuElementUsed);
  g_free(nbOfNodesPerVisuElement);

  Py_INCREF(Py_None);
  return Py_None;
}
static PyObject* visuDataPy_createAllNodes(PyObject *self)
{
  visuData_createAllNodes(((VisuDataPy*)self)->obj);

  Py_INCREF(Py_None);
  return Py_None;
}
static PyObject* visuDataPy_setBox(PyObject *self, PyObject *args, PyObject *kwds)
{
  static char *kwlist[] = {"box", "periodic", "scale", NULL};
  float box[6], size;
  double rprimd[3][3];
  PyObject *bool, *boxPy;

  size = 0.f;
  if (! PyArg_ParseTupleAndKeywords(args, kwds, "O!|O!f", kwlist,
			 &PyTuple_Type, &boxPy, &PyBool_Type, &bool, &size))
    return NULL;
  if (PyTuple_Size(boxPy) == 6 &&
      ! PyArg_ParseTuple(boxPy, "ffffff", box, box + 1, box + 2,
			 box + 3, box + 4, box + 5))
    return NULL;
  if (PyTuple_Size(boxPy) == 3 &&
      ! PyArg_ParseTuple(boxPy, "(ddd)(ddd)(ddd)",
			 rprimd[0], rprimd[0] + 1, rprimd[0] + 2,
			 rprimd[1], rprimd[1] + 1, rprimd[1] + 2,
			 rprimd[2], rprimd[2] + 1, rprimd[2] + 2))
    return NULL;

  if (PyTuple_Size(boxPy) == 3)
    tool_matrix_reducePrimitiveVectors(box, rprimd);

  visu_data_setBoxGeometry(((VisuDataPy*)self)->obj, box, (bool == Py_True));
  visu_data_applyBoxGeometry(((VisuDataPy*)self)->obj);

  Py_INCREF(Py_None);
  return Py_None;
}
static PyObject* visuDataPy_addNode(PyObject *self, PyObject *args, PyObject *kwds)
{
  float xyz[3];
  static char *kwlist[] = {"element", "coord", "emit", NULL};
  PyObject *ele, *emitPy = Py_False, *xyzPy;
  VisuNode *node;
  VisuNodePy *nodePy;

  if (! PyArg_ParseTupleAndKeywords(args, kwds, "O!O!|O!", kwlist,
			 &VisuElementPyType, &ele,
			 &PyTuple_Type, &xyzPy,
			 &PyBool_Type, &emitPy))
    return NULL;
  if (! PyArg_ParseTuple(xyzPy, "fff", xyz, xyz + 1, xyz + 2))
    return NULL;

  node = visu_data_addNodeFromElement(((VisuDataPy*)self)->obj,
				     ((VisuElementPy*)ele)->obj,
				     xyz, (emitPy == Py_True));
  if (!node)
    {
      PyErr_SetString(PyExc_ValueError, "can't add new node.");
      return NULL;
    }
  DBG_fprintf(stderr, "Visu Python: create a new object for node %d %p.\n",
	      node->number, node);
  nodePy = (VisuNodePy*)PyObject_New(VisuNodePy, &VisuNodePyType);
  if (nodePy == NULL)
    return NULL;
  Py_INCREF(self);
  nodePy->nodeId = node->number;
  nodePy->parent = (VisuDataPy*)self;

  Py_INCREF(nodePy);
  return (PyObject *)nodePy;
}
static PyObject* visuDataPy_delNodes(PyObject *self, PyObject *args)
{
  PyObject *nodesPy, *nodePy;
  int *nodes, n, i;

  if (! PyArg_ParseTuple(args, "O", &nodesPy))
    return NULL;

  nodes = (int*)0;
  if (PyObject_TypeCheck(nodesPy, &PyInt_Type))
    {
      nodes = g_malloc(sizeof(int) * 2);
      nodes[0] = (int) ((PyIntObject*)nodesPy)->ob_ival;
      if (nodes[0] < 0)
	{
	  PyErr_SetString(PyExc_ValueError, "Only positive numbers identify nodes.");
	  g_free(nodes);
	  return NULL;
	}
      nodes[1] = -1;
    }
  else if (PyObject_TypeCheck(nodesPy, &VisuNodePyType))
    {
      nodes = g_malloc(sizeof(int) * 2);
      nodes[0] = ((VisuNodePy*)nodesPy)->nodeId;
      nodes[1] = -1;
    }
  else if (PyObject_TypeCheck(nodesPy, &PyTuple_Type))
    {
      n = PyTuple_GET_SIZE(nodesPy);
      nodes = g_malloc(sizeof(int) * (n + 1));
      for (i = 0; i < n; i++)
	{
	  nodePy = PyTuple_GET_ITEM(nodesPy, i);
	  if (! PyObject_TypeCheck(nodePy, &PyInt_Type) &&
	      ! PyObject_TypeCheck(nodePy, &VisuNodePyType))
	    {
	      PyErr_SetString(PyExc_TypeError,
			      "Arguments should be integers or VisuNodePy.");
	      g_free(nodes);
	      return NULL;
	    }
	  if (PyObject_TypeCheck(nodePy, &PyInt_Type))
	    {
	      nodes[i] = (int) ((PyIntObject*)nodePy)->ob_ival;
	      if (nodes[i] < 0)
		{
		  PyErr_SetString(PyExc_ValueError,
				  "Only positive numbers identify nodes.");
		  g_free(nodes);
		  return NULL;
		}
	    }
	  else
	    nodes[i] = ((VisuNodePy*)nodePy)->nodeId;
	}
      nodes[n] = -1;
    }
  else
    {
      PyErr_SetString(PyExc_TypeError,
		      "Argument must be tuple, integer or VisuNodePy.");
      return NULL;
    }
  
  visuDataRemove_nodes(((VisuDataPy*)self)->obj, nodes);

  g_free(nodes);

  Py_INCREF(Py_None);
  return Py_None;
}
static VisuPlane** _buildArrayOfVisuPlanes(VisuDataPy *self)
{
  int i, n;
  VisuPlane **planes;

  n = PyTuple_GET_SIZE(self->planes);
  planes = g_malloc(sizeof(VisuPlane*) * (n + 1));
  for (i = 0; i < n; i++)
    planes[i] = ((VisuPlanePy*)PyTuple_GET_ITEM(self->planes, i))->obj;
  planes[n] = (VisuPlane*)0;
  
  return planes;
}
static void _rebuildVisuPlanes(VisuData *dataObj)
{
  VisuPlane **planes;
  VisuDataPy *self;

  self = (VisuDataPy*)g_object_get_data(G_OBJECT(dataObj), "selfPython");
  if (!self)
    return;

  planes = _buildArrayOfVisuPlanes(self);
  
  DBG_fprintf(stderr, "Visu Python: create OpenGL list for planes.\n");
  visu_plane_class_drawList(planes, self->planesId);

  DBG_fprintf(stderr, " | OK\n");
  g_free(planes);
}
static void _onVisuPlaneMoved(VisuPlane *plane _U_, VisuDataPy *self)
{
  _rebuildVisuPlanes(self->obj);
}
static void _onAskForHide(VisuData *visuData, gboolean *redraw, VisuDataPy *self)
{
  VisuPlane **planes;

  if (!self->planes)
    return;

  DBG_fprintf(stderr, "Visu Python: caught the 'AskForShowHide' signal for"
	      " VisuData %p.\n", (gpointer)visuData);
  planes = _buildArrayOfVisuPlanes(self);
  *redraw = visu_plane_class_showHideAll(visuData, planes) || *redraw;
  g_free(planes);
}
static void _onPositionChanged(VisuData *dataObj, VisuDataPy *self)
{
  gboolean redraw;

  DBG_fprintf(stderr, "Visu Python: caught the 'PositionChanged' signal,"
	      " recalculating masking properties.\n");

  if (self->planes)
    {
      visuDataEmit_askForShowHideNodes(dataObj, &redraw);
      if (redraw)
	visuDataEmit_nodeRenderedChange(dataObj);
    }
}
static PyObject* visuDataPy_setVisuPlanes(PyObject *obj, PyObject *planes)
{
  VisuDataPy *self = (VisuDataPy*)obj;
  int i;
  PyObject *plane;
  float vertices[8][3];
  VisuPlane *planeObj;
  VisuGlExt *planeExt;

  if (!PyObject_TypeCheck(planes, &PyTuple_Type))
    {
      PyErr_SetString(PyExc_TypeError,
		      "Argument must be tuple of v_sim.plane.");
      return NULL;
    }
  for (i = 0; i < PyTuple_GET_SIZE(planes); i++)
    {
      plane = PyTuple_GET_ITEM(planes, i);
      if (! PyObject_TypeCheck(plane, PLANEPY_TYPE))
	{
	  PyErr_SetString(PyExc_TypeError,
			  "Tuple argument must contain v_sim.plane objects.");
	  return NULL;
	}
    }

  Py_XDECREF(self->planes);
  Py_INCREF(planes);
  self->planes = planes;

  /* We compute the plane intersections with this VisuData box,
     and connect the change signals. */
  visuDataGet_boxVertices(self->obj, vertices, TRUE);
  for (i = 0; i < PyTuple_GET_SIZE(self->planes); i++)
    {
      planeObj = ((VisuPlanePy*)PyTuple_GET_ITEM(self->planes, i))->obj;
      g_signal_connect(G_OBJECT(planeObj), "moved",
		       G_CALLBACK(_onVisuPlaneMoved), (gpointer)self);
      visu_plane_setBox(planeObj, vertices);
    }

  /* We create an OpenGL extension to draw the planes if not already
     done. */
  if (!self->planesId)
    {
      self->planesId = visu_gl_objectlist_new(1);
      planeExt = visu_gl_ext_new("VisuPlanes", "VisuPlanes", (char*)0,
				     self->planesId, _rebuildVisuPlanes);
      planeExt->used = 1;
      visu_gl_ext_setSensitiveToRenderingMode(planeExt, TRUE);
      visu_gl_ext_setPriority(planeExt, VISU_GL_EXT_PRIORITY_LOW + 1);
      visu_gl_ext_register(planeExt);
    }
  _rebuildVisuPlanes(self->obj);

  Py_INCREF(Py_None);
  return Py_None;
}





static int width, height;

static void _createMain(GtkWindow **panel, GtkWindow **render)
{
  GtkWidget *renderingWindow;

  /* Force the creation of the Scale class. */
  DBG_fprintf(stderr,"Visu Python: create a rendering window (%dx%d).\n",
	      width, height);
  renderingWindow =
    visu_ui_rendering_window_new(width, height, TRUE, FALSE);
  visuVisuUiRenderingWindowSet_default(renderingWindow);

  *panel = (GtkWindow*)0;
  *render = (GtkWindow*)0;

  return;
}
static gboolean _parseFiles(gpointer data _U_)
{
  GString *message;

  message = visu_basic_parseConfigFiles();
  if (message)
    {
      g_string_free(message, TRUE);
    }
}

static PyObject* visuPyMain(PyObject *self _U_, PyObject *args, PyObject *kwds)
{
  PyObject *me;
  static char *kwlist[] = {"width", "height", NULL};
  GtkWidget *renderingWindow;

  width = 600;
  height = 600;
  /* Try to get the width and the height. */
  if (! PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwlist, 
				    &width, &height))
    return NULL;

  /* Force the creation of the Scale class. */
  DBG_fprintf(stderr,"Visu Python: create a rendering window (%dx%d).\n",
	      width, height);
  renderingWindow =
    visu_ui_rendering_window_new(width, height, TRUE, FALSE);
  visuVisuUiRenderingWindowSet_default(renderingWindow);
  g_idle_add(_parseFiles, (gpointer)0);
  me = pygobject_new(G_OBJECT(renderingWindow));

  Py_INCREF(me);
  return me;
}

static PyObject* visuPyLoad(PyObject *self _U_, PyObject *args, PyObject *kwds)
{
  PyObject *obj;
  int iSet;
  PyObject *bool;
  static char *kwlist[] = {"data", "iSet", "internal", NULL};
  GError *error;
  gboolean res;

  /* Try to get the width and the height. */
  iSet = 0;
  if (! PyArg_ParseTupleAndKeywords(args, kwds, "O!|iO!", kwlist, 
				    &VisuDataPyType, &obj, &iSet, &PyBool_Type, &bool))
    return NULL;

  DBG_fprintf(stderr,"Visu Python: load VisuData from %p (%p).\n",
	      obj, ((VisuDataPy*)obj)->obj);
  if ((bool == Py_False))
    visuGtkLoad_file(((VisuDataPy*)obj)->obj, iSet);
  else
    {
      error = (GError*)0;
      res = visu_object_load(((VisuDataPy*)obj)->obj,
				       (ToolFileFormat*)0, iSet, &error);
      if (error)
	{
	  PyErr_SetString(PyExc_TypeError, error->message);
	  g_error_free(error);
	  return NULL;
	}
      visuVisuUiRenderingWindowSet_visuData
	(VISU_UI_RENDERING_WINDOW(visuVisuUiRenderingWindowGet_current()), ((VisuDataPy*)obj)->obj);
      /* We release a ref on obj, since
	 visu_ui_rendering_window_setData has increased it. */
      g_object_unref(G_OBJECT(((VisuDataPy*)obj)->obj));
      visuData_createAllNodes(((VisuDataPy*)obj)->obj);
    }

  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject* visuPySet(PyObject *self _U_, PyObject *data)
{
  if (! PyObject_TypeCheck(data, &VisuDataPyType) && data != Py_None)
    {
      PyErr_SetString(PyExc_TypeError, "argument must be of type VisuDataPy or None.");
      return NULL;
    }

  if (data != Py_None)
    {
      visu_ui_rendering_window_setData(VISU_UI_RENDERING_WINDOW(visuVisuUiRenderingWindowGet_current()),
				  ((VisuDataPy*)data)->obj);
      rebuildAllExtensionsLists(((VisuDataPy*)data)->obj);
    }
  else
    visu_ui_rendering_window_setData(VISU_UI_RENDERING_WINDOW(visuVisuUiRenderingWindowGet_current()),
				(VisuData*)0);

  g_idle_add(visu_object_redraw, (gpointer)0);

  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject* visuPyRedraw(PyObject *self _U_)
{
  g_idle_add(visu_object_redraw, (gpointer)0);

  Py_INCREF(Py_None);
  return Py_None;
}

/* We define here all the methods of the module. */
static PyMethodDef ModuleMethods[] =
  {
    {"main", (PyCFunction)visuPyMain, METH_VARARGS | METH_KEYWORDS,
     "Start a V_Sim run and open an empty rendering window."},
    {"load", (PyCFunction)visuPyLoad, METH_VARARGS | METH_KEYWORDS,
     "Load the given data."},
    {"set", (PyCFunction)visuPySet, METH_O,
     "Associate a VisuData to a rendering area."},
    {"redraw", (PyCFunction)visuPyRedraw, METH_NOARGS,
     "Redraw the rendering area."},
    {NULL, NULL, 0, NULL}
  };










/* The name initv_sim is mandatory, do not change it! */
PyMODINIT_FUNC initv_sim(void)
{
  PyObject *module;

  if (PyType_Ready(&VisuDataPyType) < 0)
    return;
  if (PyType_Ready(&VisuElementPyType) < 0)
    return;
  if (PyType_Ready(&VisuNodePyType) < 0)
    return;
  if (PyType_Ready(PLANEPY_TYPE) < 0)
    return;

  module = Py_InitModule("v_sim", ModuleMethods);
  if (module == NULL)
    return;

  Py_INCREF(&VisuDataPyType);
  PyModule_AddObject(module, "data", (PyObject*)&VisuDataPyType);
  Py_INCREF(&VisuElementPyType);
  PyModule_AddObject(module, "element", (PyObject*)&VisuElementPyType);
  Py_INCREF(&VisuNodePyType);
  PyModule_AddObject(module, "node", (PyObject*)&VisuNodePyType);
  Py_INCREF(PLANEPY_TYPE);
  PyModule_AddObject(module, "plane", (PyObject*)PLANEPY_TYPE);

  init_pygobject();
  init_pygtk();

  /* Set the static paths for V_Sim. */
  visuBasicSet_paths((const gchar*)0);

  visu_basic_init();
}
