/*   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 "iface_nodeArrayRenderer.h"
#include <visu_dataloadable.h>

/**
 * SECTION:iface_nodeArrayRenderer
 * @short_description: An interface grouping all the
 * #VisuElementRenderer used to represent a set of #VisuNode.
 *
 * <para>This interface provides common methods for object displaying
 * #VisuData information. In particular, it provides access to all the
 * #VisuElementRenderer used to render the nodes and an iterator to
 * run over #VisuElementRenderer.</para>
 */

/**
 * VisuNodeArrayRendererIter:
 * @self: the #VisuNodeArrayRenderer this iterator is working on.
 * @element: the current #VisuElement.
 * @renderer: the #VisuElementRenderer of @element.
 * @nStoredNodes: the number of nodes of @element.
 * @parent: the #VisuNodeArrayIter this iter is based upon.
 *
 * An iterator over #VisuElementRenderer of @self.
 */

enum {
  PROP_0,
  PROP_NODE_ARRAY,
  PROP_TYPE,
  PROP_MAX_NODE_SIZE,
  PROP_COLORIZER,
  N_PROPS
};
static GParamSpec *_properties[N_PROPS];

enum
  {
    ELEMENT_SIGNAL,
    SIZE_SIGNAL,
    NODES_SIGNAL,
    NB_SIGNAL
  };
static guint _signals[NB_SIGNAL] = { 0 };

/* Boxed interface. */
G_DEFINE_INTERFACE(VisuNodeArrayRenderer, visu_node_array_renderer, G_TYPE_OBJECT)

static void g_cclosure_marshal_ELEMENT_FLOAT(GClosure *closure,
                                             GValue *return_value _U_,
                                             guint n_param_values,
                                             const GValue *param_values,
                                             gpointer invocation_hint _U_,
                                             gpointer marshal_data)
{
  typedef void (*callbackFunc)(gpointer data1, VisuElementRenderer *ele,
                               gfloat size, gpointer data2);
  register callbackFunc callback;
  register GCClosure *cc = (GCClosure*)closure;
  register gpointer data1, data2;

  g_return_if_fail(n_param_values == 3);

  if (G_CCLOSURE_SWAP_DATA(closure))
    {
      data1 = closure->data;
      data2 = g_value_peek_pointer(param_values + 0);
    }
  else
    {
      data1 = g_value_peek_pointer(param_values + 0);
      data2 = closure->data;
    }
  callback = (callbackFunc)(size_t)(marshal_data ? marshal_data : cc->callback);

  callback(data1, VISU_ELEMENT_RENDERER(g_value_get_object(param_values + 1)),
           g_value_get_float(param_values + 2),
           data2);
}
static void visu_node_array_renderer_default_init(VisuNodeArrayRendererInterface *iface)
{
  /**
   * VisuNodeArrayRenderer::element-notify:
   * @self: the object emitting the signal.
   * @renderer: the #VisuElementRenderer that has been changed.
   *
   * This signal is emitted each time one of the #VisuElementRenderer
   * used to represent the #VisuNodeArray is changed.
   *
   * Since: 3.8
   */
  _signals[ELEMENT_SIGNAL] =
    g_signal_new("element-notify", G_TYPE_FROM_INTERFACE(iface),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS | G_SIGNAL_DETAILED,
                 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
                 G_TYPE_NONE, 1, VISU_TYPE_ELEMENT_RENDERER);
  /**
   * VisuNodeArrayRenderer::element-size-changed:
   * @self: the object emitting the signal.
   * @renderer: the #VisuElementRenderer that has been changed.
   * @extent: the new size.
   *
   * This signal is emitted each time one of the #VisuElementRenderer
   * used to represent the #VisuNodeArray has changed its size.
   *
   * Since: 3.8
   */
  _signals[SIZE_SIGNAL] =
    g_signal_new("element-size-changed", G_TYPE_FROM_INTERFACE(iface),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                 0, NULL, NULL, g_cclosure_marshal_ELEMENT_FLOAT,
                 G_TYPE_NONE, 2, VISU_TYPE_ELEMENT_RENDERER, G_TYPE_FLOAT);
  /**
   * VisuNodeArrayRenderer::nodes:
   * @self: the object emitting the signal.
   * @nodeIds: (element-type uint): the id of modified nodes.
   *
   * This signal is emitted when node rendering properties are changed
   * because of population change, visibility change, position change...
   *
   * Since: 3.8
   */
  _signals[NODES_SIGNAL] =
    g_signal_new("nodes", G_TYPE_FROM_INTERFACE(iface),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS | G_SIGNAL_DETAILED,
                 0, NULL, NULL, g_cclosure_marshal_VOID__BOXED,
                 G_TYPE_NONE, 1, G_TYPE_ARRAY);

  /**
   * VisuNodeArrayRenderer::data:
   *
   * The node_array the renderer refers to.
   *
   * Since: 3.8
   */
  _properties[PROP_NODE_ARRAY] =
    g_param_spec_object("data", "Data", "data associated to this renderer",
                        VISU_TYPE_NODE_ARRAY, G_PARAM_READWRITE);
  g_object_interface_install_property(iface, _properties[PROP_NODE_ARRAY]);

  /**
   * VisuNodeArrayRenderer::type:
   *
   * Store the actual type of the node_array.
   *
   * Since: 3.8
   */
  _properties[PROP_TYPE] =
    g_param_spec_gtype("type", "Type", "type of 'data'",
                        VISU_TYPE_DATA_LOADABLE, G_PARAM_READABLE);
  g_object_interface_install_property(iface, _properties[PROP_TYPE]);

  /**
   * VisuNodeArrayRenderer::max-element-size:
   *
   * Store the maximum size of a rendered element in the node array.
   *
   * Since: 3.8
   */
  _properties[PROP_MAX_NODE_SIZE] =
    g_param_spec_float("max-element-size", "Maximum element size",
                       "maximum size of anyrendered element",
                       0.f, G_MAXFLOAT, 1.f, G_PARAM_READABLE);
  g_object_interface_install_property(iface, _properties[PROP_MAX_NODE_SIZE]);

  /**
   * VisuNodeArrayRenderer::colorizer:
   *
   * Store the current colorizer.
   *
   * Since: 3.8
   */
  _properties[PROP_COLORIZER] =
    g_param_spec_object("colorizer", "Colorizer", "current node colorizer.",
                        VISU_TYPE_DATA_COLORIZER, G_PARAM_READWRITE);
  g_object_interface_install_property(iface, _properties[PROP_COLORIZER]);
}

