/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Jérémy BLANC et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)
  
	Adresse mèl :
	BLANC, 
	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 : Jérémy BLANC 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 "vibration.h"

#include <string.h>
#include <math.h> /*for sinus*/
#include <coreTools/toolMatrix.h> /* for spherical coordonates */

#include <extensions/node_vectors.h>
#include <visu_dataloadable.h>
#include <iface_animatable.h>

/**
 * SECTION:vibration
 * @short_description: Add a support to animate the node positions
 * with a phonon frequency.
 *
 * <para>Define a way to store vibration or phonons to a #VisuData
 * object. One can store several phonons in one object, each phonon is
 * them represented by its id. The phonons can be animated on screen,
 * using a user defined frequency (visu_vibration_setUserFrequency)
 * and amplitue (visu_vibration_setAmplitude), by calling
 * visu_vibration_play().</para>
 * <para>Phonons are set with visu_vibration_setCharacteristic() and
 * #VisuNode displacements are set with visu_vibration_setDisplacements().</para>
 */

/**
 * VISU_VIBRATION_ID:
 *
 * The default name used for the #VisuGlExt representing
 * vibrations, see visu_gl_ext_getFromName().
 */
#define VISU_VIBRATION_ID "Vibration"

#define AMPL_COEFF 0.5f

/* Displacement vector per atom is (U+iV) = (Ux + iVx, ...).
   Current applied displacement on one atom is (dx, dy, dz).
   qr is the Q.R part of the exponential. */
enum
  {
    Ux,
    Uy,
    Uz,
    mod,
    theta,
    phi,
    Vx,
    Vy,
    Vz,
    dx,
    dy,
    dz,
    qr,
    nData
  };

struct _VisuVibrationPrivate
{
  gboolean dispose_has_run;

  /* Parameters for the available phonons. */
  guint n;
  float *q;
  float *omega;
  float *en;
  /* Array of all the displacement vectors. */
  gboolean *complex;
  GArray **u;
  float *norm;

  /* Current applied frequency and amplitude, phonon id, time and delta time. */
  float freq, ampl;
  gint iph;
  gfloat t;
  VisuAnimation *time_anim;

  /* Internal parameters. */
  gulong signal;
} Vibration;

enum
  {
    PROP_0,
    N_MODE_PROP,
    FREQ_PROP,
    AMPL_PROP,
    TIME_PROP,
    N_PROP
  };
static GParamSpec *_properties[N_PROP];

static void visu_vibration_dispose  (GObject* obj);
static void visu_vibration_finalize (GObject* obj);
static void visu_vibration_get_property(GObject* obj, guint property_id,
                                        GValue *value, GParamSpec *pspec);
static void visu_vibration_set_property(GObject* obj, guint property_id,
                                        const GValue *value, GParamSpec *pspec);
static gfloat visu_vibration_nrm2(const VisuNodeValuesFarray *vect, const GValue *value);
static void _shift(const VisuNodeValuesVector *vect, const VisuNode *node,
                   gfloat dxyz[3]);
static void visu_animatable_interface_init(VisuAnimatableInterface *iface);
static VisuAnimation* _getAnimation(const VisuAnimatable *animatable, const gchar *prop);
static void onPopulationChanged(VisuVibration *vib, GArray *ids, VisuData *dataObj);
static void onPositionChanged(VisuVibration *vib, GArray *ids, VisuData *dataObj);
static void vibrationAt(VisuVibration *vib, gboolean withPhase);

G_DEFINE_TYPE_WITH_CODE(VisuVibration, visu_vibration, VISU_TYPE_NODE_VALUES_VECTOR,
                        G_ADD_PRIVATE(VisuVibration)
                        G_IMPLEMENT_INTERFACE(VISU_TYPE_ANIMATABLE,
                                              visu_animatable_interface_init))

