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

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

#include <string.h>

/**
 * SECTION:nodeProp
 * @short_description: generic way to store (and being notify) data
 * for each node.
 *
 * <para>This is the base object to store data for each node. In
 * addition, it provides routines to convert this information from and
 * to strings. It is also possible to get notification on any changes
 * of this information.</para>
 */

struct _VisuNodeValuesPrivate
{
  gboolean dispose_has_run;

  gchar *label;
  GType type;
  gboolean editable;
  guint dim;

  GWeakRef nodes;
  gboolean internal;
  VisuNodeProperty *prop;
};

enum {
  CHANGED_SIGNAL,
  LAST_SIGNAL
};
static guint _signals[LAST_SIGNAL] = { 0 };
enum
  {
    PROP_0,
    INTERNAL_PROP,
    NODES_PROP,
    LABEL_PROP,
    TYPE_PROP,
    DIM_PROP,
    EDIT_PROP,
    N_PROP
  };
static GParamSpec *_properties[N_PROP];

static void visu_node_values_dispose     (GObject* obj);
static void visu_node_values_finalize    (GObject* obj);
static void visu_node_values_constructed (GObject* obj);
static void visu_node_values_get_property(GObject* obj, guint property_id,
                                          GValue *value, GParamSpec *pspec);
static void visu_node_values_set_property(GObject* obj, guint property_id,
                                          const GValue *value, GParamSpec *pspec);
static gboolean visu_node_values_parse(VisuNodeValues *vals,
                                       VisuNode *node,
                                       const gchar *from);
static gchar* visu_node_values_serialize(const VisuNodeValues *vals,
                                         const VisuNode *node);
static gboolean _setAt(VisuNodeValues *vals, const VisuNode *node,
                       GValue *value);
static gboolean _getAt(const VisuNodeValues *vals, const VisuNode *node,
                       GValue *value);
static void _freeBoxed(gpointer data, VisuNodeValues *self);
static gpointer _copyBoxed(gconstpointer data, VisuNodeValues *self);

G_DEFINE_TYPE_WITH_CODE(VisuNodeValues, visu_node_values, VISU_TYPE_OBJECT,
                        G_ADD_PRIVATE(VisuNodeValues))

