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

typedef struct Trigger Trigger;

struct Trigger
{
    float flux_t;

    void* data; /* trigger does not *own* this data and will *not* delete it! */
    ZfSmartPointer* smart_pointer;
    ZfSpawn* spawn;
};

static GList* trigger_list;

static ZfSmartPointer trigger_smart_pointer;


/*!
  \todo Check the call to g_list_delete_link() here, it ignores the return
  value (BAD) and also seems to have dodgy parameters, better comments
  required.
*/
static void
animate(void* nothing)
{
    float ship_flux_t;
    Trigger* trigger;

    ship_flux_t = zf_ship_query_flux_t();

    while (trigger_list
	   && ((trigger = (Trigger*)trigger_list->data)->flux_t < ship_flux_t))
    {
	GList* li;

	li = trigger_list;
#if 0
	/* move start of list */
	trigger_list = trigger_list->next;

	/* call spawn event */
	trigger->spawn(trigger->data);

	/* release trigger */
	trigger->smart_pointer->release(trigger->data);
	g_free(trigger);

	/* remove trigger from list */
	g_list_delete_link(li, li);
#endif
	/* call spawn event */
	trigger->spawn(trigger->data);
	
	/* remove trigger from list */
	trigger_list = g_list_remove_link(trigger_list, li);

	/* release trigger */
	trigger->smart_pointer->release(trigger->data);
	trigger_smart_pointer.release(trigger);
/*	g_free(trigger);*/

    }
}

static void
render(Trigger* trigger)
{
    CL_FUNCENTRY();

#if 1
    if (zf_render_system_get_tool_mode())
    {
	CLvertex trigger_position;

	/* get trigger position */
	zf_flux_query_position(&trigger_position, trigger->flux_t);
	
	glDisable(GL_COLOR_MATERIAL);
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_LIGHTING);

	glColor3f(1.0f, 0.0f, 1.0f);
	glPointSize(8.0f);
	glBegin(GL_POINTS);
	glVertex3fv((GLfloat*) &trigger_position);
	glEnd();
    }
#endif

    CL_FUNCEXIT();
}

/*
  filler methods to meet interface (in Ci would use void implements...) 
  
  these functions are for the *system* and *not* for individual triggers!!
*/
static bool
is_valid(const void* nothing)
{
    return true;
}

static void
reference(void* nothing)
{
}

static void
release(void* nothing)
{
}

void
zf_trigger_system_init(void)
{
    //printf("%s()\n", __FUNCTION__);

    trigger_smart_pointer.is_valid = is_valid;
    trigger_smart_pointer.reference = reference;
    trigger_smart_pointer.release = release;
    
    zf_animation_system_add(0, /* null */
			    &trigger_smart_pointer,
			    (ZfAnimate*)animate);

    trigger_list = 0;

    /* TESTING!!! make sure to remove this!!! 
       zf_trigger_system_add_trigger(5.0f,
       g_strdup("this was printed by a trigger\n"),
       &smart_pointer,
       (ZfSpawn*) printf);
    */
}

static void
release_trigger(Trigger* trigger)
{
    /* release data */
    trigger->smart_pointer->release(trigger->data);
    g_free(trigger);
}

void
zf_trigger_system_close(void)
{
    g_list_foreach(trigger_list, (GFunc)release_trigger, NULL);
    g_list_free(trigger_list);
}

static gint
compare_trigger(Trigger* trigger0,
		Trigger* trigger1)
{
    if (trigger0->flux_t < trigger1->flux_t)
	return -1;

    if (trigger0->flux_t > trigger1->flux_t)
	return 1;

    return 0;
}

void
zf_trigger_system_add_trigger(float flux_t,
			      void* data,
			      ZfSmartPointer* smart_pointer,
			      ZfSpawn* spawn) /* this as well!!! */
{
    Trigger* trigger;

    trigger = g_new(Trigger, 1);

    trigger->flux_t = flux_t;
    trigger->data = data;
    trigger->smart_pointer = smart_pointer;
    trigger->spawn = spawn;

    /* reference data */
    assert(trigger->smart_pointer->reference);
    trigger->smart_pointer->reference(trigger->data);
 
    trigger_list = g_list_insert_sorted(trigger_list,
					trigger,
					(GCompareFunc)compare_trigger);

    /* add trigger to render system */
    zf_render_system_add_opaque(trigger,
				&trigger_smart_pointer,
				(ZfRender*) render);
}
