/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Damien
	CALISTE, laboratoire L_Sim, (2016)
  
	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, (2016)

	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 "elementRenderer.h"

#include <string.h>

#include <visu_configFile.h>
#include <opengl.h>

#define FLAG_ELEMENT_COLOR      "element_color"
#define DESC_ELEMENT_COLOR      "Codes the main color in RedGreenBlueAlpha format" \
  "and the light effects on material, nine floats between 0. and 1."
static gfloat _elementColor[9];

static ToolColor *_defaultColor;
static gfloat _defaultMaterial[TOOL_MATERIAL_N_VALUES] = {0.25f, 0.25f, 0.25f, 0.25f, 0.25f};

/**
 * SECTION:elementRenderer
 * @short_description: a virtual class to render #VisuElement.
 *
 * <para>This class is virtual and doesn't provide any rendering
 * method itself. It just gathers the basics generic to all
 * #VisuElement rendering classes. It provides in addition two
 * properties, the color and the material.</para>
 * <para>visu_element_renderer_getFromPool() is a specific function to
 * associate a unique #VisuElementRenderer to a given #VisuElement.</para>
 */

/**
 * VisuElementRendererClass:
 * @parent: its parent.
 * @compile: a virtual method to compile an OpenGL list representing
 * the shape for a given #VisuElement.
 * @call: a virtual method to call the OpenGL list used for elements.
 * @callAt: a virtual method to position and draw a specific #VisuNode.
 * @getExtent: a virtual method to get the size of a given #VisuElement.
 *
 * Interface for class that can represent #VisuElementRenderer.
 *
 * Since: 3.8
 */

/**
 * VisuElementRenderer:
 *
 * Structure used to define #VisuElementRenderer objects.
 *
 * Since: 3.8
 */
struct _VisuElementRendererPrivate
{
  gboolean dispose_has_run;

  VisuElement *element;

  VisuGlView *view;
  gulong precId;

  ToolColor color;
  gfloat material[TOOL_MATERIAL_N_VALUES];
};

enum {
  PROP_0,
  PROP_ELEMENT,
  PROP_RENDERED,
  PROP_MASKABLE,
  PROP_COLOR,
  PROP_MATERIAL,
  PROP_CACHE_MATERIAL,
  N_PROPS
};
static GParamSpec *_properties[N_PROPS];

enum {
  ELEMENT_SIZE_CHANGED,
  LAST_SIGNAL
};
static guint _signals[LAST_SIGNAL];

static GList *_pool = NULL;

static void visu_element_renderer_dispose(GObject* obj);
static void visu_element_renderer_get_property(GObject* obj, guint property_id,
                                               GValue *value, GParamSpec *pspec);
static void visu_element_renderer_set_property(GObject* obj, guint property_id,
                                               const GValue *value, GParamSpec *pspec);
static void notifyRendered(VisuElementRenderer *renderer);
static void notifyMaskable(VisuElementRenderer *renderer);

static void exportRenderer(GString *data, VisuData* dataObj);
static void onEntryColor(VisuConfigFile *obj, VisuConfigFileEntry *entry, gpointer data);

G_DEFINE_TYPE_WITH_CODE(VisuElementRenderer, visu_element_renderer, VISU_TYPE_OBJECT,
                        G_ADD_PRIVATE(VisuElementRenderer))