static void visu_node_values_class_init(VisuNodeValuesClass *klass)
{
  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose  = visu_node_values_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_node_values_finalize;
  G_OBJECT_CLASS(klass)->constructed  = visu_node_values_constructed;
  G_OBJECT_CLASS(klass)->set_property = visu_node_values_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_node_values_get_property;
  klass->parse = visu_node_values_parse;
  klass->serialize = visu_node_values_serialize;
  klass->setAt = _setAt;
  klass->getAt = _getAt;
  
  /**
   * VisuNodeValues::internal:
   *
   * Values of are internal to #VisuNode structure.
   *
   * Since: 3.8
   */
  _properties[INTERNAL_PROP] =
    g_param_spec_boolean("internal", "Internal", "internal to VisuNode structure",
                         FALSE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
  /**
   * VisuNodeValues::nodes:
   *
   * Holds the #VisuNodeArray the values are related to.
   *
   * Since: 3.8
   */
  _properties[NODES_PROP] =
    g_param_spec_object("nodes", "Nodes", "nodes values are related to",
                        VISU_TYPE_NODE_ARRAY,
                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
  /**
   * VisuNodeValues::label:
   *
   * Holds the label (translated) that describes the values.
   *
   * Since: 3.8
   */
  _properties[LABEL_PROP] =
    g_param_spec_string("label", "Label", "description label",
                        "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
  /**
   * VisuNodeValues::type:
   *
   * Holds the type of the values.
   *
   * Since: 3.8
   */
  _properties[TYPE_PROP] =
    g_param_spec_gtype("type", "Type", "type of the values",
                       G_TYPE_NONE,
                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
  /**
   * VisuNodeValues::n-elements:
   *
   * Holds the dimension of the property for each nodes.
   *
   * Since: 3.8
   */
  _properties[DIM_PROP] =
    g_param_spec_uint("n-elements", "N-elements", "number of dimensions",
                     1, G_MAXINT, 1,
                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
  /**
   * VisuNodeValues::editable:
   *
   * Wether the property is editable or not.
   *
   * Since: 3.8
   */
  _properties[EDIT_PROP] =
    g_param_spec_boolean("editable", "Editable", "user can modify the value",
                         TRUE, G_PARAM_READWRITE);

  g_object_class_install_properties(G_OBJECT_CLASS(klass), N_PROP, _properties);
  
  /**
   * VisuNodeValues::changed:
   * @vals: the object which received the signal ;
   * @node: the #VisuNode that is modified.
   *
   * Gets emitted when the values corresponding to this node property
   * are changed for @node.
   *
   * Since: 3.8
   */
  _signals[CHANGED_SIGNAL] =
    g_signal_new("changed", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                 0, NULL, NULL, g_cclosure_marshal_VOID__BOXED,
                 G_TYPE_NONE, 1, VISU_TYPE_NODE, NULL);
}

static void visu_node_values_init(VisuNodeValues *self)
{
  self->priv = visu_node_values_get_instance_private(self);
  self->priv->dispose_has_run = FALSE;
  self->priv->label = (gchar*)0;
  self->priv->editable = TRUE;
  self->priv->internal = FALSE;
  g_weak_ref_init(&self->priv->nodes, (gpointer)0);
  self->priv->type = 0;
  self->priv->dim = 0;
  self->priv->prop = (VisuNodeProperty*)0;
}

static void visu_node_values_dispose(GObject* obj)
{
  VisuNodeValues *self;

  self = VISU_NODE_VALUES(obj);
  if (self->priv->dispose_has_run)
    return;
  self->priv->dispose_has_run = TRUE;
  
  g_weak_ref_clear(&self->priv->nodes);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_node_values_parent_class)->dispose(obj);
}
/* This method is called once only. */
static void visu_node_values_finalize(GObject* obj)
{
  VisuNodeValues *self;

  g_return_if_fail(obj);

  self = VISU_NODE_VALUES(obj);
  DBG_fprintf(stderr, "Visu NodeProp: finalising '%s'.\n", self->priv->label);
  g_free(self->priv->label);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_node_values_parent_class)->finalize(obj);
}
static void visu_node_values_get_property(GObject* obj, guint property_id,
                                          GValue *value, GParamSpec *pspec)
{
  VisuNodeValues *self = VISU_NODE_VALUES(obj);

  switch (property_id)
    {
    case NODES_PROP:
      g_value_take_object(value, g_weak_ref_get(&self->priv->nodes));
      break;
    case LABEL_PROP:
      g_value_set_string(value, self->priv->label);
      break;
    case TYPE_PROP:
      g_value_set_gtype(value, self->priv->type);
      break;
    case DIM_PROP:
      g_value_set_uint(value, self->priv->dim);
      break;
    case EDIT_PROP:
      g_value_set_boolean(value, self->priv->editable);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_node_values_set_property(GObject* obj, guint property_id,
                                         const GValue *value, GParamSpec *pspec)
{
  VisuNodeValues *self = VISU_NODE_VALUES(obj);

  switch (property_id)
    {
    case INTERNAL_PROP:
      self->priv->internal = g_value_get_boolean(value);
      break;
    case NODES_PROP:
      g_weak_ref_set(&self->priv->nodes, g_value_dup_object(value));
      g_object_unref(g_value_get_object(value));
      break;
    case LABEL_PROP:
      self->priv->label = g_value_dup_string(value);
      break;
    case TYPE_PROP:
      self->priv->type = g_value_get_gtype(value);
      break;
    case DIM_PROP:
      self->priv->dim = g_value_get_uint(value);
      break;
    case EDIT_PROP:
      visu_node_values_setEditable(self, g_value_get_boolean(value));
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void _stringFree(gchar *data, gpointer add _U_)
{
  g_free(data);
}
static gchar* _stringCopy(const gchar *data, gpointer add _U_)
{
  return g_strdup(data);
}
static void _objectFree(GObject *data, gpointer add _U_)
{
  g_object_unref(data);
}
static GObject* _objectCopy(GObject *data, gpointer add _U_)
{
  return g_object_ref(data);
}
static void visu_node_values_constructed(GObject *obj)
{
  VisuNodeValues *self = VISU_NODE_VALUES(obj);
  gpointer nodes;

  if (self->priv->type && self->priv->dim && !self->priv->internal)
    {
      nodes = g_weak_ref_get(&self->priv->nodes);
      if (!nodes)
        return;
      switch (G_TYPE_FUNDAMENTAL(self->priv->type))
        {
        case (G_TYPE_INT):
        case (G_TYPE_UINT):
        case (G_TYPE_BOOLEAN):
          self->priv->prop =
            visu_node_array_property_newInteger(VISU_NODE_ARRAY(nodes),
                                                self->priv->label);
          break;
        case (G_TYPE_FLOAT):
          self->priv->prop =
            visu_node_array_property_newFloatArray(VISU_NODE_ARRAY(nodes),
                                                   self->priv->label,
                                                   self->priv->dim);
          break;
        case (G_TYPE_STRING):
          self->priv->prop =
            visu_node_array_property_newPointer(VISU_NODE_ARRAY(nodes),
                                                self->priv->label,
                                                (GFunc)_stringFree,
                                                (GCopyFunc)_stringCopy,
                                                (gpointer)0);
          break;
        case (G_TYPE_BOXED):
          self->priv->prop =
            visu_node_array_property_newPointer(VISU_NODE_ARRAY(nodes),
                                                self->priv->label,
                                                (GFunc)_freeBoxed,
                                                (GCopyFunc)_copyBoxed,
                                                (gpointer)self);
          break;
        case (G_TYPE_OBJECT):
          self->priv->prop =
            visu_node_array_property_newPointer(VISU_NODE_ARRAY(nodes),
                                                self->priv->label,
                                                (GFunc)_objectFree,
                                                (GCopyFunc)_objectCopy,
                                                (gpointer)0);
          break;
        default:
          g_warning("Unsupported NodeValues type.");
        }
      g_object_unref(nodes);
    }

  G_OBJECT_CLASS(visu_node_values_parent_class)->constructed(obj);
}

static void _freeBoxed(gpointer data, VisuNodeValues *self)
{
  g_boxed_free(self->priv->type, data);
}
static gpointer _copyBoxed(gconstpointer data, VisuNodeValues *self)
{
  return g_boxed_copy(self->priv->type, data);
}

gboolean visu_node_values_parse(VisuNodeValues *vals,
                                VisuNode *node,
                                const gchar *from)
{
  int ival;
  guint uval;
  gboolean bval, modify;
  float *fvals, *rvals;
  gchar **datas;
  guint i;
  GValue value = G_VALUE_INIT;

  g_return_val_if_fail(VISU_IS_NODE_VALUES(vals) && node && from, FALSE);

  if (!visu_node_values_getAt(vals, node, &value))
    return FALSE;

  fvals = (float*)0;
  switch (vals->priv->type)
    {
    case (G_TYPE_INT):
      if (sscanf(from, "%d", &ival) != 1)
        return FALSE;
      if (ival == g_value_get_int(&value))
        return TRUE;
      g_value_set_int(&value, ival);
      break;
    case (G_TYPE_UINT):
      if (sscanf(from, "%u", &uval) != 1)
        return FALSE;
      if (uval == g_value_get_uint(&value))
        return TRUE;
      g_value_set_uint(&value, uval);
      break;
    case (G_TYPE_BOOLEAN):
      if (!strcmp(from, _("T")))
        bval = TRUE;
      else if (!strcmp(from, _("F")))
        bval = FALSE;
      else
        return FALSE;
      if (bval == g_value_get_boolean(&value))
        return TRUE;
      g_value_set_boolean(&value, bval);
      break;
    case (G_TYPE_FLOAT):
      datas = g_strsplit_set(from, "(;)", vals->priv->dim);
      if (datas && datas[0] && !datas[1] && !strcmp(datas[0], _("none")))
        {
          g_strfreev(datas);
          g_value_set_pointer(&value, (gpointer)0);
          break;
        }
      fvals = g_malloc(sizeof(float) * vals->priv->dim);
      for (i = 0; datas[i]; i++)
        if (sscanf(from, "%f", fvals + i) != 1)
          {
            g_free(fvals);
            g_strfreev(datas);
            return FALSE;
          }
      g_strfreev(datas);
      if (i != vals->priv->dim)
        {
          g_free(fvals);
          return FALSE;
        }
      rvals = (float*)g_value_get_pointer(&value);
      modify = (rvals == (float*)0);
      for (i = 0; i < (rvals ? vals->priv->dim : 0); i++)
        modify = modify || (rvals[i] != fvals[i]);
      if (!modify)
        {
          g_free(fvals);
          return TRUE;
        }
      g_value_set_pointer(&value, fvals);
      break;
    case (G_TYPE_STRING):
      if (!strcmp(from, g_value_get_string(&value)))
        return TRUE;
      g_value_set_string(&value, from);
      break;
    default:
      g_warning("Unsupported NodeValues type.");
    }
  modify = visu_node_values_setAt(vals, node, &value);
  if (vals->priv->type == G_TYPE_FLOAT)
    g_free(fvals);
  return modify;
}
gchar* visu_node_values_serialize(const VisuNodeValues *vals,
                                  const VisuNode *node)
{
  GString *str;
  guint i;
  float *fvals;
  gchar *ret;
  GValue value = {0, {{0}, {0}}};

  g_return_val_if_fail(VISU_IS_NODE_VALUES(vals) && node, (gchar*)0);

  if (!visu_node_values_getAt(vals, node, &value))
    return (gchar*)0;
  switch (vals->priv->type)
    {
    case (G_TYPE_INT):
      return g_strdup_printf("%d", g_value_get_int(&value));
    case (G_TYPE_UINT):
      return g_strdup_printf("%d", g_value_get_uint(&value));
    case (G_TYPE_BOOLEAN):
      if (g_value_get_boolean(&value))
        return g_strdup(_("T"));
      else
        return g_strdup(_("F"));
    case (G_TYPE_FLOAT):
      fvals = (float*)g_value_get_pointer(&value);
      if (fvals)
        {
          str = g_string_new("");
          if (vals->priv->dim > 1)
            g_string_append(str, "( ");
          g_string_append_printf(str, "%#.3g", fvals[0]);
          for (i = 1; i < vals->priv->dim; i++)
            g_string_append_printf(str, " ; %#.3g", fvals[i]);
          if (vals->priv->dim > 1)
            g_string_append(str, " )");
          ret = str->str;
          g_string_free(str, FALSE);
          return ret;
        }
      else
        {
          return g_strdup(_("none"));
        }
    case (G_TYPE_STRING):
      return g_value_dup_string(&value);
    default:
      g_warning("Unsupported NodeValues type.");
    }
  return (gchar*)0;
}
static gboolean _setAt(VisuNodeValues *vals, const VisuNode *node,
                       GValue *value)
{
  gpointer nodes;

  g_return_val_if_fail(VISU_IS_NODE_VALUES(vals) &&
                       (vals->priv->prop || vals->priv->internal), FALSE);
  nodes = g_weak_ref_get(&vals->priv->nodes);
  if (!nodes)
    return FALSE;
  if (!vals->priv->internal)
    visu_node_property_setValue(vals->priv->prop, node, value);
  g_signal_emit(G_OBJECT(vals), _signals[CHANGED_SIGNAL], 0, node);
  g_object_unref(nodes);
  return TRUE;
}
static gboolean _getAt(const VisuNodeValues *vals, const VisuNode *node,
                       GValue *value)
{
  gpointer nodes;

  g_return_val_if_fail(VISU_IS_NODE_VALUES(vals) &&
                       (vals->priv->prop || vals->priv->internal), FALSE);

  if (vals->priv->internal)
    return TRUE;

  nodes = g_weak_ref_get(&vals->priv->nodes);
  if (!nodes)
    return FALSE;
  visu_node_property_getValue(vals->priv->prop, node, value);
  g_object_unref(nodes);
  return TRUE;
}

/**
 * visu_node_values_new:
 * @arr: a #VisuNodeArray object.
 * @label: a label.
 * @type: a type.
 * @dim: some dimension.
 *
 * Creates a new #VisuNodeValues object, used to store @dim values of
 * type @type for each node in @arr.
 *
 * Since: 3.8
 *
 * Returns: (transfer full): a newly created #VisuNodeValues object.
 **/
VisuNodeValues* visu_node_values_new(VisuNodeArray *arr,
                                     const gchar *label,
                                     GType type, guint dim)
{
  VisuNodeValues *vals;

  vals = VISU_NODE_VALUES(g_object_new(VISU_TYPE_NODE_VALUES,
                                       "nodes", arr, "label", label,
                                       "type", type, "n-elements", dim, NULL));
  return vals;
}

/**
 * visu_node_values_getLabel:
 * @vals: a #VisuNodeValues object.
 *
 * Retrives the label used to identify @vals.
 *
 * Since: 3.8
 *
 * Returns: a string.
 **/
const gchar* visu_node_values_getLabel(const VisuNodeValues *vals)
{
  g_return_val_if_fail(VISU_IS_NODE_VALUES(vals), (const gchar*)0);

  return vals->priv->label;
}

/**
 * visu_node_values_getDimension:
 * @vals: a #VisuNodeValues object.
 *
 * Retrieves how many values are stored per node, aka the column number.
 *
 * Since: 3.8
 *
 * Returns: a number of columns.
 **/
guint visu_node_values_getDimension(const VisuNodeValues *vals)
{
  g_return_val_if_fail(VISU_IS_NODE_VALUES(vals), 0);

  return vals->priv->dim;
}

/**
 * visu_node_values_setEditable:
 * @vals: a #VisuNodeValues object.
 * @status: a boolean.
 *
 * Change the writable @status of @vals.
 *
 * Since: 3.8
 **/
void visu_node_values_setEditable(VisuNodeValues *vals, gboolean status)
{
  g_return_if_fail(VISU_IS_NODE_VALUES(vals));
  if (vals->priv->editable == status)
    return;
  
  vals->priv->editable = status;
  g_object_notify_by_pspec(G_OBJECT(vals), _properties[EDIT_PROP]);
}
/**
 * visu_node_values_getEditable:
 * @vals: a #VisuNodeValues object.
 *
 * Retrieves if @vals are writable or not.
 *
 * Since: 3.8
 *
 * Returns: TRUE if values can be changed.
 **/
gboolean visu_node_values_getEditable(const VisuNodeValues *vals)
{
  g_return_val_if_fail(VISU_IS_NODE_VALUES(vals), FALSE);
  return vals->priv->editable;
}

/**
 * visu_node_values_setFromString:
 * @vals: a #VisuNodeValues object.
 * @node: a #VisuNode pointer.
 * @from: a string.
 *
 * Parse @from to set a value to @node in @vals.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_node_values_setFromString(VisuNodeValues *vals,
                                        VisuNode *node,
                                        const gchar *from)
{
  VisuNodeValuesClass *klass = VISU_NODE_VALUES_GET_CLASS(vals);
  g_return_val_if_fail(klass && klass->parse, FALSE);

  g_return_val_if_fail(VISU_IS_NODE_VALUES(vals), FALSE);
  if (!vals->priv->editable)
    return FALSE;

  return klass->parse(vals, node, from);
}
/**
 * visu_node_values_setFromStringForId:
 * @vals: a #VisuNodeValues object.
 * @nodeId: a node id.
 * @from: a string.
 *
 * Same as visu_node_values_setFromString(), but uses a node id to
 * identify a #VisuNode in @vals.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_node_values_setFromStringForId(VisuNodeValues *vals,
                                             guint nodeId,
                                             const gchar *from)
{
  VisuNode *node;
  VisuNodeArray *arr;

  arr = visu_node_values_getArray(vals);
  node = visu_node_array_getFromId(arr, nodeId);
  g_object_unref(arr);
  if (!node)
    return FALSE;
  return visu_node_values_setFromString(vals, node, from);
}
/**
 * visu_node_values_toString:
 * @vals: a #VisuNodeValues object.
 * @node: a #VisuNode pointer.
 *
 * Creates a string representation of the value stored for @node.
 *
 * Since: 3.8
 *
 * Returns: (transfer full): a newly created string.
 **/
gchar* visu_node_values_toString(const VisuNodeValues *vals, const VisuNode *node)
{
  g_return_val_if_fail(VISU_IS_NODE_VALUES(vals), (gchar*)0);

  VisuNodeValuesClass *klass = VISU_NODE_VALUES_GET_CLASS(vals);
  g_return_val_if_fail(klass && klass->serialize, (gchar*)0);

  return klass->serialize(vals, node);
}
/**
 * visu_node_values_toStringFromId:
 * @vals: a #VisuNodeValues object.
 * @nodeId: a node id.
 *
 * Like visu_node_values_toString(), but using a string id.
 *
 * Since: 3.8
 *
 * Returns: (transfer full): a newly created string.
 **/
gchar* visu_node_values_toStringFromId(const VisuNodeValues *vals,
                                       guint nodeId)
{
  VisuNode *node;
  VisuNodeArray *arr;

  arr = visu_node_values_getArray(vals);
  node = visu_node_array_getFromId(arr, nodeId);
  g_object_unref(arr);
  if (!node)
    return (gchar*)0;
  return visu_node_values_toString(vals, node);
}
/**
 * visu_node_values_setAt:
 * @vals: a #VisuNodeValues object.
 * @node: a #VisuNode pointer.
 * @value: some value.
 *
 * Set @value for @node in @vals.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_node_values_setAt(VisuNodeValues *vals, const VisuNode *node,
                                GValue *value)
{
  VisuNodeValuesClass *klass = VISU_NODE_VALUES_GET_CLASS(vals);
  g_return_val_if_fail(klass && klass->setAt, FALSE);
  
  g_return_val_if_fail(VISU_IS_NODE_VALUES(vals), FALSE);
  return klass->setAt(vals, node, value);
}
/**
 * visu_node_values_copy:
 * @vals: a #VisuNodeValues object.
 * @from: a #VisuNodeValues object.
 *
 * Copies all values @from to @vals, providing that @from and @vals
 * store compatible data. The values are copied using the #VisuNode
 * id. If an id for a node of @vals is not found in @from, no node
 * value is copied for this node. The original id is used.
 *
 * Since: 3.8
 *
 * Returns: TRUE if @vals is editable and all values @from have been
 * actually copied.
 **/
gboolean visu_node_values_copy(VisuNodeValues *vals, const VisuNodeValues *from)
{
  g_return_val_if_fail(VISU_IS_NODE_VALUES(vals), FALSE);

  VisuNodeValuesClass *klass = VISU_NODE_VALUES_GET_CLASS(vals);
  g_return_val_if_fail(klass && klass->setAt, FALSE);

  VisuNodeValuesClass *klassFrom = VISU_NODE_VALUES_GET_CLASS(from);
  g_return_val_if_fail(klassFrom && klassFrom->getAt, FALSE);

  VisuNodeArrayIter iter;
  VisuNodeArray *arr, *cur;
  VisuNode *fromNode;
  GValue value = G_VALUE_INIT;
  int orig;

  DBG_fprintf(stderr, "Node Values : copy from %p to %p.\n",
              (gpointer)from, (gpointer)vals);

  if (!vals->priv->editable)
    return FALSE;

  switch (G_TYPE_FUNDAMENTAL(vals->priv->type))
    {
    case (G_TYPE_INT):
      g_value_init(&value, G_TYPE_INT);
      break;
    case (G_TYPE_UINT):
      g_value_init(&value, G_TYPE_UINT);
      break;
    case (G_TYPE_BOOLEAN):
      g_value_init(&value, G_TYPE_BOOLEAN);
      break;
    case (G_TYPE_FLOAT):
      g_value_init(&value, G_TYPE_POINTER);
      break;
    case (G_TYPE_STRING):
      g_value_init(&value, G_TYPE_STRING);
      break;
    case (G_TYPE_BOXED):
      g_value_init(&value, vals->priv->type);
      break;
    case (G_TYPE_OBJECT):
      g_value_init(&value, vals->priv->type);
      break;
    default:
      g_warning("Unsupported NodeValues type.");
      return FALSE;
    }

  cur = visu_node_values_getArray(vals);
  arr = visu_node_values_getArray(from);
  g_return_val_if_fail(arr && cur, FALSE);

  visu_node_array_iter_new(cur, &iter);
  for (visu_node_array_iterStart(cur, &iter); iter.node;
       visu_node_array_iterNext(cur, &iter))
    {
      orig = visu_node_array_getOriginal(cur, iter.node->number);
      fromNode = visu_node_array_getFromId
        (arr, orig < 0 ? iter.node->number : (guint)orig);
      if (fromNode && klassFrom->getAt(from, fromNode, &value))
        klass->setAt(vals, iter.node, &value);
    }

  g_object_unref(arr);
  g_object_unref(cur);

  DBG_fprintf(stderr, "Node Values : copy done.\n");

  return TRUE;
}
/**
 * visu_node_values_reset:
 * @vals: a #VisuNodeValues object.
 *
 * Reset all stored values.
 *
 * Since: 3.8
 **/
void visu_node_values_reset(VisuNodeValues *vals)
{
  g_return_if_fail(VISU_IS_NODE_VALUES(vals));

  visu_node_property_reset(vals->priv->prop);
  g_signal_emit(G_OBJECT(vals), _signals[CHANGED_SIGNAL], 0, NULL);
}
/**
 * visu_node_values_getAt:
 * @vals: a #VisuNodeValues object.
 * @node: a #VisuNode structure.
 * @value: (out caller-allocates): a value location.
 *
 * Retrieves the value stored in @vals for @node. @value is
 * initialised to the proper type at each call. For repeatedly
 * retrieving values for various nodes, prefer to use a
 * #VisuNodeValuesIter instead.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value is properly retrieved.
 **/
gboolean visu_node_values_getAt(const VisuNodeValues *vals,
                                const VisuNode *node,
                                GValue *value)
{
  VisuNodeValuesClass *klass = VISU_NODE_VALUES_GET_CLASS(vals);
  g_return_val_if_fail(klass && klass->getAt, FALSE);
  
  switch (G_TYPE_FUNDAMENTAL(vals->priv->type))
    {
    case (G_TYPE_INT):
      g_value_init(value, G_TYPE_INT);
      break;
    case (G_TYPE_UINT):
      g_value_init(value, G_TYPE_UINT);
      break;
    case (G_TYPE_BOOLEAN):
      g_value_init(value, G_TYPE_BOOLEAN);
      break;
    case (G_TYPE_FLOAT):
      g_value_init(value, G_TYPE_POINTER);
      break;
    case (G_TYPE_STRING):
      g_value_init(value, G_TYPE_STRING);
      break;
    case (G_TYPE_BOXED):
      g_value_init(value, vals->priv->type);
      break;
    case (G_TYPE_OBJECT):
      g_value_init(value, vals->priv->type);
      break;
    default:
      g_warning("Unsupported NodeValues type.");
    }
  return klass->getAt(vals, node, value);
}
/**
 * visu_node_values_getPtrAt:
 * @vals: a #VisuNodeValues object.
 * @node: a #VisuNode object.
 *
 * Retrieve the values associated to @node, as long as it can be
 * stored as a pointer. This is aconvenient function to avoid
 * instanciating a GValue.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a pointer to some data.
 **/
gpointer visu_node_values_getPtrAt(const VisuNodeValues *vals,
                                   const VisuNode *node)
{
  GValue value;

  VisuNodeValuesClass *klass = VISU_NODE_VALUES_GET_CLASS(vals);
  g_return_val_if_fail(klass && klass->getAt, FALSE);

  memset(&value, '\0', sizeof(GValue));
  g_value_init(&value, G_TYPE_POINTER);
  if (klass->getAt(vals, node, &value))
    return g_value_get_pointer(&value);
  else
    return (gpointer)0;
}
/**
 * visu_node_values_getArray:
 * @vals: a #VisuNodeValues object.
 *
 * Retrieves the #VisuNodeArray, @vels is based on.
 *
 * Since: 3.8
 *
 * Returns: (transfer full) (allow-none): a #VisuNodeArray object.
 **/
VisuNodeArray* visu_node_values_getArray(const VisuNodeValues *vals)
{
  gpointer nodes;

  g_return_val_if_fail(VISU_IS_NODE_VALUES(vals), (VisuNodeArray*)0);
  nodes = g_weak_ref_get(&vals->priv->nodes);
  return (nodes) ? VISU_NODE_ARRAY(nodes) : (VisuNodeArray*)0;
}
/**
 * visu_node_values_fromArray:
 * @vals: a #VisuNodeValues object.
 * @arr: a #VisuNodeArray object.
 *
 * Tests if @arr is used as base for @vals.
 *
 * Since: 3.8
 *
 * Returns: TRUE if @vals is stored in @arr.
 **/
gboolean visu_node_values_fromArray(VisuNodeValues *vals, const VisuNodeArray *arr)
{
  VisuNodeArray *source;
  gboolean res;

  source = visu_node_values_getArray(vals);
  res = (source == arr);
  if (source)
    g_object_unref(source);
  return res;
}

/**
 * visu_node_values_iter_new:
 * @iter: (out caller-allocates): an iterator location.
 * @type: a iteration type.
 * @vals: a #VisuNodeValues object.
 *
 * Creates an iterator to run over node values stored in @vals,
 * following @type way of iterate.
 *
 * Since: 3.8
 *
 * Returns: TRUE if the iterator is in a valid state.
 **/
gboolean visu_node_values_iter_new(VisuNodeValuesIter *iter,
                                   VisuNodeArrayIterType type,
                                   VisuNodeValues *vals)
{
  g_return_val_if_fail(VISU_IS_NODE_VALUES(vals) && iter, FALSE);

  visu_node_values___iter__(vals, iter);
  if (!iter->vals)
    return FALSE;
  iter->iter.type = type;
  return visu_node_values_iter_next(iter);
}
/**
 * visu_node_values___iter__:
 * @iter: (out caller-allocates): an iterator location.
 * @vals: a #VisuNodeValues object.
 *
 * Creates an iterator to run over node values stored in @vals. This
 * routine is mainly for Python bindings, use
 * visu_node_values_iter_new() instead.
 *
 * Since: 3.8
 **/
void visu_node_values___iter__(VisuNodeValues *vals, VisuNodeValuesIter *iter)
{
  gpointer nodes;

  g_return_if_fail(VISU_IS_NODE_VALUES(vals) && iter);

  iter->vals = (VisuNodeValues*)0;
  nodes = g_weak_ref_get(&vals->priv->nodes);
  if (!nodes)
    return;
  /* Currently don't guarantee the availability of nodes during the
     iter. */
  g_object_unref(nodes);

  iter->vals = vals;
  memset(&iter->value, '\0', sizeof(GValue));
  switch (G_TYPE_FUNDAMENTAL(vals->priv->type))
    {
    case (G_TYPE_INT):
      g_value_init(&iter->value, G_TYPE_INT);
      break;
    case (G_TYPE_UINT):
      g_value_init(&iter->value, G_TYPE_UINT);
      break;
    case (G_TYPE_BOOLEAN):
      g_value_init(&iter->value, G_TYPE_BOOLEAN);
      break;
    case (G_TYPE_FLOAT):
      g_value_init(&iter->value, G_TYPE_POINTER);
      break;
    case (G_TYPE_STRING):
      g_value_init(&iter->value, G_TYPE_STRING);
      break;
    case (G_TYPE_BOXED):
      g_value_init(&iter->value, vals->priv->type);
      break;
    case (G_TYPE_OBJECT):
      g_value_init(&iter->value, vals->priv->type);
      break;
    default:
      g_warning("Unsupported NodeValues type.");
    }
  visu_node_array_iter_new(VISU_NODE_ARRAY(nodes), &iter->iter);
}
/**
 * visu_node_values_iter_next:
 * @iter: a #VisuNodeValuesIter object.
 *
 * Iterates to the next node value.
 *
 * Since: 3.8
 *
 * Returns: TRUE if the iterator is still in a valid state.
 **/
gboolean visu_node_values_iter_next(VisuNodeValuesIter *iter)
{
  gboolean res;

  VisuNodeValuesClass *klass = VISU_NODE_VALUES_GET_CLASS(iter->vals);
  g_return_val_if_fail(klass && klass->getAt, FALSE);

  res = visu_node_array_iter_next(&iter->iter);
  if (res)
    klass->getAt(iter->vals, iter->iter.node, &iter->value);
  return res;
}
