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

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

#include "config.h"

/**
 * SECTION:iface_animatable
 * @short_description: an interface defining object with animatable properties.
 *
 * <para>An object with properties that can be animated whould
 * implement this interface.</para>
 */


/* enum { */
/*   PROP_0, */
/*   N_PROPS */
/* }; */
/* static GParamSpec *properties[N_PROPS]; */

enum
  {
    ANIMATE_SIGNAL,
    NB_SIGNAL
  };
static guint _signals[NB_SIGNAL] = { 0 };

/* NodeMasker interface. */
G_DEFINE_INTERFACE(VisuAnimatable, visu_animatable, G_TYPE_OBJECT)

static void g_cclosure_marshal_ANIM_PARAM(GClosure *closure,
                                          GValue *return_value,
                                          guint n_param_values,
                                          const GValue *param_values,
                                          gpointer invocation_hint _U_,
                                          gpointer marshal_data)
{
  typedef gboolean (*callbackFunc)(gpointer data1, VisuAnimation *anim,
                                   const GValue *to,
                                   gulong duration, gboolean loop,
                                   VisuAnimationType type,
                                   gpointer data2);
  register callbackFunc callback;
  register GCClosure *cc = (GCClosure*)closure;
  register gpointer data1, data2;

  g_return_if_fail(n_param_values == 6);

  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);

  DBG_fprintf(stderr, "Visu Animatable: marshall callback for animation %p.\n",
              (gpointer)g_value_get_object(param_values + 1));
  g_value_set_boolean(return_value,
                      callback(data1, g_value_get_object(param_values + 1),
                               g_value_get_boxed(param_values + 2),
                               g_value_get_ulong(param_values + 3),
                               g_value_get_boolean(param_values + 4),
                               g_value_get_uint(param_values + 5),
                               data2));
}

static void visu_animatable_default_init(VisuAnimatableInterface *iface)
{
  /**
   * VisuAnimatable::animate:
   * @self: the object emitting the signal.
   * @animation: the object emitting the signal.
   * @value: the target value.
   * @at: the current tick time.
   * @loop: if animation should loop.
   * @type: the animation type.
   *
   * This signal is emitted when properties start an animation.
   *
   * Since: 3.8
   *
   * Returns: TRUE if animation is successfully started.
   */
  _signals[ANIMATE_SIGNAL] =
    g_signal_new("animate", G_TYPE_FROM_INTERFACE(iface),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                 0, NULL, NULL, g_cclosure_marshal_ANIM_PARAM,
                 G_TYPE_BOOLEAN, 5, VISU_TYPE_ANIMATION, G_TYPE_VALUE,
                 G_TYPE_ULONG, G_TYPE_BOOLEAN, G_TYPE_UINT);
}

/**
 * visu_animatable_getAnimation:
 * @animatable: a #VisuAnimatable object.
 * @prop: a property name.
 *
 * A method to retrieve the #VisuAnimation from @animatable given the
 * property name @prop.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuAnimation object or %NULL if none
 * is associated to @prop.
 **/
VisuAnimation* visu_animatable_getAnimation(const VisuAnimatable *animatable,
                                            const gchar *prop)
{
  g_return_val_if_fail(VISU_IS_ANIMATABLE(animatable), (VisuAnimation*)0);
  g_return_val_if_fail(VISU_ANIMATABLE_GET_INTERFACE(animatable)->get_animation, (VisuAnimation*)0);
  
  return VISU_ANIMATABLE_GET_INTERFACE(animatable)->get_animation(animatable, prop);
}

/**
 * visu_animatable_animate:
 * @animatable: a #VisuAnimatable object.
 * @anim: a #VisuAnimation object.
 * @to: a destination value.
 * @duration: a duration in milliseconds.
 * @loop: a boolean.
 * @type: a type.
 *
 * Animates @anim from its current value to @to. The animation is
 * scheduled to last @duration and will follow the evolution described
 * by @type. If @loop is true, when the value of @anim reaches @to, it
 * goes back to its current value and animates again to reach @to. In
 * that case, use visu_animation_abort() on @anim to stop it.
 *
 * Since: 3.8
 *
 * Returns: TRUE if animation is started.
 **/
gboolean visu_animatable_animate(VisuAnimatable *animatable, VisuAnimation *anim,
                                 const GValue *to, gulong duration, gboolean loop, VisuAnimationType type)
{
  gboolean success;

  success = FALSE;
  g_signal_emit(animatable, _signals[ANIMATE_SIGNAL], 0, anim, to, duration * 1000, loop, type, &success);
  
  return success;
}

