/*
 * javaIdle.c --
 *
 *	This file contains the native methods for the IdleHandler class.
 *
 * Copyright (c) 1998 by Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: javaIdle.c,v 1.7 2006/07/26 20:55:27 mdejong Exp $
 */

#include "java.h"
#include "javaNative.h"

/*
 * Static functions used in this file.
 */

static void	JavaIdleProc(ClientData clientData);

/*
 *----------------------------------------------------------------------
 *
 * Java_tcl_lang_IdleHandler_doWhenIdle --
 *
 *	Create a C level idle handler for a Java IdleHandler object.
 *
 * Results:
 *	Retuns an address to be stored in clientData.
 *
 * Side effects:
 *	Will create a new global ref to this object.
 *
 *----------------------------------------------------------------------
 */

jlong JNICALL
Java_tcl_lang_IdleHandler_doWhenIdle(
    JNIEnv *env,		/* Java environment. */
    jobject idle)		/* Handle to IdleHandler object. */
{
    jobject gref = (*env)->NewGlobalRef(env, idle);

    /*
     * It is not possible to store the global ref as an Object
     * field because the garbage collector could move the
     * object in memory before cancel was invoked. Instead,
     * store it as a long.
     */

    jlong clientData = 0;
    *(jobject *)&clientData = gref;

    /*
    fprintf(stderr, "doWhenIdle idle instance is %x\n", idle);
    fprintf(stderr, "doWhenIdle wrote long %llx\n", clientData);
    fprintf(stderr, "doWhenIdle gref is %x\n", gref);
    */

    Tcl_DoWhenIdle(JavaIdleProc, (ClientData) gref);
    return clientData;
}

/*
 *----------------------------------------------------------------------
 *
 * Java_tcl_lang_IdleHandler_cancelIdleCall --
 *
 *	Delete a C level idle handler for a Java IdleHandler object.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void JNICALL
Java_tcl_lang_IdleHandler_cancelIdleCall(
    JNIEnv *env,		/* Java environment. */
    jobject idle,		/* Handle to IdleHandler object. */
    jlong clientData)		/* Ptr to global ref passed as client data */
{
    jobject gref = *(jobject *)&clientData;

    /*
    fprintf(stderr, "cancelIdleCall idle instance is %x\n", idle);
    fprintf(stderr, "cancelIdleCall read long %llx\n", clientData);
    fprintf(stderr, "cancelIdleCall gref is %x\n", gref);
    */

    Tcl_CancelIdleCall(JavaIdleProc, (ClientData) gref);

    /*
     * Free global ref so object can be garbage collected.
     */

    (*env)->DeleteGlobalRef(env, gref);
}

/*
 *----------------------------------------------------------------------
 *
 * JavaIdleProc --
 *
 *	This function is called when a Java idle event occurs.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Invokes arbitrary Java code.
 *
 *----------------------------------------------------------------------
 */

static void
JavaIdleProc(
    ClientData clientData)	/* Global IdleHandler reference */
{
    JNIEnv *env = JavaGetEnv();
    jobject exception;
    jobject gref = (jobject) clientData;
    JavaInfo* jcache = JavaGetCache();
    int fromJNIMethod = JavaNotifierInDoOneEvent();

#ifdef TCLBLEND_DEBUG
    fprintf(stderr, "TCLBLEND_DEBUG: JavaIdleProc : fromJNIMethod is %d\n", fromJNIMethod);
#endif /* TCLBLEND_DEBUG */

    /*fprintf(stderr, "JavaIdleProc got global ref %x\n", gref);*/

    if ((*env)->ExceptionOccurred(env)) {
	(*env)->ExceptionDescribe(env);
	Tcl_Panic("JavaIdleProc : unexpected pending exception");
    }

    /*
     * Call IdleHandler.invoke(). If an exception was raised and
     * this method was invoked as a result of a Notifier.doOneEvent
     * then propagate the exception. If this method was
     * called from Tcl, then just print the error since we can't
     * leave the exception pending.
     */

    (*env)->CallVoidMethod(env, gref, jcache->invokeIdle);
    exception = (*env)->ExceptionOccurred(env);
    if (exception) {
	if (!fromJNIMethod) {
	    fprintf(stderr, "Java Exception in JavaIdleProc (Tcl_DoWhenIdle handler)\n");
	    (*env)->ExceptionDescribe(env);
	}
	(*env)->ExceptionClear(env);
    }

    /*
     * Free global ref so object can be garbage collected.
     */

    (*env)->DeleteGlobalRef(env, gref);

    /*
     * Rethrow the exception.
     */

    if (exception) {
	 if (fromJNIMethod) {
#ifdef TCLBLEND_DEBUG
    fprintf(stderr, "TCLBLEND_DEBUG: JavaIdleProc : rethrowing Exception\n");
#endif /* TCLBLEND_DEBUG */

	     (*env)->Throw(env, exception);
	 }
	 (*env)->DeleteLocalRef(env, exception);
    }
}