static void visu_element_renderer_class_init(VisuElementRendererClass *klass)
{
  VisuConfigFileEntry *resourceEntry, *oldEntry;
  float rgColor[2] = {0.f, 1.f};
  gfloat rgba[4] = {1.f, 1.f, 1.f, 1.f};

  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose  = visu_element_renderer_dispose;
  G_OBJECT_CLASS(klass)->set_property = visu_element_renderer_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_element_renderer_get_property;

  /**
   * VisuElementRenderer::size-changed:
   * @obj: the object emitting the signal.
   * @size: the new size.
   *
   * Emitted when the size of a element is changed.
   *
   * Since: 3.8
   */
  _signals[ELEMENT_SIZE_CHANGED] = 
    g_signal_new("size-changed", G_TYPE_FROM_CLASS (klass),
		   G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
		   0, NULL /* accumulator */, NULL /* accu_data */,
		   g_cclosure_marshal_VOID__FLOAT,
		   G_TYPE_NONE, 1, G_TYPE_FLOAT);

  /**
   * VisuElementRenderer::element:
   *
   * The element the renderer refers to.
   *
   * Since: 3.8
   */
  _properties[PROP_ELEMENT] =
    g_param_spec_object("element", "Element", "element",
                        VISU_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
  /**
   * VisuElementRenderer::rendered:
   *
   * If element is rendered or not.
   *
   * Since: 3.8
   */
  _properties[PROP_RENDERED] =
    g_param_spec_boolean("rendered", "Rendered", "if element is rendered",
                         TRUE, G_PARAM_READWRITE);
  /**
   * VisuElementRenderer::maskable:
   *
   * If element is maskable or not.
   *
   * Since: 3.8
   */
  _properties[PROP_MASKABLE] =
    g_param_spec_boolean("maskable", "Maskable", "if element is maskable",
                         TRUE, G_PARAM_READWRITE);
  /**
   * VisuElementRenderer::color:
   *
   * The element color.
   *
   * Since: 3.8
   */
  _properties[PROP_COLOR] =
    g_param_spec_boxed("color", "Color",
                       "element color", TOOL_TYPE_COLOR, G_PARAM_READWRITE);
  /**
   * VisuElementRenderer::material:
   *
   * The element material properties.
   *
   * Since: 3.8
   */
  _properties[PROP_MATERIAL] =
    g_param_spec_boxed("material", "Material",
                       "element material", TOOL_TYPE_MATERIAL, G_PARAM_READWRITE);
  /**
   * VisuElementRenderer::cache-material:
   *
   * The element can cache material properties.
   *
   * Since: 3.8
   */
  _properties[PROP_CACHE_MATERIAL] =
    g_param_spec_boolean("cache-material", "Cache material",
                         "can put material in cache.", TRUE, G_PARAM_READABLE);

  g_object_class_install_properties(G_OBJECT_CLASS(klass), N_PROPS, _properties);

  oldEntry = visu_config_file_addEntry(VISU_CONFIG_FILE_RESOURCE,
                                       "material",
                                       "Obsolete entry for element_color",
                                       1, NULL);
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                      FLAG_ELEMENT_COLOR,
                                                      DESC_ELEMENT_COLOR,
                                                      9, _elementColor, rgColor, TRUE);
  visu_config_file_entry_setVersion(resourceEntry, 3.4f);
  visu_config_file_entry_setReplace(resourceEntry, oldEntry);
  g_signal_connect(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_ELEMENT_COLOR,
                   G_CALLBACK(onEntryColor), (gpointer)0);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_RESOURCE, exportRenderer);

  _defaultColor = tool_color_addFloatRGBA(rgba, NULL);
}
static void visu_element_renderer_init(VisuElementRenderer *obj)
{
  DBG_fprintf(stderr, "Visu Pair Renderer: initializing a new object (%p).\n",
	      (gpointer)obj);
  
  obj->priv = visu_element_renderer_get_instance_private(obj);
  obj->priv->dispose_has_run = FALSE;

  obj->priv->view = (VisuGlView*)0;

  /* Private data. */
  tool_color_copy(&obj->priv->color, _defaultColor);
  memcpy(&obj->priv->material, _defaultMaterial, sizeof(gfloat) * TOOL_MATERIAL_N_VALUES);
}
static void visu_element_renderer_dispose(GObject* obj)
{
  VisuElementRenderer *data;

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

  data = VISU_ELEMENT_RENDERER(obj);
  if (data->priv->dispose_has_run)
    return;
  data->priv->dispose_has_run = TRUE;

  g_object_unref(data->priv->element);
  visu_element_renderer_setGlView(data, (VisuGlView*)0);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_element_renderer_parent_class)->dispose(obj);
}
static void visu_element_renderer_get_property(GObject* obj, guint property_id,
                                             GValue *value, GParamSpec *pspec)
{
  VisuElementRenderer *self = VISU_ELEMENT_RENDERER(obj);

  DBG_fprintf(stderr, "Visu Element Renderer: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case PROP_ELEMENT:
      g_value_set_object(value, self->priv->element);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->element);
      break;
    case PROP_RENDERED:
      g_value_set_boolean(value, visu_element_getRendered(self->priv->element));
      DBG_fprintf(stderr, "%d.\n", visu_element_getRendered(self->priv->element));
      break;
    case PROP_MASKABLE:
      g_value_set_boolean(value, visu_element_getMaskable(self->priv->element));
      DBG_fprintf(stderr, "%d.\n", visu_element_getMaskable(self->priv->element));
      break;
    case PROP_COLOR:
      g_value_set_boxed(value, &self->priv->color);
      DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->color.rgba[0], self->priv->color.rgba[1], self->priv->color.rgba[2]);
      break;
    case PROP_MATERIAL:
      g_value_set_boxed(value, &self->priv->material);
      DBG_fprintf(stderr, "%gx%gx%gx%gx%g.\n", self->priv->material[0], self->priv->material[1], self->priv->material[2], self->priv->material[3], self->priv->material[4]);
      break;
    case PROP_CACHE_MATERIAL:
      g_value_set_boolean(value, TRUE);
      DBG_fprintf(stderr, "%d.\n", TRUE);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_element_renderer_set_property(GObject* obj, guint property_id,
                                             const GValue *value, GParamSpec *pspec)
{
  VisuElementRenderer *self = VISU_ELEMENT_RENDERER(obj);

  DBG_fprintf(stderr, "Visu Element Renderer: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case PROP_ELEMENT:
      DBG_fprintf(stderr, "%p.\n", g_value_get_object(value));
      self->priv->element = VISU_ELEMENT(g_value_dup_object(value));
      g_signal_connect_swapped(self->priv->element, "notify::rendered",
                               G_CALLBACK(notifyRendered), obj);
      g_signal_connect_swapped(self->priv->element, "notify::maskable",
                               G_CALLBACK(notifyMaskable), obj);
      break;
    case PROP_RENDERED:
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      if (visu_element_setRendered(self->priv->element, g_value_get_boolean(value)))
        g_object_notify(obj, "rendered");
      break;
    case PROP_MASKABLE:
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      if (visu_element_setMaskable(self->priv->element, g_value_get_boolean(value)))
        g_object_notify(obj, "maskable");
      break;
    case PROP_COLOR:
      visu_element_renderer_setColor(VISU_ELEMENT_RENDERER(self),
                                     (ToolColor*)g_value_get_boxed(value));
      DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->color.rgba[0], self->priv->color.rgba[1], self->priv->color.rgba[2]);
      break;
    case PROP_MATERIAL:
      visu_element_renderer_setMaterial(VISU_ELEMENT_RENDERER(self),
                                        (gfloat*)g_value_get_boxed(value));
      DBG_fprintf(stderr, "%gx%gx%gx%gx%g.\n", self->priv->material[0], self->priv->material[1], self->priv->material[2], self->priv->material[3], self->priv->material[4]);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}

