/*   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 "fragProp.h"

#include <string.h>

#include <visu_data.h>
#include <coreTools/toolColor.h>

static VisuNodeFragment* _copyFrag(VisuNodeFragment* boxed)
{
  VisuNodeFragment *frag;

  if (!boxed)
    return (VisuNodeFragment*)0;
  frag = g_slice_new(VisuNodeFragment);
  frag->label = g_strdup(boxed->label);
  frag->id = boxed->id;
  return frag;
}
static void _freeFrag(VisuNodeFragment *boxed)
{
  if (!boxed)
    return;
  g_free(boxed->label);
  g_slice_free(VisuNodeFragment, boxed);
}

/**
 * visu_node_fragment_get_type:
 *
 * Create and retrieve a #GType for a #VisuNodeFragment object. This
 * function is mainly intended to be used with visu_node_values_frag_setAt().
 *
 * Since: 3.8
 *
 * Returns: a new type for #VisuNodeFragment structures.
 */
GType visu_node_fragment_get_type(void)
{
  static GType g_define_type_id = 0;

  if (g_define_type_id == 0)
    g_define_type_id = g_boxed_type_register_static("VisuNodeFragment", 
                                                    (GBoxedCopyFunc)_copyFrag,
                                                    (GBoxedFreeFunc)_freeFrag);
  return g_define_type_id;
}

/**
 * visu_node_fragment_new:
 * @label: a label.
 * @id: an id.
 *
 * Creates a new #VisuNodeFragment, based on @label and @id.
 *
 * Since: 3.8
 *
 * Returns: (transfer full): a newly allocated #VisuNodeFragment structure.
 **/
VisuNodeFragment* visu_node_fragment_new(const gchar *label, guint id)
{
  VisuNodeFragment *frag;

  g_return_val_if_fail(label, (VisuNodeFragment*)0);

  frag = g_malloc(sizeof(VisuNodeFragment));
  frag->label = g_strdup(label);
  frag->id = id;

  return frag;
}

/**
 * SECTION:fragProp
 * @short_description: define a #VisuNodeValues object to handle frags.
 *
 * <para>Defines a #VisuNodeValues object to store frags on every
 * nodes and get notification for them.</para>
 */

struct _VisuNodeValuesFragPrivate
{
  gboolean dispose_has_run;

  GHashTable *labels;
};

static void _finalize(GObject *obj);
static gboolean _parse(VisuNodeValues *vals, VisuNode *node, const gchar *from);
static gchar* _serialize(const VisuNodeValues *vals, const VisuNode *node);

G_DEFINE_TYPE_WITH_CODE(VisuNodeValuesFrag, visu_node_values_frag, VISU_TYPE_NODE_VALUES,
                        G_ADD_PRIVATE(VisuNodeValuesFrag))

static void visu_node_values_frag_class_init(VisuNodeValuesFragClass *klass)
{
  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->finalize = _finalize;
  VISU_NODE_VALUES_CLASS(klass)->parse = _parse;
  VISU_NODE_VALUES_CLASS(klass)->serialize = _serialize;
}

static void visu_node_values_frag_init(VisuNodeValuesFrag *self)
{
  self->priv = visu_node_values_frag_get_instance_private(self);
  self->priv->dispose_has_run = FALSE;
  self->priv->labels = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
}

static void _finalize(GObject* obj)
{
  VisuNodeValuesFrag *self;

  g_return_if_fail(obj);

  self = VISU_NODE_VALUES_FRAG(obj);

  g_hash_table_unref(self->priv->labels);

  G_OBJECT_CLASS(visu_node_values_frag_parent_class)->finalize(obj);
}

static gboolean _parse(VisuNodeValues *vals, VisuNode *node, const gchar *from)
{
  VisuNodeFragment frag;
  gchar *at;
  gboolean res;

  frag.label = g_strdup(from);
  at = strchr(frag.label, ':');
  if (!at)
    {
      g_free(frag.label);
      return FALSE;
    }
  *at = '\0';
  if (sscanf(at + 1, "%u", &frag.id) != 1)
    {
      g_free(frag.label);
      return FALSE;
    }
  res = visu_node_values_frag_setAt(VISU_NODE_VALUES_FRAG(vals), node, &frag);
  g_free(frag.label);
  return res;
}
static gchar* _serialize(const VisuNodeValues *vals, const VisuNode *node)
{
  GValue value = {0, {{0}, {0}}};
  VisuNodeFragment *frag;

  if (!visu_node_values_getAt(vals, node, &value))
    return (gchar*)0;

  frag = (VisuNodeFragment*)g_value_get_boxed(&value);
  if (!frag)
    return (gchar*)0;

  return g_strdup_printf("%s: %u", frag->label, frag->id);
}

/**
 * visu_node_values_frag_new:
 * @arr: a #VisuNodeArray object.
 * @label: a translatable label.
 *
 * Create a new frag field located on nodes.
 *
 * Since: 3.8
 *
 * Returns: (transfer full): a newly created #VisuNodeValuesFrag object.
 **/
