#include "jg_jnu.h"
#include <sys/types.h>
#include <glib.h>
#include <glib-object.h>

#ifdef __cplusplus
extern "C" {
#endif

static jfieldID  targetFID;

void cleanup(JNIEnv *env, jobject obj) {
    // reset handle field
    jclass cls = (*env)->GetObjectClass(env, obj);
    jfieldID fid = (*env)->GetFieldID(env, cls, "handle", "I");
    (*env)->SetIntField(env, obj, fid, (jint)-1);

    // delete back pointer
    (*env)->DeleteGlobalRef(env, obj);
}

gboolean fire_method_invoker(gpointer data)
{
    JNIEnv *env = JNU_GetEnv();
    jobject obj = (jobject)data;
    jboolean exceptionThrown;
    
    jobject target = (*env)->GetObjectField(env, obj, targetFID);
    jclass cls = (*env)->GetObjectClass(env, target);
    jmethodID mid = (*env)->GetMethodID(env, cls, "fire", "()Z");

    jboolean keepFiring = (*env)->CallBooleanMethod(env, target, mid);
    (*env)->DeleteLocalRef(env, cls);
    (*env)->DeleteLocalRef(env, target);

    exceptionThrown = (*env)->ExceptionCheck(env);
    if (exceptionThrown) {
	// We cannot throw this exception, since this timer was called by GLib.
	(*env)->ExceptionDescribe(env);  // ExceptionDescribe clears exception
	keepFiring = JNI_FALSE;
    }

    if (keepFiring == JNI_FALSE) {
	cleanup(env, obj);
    }
    return keepFiring;
}

/*
 * Class:     org_gnu_glib_Timer
 * Method:    start_timer
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_org_gnu_glib_Timer_start_1timer (JNIEnv *env, jobject obj, jint interval)
{
    return (jint)g_timeout_add((gint)interval, 
			       fire_method_invoker, 
			       (gpointer)(*env)->NewGlobalRef(env, obj));
}

/*
 * Class:     org_gnu_glib_Timer
 * Method:    stop_timer
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_gnu_glib_Timer_stop_1timer (JNIEnv *env, jobject obj, jint handle)
{
    cleanup(env, obj);
    g_source_remove(handle);
}

JNIEXPORT void JNICALL Java_org_gnu_glib_Timer_initIDs (
    JNIEnv *env, jclass cls)
{
    targetFID = (*env)->GetFieldID(env, cls,
				   "target", "Lorg/gnu/glib/Fireable;");
}

#ifdef __cplusplus
}
#endif
