/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)
  
	Adresse mèl :
	BILLARD, non joignable par 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 : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)

	E-mail address:
	BILLARD, not reachable any more ;
	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 "visu_extension.h"
#include "visu_configFile.h"
#include "openGLFunctions/objectList.h"

#include <stdlib.h>
#include <string.h>
#include <GL/gl.h>

#include "visu_tools.h"

/**
 * SECTION:visu_extension
 * @short_description: All objects drawn by V_Sim are defined in by a
 * #VisuGlExt object
 *
 * <para>All objects that are drawn by V_Sim are handled by a
 * #VisuGlExt object. Such an object has an OpenGL list. This
 * list is only COMPILED. When V_Sim receives the 'OpenGLAskForReDraw'
 * or the 'OpenGLForceReDraw' signals, each list of all known
 * #VisuGlExt are excecuted. This excecution can be canceled if
 * the used flag of the #VisuGlExt object is set to FALSE. The
 * order in which the lists are called depends on the priority of the
 * #VisuGlExt object. This priority is set to
 * #VISU_GL_EXT_PRIORITY_NORMAL as default value, but it can be
 * tune by a call to visu_gl_ext_setPriority(). This priority is
 * an integer, the lower it is, the sooner the list is
 * excecuted.</para>
 *
 * <para>The method registerVisuGlExt() is used to declare to
 * V_Sim that there is a new #VisuGlExt object available. This
 * allows to create extension when V_Sim is already
 * running. Nevertheless, an extension must be initialized in the
 * initialisation process, it is better to add an
 * #initVisuGlExtFunc method in the listInitExtensionFunc array
 * declared in extensions/externalVisuGlExts.h.</para>
 *
 * <para>Once again, the OpenGL list corresponding to an OpenGL
 * extension is COMPILE only. Then, OpenGL methods like glIsenabled()
 * are totally unusefull because it is called when the list is
 * compiled not when the list is called. If the extension needs to
 * alter some OpenGL state, such as desable GL_LIGHTING, it needs to
 * set a flag for the extension. With this flag, V_Sim will save the
 * OpenGL states and restore it when the list is called. Use
 * visu_gl_ext_setSaveState() to set this flag.</para>
 */

#define FLAG_PARAMETER_MODE "extension_render"
#define DESC_PARAMETER_MODE "Rules the way OpenGl draws extensions (see opengl_render); name (string) value (string)"
#define FLAG_RESOURCE_MODE "glExtension_render"
#define DESC_RESOURCE_MODE "Rules the way OpenGl draws extensions (see gl_render); name (string) value (string)"
static guint _rMode;
static void exportRendering(GString *data, VisuData *dataObj);

static void callList(GList *lst, VisuGlRenderingMode *renderingMode,
		     VisuGlRenderingMode globalRenderingMode);

enum
  {
    PROP_0,
    NAME_PROP,
    ACTIVE_PROP,
    LABEL_PROP,
    DESCRIPTION_PROP,
    PRIORITY_PROP,
    SAVE_STATE_PROP,
    DIRTY_PROP,
    NGLOBJ_PROP,
    RMODE_PROP,
    N_PROP
  };
static GParamSpec *properties[N_PROP];

struct _VisuGlExtPrivate
{
  gboolean dispose_has_run;

  /* Some variable to describe this OpenGL extension.
     The attribute name is mandatory since it is
     used to identify the method. */
  gchar *name, *nameI18n;
  gchar *description;

  /* The id of the possible objects list brings by
     the extension is refered by this int. */
  guint nGlObj;
  int objectListId;
  /* Global translation to apply to the list before displaying. */
  float trans[3];

  /* A priority for the extension. */
  guint priority;

  /* If set, V_Sim save the OpenGL state before the list
     id is called and restore all states after. */
  gboolean saveState;

  /* Fine tune of rendering mode (VISU_GL_RENDERING_WIREFRAME, smooth...).
     When FOLLOW, the global value for rendering
     mode is used. Otherwise the value is stored in
     preferedRenderingMode. */
  VisuGlRenderingMode preferedRenderingMode;

  /* A boolean to know if this extension is actually used
     or not. */
  gboolean used;
  