/**
 * visu_node_array_renderer_getMaxElementSize:
 * @node_array: a #VisuNodeArrayRenderer object.
 * @n: (out caller-allocates): a location to store an integer.
 *
 * Retrieves the maximum rendering size for all #VisuElementRenderer
 * used by @node_array. @n stores on aoutput the number of
 * #VisuElementRenderer used by @node_array.
 *
 * Since: 3.8
 *
 * Returns: a positive float value.
 **/
gfloat visu_node_array_renderer_getMaxElementSize(VisuNodeArrayRenderer *node_array, guint *n)
{
  return VISU_NODE_ARRAY_RENDERER_GET_INTERFACE(node_array)->getExtent(node_array, n);
}

/**
 * visu_node_array_renderer_get:
 * @node_array: a #VisuNodeArrayRenderer object.
 * @element: a #VisuElementRenderer object.
 *
 * Retrieve the #VisuElementRenderer that is used by @node_array to represent @element.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): the #VisuElementRenderer used for @element.
 **/
VisuElementRenderer* visu_node_array_renderer_get(VisuNodeArrayRenderer *node_array,
                                                  const VisuElement *element)
{
  return VISU_NODE_ARRAY_RENDERER_GET_INTERFACE(node_array)->getElement(node_array, element);
}

/**
 * visu_node_array_renderer_getNodeArray:
 * @self: a #VisuNodeArrayRenderer object.
 *
 * Retrieve the #VisuNodeArray represented by @self.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): the #VisuNodeArray represented by @element.
 **/
VisuNodeArray* visu_node_array_renderer_getNodeArray(VisuNodeArrayRenderer *self)
{
  return VISU_NODE_ARRAY_RENDERER_GET_INTERFACE(self)->getNodeArray(self);
}
/**
 * visu_node_array_renderer_setNodeArray:
 * @self: a #VisuNodeArrayRenderer object.
 * @array: (allow-none): a #VisuNodeArray object.
 *
 * Changes the #VisuNodeArray that is used as a model for @self.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_node_array_renderer_setNodeArray(VisuNodeArrayRenderer *self, VisuNodeArray *array)
{
  if (!VISU_NODE_ARRAY_RENDERER_GET_INTERFACE(self)->setNodeArray(self, array))
    return FALSE;

  g_object_notify_by_pspec(G_OBJECT(self), _properties[PROP_NODE_ARRAY]);
  g_object_notify_by_pspec(G_OBJECT(self), _properties[PROP_TYPE]);

  return TRUE;
}

/**
 * visu_node_array_renderer_pushColorizer:
 * @self: a #VisuNodeArrayRenderer object.
 * @colorizer: a #VisuDataColorizer object.
 *
 * Changes the #VisuDataColorizer of @self.
 *
 * Since: 3.8
 *
 * Returns: TRUE if the value is actually changed.
 **/