/**
 * visu_element_renderer_getFromPool:
 * @element: a #VisuElement object.
 *
 * Retrieve a #VisuElementRenderer representing @element. This
 * #VisuElementRenderer is unique.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuElementRenderer for @element.
 **/
VisuElementRenderer* visu_element_renderer_getFromPool(VisuElement *element)
{
  GList *lst;

  for (lst = _pool; lst; lst = g_list_next(lst))
    if (visu_element_renderer_getElement(VISU_ELEMENT_RENDERER(lst->data)) == element)
      return VISU_ELEMENT_RENDERER(lst->data);
  
  _pool = g_list_prepend(_pool, g_object_new(VISU_TYPE_ELEMENT_RENDERER, "element", element, NULL));
  return VISU_ELEMENT_RENDERER(_pool->data);
}
/**
 * visu_element_renderer_bindToPool:
 * @element: a #VisuElementRenderer object.
 *
 * Bind @element to the corresponding #VisuElementRenderer of the
 * pool. This allows to have #VisuElement renderers that follow the
 * same color or maskable properties for instance of a given #VisuElement.
 *
 * Since: 3.8
 **/
void visu_element_renderer_bindToPool(VisuElementRenderer *element)
{
  VisuElementRenderer *pool;

  g_return_if_fail(VISU_IS_ELEMENT_RENDERER(element));

  pool = visu_element_renderer_getFromPool(element->priv->element);

  g_object_bind_property(pool, "rendered", element, "rendered",
                         G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
  g_object_bind_property(pool, "maskable", element, "maskable",
                         G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
  g_object_bind_property(pool, "color", element, "color",
                         G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
  g_object_bind_property(pool, "material", element, "material",
                         G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
}
/**
 * visu_element_renderer_pool_finalize: (skip)
 *
 * Destroy the list of known #VisuElementRenderer, see
 * visu_element_renderer_getFromPool().
 *
 * Since: 3.8
 **/
void visu_element_renderer_pool_finalize(void)
{
  g_list_free_full(_pool, (GDestroyNotify)g_object_unref);
  _pool = (GList*)0;
}

/**
 * visu_element_renderer_getExtent:
 * @element: a #VisuElementRenderer object.
 *
 * Retrives the radius of a sphere containing the representation of @element.
 *
 * Since: 3.8
 *
 * Returns: a positive float value.
 **/
gfloat visu_element_renderer_getExtent(const VisuElementRenderer *element)
{
  return VISU_ELEMENT_RENDERER_GET_CLASS(element)->getExtent(element);
}
/**
 * visu_element_renderer_getColor:
 * @element: a #VisuElementRenderer object.
 *
 * Retrieve the #ToolColor used by @element to represent a #VisuElement.
 *
 * Since: 3.8
 *
 * Returns: (transfer none) (allow-none): a #ToolColor or %NULL.
 **/
const ToolColor* visu_element_renderer_getColor(const VisuElementRenderer *element)
{
  g_return_val_if_fail(VISU_IS_ELEMENT_RENDERER(element), (const ToolColor*)0);

  return &VISU_ELEMENT_RENDERER(element)->priv->color;
}
/**
 * visu_element_renderer_setColor:
 * @ele: a #VisuElementRenderer object.
 * @color: a #ToolColor object.
 *
 * Changes the representation of @ele to use @color.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_element_renderer_setColor(VisuElementRenderer* ele,
                                        const ToolColor *color)
{
  g_return_val_if_fail(VISU_IS_ELEMENT_RENDERER(ele), FALSE);

  if (tool_color_equal(&VISU_ELEMENT_RENDERER(ele)->priv->color, color))
    return FALSE;

  tool_color_copy(&VISU_ELEMENT_RENDERER(ele)->priv->color, color);
  g_object_notify_by_pspec(G_OBJECT(ele), _properties[PROP_COLOR]);
  return TRUE;
}
/**
 * visu_element_renderer_setRGBAValue:
 * @ele: a #VisuElementRenderer object.
 * @value: a float vlaue in [0;1].
 * @id: an value in [0;3].
 *
 * Change one of the RGBA channel of the representation of @ele.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_element_renderer_setRGBAValue(VisuElementRenderer* ele, gfloat value, guint id)
{
  ToolColor *color;
  gboolean res;

  g_return_val_if_fail(id < 4, FALSE);

  color = g_boxed_copy(TOOL_TYPE_COLOR, visu_element_renderer_getColor(ele));
  color->rgba[id] = CLAMP(value, 0.f, 1.f);
  res = visu_element_renderer_setColor(ele, color);
  g_boxed_free(TOOL_TYPE_COLOR, color);
  return res;
}

/**
 * visu_element_renderer_getMaterial:
 * @element: a #VisuElementRenderer object.
 *
 * Retrieve the #ToolMaterial used by @element to represent a #VisuElement.
 *
 * Since: 3.8
 *
 * Returns: (transfer none) (allow-none): a #ToolMaterial or %NULL.
 **/
const gfloat* visu_element_renderer_getMaterial(const VisuElementRenderer *element)
{
  g_return_val_if_fail(VISU_IS_ELEMENT_RENDERER(element), (const gfloat*)0);

  return VISU_ELEMENT_RENDERER(element)->priv->material;
}
/**
 * visu_element_renderer_setMaterial:
 * @ele: a #VisuElementRenderer object.
 * @material: (type ToolMaterial): an array of float values in [0;1].
 *
 * Changes all the material channel of @ele.
 *
 * Since: 3.8
 *
 * Returns: TRUE if any value is actually changed.
 **/
gboolean visu_element_renderer_setMaterial(VisuElementRenderer* ele,
                                           const gfloat material[TOOL_MATERIAL_N_VALUES])
{
  g_return_val_if_fail(VISU_IS_ELEMENT_RENDERER(ele), FALSE);

  if (VISU_ELEMENT_RENDERER(ele)->priv->material[0] == material[0] &&
      VISU_ELEMENT_RENDERER(ele)->priv->material[1] == material[1] &&
      VISU_ELEMENT_RENDERER(ele)->priv->material[2] == material[2] &&
      VISU_ELEMENT_RENDERER(ele)->priv->material[3] == material[3] &&
      VISU_ELEMENT_RENDERER(ele)->priv->material[4] == material[4])
    return FALSE;

  memcpy(VISU_ELEMENT_RENDERER(ele)->priv->material, material,
         sizeof(gfloat) * TOOL_MATERIAL_N_VALUES);
  g_object_notify_by_pspec(G_OBJECT(ele), _properties[PROP_MATERIAL]);
  return TRUE;
}
/**
 * visu_element_renderer_setMaterialValue:
 * @ele: a #VisuElementRenderer object.
 * @value: a float in [0;1].
 * @id: a #ToolMaterialIds value.
 *
 * Changes the material channel @id with @value.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_element_renderer_setMaterialValue(VisuElementRenderer* ele,
                                                gfloat value, ToolMaterialIds id)
{
  gfloat *material;
  gboolean res;

  material = g_boxed_copy(TOOL_TYPE_MATERIAL, visu_element_renderer_getMaterial(ele));
  material[id] = CLAMP(value, 0.f, 1.f);
  res = visu_element_renderer_setMaterial(ele, material);
  g_boxed_free(TOOL_TYPE_MATERIAL, material);
  return res;
}

/**
 * visu_element_renderer_colorize:
 * @element: a #VisuElementRenderer object.
 * @effect: a #VisuElementRendererEffects value.
 *
 * Change the current OpenGL color, according to the color of
 * @element. An additional @effect can be added.
 *
 * Since: 3.8
 **/
void visu_element_renderer_colorize(const VisuElementRenderer *element,
                                    VisuElementRendererEffects effect)
{
  const ToolColor *color;
  const gfloat *material;
  float rgba[4], hsl[3], mat[TOOL_MATERIAL_N_VALUES];
  float hmaterial[5] = {1.f, 1.f, 1.f, 0.f, 0.f};

  DBG_fprintf(stderr, "Visu Element Renderer: colorize with effect %d.\n",
              effect);
  color = visu_element_renderer_getColor(element);
  g_return_if_fail(color);
  material = visu_element_renderer_getMaterial(element);
  if (effect == VISU_ELEMENT_RENDERER_FLATTEN ||
      effect == VISU_ELEMENT_RENDERER_FLATTEN_DARK ||
      effect == VISU_ELEMENT_RENDERER_FLATTEN_LIGHT)
    {
      switch (effect)
        {
        case (VISU_ELEMENT_RENDERER_FLATTEN_DARK):  mat[TOOL_MATERIAL_AMB] = .2f; break;
        case (VISU_ELEMENT_RENDERER_FLATTEN):       mat[TOOL_MATERIAL_AMB] = .75f; break;
        case (VISU_ELEMENT_RENDERER_FLATTEN_LIGHT): mat[TOOL_MATERIAL_AMB] = 1.f; break;
        default: break;
        }
      mat[TOOL_MATERIAL_DIF] = 0.f;
      mat[TOOL_MATERIAL_SHI] = 0.f;
      mat[TOOL_MATERIAL_SPE] = 0.f;
      mat[TOOL_MATERIAL_EMI] = 0.f;
      visu_gl_setColor((VisuGl*)0, mat, color->rgba);
    }
  else if (effect == VISU_ELEMENT_RENDERER_INVERT)
    {
      tool_color_invertRGBA(rgba, color->rgba);
      visu_gl_setColor((VisuGl*)0, material, rgba);
    }
  else if (effect == VISU_ELEMENT_RENDERER_HIGHLIGHT)
    {
      visu_gl_setHighlightColor((VisuGl*)0, material, color->rgba, color->rgba[3]);
    }
  else if (effect == VISU_ELEMENT_RENDERER_HIGHLIGHT_SEMI)
    {
      visu_gl_setHighlightColor((VisuGl*)0, hmaterial, color->rgba, 0.5f);
    }
  else if (effect != VISU_ELEMENT_RENDERER_NO_EFFECT)
    {
      tool_color_convertRGBtoHSL(hsl, color->rgba);
      switch (effect)
        {
        case (VISU_ELEMENT_RENDERER_DESATURATE): hsl[1] = 0.f; break;
        case (VISU_ELEMENT_RENDERER_SATURATE):   hsl[1] = 1.f; break;
        case (VISU_ELEMENT_RENDERER_DARKEN):     hsl[2] -= 0.2f; break;
        case (VISU_ELEMENT_RENDERER_LIGHTEN):    hsl[2] += 0.2f; break;
        default: break;
        }
      tool_color_convertHSLtoRGB(rgba, hsl);
      rgba[3] = color->rgba[3];
      visu_gl_setColor((VisuGl*)0, material, rgba);
    }
  else
    {
      visu_gl_setColor((VisuGl*)0, material, color->rgba);
    }
}

/**
 * visu_element_renderer_setGlView:
 * @element: a #VisuElementRenderer object.
 * @view: a #VisuGlView object.
 *
 * Associates @view to @element, so any changes to the rendering
 * precision happening in @view can imply a rebuild of the
 * representation of @element.
 *
 * Since: 3.8
 **/
void visu_element_renderer_setGlView(VisuElementRenderer *element, VisuGlView *view)
{
  g_return_if_fail(VISU_IS_ELEMENT_RENDERER(element));

  if (element->priv->view == view)
    return;

  if (element->priv->view)
    {
      g_signal_handler_disconnect(element->priv->view, element->priv->precId);
      g_object_unref(element->priv->view);
    }
  element->priv->view = view;
  if (view)
    {
      g_object_ref(view);
      element->priv->precId =
	g_signal_connect_swapped(view, "DetailLevelChanged",
                                 G_CALLBACK(visu_element_renderer_rebuild), (gpointer)element);
    }
  visu_element_renderer_rebuild(element, view);
}
/**
 * visu_element_renderer_getConstGlView:
 * @element: a #VisuElementRenderer object.
 *
 * Retrieves the #VisuGlView associated to @element, see
 * visu_element_renderer_setGlView().
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a const #VisuGlView object.
 **/
const VisuGlView* visu_element_renderer_getConstGlView(const VisuElementRenderer *element)
{
  g_return_val_if_fail(VISU_IS_ELEMENT_RENDERER(element), (const VisuGlView*)0);

  return element->priv->view;
}

/**
 * visu_element_renderer_call:
 * @element: a #VisuElementRenderer object.
 *
 * A convenience function to run the call method of @element.
 *
 * Since: 3.8
 **/
void visu_element_renderer_call(const VisuElementRenderer *element)
{
  VISU_ELEMENT_RENDERER_GET_CLASS(element)->call(element);
}

/**
 * visu_element_renderer_callAt:
 * @element: a #VisuElementRenderer object.
 * @colorizer: (allow-none): a #VisuDataColorizer object.
 * @data: a #VisuData object.
 * @node: a #VisuNode belonging to @data.
 *
 * A convenience function to run the callAt method of @element. This
 * will represent @node of @data with the given @colorizer at the
 * coordinates of @node.
 *
 * Since: 3.8
 **/
void visu_element_renderer_callAt(const VisuElementRenderer *element,
                                  const VisuDataColorizer *colorizer,
                                  const VisuData *data, const VisuNode *node)
{
  VISU_ELEMENT_RENDERER_GET_CLASS(element)->callAt(element, colorizer, data, node);
}

/**
 * visu_element_renderer_featureMaterialCache:
 * @element: a #VisuElementRenderer object.
 *
 * Inquires if the given @element renderer can use Gl list to store
 * its material representation.
 *
 * Since: 3.8
 *
 * Returns: TRUE if material representation can be stored in a Gl list.
 **/
gboolean visu_element_renderer_featureMaterialCache(const VisuElementRenderer *element)
{
  gboolean feature;
  g_object_get(G_OBJECT(element), "cache-material", &feature, NULL);
  return feature;
}

/**
 * visu_element_renderer_getElement:
 * @element: a #VisuElementRenderer object.
 *
 * Retrieve the #VisuElement that is represented by @element. See
 * visu_element_renderer_getConstElement() if the returned value
 * should not be modified.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): the #VisuElement represented by @element.
 **/
VisuElement* visu_element_renderer_getElement(VisuElementRenderer *element)
{
  g_return_val_if_fail(VISU_IS_ELEMENT_RENDERER(element), (VisuElement*)0);

  return VISU_ELEMENT_RENDERER(element)->priv->element;
}

/**
 * visu_element_renderer_getConstElement:
 * @element: a #VisuElementRenderer object.
 *
 * Retrieve the #VisuElement represented by @element. See
 * visu_element_renderer_getElement() if the return value should be modified.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a const #VisuElement.
 **/
const VisuElement* visu_element_renderer_getConstElement(const VisuElementRenderer *element)
{
  g_return_val_if_fail(VISU_IS_ELEMENT_RENDERER(element), (const VisuElement*)0);

  return VISU_ELEMENT_RENDERER(element)->priv->element;
}

/**
 * visu_element_renderer_rebuild:
 * @element: a #VisuElementRenderer object.
 * @view: a #VisuGlView object.
 *
 * Rebuild the OpenGL list representing @element for @view.
 *
 * Since: 3.8
 **/
void visu_element_renderer_rebuild(VisuElementRenderer *element, const VisuGlView *view)
{
  VISU_ELEMENT_RENDERER_GET_CLASS(element)->compile(element, view);
}

static void notifyRendered(VisuElementRenderer *renderer)
{
  g_object_notify_by_pspec(G_OBJECT(renderer), _properties[PROP_RENDERED]);
}
static void notifyMaskable(VisuElementRenderer *renderer)
{
  g_object_notify_by_pspec(G_OBJECT(renderer), _properties[PROP_MASKABLE]);
}

static void onEntryColor(VisuConfigFile *obj _U_, VisuConfigFileEntry *entry,
                         gpointer data _U_)
{
  VisuElementRenderer *ele;
  const gchar *label;
  VisuElement *element;

  label = visu_config_file_entry_getLabel(entry);
  element = visu_element_retrieveFromName(label, (gboolean*)0);
  if (!element)
    {
      visu_config_file_entry_setErrorMessage(entry, _("'%s' wrong element name"), label);
      return;
    }

  ele = visu_element_renderer_getFromPool(element);
  visu_element_renderer_setColor(ele, tool_color_addFloatRGBA(_elementColor, (int*)0));
  visu_element_renderer_setMaterial(ele, _elementColor + 4);
}

static void exportRenderer(GString *data, VisuData *dataObj)
{
  GList *pos;
  VisuElementRenderer *ele;

  visu_config_file_exportComment(data, DESC_ELEMENT_COLOR);
  for (pos = _pool; pos; pos = g_list_next(pos))
    {
      ele = VISU_ELEMENT_RENDERER(pos->data);
      if (!dataObj || visu_node_array_containsElement(VISU_NODE_ARRAY(dataObj), ele->priv->element))
        visu_config_file_exportEntry(data, FLAG_ELEMENT_COLOR,
                                     visu_element_getName(ele->priv->element),
                                     "%4.3f %4.3f %4.3f %4.3f   %4.2f %4.2f %4.2f %4.2f %4.2f",
                                     ele->priv->color.rgba[0],
                                     ele->priv->color.rgba[1],
                                     ele->priv->color.rgba[2],
                                     ele->priv->color.rgba[3],
                                     ele->priv->material[0],
                                     ele->priv->material[1],
                                     ele->priv->material[2],
                                     ele->priv->material[3],
                                     ele->priv->material[4]);
    }
  visu_config_file_exportComment(data, "");
}
