#include "../include/zf.h"

typedef struct Animator Animator;

struct Animator
{
    void* data;

    ZfSmartPointer* smart_pointer;

    ZfAnimate* animate;
};

/* "pass_through" smart pointer */
static ZfSmartPointer animator_smart_pointer;

/*
  A list of animators. Note that these are wrappers to animator data
  and functions. The data added to the list is *not* stored directly!
*/
static GList* list;

/*
  If the animator data is invalid, this animator is invalid.
*/
static bool
animator_is_valid(const Animator* animator)
{
    return animator->smart_pointer->is_valid(animator->data);
}

/*
  Nothing can *ever* reference animator data aside from when adding
  the animator to the animator list. At this point, the animator data
  will need to be referenced. So that's what we do here.
*/
static void
animator_reference(Animator* animator)
{
    animator->smart_pointer->reference(animator->data);
}

/*
  Since nothing can *ever* reference the animator, we know that this
  signal means that the animator data is invalid and that it is being
  removed from the system by the garbage collector. Since it is no
  longer part of the system, it is released here. The animator will
  also be removed from the list by the garbage collector. Therefore,
  we should delete the animator to avoid a memory hole.
*/
static void
animator_release(Animator* animator)
{
    animator->smart_pointer->release(animator->data);

    g_free(animator);
}

/*
  Called by zf_animation_system_step
*/
static void
animate(Animator* animator)
{
    animator->animate(animator->data);
}

/*
  Initialise animation system.
  
  Create an empty list of animators and create the "pass-through" (or
  "proxy") smart pointer for animator instances.
*/
void
zf_animation_system_init(void)
{
    list = 0;

    animator_smart_pointer.is_valid = (ZfIsValid*) animator_is_valid;
    animator_smart_pointer.reference = (ZfReference*) animator_reference;
    animator_smart_pointer.release = (ZfRelease*) animator_release;
}

/*
  Close animation system.

  Go through all animators and release them. Finally, free the list.
*/
void
zf_animation_system_close(void)
{
    zf_list_destroy(list,
		    &animator_smart_pointer);
}

/*
  Step animation system.

  Go through all animators. Check if they are valid. If so, animate
  them. Perform garbage collection.
*/
void
zf_animation_system_step(void)
{
    /*
      printf("no. of animators = %u\n", g_list_length(list));
      
      {
	GList* li;
	
	for (li = list ; li ; li=li->next)
	{
	printf("p = %p ", ((Animator*)li)->animate);
	}
	printf("\n");
	}
    */
    zf_list_foreach_valid(list,
			  &animator_smart_pointer,
			  (ZfAnimate*) animate);

    list = zf_list_remove_invalids(list,
				   &animator_smart_pointer);
}

/*
  Add an animator to the animation system.

  The animator can be any object that provides functions required for
  list management and an animation function. The animator will be
  animated at each timestep by means of the animate function.
*/
void
zf_animation_system_add(void* data,
			ZfSmartPointer* smart_pointer,
			ZfAnimate* data_animate)
{
    Animator* animator;

    // printf("%s() : new animation func pointer = %p\n", __FUNCTION__, (void*) data_animate);

    animator = g_new(Animator, 1);

    animator->data = data;
    animator->smart_pointer = smart_pointer;
    animator->animate = data_animate;

    //printf("beforeanimators = %u\n", g_list_length(list));
    list = zf_list_add_data(list,
			    animator,
			    &animator_smart_pointer);

    // printf("add animators = %u\n", g_list_length(list));
  /*  
    {
	GList* li;
	bool found = false;
	
	for (li = list ; li ; li=li->next)
	{
	    printf("checking %p vs %p\n", ((Animator*)li->data)->animate, data_animate);
	    printf("data should have been %p\n", li->data);
	    if(((Animator*)li->data)->animate == data_animate)
	    {
		found = true;
	    }
	}

	if(found)
	    printf(" %p has been added properly\n", data_animate);
	else
	{
	    printf(" not found! %p\n", data_animate);
	    exit(1);
	}
    }
*/
}

unsigned int
zf_animation_system_get_animator_count(void)
{
    return g_list_length(list);
}

void
zf_animation_system_print(void)
{
    printf("no. of animators = %u\n", g_list_length(list));
}