  /* Set to FALSE to skip draw calls. */
  gboolean dirty;

  /* The context with gl options the extension is drawing to. */
  VisuGl *gl;
};

static VisuGlExtClass *my_class = NULL;

static void visu_gl_ext_dispose     (GObject* obj);
static void visu_gl_ext_finalize    (GObject* obj);
static void visu_gl_ext_get_property(GObject* obj, guint property_id,
                                     GValue *value, GParamSpec *pspec);
static void visu_gl_ext_set_property(GObject* obj, guint property_id,
                                     const GValue *value, GParamSpec *pspec);

static void onEntryMode(VisuGlExt *ext, VisuConfigFileEntry *entry, VisuConfigFile *obj);

G_DEFINE_TYPE_WITH_CODE(VisuGlExt, visu_gl_ext, VISU_TYPE_OBJECT,
                        G_ADD_PRIVATE(VisuGlExt))

static void visu_gl_ext_class_init(VisuGlExtClass *klass)
{
  VisuConfigFileEntry *confEntry, *oldEntry;

  DBG_fprintf(stderr, "Visu Extension: creating the class of the object.\n");
  /* DBG_fprintf(stderr, "                - adding new signals ;\n"); */

  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose  = visu_gl_ext_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_gl_ext_finalize;
  G_OBJECT_CLASS(klass)->set_property = visu_gl_ext_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_gl_ext_get_property;

  /**
   * VisuGlExt::name:
   *
   * The name of the extension (used as an id).
   *
   * Since: 3.7
   */
  g_object_class_install_property
    (G_OBJECT_CLASS(klass), NAME_PROP,
     g_param_spec_string("name", "Name", "name (id) of extension", "",
                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
                         G_PARAM_STATIC_STRINGS));
  /**
   * VisuGlExt::active:
   *
   * The extension is used or not.
   *
   * Since: 3.7
   */
  properties[ACTIVE_PROP] = g_param_spec_boolean("active", "Active",
                                                 "extension is used or not", FALSE,
                                                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
  g_object_class_install_property(G_OBJECT_CLASS(klass), ACTIVE_PROP,
                                  properties[ACTIVE_PROP]);
  /**
   * VisuGlExt::label:
   *
   * The label of extension (translated).
   *
   * Since: 3.7
   */
  g_object_class_install_property
    (G_OBJECT_CLASS(klass), LABEL_PROP,
     g_param_spec_string("label", "Label", "label (translated) of extension", "",
                         G_PARAM_CONSTRUCT | G_PARAM_READWRITE |
                         G_PARAM_STATIC_STRINGS));
  /**
   * VisuGlExt::description:
   *
   * The description of the extension.
   *
   * Since: 3.7
   */
  g_object_class_install_property
    (G_OBJECT_CLASS(klass), DESCRIPTION_PROP,
     g_param_spec_string("description", "Description", "description of extension", "",
                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  /**
   * VisuGlExt::priority:
   *
   * The drawing priority of the extension.
   *
   * Since: 3.7
   */
  g_object_class_install_property
    (G_OBJECT_CLASS(klass), PRIORITY_PROP,
     g_param_spec_uint("priority", "Priority", "drawing priority of extension",
                       VISU_GL_EXT_PRIORITY_BACKGROUND,
                       VISU_GL_EXT_PRIORITY_LAST,
                       VISU_GL_EXT_PRIORITY_NORMAL,
                       G_PARAM_CONSTRUCT | G_PARAM_READWRITE |
                       G_PARAM_STATIC_STRINGS));
  /**
   * VisuGlExt::saveState:
   *
   * When set, save the OpenGL state, so the extension can modify
   * OpenGL parameters like light, depth test...
   *
   * Since: 3.7
   */
  g_object_class_install_property
    (G_OBJECT_CLASS(klass), SAVE_STATE_PROP,
     g_param_spec_boolean("saveState", "Save state", "save OpenGL state", FALSE,
                          G_PARAM_CONSTRUCT | G_PARAM_READWRITE |
                          G_PARAM_STATIC_STRINGS));
  /**
   * VisuGlExt::nGlObj:
   *
   * The number of GL list the extension is dealing with.
   *
   * Since: 3.7
   */
  g_object_class_install_property
    (G_OBJECT_CLASS(klass), NGLOBJ_PROP,
     g_param_spec_uint("nGlObj", "N GL objects", "number of GL lists dealt with",
                       1, 2048, 1,
                       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
  /**
   * VisuGlExt::dirty:
   *
   * Rendering properties have changed and the object should be drawn again.
   *
   * Since: 3.8
   */
  properties[DIRTY_PROP] = g_param_spec_boolean("dirty", "Dirty", "object rendering is out of date",
                                                FALSE, G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), DIRTY_PROP, properties[DIRTY_PROP]);
  /**
   * VisuGlExt::rendering-mode:
   *
   * The specific rendering mode of the extension or follow global setting.
   *
   * Since: 3.8
   */
  properties[RMODE_PROP] = g_param_spec_uint("rendering-mode", "Rendering mode",
                                             "specific rendering mode for the extension",
                                             VISU_GL_RENDERING_WIREFRAME,
                                             VISU_GL_RENDERING_FOLLOW,
                                             VISU_GL_RENDERING_FOLLOW,
                                             G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), RMODE_PROP, properties[RMODE_PROP]);
  
  oldEntry = visu_config_file_addEntry(VISU_CONFIG_FILE_PARAMETER,
                                       FLAG_PARAMETER_MODE, DESC_PARAMETER_MODE, 1, NULL);
  visu_config_file_entry_setVersion(oldEntry, 3.4f);
  confEntry = visu_config_file_addEnumEntry(VISU_CONFIG_FILE_RESOURCE,
                                            FLAG_RESOURCE_MODE,
                                            DESC_RESOURCE_MODE,
                                            &_rMode, visu_gl_rendering_getModeFromName, TRUE);
  visu_config_file_entry_setVersion(confEntry, 3.8f);
  visu_config_file_entry_setReplace(confEntry, oldEntry);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_RESOURCE, exportRendering);

  klass->allExtensions = (GList*)0;

  my_class = klass;
}

static void visu_gl_ext_init(VisuGlExt *ext)
{
  DBG_fprintf(stderr, "Visu Extension: initializing a new object (%p).\n",
	      (gpointer)ext);
  ext->priv = visu_gl_ext_get_instance_private(ext);
  ext->priv->dispose_has_run = FALSE;

  /* Set-up all not parameters attributes. */
  ext->priv->used = TRUE;
  ext->priv->dirty = TRUE;
  ext->priv->priority = VISU_GL_EXT_PRIORITY_NORMAL;
  ext->priv->objectListId = 0;
  ext->priv->preferedRenderingMode = VISU_GL_RENDERING_FOLLOW;

  ext->priv->trans[0] = 0.f;
  ext->priv->trans[1] = 0.f;
  ext->priv->trans[2] = 0.f;

  ext->priv->gl = (VisuGl*)0;

  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_MODE,
                          G_CALLBACK(onEntryMode), (gpointer)ext, G_CONNECT_SWAPPED);

  my_class->allExtensions = g_list_append(my_class->allExtensions, (gpointer)ext);
}

/* This method can be called several times.
   It should unref all of its reference to
   GObjects. */
static void visu_gl_ext_dispose(GObject* obj)
{
  VisuGlExt *ext;

  DBG_fprintf(stderr, "Visu Extension: dispose object %p.\n", (gpointer)obj);

  ext = VISU_GL_EXT(obj);
  if (ext->priv->dispose_has_run)
    return;
  ext->priv->dispose_has_run = TRUE;

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_gl_ext_parent_class)->dispose(obj);
}
/* This method is called once only. */
static void visu_gl_ext_finalize(GObject* obj)
{
  VisuGlExtPrivate *ext;

  g_return_if_fail(obj);

  DBG_fprintf(stderr, "Visu Extension: finalize object %p.\n", (gpointer)obj);

  ext = VISU_GL_EXT(obj)->priv;
  if (ext->name)
    g_free(ext->name);
  if (ext->nameI18n)
    g_free(ext->nameI18n);
  if (ext->description)
    g_free(ext->description);
  glDeleteLists(ext->objectListId, ext->nGlObj);

  my_class->allExtensions = g_list_remove_all(my_class->allExtensions, (gpointer)obj);

  /* Chain up to the parent class */
  DBG_fprintf(stderr, "Visu Extension: chain to parent.\n");
  G_OBJECT_CLASS(visu_gl_ext_parent_class)->finalize(obj);
  DBG_fprintf(stderr, "Visu Extension: freeing ... OK.\n");
}
static void visu_gl_ext_get_property(GObject* obj, guint property_id,
                                        GValue *value, GParamSpec *pspec)
{
  VisuGlExtPrivate *self = VISU_GL_EXT(obj)->priv;

  DBG_fprintf(stderr, "Visu Extension: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case NAME_PROP:
      g_value_set_string(value, self->name);
      DBG_fprintf(stderr, "%s.\n", self->name);
      break;
    case LABEL_PROP:
      if (self->nameI18n && self->nameI18n[0])
        g_value_set_string(value, self->nameI18n);
      else
        g_value_set_string(value, self->name);
      DBG_fprintf(stderr, "%s.\n", self->nameI18n);
      break;
    case DESCRIPTION_PROP:
      g_value_set_string(value, self->description);
      DBG_fprintf(stderr, "%s.\n", self->description);
      break;
    case ACTIVE_PROP:
      g_value_set_boolean(value, self->used);
      DBG_fprintf(stderr, "%d.\n", self->used);
      break;
    case SAVE_STATE_PROP:
      g_value_set_boolean(value, self->saveState);
      DBG_fprintf(stderr, "%d.\n", self->saveState);
      break;
    case PRIORITY_PROP:
      g_value_set_uint(value, self->priority);
      DBG_fprintf(stderr, "%d.\n", self->priority);
      break;
    case NGLOBJ_PROP:
      g_value_set_uint(value, self->nGlObj);
      DBG_fprintf(stderr, "%d.\n", self->nGlObj);
      break;
    case DIRTY_PROP:
      g_value_set_boolean(value, self->dirty);
      DBG_fprintf(stderr, "%d.\n", self->dirty);
      break;
    case RMODE_PROP:
      g_value_set_uint(value, self->preferedRenderingMode);
      DBG_fprintf(stderr, "%d.\n", self->preferedRenderingMode);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_gl_ext_set_property(GObject* obj, guint property_id,
                                        const GValue *value, GParamSpec *pspec)
{
  VisuGlExtPrivate *self = VISU_GL_EXT(obj)->priv;

  DBG_fprintf(stderr, "Visu Extension: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case NAME_PROP:
      self->name = g_value_dup_string(value);
      DBG_fprintf(stderr, "%s.\n", self->name);
      break;
    case LABEL_PROP:
      self->nameI18n = g_value_dup_string(value);
      DBG_fprintf(stderr, "%s.\n", self->nameI18n);
      break;
    case DESCRIPTION_PROP:
      self->description = g_value_dup_string(value);
      DBG_fprintf(stderr, "%s.\n", self->description);
      break;
    case ACTIVE_PROP:
      visu_gl_ext_setActive(VISU_GL_EXT(obj), g_value_get_boolean(value));
      DBG_fprintf(stderr, "%d.\n", self->used);
      /* visu_gl_ext_draw(VISU_GL_EXT(obj)); */
      break;
    case SAVE_STATE_PROP:
      self->saveState = g_value_get_boolean(value);
      DBG_fprintf(stderr, "%d.\n", self->saveState);
      break;
    case PRIORITY_PROP:
      self->priority = g_value_get_uint(value);
      DBG_fprintf(stderr, "%d.\n", self->priority);
      break;
    case NGLOBJ_PROP:
      self->nGlObj = g_value_get_uint(value);
      DBG_fprintf(stderr, "%d.\n", self->nGlObj);
      self->objectListId = visu_gl_objectlist_new(self->nGlObj);
      break;
    case DIRTY_PROP:
      DBG_fprintf(stderr, "%d.\n", self->nGlObj);
      visu_gl_ext_setDirty(VISU_GL_EXT(obj), g_value_get_boolean(value));
      break;
    case RMODE_PROP:
      DBG_fprintf(stderr, "%d.\n", g_value_get_uint(value));
      visu_gl_ext_setPreferedRenderingMode(VISU_GL_EXT(obj), g_value_get_uint(value));
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}

/**
 * visu_gl_ext_setDirty:
 * @ext: a #VisuGlExt object.
 * @status: a boolean.
 *
 * Set an internal flag to mark that @ext should be redrawn before
 * next OpenGL frame view.
 *
 * Since: 3.8
 *
 * Returns: TRUE is status is actually changed.
 **/
gboolean visu_gl_ext_setDirty(VisuGlExt *ext, gboolean status)
{
  g_return_val_if_fail(VISU_IS_GL_EXT(ext), FALSE);

  /* It's an exception here, it's VisuGlExtSet that will ignore
     redundant call to dirty status. */
  if (ext->priv->dirty == status)
    return FALSE;

  DBG_fprintf(stderr, "Visu Extension: '%s' becomes dirty (%d).\n", ext->priv->name, status);
  ext->priv->dirty = status;
  if (status)
    g_object_notify_by_pspec(G_OBJECT(ext), properties[DIRTY_PROP]);
  return TRUE;
}

/**
 * visu_gl_ext_getActive:
 * @extension: the extension.
 *
 * Get if the extension is used or not. If not its ObjectList
 * is not rendered.
 *
 * Returns: TRUE if used, FALSE otherwise.
 */
gboolean visu_gl_ext_getActive(VisuGlExt* extension)
{
  g_return_val_if_fail(VISU_IS_GL_EXT(extension), FALSE);

  if (extension)
    return extension->priv->used;
  else
    return FALSE;
}
/**
 * visu_gl_ext_setActive:
 * @extension: the extension,
 * @value: the new value.
 *
 * Set if an extension is actually used or not.
 */
gboolean visu_gl_ext_setActive(VisuGlExt* extension, gboolean value)
{
  g_return_val_if_fail(VISU_IS_GL_EXT(extension), FALSE);

  if (extension->priv->used == value)
    return FALSE;

  DBG_fprintf(stderr, "Visu Extension: '%s' becomes active (%d).\n",
              extension->priv->name, value);
  extension->priv->used = value;
  g_object_notify_by_pspec(G_OBJECT(extension), properties[ACTIVE_PROP]);

  return TRUE;
}
/**
 * visu_gl_ext_getPriority:
 * @extension: a #VisuGlExt object.
 *
 * Inquire the priority of @extension.
 *
 * Since: 3.8
 *
 * Returns: the #VisuGlExt priority.
 **/
guint visu_gl_ext_getPriority(VisuGlExt *extension)
{
  g_return_val_if_fail(VISU_IS_GL_EXT(extension), VISU_GL_EXT_PRIORITY_BACKGROUND);

  return extension->priv->priority;
}

/**
 * visu_gl_ext_setPreferedRenderingMode:
 * @extension: a #VisuGlExt object ;
 * @value: see #VisuGlRenderingMode to choose one.
 *
 * This method is used to specify the rendering mode that the extension should use
 * to be drawn. If the @value is set to #VISU_GL_RENDERING_FOLLOW, the
 * extension follows the global setting for rendering mode.
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_ext_setPreferedRenderingMode(VisuGlExt* extension,
                                              VisuGlRenderingMode value)
{
  g_return_val_if_fail(VISU_IS_GL_EXT(extension), FALSE);
  g_return_val_if_fail(value < VISU_GL_RENDERING_N_MODES ||
		       value == VISU_GL_RENDERING_FOLLOW, FALSE);

  if (extension->priv->preferedRenderingMode == value)
    return FALSE;

  extension->priv->preferedRenderingMode = value;
  g_object_notify_by_pspec(G_OBJECT(extension), properties[RMODE_PROP]);

  return TRUE;
}
/**
 * visu_gl_ext_getPreferedRenderingMode:
 * @extension: a #VisuGlExt method.
 *
 * Each #VisuGlExt method can draw in a mode different from the
 * global one, see #VisuGlRenderingMode. See also
 * visu_gl_ext_setPreferedRenderingMode().
 *
 * Since: 3.7
 *
 * Returns: the prefered rendering mode of this @extension.
 **/
VisuGlRenderingMode visu_gl_ext_getPreferedRenderingMode(VisuGlExt* extension)
{
  g_return_val_if_fail(VISU_IS_GL_EXT(extension), VISU_GL_RENDERING_FOLLOW);
  return extension->priv->preferedRenderingMode;
}
/**
 * visu_gl_ext_getGlList:
 * @extension: a #VisuGlExt method.
 *
 * All #VisuGlExt objects have a master OpenGL list to draw
 * to. This routine gets the identifier of this list.
 *
 * Since: 3.7
 *
 * Returns: an OpenGL identifier id for @extension.
 **/
guint visu_gl_ext_getGlList(VisuGlExt *extension)
{
  g_return_val_if_fail(VISU_IS_GL_EXT(extension), 0);
  return extension->priv->objectListId;
}
/**
 * visu_gl_ext_setTranslation:
 * @extension: a #VisuGlExt method.
 * @trans: (array fixed-size=3): a translation vector in real space.
 *
 * Change the translation the extension is drawn at.
 *
 * Since: 3.8
 *
 * Returns: TRUE if the translations are indeed changed.
 **/
gboolean visu_gl_ext_setTranslation(VisuGlExt *extension, const gfloat trans[3])
{
  g_return_val_if_fail(VISU_IS_GL_EXT(extension), FALSE);

  if (extension->priv->trans[0] == trans[0] &&
      extension->priv->trans[1] == trans[1] &&
      extension->priv->trans[2] == trans[2])
    return FALSE;

  extension->priv->trans[0] = trans[0];
  extension->priv->trans[1] = trans[1];
  extension->priv->trans[2] = trans[2];

  return TRUE;
}

/********************/
/* Class functions. */
/********************/

/**
 * visu_gl_ext_call:
 * @extension: a #VisuGlExt object.
 * @lastOnly: a boolean.
 *
 * Select the #VisuGlExt matching the given @name and call
 * it. The call is indeed done only if the extension is used. If
 * @lastOnly is TRUE, the list is called only if it has a
 * #VISU_GL_EXT_PRIORITY_LAST priority. On the contrary the list
 * is called only if its priority is lower than
 * #VISU_GL_EXT_PRIORITY_LAST.
 */
void visu_gl_ext_call(VisuGlExt *ext, gboolean lastOnly)
{
  VisuGlRenderingMode renderingMode, globalRenderingMode;
  GList lst;

  g_return_if_fail(VISU_IS_GL_EXT(ext));
  /* DBG_fprintf(stderr, "Visu Extension: call '%s' list.\n", ext->priv->name); */
  /* DBG_fprintf(stderr, "|  has %d ref counts.\n", G_OBJECT(ext)->ref_count); */

  if (!ext->priv->used || ext->priv->objectListId < 1000)
    return;

  if ((lastOnly && ext->priv->priority == VISU_GL_EXT_PRIORITY_LAST) ||
      (!lastOnly && ext->priv->priority < VISU_GL_EXT_PRIORITY_LAST))
    {
      /* Rebuild it if necessary. */
      if (VISU_GL_EXT_GET_CLASS(ext)->draw && ext->priv->dirty)
        VISU_GL_EXT_GET_CLASS(ext)->draw(ext);
      globalRenderingMode = visu_gl_getMode(ext->priv->gl);
      renderingMode = globalRenderingMode;
      lst.data = (gpointer)ext;
      lst.next = lst.prev = (GList*)0;
      glTranslatef(ext->priv->trans[0], ext->priv->trans[1], ext->priv->trans[2]);
      callList(&lst, &renderingMode, globalRenderingMode);
      glTranslatef(-ext->priv->trans[0], -ext->priv->trans[1], -ext->priv->trans[2]);
      if (renderingMode != globalRenderingMode)
	/* Return the rendering mode to normal. */
	visu_gl_rendering_applyMode(globalRenderingMode);
    }
}
/**
 * visu_gl_ext_rebuild:
 * @self: a #VisuGlExt object.
 *
 * This routine does not sort the extension on their priority and
 * should be used only to draw some selected extensions. This method
 * is called automatically for all extensions in a #VisuGlExtSet when required.
 */
void visu_gl_ext_rebuild(VisuGlExt *self)
{
  g_return_if_fail(VISU_IS_GL_EXT(self));

  DBG_fprintf(stderr, "Visu Extension: rebuilding '%s' list.\n", self->priv->name);

  if (self->priv->used && VISU_GL_EXT_GET_CLASS(self)->rebuild)
    VISU_GL_EXT_GET_CLASS(self)->rebuild(self);
}
/**
 * visu_gl_ext_setGlView:
 * @ext: a #VisuGlExt object.
 * @view: a #VisuGlView object.
 *
 * Attach a @view to the @self extension.
 *
 * Since: 3.8
 *
 * Returns: TRUE if the @view is changed.
 */
gboolean visu_gl_ext_setGlView(VisuGlExt *ext, VisuGlView *view)
{
  g_return_val_if_fail(VISU_IS_GL_EXT(ext), FALSE);

  DBG_fprintf(stderr, "Visu Extension: set view %p to '%s' (%d).\n",
              (gpointer)view, ext->priv->name, ext->priv->used);

  return (VISU_GL_EXT_GET_CLASS(ext)->setGlView &&
          VISU_GL_EXT_GET_CLASS(ext)->setGlView(ext, view));
}

/**
 * visu_gl_ext_getGlContext:
 * @extension: a #VisuGlExt object.
 *
 * The #VisuGl object this extension draws to.
 *
 * Since: 3.8
 *
 * Returns: (transfer none) (allow-none): the #VisuGl object this
 * extension draws to.
 **/
VisuGl* visu_gl_ext_getGlContext(VisuGlExt* extension)
{
  g_return_val_if_fail(VISU_IS_GL_EXT(extension), (VisuGl*)0);
  return extension->priv->gl;
}
/**
 * visu_gl_ext_setGlContext:
 * @extension: a #VisuGlExt object.
 * @gl: (transfer none): a #VisuGl object.
 *
 * Associate @gl to @extension.
 *
 * Since: 3.8
 *
 * Returns: TRUE if the value is actually changed.
 **/
gboolean visu_gl_ext_setGlContext(VisuGlExt* extension, VisuGl *gl)
{
  g_return_val_if_fail(VISU_IS_GL_EXT(extension), FALSE);
  if (extension->priv->gl == gl)
    return FALSE;
  extension->priv->gl = gl;
  return TRUE;
}

/**
 * visu_gl_ext_getName:
 * @extension: a #VisuGlExt object.
 *
 * Retrieve the name of the extension.
 *
 * Since: 3.8
 *
 * Returns: the name of the extension.
 **/
const gchar* visu_gl_ext_getName(const VisuGlExt *extension)
{
  g_return_val_if_fail(VISU_IS_GL_EXT(extension), (const gchar*)0);
  
  return extension->priv->name;
}

/**
 * visu_gl_ext_startDrawing:
 * @extension: a #VisuGlExt object.
 *
 * Method to be called in the draw function to start compiling the
 * list. Wghen drawing primitives are all done, call
 * visu_gl_ext_completeDrawing().
 *
 * Since: 3.8
 **/
void visu_gl_ext_startDrawing(VisuGlExt *extension)
{
  g_return_if_fail(VISU_IS_GL_EXT(extension));
  
  glNewList(extension->priv->objectListId, GL_COMPILE);
}
/**
 * visu_gl_ext_completeDrawing:
 * @extension: a #VisuGlExt object.
 *
 * A method to be called after calling visu_gl_ext_startDrawing(),
 * when the drawing primitives have all been called. It also reset the
 * dirty flag for this extension.
 *
 * Since: 3.8
 **/
void visu_gl_ext_completeDrawing(VisuGlExt *extension)
{
  g_return_if_fail(VISU_IS_GL_EXT(extension));
  
  visu_gl_ext_setDirty(extension, FALSE);
  glEndList();
}

/****************/
/* Private area */
/****************/
static void callList(GList *lst, VisuGlRenderingMode *renderingMode,
		     VisuGlRenderingMode globalRenderingMode)
{
  VisuGlExt *ext;
  GTimer *timer;
  gulong fractionTimer;

#if DEBUG == 1
  timer = g_timer_new();
  g_timer_start(timer);
#endif

  for (; lst; lst = g_list_next(lst))
    {
      ext = (VisuGlExt*)lst->data;
      /* The extension needs its own rendering mode. */
      if (ext->priv->preferedRenderingMode < VISU_GL_RENDERING_N_MODES)
	{
	  if (ext->priv->preferedRenderingMode != *renderingMode)
	    {
	      visu_gl_rendering_applyMode(ext->priv->preferedRenderingMode);
	      *renderingMode = ext->priv->preferedRenderingMode;
	    }
	}
      else
	{
	  if (*renderingMode != globalRenderingMode)
	    {
	      visu_gl_rendering_applyMode(globalRenderingMode);
	      *renderingMode = globalRenderingMode;
	    }
	}
      /* Save OpenGL state if necessary. */
      if (ext->priv->saveState)
	{
	  DBG_fprintf(stderr, "Visu Extension: save state.\n");
	  glPushAttrib(GL_ENABLE_BIT);
	}

      if (ext->priv->preferedRenderingMode < VISU_GL_RENDERING_N_MODES &&
          *renderingMode == VISU_GL_RENDERING_SMOOTH_AND_EDGE)
        {
          glPushAttrib(GL_ENABLE_BIT);
          glEnable(GL_POLYGON_OFFSET_FILL);
          glPolygonOffset(1.0, 1.0);
        }

      /* Call the compiled list. */
      DBG_fprintf(stderr, "Visu Extension: call list %d (%s %d)",
                  ext->priv->objectListId, ext->priv->name, ext->priv->used);
      glCallList(ext->priv->objectListId);
      DBG_fprintf(stderr, " at %g micro-s",
                  g_timer_elapsed(timer, &fractionTimer)*1e6);
      DBG_fprintf(stderr, ".\n");

      /* Add a wireframe draw if renderingMode is VISU_GL_RENDERING_SMOOTH_AND_EDGE. */
      if (ext->priv->preferedRenderingMode < VISU_GL_RENDERING_N_MODES &&
          *renderingMode == VISU_GL_RENDERING_SMOOTH_AND_EDGE)
        {
          glDisable(GL_POLYGON_OFFSET_FILL);
          glDisable(GL_LIGHTING);
          glColor3f (0.0, 0.0, 0.0);
          glLineWidth(1);
          glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
          glCallList(ext->priv->objectListId);
          glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
          glPopAttrib();
        }

      if (ext->priv->saveState)
	{
	  DBG_fprintf(stderr, "Visu Extension: restore state.\n");
	  glPopAttrib();
	}
    }

#if DEBUG == 1
  g_timer_stop(timer);
  g_timer_destroy(timer);
#endif
}

static void onEntryMode(VisuGlExt *ext, VisuConfigFileEntry *entry, VisuConfigFile *obj _U_)
{
  if (strcmp(ext->priv->name, visu_config_file_entry_getLabel(entry)))
    return;

  visu_gl_ext_setPreferedRenderingMode(ext, _rMode);
}
static void exportRendering(GString *data, VisuData *dataObj _U_)
{
  GList *tmp;
  VisuGlExt *ext;
  const char **names;

  if (!my_class)
    g_type_class_ref(VISU_TYPE_GL_EXT);

  visu_config_file_exportComment(data, DESC_RESOURCE_MODE);

  names = visu_gl_rendering_getAllModes();
  for (tmp = my_class->allExtensions; tmp; tmp = g_list_next(tmp))
    {
      ext = (VisuGlExt*)tmp->data;
      if (ext->priv->preferedRenderingMode < VISU_GL_RENDERING_N_MODES)
        visu_config_file_exportEntry(data, FLAG_RESOURCE_MODE, ext->priv->name,
                                     "%s", names[ext->priv->preferedRenderingMode]);
    }
  visu_config_file_exportComment(data, "");
}