VisuNodeValuesFrag* visu_node_values_frag_new(VisuNodeArray *arr,
                                              const gchar *label)
{
  VisuNodeValuesFrag *vals;

  vals = VISU_NODE_VALUES_FRAG(g_object_new(VISU_TYPE_NODE_VALUES_FRAG,
                                            "nodes", arr, "label", label,
                                            "type", VISU_TYPE_NODE_FRAGMENT, NULL));
  return vals;
}

/**
 * visu_node_values_frag_getAt:
 * @vect: a #VisuNodeValuesFrag object.
 * @node: a #VisuNode object.
 *
 * Retrieves the float array hosted on @node.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): the coordinates of
 * float array for @node.
 **/
const VisuNodeFragment* visu_node_values_frag_getAt(VisuNodeValuesFrag *vect,
                                                    const VisuNode *node)
{
  GValue value = {0, {{0}, {0}}};

  g_return_val_if_fail(VISU_IS_NODE_VALUES_FRAG(vect), NULL);

  visu_node_values_getAt(VISU_NODE_VALUES(vect), node, &value);
  return g_value_get_boxed(&value);
}
/**
 * visu_node_values_frag_getAtIter:
 * @vect: a #VisuNodeValuesFrag object.
 * @iter: a #VisuNodeValuesIter object.
 *
 * Retrieve the current #VisuNodeFragment of @iter.
 *
 * Since: 3.8
 *
 * Returns: A #VisuNodeFragment pointer.
 **/
const VisuNodeFragment* visu_node_values_frag_getAtIter(VisuNodeValuesFrag *vect,
                                                        const VisuNodeValuesIter *iter)
{
  g_return_val_if_fail(VISU_IS_NODE_VALUES_FRAG(vect), NULL);

  return g_value_get_boxed(&iter->value);
}

/**
 * visu_node_values_frag_setAt:
 * @vect: a #VisuNodeValuesFrag object.
 * @node: a #VisuNode object.
 * @frag: a #VisuNodeFragment value.
 *
 * Changes the frag hosted at @node for one of defined by @str.
 *
 * Since: 3.8
 *
 * Returns: TRUE if frag for @node is indeed changed.
 **/
gboolean visu_node_values_frag_setAt(VisuNodeValuesFrag *vect,
                                     const VisuNode *node,
                                     const VisuNodeFragment *frag)
{
  GValue value = G_VALUE_INIT;
  VisuNodeFragment *old;

  visu_node_values_getAt(VISU_NODE_VALUES(vect), node, &value);
  old = (VisuNodeFragment*)g_value_get_boxed(&value);
  if (frag && old && !strcmp(frag->label, old->label) && frag->id == old->id)
    return FALSE;

  if (!g_hash_table_contains(vect->priv->labels, frag->label))
    g_hash_table_add(vect->priv->labels, g_strdup(frag->label));
  g_value_set_static_boxed(&value, frag);
  return visu_node_values_setAt(VISU_NODE_VALUES(vect), node, &value);
}

/**
 * visu_node_values_frag_getLabels:
 * @frag: a #VisuNodeValuesFrag object.
 *
 * Retrieves the set of fragment labels.
 *
 * Since: 3.8
 *
 * Returns: (transfer none) (element-type utf8 utf8): a set of labels.
 **/
GHashTable* visu_node_values_frag_getLabels(VisuNodeValuesFrag *frag)
{
  g_return_val_if_fail(VISU_IS_NODE_VALUES_FRAG(frag), (GHashTable*)0);

  return frag->priv->labels;
}

/**
 * visu_node_values_frag_getNodeIds:
 * @frag: a #VisuNodeValuesFrag object.
 * @label: a label.
 *
 * Construct a list of node ids which fragment label is matching @label;
 *
 * Since: 3.8
 *
 * Returns: (transfer full) (element-type guint): a set of node ids.
 **/
GArray* visu_node_values_frag_getNodeIds(const VisuNodeValuesFrag *frag,
                                         const gchar *label)
{
  GArray *array;
  VisuNodeValuesIter iter;
  gboolean valid;
  
  g_return_val_if_fail(VISU_IS_NODE_VALUES_FRAG(frag), (GArray*)0);

  if (!label)
    return (GArray*)0;

  array = g_array_new(FALSE, FALSE, sizeof(guint));
  for (valid = visu_node_values_iter_new(&iter, ITER_NODES_BY_TYPE, VISU_NODE_VALUES(frag));
       valid; valid = visu_node_values_iter_next(&iter))
    {
      const VisuNodeFragment *fragment = g_value_get_boxed(&iter.value);
      if (fragment && fragment->label && !strcmp(fragment->label, label))
        g_array_append_val(array, iter.iter.node->number);
    }
  return array;
}