gboolean visu_node_array_renderer_pushColorizer(VisuNodeArrayRenderer *self,
                                                VisuDataColorizer *colorizer)
{
  gboolean res;

  res = VISU_NODE_ARRAY_RENDERER_GET_INTERFACE(self)->pushColorizer(self, colorizer);
  if (res)
    g_object_notify_by_pspec(G_OBJECT(self), _properties[PROP_COLORIZER]);
  return res;
}
/**
 * visu_node_array_renderer_removeColorizer:
 * @self: a #VisuNodeArrayRenderer object.
 * @colorizer: a #VisuDataColorizer object.
 *
 * Remove the #VisuDataColorizer from the stack of colorizers of @self.
 *
 * Since: 3.8
 *
 * Returns: TRUE if the current colorizer is actually changed.
 **/
gboolean visu_node_array_renderer_removeColorizer(VisuNodeArrayRenderer *self,
                                                  VisuDataColorizer *colorizer)
{
  gboolean res;

  res = VISU_NODE_ARRAY_RENDERER_GET_INTERFACE(self)->removeColorizer(self, colorizer);
  if (res)
    g_object_notify_by_pspec(G_OBJECT(self), _properties[PROP_COLORIZER]);
  return res;
}
/**
 * visu_node_array_renderer_getColorizer:
 * @self: a #VisuNodeArrayRenderer object.
 *
 * The renderer @self can use a #VisuDataColorizer object to modify
 * the pristine color of nodes.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuDataColorizer object, owned by V_Sim.
 **/
VisuDataColorizer* visu_node_array_renderer_getColorizer(VisuNodeArrayRenderer *self)
{
  return VISU_NODE_ARRAY_RENDERER_GET_INTERFACE(self)->getColorizer(self);
}

/**
 * visu_node_array_renderer_iter_new:
 * @self: a #VisuNodeArrayRenderer object.
 * @iter: (out caller-allocates): a location to an uninitialised
 * #VisuNodeArrayRendererIter object.
 * @physical: a boolean.
 *
 * Creates an iterator to run over all #VisuElementRenderer of
 * @self. The iterator runs only over physical #VisuElement when
 * @physical is %TRUE, see visu_element_getPhysical().
 *
 * Since: 3.8
 *
 * Returns: TRUE if the iterator is in a valid state.
 **/
gboolean visu_node_array_renderer_iter_new(VisuNodeArrayRenderer *self,
                                           VisuNodeArrayRendererIter *iter,
                                           gboolean physical)
{
  g_return_val_if_fail(VISU_IS_NODE_ARRAY_RENDERER(self) && iter, FALSE);
  
  iter->self = self;
  iter->physical = physical;
  
  visu_node_array_iter_new(visu_node_array_renderer_getNodeArray(self), &iter->parent);
  return visu_node_array_renderer_iter_next(iter);
}
/**
 * visu_node_array_renderer_iter_next:
 * @iter: a #VisuNodeArrayRendererIter object.
 *
 * Iterates to the next #VisuElementRenderer object.
 *
 * Since: 3.8
 *
 * Returns: TRUE if the iterator is still valid.
 **/
gboolean visu_node_array_renderer_iter_next(VisuNodeArrayRendererIter *iter)
{
  g_return_val_if_fail(iter, FALSE);

  if (!iter->parent.init)
    visu_node_array_iterStart(visu_node_array_renderer_getNodeArray(iter->self),
                              &iter->parent);
  else
    visu_node_array_iterNextElement(visu_node_array_renderer_getNodeArray(iter->self),
                                    &iter->parent, TRUE);
  
  while (iter->physical &&
         iter->parent.element && !visu_element_getPhysical(iter->parent.element))
    visu_node_array_iterNextElement(visu_node_array_renderer_getNodeArray(iter->self),
                                    &iter->parent, TRUE);

  iter->element = iter->parent.element;
  iter->renderer = (VisuElementRenderer*)0;
  iter->nStoredNodes = 0;
  if (!iter->element)
    return FALSE;

  iter->nStoredNodes = iter->parent.nStoredNodes;
  iter->renderer = VISU_NODE_ARRAY_RENDERER_GET_INTERFACE(iter->self)->getElement(iter->self, iter->element);

  return TRUE;
}