static void visu_vibration_class_init(VisuVibrationClass *klass)
{
  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose  = visu_vibration_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_vibration_finalize;
  G_OBJECT_CLASS(klass)->set_property = visu_vibration_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_vibration_get_property;
  VISU_NODE_VALUES_FARRAY_CLASS(klass)->nrm2 = visu_vibration_nrm2;
  VISU_NODE_VALUES_VECTOR_CLASS(klass)->shift = _shift;
  
  /**
   * VisuVibration::n-modes:
   *
   * Holds the number of modes for this vibration.
   *
   * Since: 3.8
   */
  _properties[N_MODE_PROP] =
    g_param_spec_uint("n-modes", "N-modes", "number of modes",
                      1, G_MAXINT, 1,
                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
  /**
   * VisuVibration::frequency:
   *
   * Holds the frequency at which nodes oscilate when animated.
   *
   * Since: 3.8
   */
  _properties[FREQ_PROP] =
    g_param_spec_float("frequency", "Frequency", "oscilation frequency",
                       0.f, 50.f, 5.f, G_PARAM_READWRITE);
  /**
   * VisuVibration::amplitude:
   *
   * Holds the amplitude at which nodes oscilate when animated.
   *
   * Since: 3.8
   */
  _properties[AMPL_PROP] =
    g_param_spec_float("amplitude", "Amplitude", "oscilation amplitude",
                       0.f, G_MAXFLOAT, 1.f, G_PARAM_READWRITE);
  /**
   * VisuVibration::reduced-time:
   *
   * The time during an animation, given in reduce value [0;1] mapping
   * the real time [0;1/frequency].
   *
   * Since: 3.8
   */
  _properties[TIME_PROP] =
    g_param_spec_float("reduced-time", "Reduced time", "time during an animation",
                       0.f, G_MAXFLOAT, 0.f, G_PARAM_READWRITE);

  g_object_class_install_properties(G_OBJECT_CLASS(klass), N_PROP, _properties);
}
static void visu_animatable_interface_init(VisuAnimatableInterface *iface)
{
  iface->get_animation = _getAnimation;
}

static void visu_vibration_init(VisuVibration *self)
{
  self->priv = visu_vibration_get_instance_private(self);
  self->priv->dispose_has_run = FALSE;
  self->priv->q = (float*)0;
  self->priv->omega = (float*)0;
  self->priv->en = (float*)0;
  self->priv->u = (GArray**)0;
  self->priv->complex = (gboolean*)0;
  self->priv->norm = (float*)0;
  self->priv->n    = 0;
  self->priv->iph  = -1;
  self->priv->t    = 0.f;
  self->priv->freq = 5.f;
  self->priv->ampl = 1.f;
  self->priv->time_anim = visu_animation_new(G_OBJECT(self), "reduced-time");
}

static void visu_vibration_dispose(GObject* obj)
{
  VisuVibration *self;
  guint i;

  self = VISU_VIBRATION(obj);
  if (self->priv->dispose_has_run)
    return;
  self->priv->dispose_has_run = TRUE;

  g_object_unref(self->priv->time_anim);
  for (i = 0; i < self->priv->n; i++)
    if (self->priv->u[i])
      g_array_unref(self->priv->u[i]);

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

  g_return_if_fail(obj);

  self = VISU_VIBRATION(obj);
  g_free(self->priv->q);
  g_free(self->priv->omega);
  g_free(self->priv->en);
  g_free(self->priv->u);
  g_free(self->priv->complex);
  g_free(self->priv->norm);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_vibration_parent_class)->finalize(obj);
}
static void visu_vibration_get_property(GObject* obj, guint property_id,
                                        GValue *value, GParamSpec *pspec)
{
  VisuVibration *self = VISU_VIBRATION(obj);

  switch (property_id)
    {
    case N_MODE_PROP:
      g_value_set_uint(value, self->priv->n);
      break;
    case FREQ_PROP:
      g_value_set_float(value, self->priv->freq);
      break;
    case AMPL_PROP:
      g_value_set_float(value, self->priv->ampl);
      break;
    case TIME_PROP:
      if (visu_animation_isRunning(self->priv->time_anim))
        visu_animation_getTo(self->priv->time_anim, value);
      else
        g_value_set_float(value, tool_modulo_float(self->priv->t, 1.f));
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_vibration_set_property(GObject* obj, guint property_id,
                                        const GValue *value, GParamSpec *pspec)
{
  VisuVibration *self = VISU_VIBRATION(obj);

  switch (property_id)
    {
    case N_MODE_PROP:
      self->priv->n = g_value_get_uint(value);
      self->priv->q     = g_malloc(sizeof(float) * self->priv->n * 3);
      self->priv->omega = g_malloc0(sizeof(float) * self->priv->n);
      self->priv->en    = g_malloc0(sizeof(float) * self->priv->n);
      self->priv->norm  = g_malloc(sizeof(float) * self->priv->n);
      self->priv->u     = g_malloc0(sizeof(GArray*) * self->priv->n);
      self->priv->complex = g_malloc(sizeof(gboolean) * self->priv->n);
      break;
    case FREQ_PROP:
      visu_vibration_setUserFrequency(self, g_value_get_float(value));
      break;
    case AMPL_PROP:
      visu_vibration_setAmplitude(self, g_value_get_float(value));
      break;
    case TIME_PROP:
      if (!visu_animatable_animateFloat(VISU_ANIMATABLE(self), self->priv->time_anim,
                                        g_value_get_float(value),
                                        500, FALSE, VISU_ANIMATION_SIN))
        visu_vibration_setTime(self, g_value_get_float(value));
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static gfloat visu_vibration_nrm2(const VisuNodeValuesFarray *vect _U_, const GValue *value)
{
  gfloat *diff;

  diff = (gfloat*)g_value_get_pointer(value);
  if (diff)
    return diff[mod] * diff[mod] +
      diff[Vx] * diff[Vx] + diff[Vy] * diff[Vy] + diff[Vz] * diff[Vz];
  else
    return -1.f;
}
static VisuAnimation* _getAnimation(const VisuAnimatable *animatable,
                                    const gchar *prop)
{
  g_return_val_if_fail(VISU_IS_VIBRATION(animatable), (VisuAnimation*)0);

  if (!g_strcmp0(prop, "reduced-time"))
    return VISU_VIBRATION(animatable)->priv->time_anim;
  return (VisuAnimation*)0;
}

/**
 * visu_vibration_new:
 * @data: a #VisuData object.
 * @label: a label.
 * @n: an integer.
 *
 * Creates a #VisuVibration object to store @n vibration modes for @data.
 *
 * Since: 3.8
 *
 * Returns: (transfer full): a newly created #VisuVibration object.
 **/
VisuVibration* visu_vibration_new(VisuData *data,
                                  const gchar *label,
                                  guint n)
{
  VisuVibration *vib;

  vib = VISU_VIBRATION(g_object_new(VISU_TYPE_VIBRATION,
                                    "nodes", data, "label", label,
                                    "type", G_TYPE_FLOAT,
                                    "n-elements", nData,
                                    "n-modes", n, NULL));

  g_signal_connect_swapped(G_OBJECT(data), "PopulationIncrease",
                           G_CALLBACK(onPopulationChanged), (gpointer)vib);
  vib->priv->signal =
    g_signal_connect_swapped(G_OBJECT(data), "position-changed",
                             G_CALLBACK(onPositionChanged), (gpointer)vib);
  return vib;
}

/**
 * visu_vibration_setDisplacements:
 * @vib: a #VisuVibration object.
 * @iph: a phonon id.
 * @vibes: (element-type float): a set of displacement vectors.
 * @complex: a flag.
 *
 * visu_vibration_init() must have been call before. This routine is
 * used to define a set of displacement vectors, corresponding to one
 * phonon. The displacement vectors can be @complex, in that case
 * @vibes contains 6 values for each #VisuNode.
 *
 * Since: 3.5
 *
 * Returns: TRUE on success.
 */
gboolean visu_vibration_setDisplacements(VisuVibration *vib, guint iph,
                                         const GArray *vibes, gboolean complex)
{
  gfloat u[6], *vals;
  guint i, nSet;

  g_return_val_if_fail(VISU_IS_VIBRATION(vib) && vibes, FALSE);
  g_return_val_if_fail(iph < vib->priv->n, FALSE);

  DBG_fprintf(stderr, "Vibration: set vibration data for phonon %d.\n", iph);

  nSet = (complex) ? 6 : 3;

  /* We copy the displacement information into the Vibration
     structure for the given phonon. */
  vib->priv->norm[iph] = 0.f;
  if (vib->priv->u[iph])
    g_array_unref(vib->priv->u[iph]);
  vib->priv->u[iph] = g_array_sized_new(FALSE, FALSE, sizeof(gfloat), vibes->len);
  g_array_append_vals(vib->priv->u[iph], vibes->data, vibes->len);
  vib->priv->complex[iph] = complex;
  for (i = 0; i < vibes->len / nSet; i++)
    {
      vals = &g_array_index(vibes, gfloat, i * nSet);
      u[0] = vals[0];
      u[1] = vals[1];
      u[2] = vals[2];
      u[3] = (complex) ? vals[3] : 0.f;
      u[4] = (complex) ? vals[4] : 0.f;
      u[5] = (complex) ? vals[5] : 0.f;
      vib->priv->norm[iph] = MAX(u[0] * u[0] +
                                 u[1] * u[1] +
                                 u[2] * u[2] +
                                 u[3] * u[3] +
                                 u[4] * u[4] +
                                 u[5] * u[5], vib->priv->norm[iph]);
      DBG_fprintf(stderr, "| %d -> (%g,%g,%g) + i (%g,%g,%g)\n", i,
		  u[0], u[1], u[2],
		  u[3], u[4], u[5]);
    }
  DBG_fprintf(stderr, "Vibration: set norm to %f.\n", vib->priv->norm[iph]);

  return TRUE;
}
static gfloat _getPhase(const VisuVibration *vib, guint iph,
                        const VisuData *data, const VisuBox *box,
                        const VisuNode *node)
{
  gfloat xyz[3], red[3];

  /* Set the phase. */
  visu_data_getNodePosition(data, node, xyz);
  visu_box_convertXYZtoBoxCoordinates(box, red, xyz);
  return 2.f * G_PI *
    (red[0] * vib->priv->q[3 * iph + 0] +
     red[1] * vib->priv->q[3 * iph + 1] +
     red[2] * vib->priv->q[3 * iph + 2]);
}
/**
 * visu_vibration_setCurrentMode:
 * @vib: a #VisuVibration object.
 * @iph: a phonon id.
 * @error: a location for a possible error.
 *
 * Set all node displacements to zero and setup the displacement
 * vector (u+iv) for each #VisuNode. After this call,
 * visu_vibration_play() will move the nodes according to their vibration.
 *
 * Since: 3.5
 *
 * Returns: TRUE if its the first time #VisuNodes are displaced.
 */
gboolean visu_vibration_setCurrentMode(VisuVibration *vib,
                                       guint iph, GError **error)
{
  gboolean valid;
  VisuNodeValuesIter iter;
  VisuNodeArray *nodes;
  VisuBox *box;
  gfloat vibes[nData], *vals;
  gint iNode;
  guint nSet;

  g_return_val_if_fail(VISU_IS_VIBRATION(vib), FALSE);
  g_return_val_if_fail(iph < vib->priv->n, FALSE);

  DBG_fprintf(stderr, "Vibration: set current mode to %d.\n", iph);

  nodes = visu_node_values_getArray(VISU_NODE_VALUES(vib));
  if (!vib->priv->u[iph] && VISU_IS_DATA_LOADABLE(nodes))
    {
      g_signal_handler_block(G_OBJECT(nodes), vib->priv->signal);
      visu_data_freePopulation(VISU_DATA(nodes));
      valid = visu_data_loadable_load(VISU_DATA_LOADABLE(nodes),
                                      iph, (GCancellable*)0, error);
      g_signal_handler_unblock(G_OBJECT(nodes), vib->priv->signal);
      if (!valid)
        {
          g_object_unref(nodes);
          return FALSE;
        }
    }
  g_return_val_if_fail(iph < vib->priv->n, FALSE);
  g_return_val_if_fail(vib->priv->u[iph], FALSE);

  vib->priv->iph = iph;
  nSet = vib->priv->complex[iph] ? 6 : 3;
  g_return_val_if_fail(vib->priv->u[iph]->len == visu_node_array_getNOriginalNodes(nodes) * nSet, FALSE);

  visu_vibration_resetPosition(vib);
  
  /* We copy for all the nodes the displacements into the VISU_VIBRATION_ID
     node property for each node*/
  box = visu_boxed_getBox(VISU_BOXED(nodes));
  valid = visu_node_values_iter_new(&iter, ITER_NODES_BY_TYPE,
                                    VISU_NODE_VALUES(vib));
  while (valid)
    {
      iNode = visu_node_array_getOriginal(nodes, iter.iter.node->number);
      iNode = (iNode >= 0) ? iNode : (int)iter.iter.node->number;
      vals = &g_array_index(vib->priv->u[iph], gfloat, iNode * nSet);
      vibes[Ux] = vals[0];
      vibes[Uy] = vals[1];
      vibes[Uz] = vals[2];
      vibes[mod] = G_MAXFLOAT;
      vibes[Vx] = vib->priv->complex[iph] ? vals[3] : 0.f;
      vibes[Vy] = vib->priv->complex[iph] ? vals[4] : 0.f;
      vibes[Vz] = vib->priv->complex[iph] ? vals[5] : 0.f;
      vibes[dx] = 0.f;
      vibes[dy] = 0.f;
      vibes[dz] = 0.f;
      vibes[qr] = _getPhase(vib, iph, VISU_DATA(nodes), box, iter.iter.node);

      g_value_set_pointer(&iter.value, vibes);
      visu_node_values_setAt(VISU_NODE_VALUES(vib),
                             iter.iter.node, &iter.value);

      valid = visu_node_values_iter_next(&iter);
    }
  g_object_unref(nodes);

  if (vib->priv->t != 0.f)
    vibrationAt(vib, TRUE);

  return TRUE;
}
/**
 * visu_vibration_setCharacteristic:
 * @vib: a #VisuVibration object.
 * @iph: a phonon id.
 * @q: a reciprocal vector.
 * @en: the phonon energy.
 * @omega: the phonon frequency.
 *
 * This routine is used to define the characteristics of a given
 * phonon.
 *
 * Since: 3.5
 *
 * Returns: TRUE on success.
 */
gboolean visu_vibration_setCharacteristic(VisuVibration *vib, guint iph,
                                          const float q[3], float en, float omega)
{
  g_return_val_if_fail(VISU_IS_VIBRATION(vib), FALSE);
  g_return_val_if_fail(iph < vib->priv->n, FALSE);

  vib->priv->q[iph * 3 + 0] = q[0];
  vib->priv->q[iph * 3 + 1] = q[1];
  vib->priv->q[iph * 3 + 2] = q[2];
  vib->priv->omega[iph]     = omega;
  vib->priv->en[iph]        = en;

  return TRUE;
}
/**
 * visu_vibration_getCharacteristic:
 * @vib: a #VisuVibration object.
 * @iph: a phonon id.
 * @q: a location for the reciprocal vector.
 * @en: a locattion for the phonon energy.
 * @omega: a location for the phonon frequency.
 *
 * This routine is used to get the characteristics of a given
 * phonon, see visu_vibration_setCharacteristic() to set them.
 *
 * Since: 3.5
 *
 * Returns: TRUE on success.
 */
gboolean visu_vibration_getCharacteristic(const VisuVibration *vib, guint iph,
                                          float q[3], float *en, float *omega)
{
  g_return_val_if_fail(VISU_IS_VIBRATION(vib), FALSE);
  g_return_val_if_fail(iph < vib->priv->n, FALSE);

  q[0]   = vib->priv->q[iph * 3 + 0];
  q[1]   = vib->priv->q[iph * 3 + 1];
  q[2]   = vib->priv->q[iph * 3 + 2];
  if (omega)
    *omega = vib->priv->omega[iph];
  if (en)
    *en    = vib->priv->en[iph];

  return TRUE;
}
/**
 * visu_vibration_setUserFrequency:
 * @vib: a #VisuVibration object.
 * @freq: a frequency.
 *
 * Change the frequency at which phonons are played with visu_vibration_play().
 *
 * Since: 3.5
 */
gboolean visu_vibration_setUserFrequency(VisuVibration *vib, float freq)
{
  float f;

  g_return_val_if_fail(VISU_IS_VIBRATION(vib), FALSE);
  g_return_val_if_fail(freq > 0.f || vib->priv->iph >= 0, FALSE);

  if (freq > 0.f)
    f = freq;
  else
    f = vib->priv->omega[vib->priv->iph] ? vib->priv->omega[vib->priv->iph] : 1.f;
  if (f == vib->priv->freq)
    return FALSE;

  vib->priv->freq = f;
  g_object_notify_by_pspec(G_OBJECT(vib), _properties[FREQ_PROP]);

  if (visu_animation_isRunning(vib->priv->time_anim))
    visu_vibration_animate(vib);

  return TRUE;
}
/**
 * visu_vibration_setAmplitude:
 * @vib: a #VisuVibration object.
 * @ampl: an amplitude.
 *
 * Change the amplitude at which phonon are displayed on screen when
 * using visu_vibration_play().
 * 
 * Since: 3.5
 *
 * Returns: TRUE if amplitude is actually changed.
 */
gboolean visu_vibration_setAmplitude(VisuVibration *vib, float ampl)
{
  g_return_val_if_fail(VISU_IS_VIBRATION(vib), FALSE);

  if (vib->priv->ampl == ampl)
    return FALSE;

  vib->priv->ampl = ampl;
  g_object_notify_by_pspec(G_OBJECT(vib), _properties[AMPL_PROP]);

  if (visu_animation_isRunning(vib->priv->time_anim))
    visu_vibration_animate(vib);
  else
    vibrationAt(vib, TRUE);

  return TRUE;
}
/**
 * visu_vibration_setTime:
 * @vib: a #VisuVibration object.
 * @at: a given reduced time in [0;1].
 *
 * Defines the current time offset used to displace nodes according to
 * phonon distortions.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value has been actually changed.
 **/
gboolean visu_vibration_setTime(VisuVibration *vib, gfloat at)
{
  gfloat t;
  
  g_return_val_if_fail(VISU_IS_VIBRATION(vib), FALSE);

  t = tool_modulo_float(at, 1.f);
  if (vib->priv->t == t)
    return FALSE;

  vib->priv->t = t;
  g_object_notify_by_pspec(G_OBJECT(vib), _properties[TIME_PROP]);

  vibrationAt(vib, TRUE);
  
  return TRUE;
}

/**
 * visu_vibration_animate:
 * @vib: a #VisuVibration object.
 *
 * Starts animating phonons.
 *
 * Since: 3.8
 **/
void visu_vibration_animate(VisuVibration *vib)
{
  g_return_if_fail(VISU_IS_VIBRATION(vib));

  
  if (visu_animation_isRunning(vib->priv->time_anim))
    visu_animation_abort(vib->priv->time_anim);

  visu_animatable_animateFloat(VISU_ANIMATABLE(vib), vib->priv->time_anim,
                               vib->priv->t + 1.f,
                               5000.f / vib->priv->freq,
                               TRUE, VISU_ANIMATION_LINEAR);
}

/**
 * visu_vibration_getNPhonons:
 * @vib: a #VisuVibration object.
 *
 * Retrieves the number of phonons in @vib.
 *
 * Since: 3.5
 *
 * Returns: a number of phonons.
 */
guint visu_vibration_getNPhonons(VisuVibration *vib)
{
  g_return_val_if_fail(VISU_IS_VIBRATION(vib), 0);

  return vib->priv->n;
}
/**
 * visu_vibration_getCurrentMode:
 * @data: a #VisuData object.
 *
 * Retrieves the phonon that is currently applied to @data.
 *
 * Since: 3.5
 *
 * Returns: a phonon id.
 */
/* guint visu_vibration_getCurrentMode(VisuData *data) */
/* { */
/*   Vibration *vib; */

/*   g_return_val_if_fail(data, 0); */

/*   vib = (Vibration*)g_object_get_data(G_OBJECT(data), "tmp_vib_object"); */
/*   g_return_val_if_fail(vib, 0); */

/*   return vib->iph; */
/* } */

static void _shift(const VisuNodeValuesVector *vect, const VisuNode *node,
                   gfloat dxyz[3])
{
  GValue vibeValue = G_VALUE_INIT;
  gfloat *values;

  dxyz[0] = 0.f;
  dxyz[1] = 0.f;
  dxyz[2] = 0.f;

  visu_node_values_getAt(VISU_NODE_VALUES(vect), node, &vibeValue);
  values = (float*)g_value_get_pointer(&vibeValue);
  if (!values)
    return;

  dxyz[0] = values[dx];
  dxyz[1] = values[dy];
  dxyz[2] = values[dz];
}
static void vibrationAt(VisuVibration *vib, gboolean withPhase)
{
  float t, c, wdtr, wdti, dU[3];
  gfloat *dxyz;
  VisuNodeValuesIter iter;
  VisuNodeArray *data;

  g_return_if_fail(VISU_IS_VIBRATION(vib));

  if (vib->priv->iph < 0)
    return;

  c =  AMPL_COEFF  * vib->priv->ampl / vib->priv->norm[vib->priv->iph];
  t = -2.f * G_PI * vib->priv->t;

  DBG_fprintf(stderr, "Vibration: incr %d\n", withPhase);
  DBG_fprintf(stderr, " | t = %g, w = %g.\n", vib->priv->t, vib->priv->freq);
  DBG_fprintf(stderr, " | c = %g, t = %g.\n", c, t);

  data = visu_node_values_getArray(VISU_NODE_VALUES(vib));
  visu_node_array_startMoving(data);
  for (visu_node_values_iter_new(&iter, ITER_NODES_BY_TYPE,
                                 VISU_NODE_VALUES(vib));
       iter.iter.node; visu_node_values_iter_next(&iter))
    {
      dxyz = (gfloat*)visu_node_values_farray_getAtIter(VISU_NODE_VALUES_FARRAY(vib), &iter);
      /* calculate xyz(t) */
      if (dxyz)
        {
          DBG_fprintf(stderr, " | dx = %f, dy = %f, dz = %f\n",
                      dxyz[mod], dxyz[theta], dxyz[phi]);
          wdtr = c * sin(t + (withPhase ? dxyz[qr] : 0.f));
          wdti = -c * cos(t + (withPhase ? dxyz[qr] : G_PI_2));
          dU[0] = dxyz[Ux] * wdtr + dxyz[Vx] * wdti - dxyz[dx];
          dU[1] = dxyz[Uy] * wdtr + dxyz[Vy] * wdti - dxyz[dy];
          dU[2] = dxyz[Uz] * wdtr + dxyz[Vz] * wdti - dxyz[dz];
          visu_node_array_shiftNode(data, iter.iter.node->number, dU);
          dxyz[dx] += dU[0];
          dxyz[dy] += dU[1];
          dxyz[dz] += dU[2];

          DBG_fprintf(stderr, " | dx = %f, dy = %f, dz = %f\n",
                      dU[0], dU[1], dU[2]);
        }
    }
  g_signal_handler_block(G_OBJECT(data), vib->priv->signal);
  visu_node_array_completeMoving(data);
  g_signal_handler_unblock(G_OBJECT(data), vib->priv->signal);
  g_object_unref(data);
}
/**
 * visu_vibration_resetPosition:
 * @vib: a #VisuVibration object.
 *
 * Reset the node position of the given VisuData.
 *
 * Since: 3.5
 */
void visu_vibration_resetPosition(VisuVibration *vib)
{
  visu_vibration_setTime(vib, 0.f);
  vibrationAt(vib, FALSE);
}
/**
 * visu_vibration_setZeroTime:
 * @vib: a #VisuVibration object.
 *
 * Reset the position of phonons to use position at time equals zero
 * (so applying just the q vector displacement).
 *
 * Since: 3.5
 */
void visu_vibration_setZeroTime(VisuVibration *vib)
{
  visu_vibration_setTime(vib, 0.f);
  vibrationAt(vib, TRUE);
}
static void onPopulationChanged(VisuVibration *vib, GArray *ids, VisuData *dataObj)
{
  guint i;
  VisuNode *node;
  VisuBox *box;
  GValue vibeValue = {0.0, {{0.0}, {0.0}}};
  float *dxyz;

  if (vib->priv->iph < 0 || !ids)
    return;

  DBG_fprintf(stderr, "Vibration: on population increase recalculate phase.\n");
  box = visu_boxed_getBox(VISU_BOXED(dataObj));
  /* We need to recalculate the phase of the new ones. */
  for (i = 0; i < ids->len; i++)
    {
      node = visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj),
                                       g_array_index(ids, guint, i));
      visu_node_values_getAt(VISU_NODE_VALUES(vib), node, &vibeValue);
      dxyz = (float*)g_value_get_pointer(&vibeValue);
      g_return_if_fail(dxyz);
      dxyz[qr] = _getPhase(vib, vib->priv->iph, dataObj, box, node);
    }
}
static void onPositionChanged(VisuVibration *vib, GArray *ids, VisuData *dataObj)
{
  VisuNodeValuesIter iter;
  VisuBox *box;
  float *dxyz;
  gboolean valid;
  guint i;
  VisuNode *node;
  GValue vibeValue = G_VALUE_INIT;

  DBG_fprintf(stderr, "Vibration: on position changed recalculate phase.\n");
  if (vib->priv->iph < 0 || !vib->priv->u[vib->priv->iph])
    return;

  box = visu_boxed_getBox(VISU_BOXED(dataObj));
  if (!ids)
    {
      valid = visu_node_values_iter_new(&iter, ITER_NODES_BY_TYPE,
                                        VISU_NODE_VALUES(vib));
      while (valid)
        {
          /* Convert GValue to Float[3]  */
          dxyz = (float*)g_value_get_pointer(&iter.value);
          g_return_if_fail(dxyz);
          dxyz[qr] = _getPhase(vib, vib->priv->iph, dataObj, box, iter.iter.node);

          valid = visu_node_values_iter_next(&iter);
        }
    }
  else
    for (i = 0; i < ids->len; i++)
      {
        node = visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj),
                                         g_array_index(ids, guint, i));
        visu_node_values_getAt(VISU_NODE_VALUES(vib), node, &vibeValue);
        dxyz = (float*)g_value_get_pointer(&vibeValue);
        g_return_if_fail(dxyz);
        dxyz[qr] = _getPhase(vib, vib->priv->iph, dataObj, box, node);
      }
}

/**
 * visu_data_getVibration:
 * @dataObj: a #VisuData object.
 * @nModes: an integer.
 *
 * Retrieves the #VisuNodeValuesVector object used to store the
 * vibration data. If this object is not existing already, it is
 * created with @nModes modes.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuNodeValuesVector owned by V_Sim.
 **/
VisuVibration* visu_data_getVibration(VisuData *dataObj, guint nModes)
{
  VisuVibration *vect;

  if (!dataObj)
    return (VisuVibration*)0;

  vect = (VisuVibration*)visu_data_getNodeProperties(dataObj, VISU_VIBRATION_ID);
  if (!vect && nModes > 0)
    {
      vect = visu_vibration_new(dataObj, VISU_VIBRATION_ID, nModes);
      visu_data_addNodeProperties(dataObj, VISU_NODE_VALUES(vect));
    }
  g_return_val_if_fail(!nModes || (vect && nModes && vect->priv->n == nModes), (VisuVibration*)0);
  return vect;
}