/**
 * visu_animatable_animateFloat:
 * @animatable: a #VisuAnimatable object.
 * @anim: a #VisuAnimation object.
 * @to: a destination value.
 * @duration: a duration in milliseconds.
 * @loop: a boolean.
 * @type: a type.
 *
 * Like visu_animatable_animate() for a float destination value.
 *
 * Since: 3.8
 *
 * Returns: TRUE if animation is started.
 **/
gboolean visu_animatable_animateFloat(VisuAnimatable *animatable, VisuAnimation *anim,
                                      float to, gulong duration, gboolean loop, VisuAnimationType type)
{
  GValue val = G_VALUE_INIT;
  gboolean success;
  
  DBG_fprintf(stderr, "Visu Animatable: emitting animate signal for %p.\n",
              (gpointer)animatable);

  g_value_init(&val, G_TYPE_FLOAT);
  g_value_set_float(&val, to);

  success = FALSE;
  g_signal_emit(animatable, _signals[ANIMATE_SIGNAL], 0, anim, &val, duration * 1000, loop, type, &success);
  
  return success;
}
/**
 * visu_animatable_animateFloatByName:
 * @animatable: a #VisuAnimatable object.
 * @prop: a property name from @animatable.
 * @to: a destination value.
 * @duration: a duration in milliseconds.
 * @loop: a boolean.
 * @type: a type.
 *
 * Like visu_animatable_animate() for a float destination value.
 *
 * Since: 3.8
 *
 * Returns: TRUE if animation is started.
 **/
gboolean visu_animatable_animateFloatByName(VisuAnimatable *animatable, const gchar *prop,
                                            float to, gulong duration, gboolean loop, VisuAnimationType type)
{
  VisuAnimation *anim;

  g_return_val_if_fail(VISU_IS_ANIMATABLE(animatable), FALSE);
  g_return_val_if_fail(VISU_ANIMATABLE_GET_INTERFACE(animatable)->get_animation, FALSE);
  
  anim = VISU_ANIMATABLE_GET_INTERFACE(animatable)->get_animation(animatable, prop);
  g_return_val_if_fail(anim, FALSE);
  return visu_animatable_animateFloat(animatable, anim, to, duration, loop, type);
}

/**
 * visu_animatable_animateDouble:
 * @animatable: a #VisuAnimatable object.
 * @anim: a #VisuAnimation object.
 * @to: a destination value.
 * @duration: a duration in milliseconds.
 * @loop: a boolean.
 * @type: a type.
 *
 * Like visu_animatable_animate() for a double destination value.
 *
 * Since: 3.8
 *
 * Returns: TRUE if animation is started.
 **/
gboolean visu_animatable_animateDouble(VisuAnimatable *animatable, VisuAnimation *anim,
                                       double to, gulong duration, gboolean loop, VisuAnimationType type)
{
  GValue val = G_VALUE_INIT;
  gboolean success;
  
  DBG_fprintf(stderr, "Visu Animatable: emitting animate signal for %p.\n",
              (gpointer)animatable);

  g_value_init(&val, G_TYPE_DOUBLE);
  g_value_set_double(&val, to);

  success = FALSE;
  g_signal_emit(animatable, _signals[ANIMATE_SIGNAL], 0, anim, &val, duration * 1000, loop, type, &success);
  return success;
}
/**
 * visu_animatable_animateDoubleByName:
 * @animatable: a #VisuAnimatable object.
 * @prop: a property name from @animatable.
 * @to: a destination value.
 * @duration: a duration in milliseconds.
 * @loop: a boolean.
 * @type: a type.
 *
 * Like visu_animatable_animate() for a double destination value.
 *
 * Since: 3.8
 *
 * Returns: TRUE if animation is started.
 **/
gboolean visu_animatable_animateDoubleByName(VisuAnimatable *animatable, const gchar *prop,
                                             double to, gulong duration, gboolean loop, VisuAnimationType type)
{
  VisuAnimation *anim;

  g_return_val_if_fail(VISU_IS_ANIMATABLE(animatable), FALSE);
  g_return_val_if_fail(VISU_ANIMATABLE_GET_INTERFACE(animatable)->get_animation, FALSE);
  
  anim = VISU_ANIMATABLE_GET_INTERFACE(animatable)->get_animation(animatable, prop);
  g_return_val_if_fail(anim, FALSE);
  return visu_animatable_animateDouble(animatable, anim, to, duration, loop, type);
}
