/*
 * Java-Gnome Bindings Library
 *
 * Copyright 1998-2002 the Java-Gnome Team, all rights reserved.
 *
 * The Java-Gnome Team Members:
 *   Jean Van Wyk <jeanvanwyk@iname.com>
 *   Jeffrey S. Morgan <jeffrey.morgan@bristolwest.com>
 *   Dan Bornstein <danfuzz@milk.com>
 *
 * The Java-Gnome bindings library is free software distributed under
 * the terms of the GNU Library General Public License version 2.
 *
 * This file was orriginally generated by the Java-GNOME Code Generator
 * Please do not modify the code that is identified as generated.  Also,
 * please insert your code above the generated code.
 *
 * Generation date: 2002-07-26 07:00:57 EDT
 */

#include <jni.h>
#include <string.h>
#include <sys/types.h>
#include <glib.h>
#include <glib-object.h>
#ifdef __cplusplus
extern "C" 
{
#endif

static jfieldID gtkobj_fieldID = NULL;

#define JGTKO(x) jobject_to_gtk(env, x)

/* Given a Java object, this method gets the native GtkObject object */
GObject *
jobject_to_gtk(JNIEnv *env, jobject obj)
{
  jint obj_pointer;

//  if(!gtkobj_fieldID)
    gtkobj_fieldID = (*env)->GetFieldID(env, (*env)->GetObjectClass(env, obj), 
					"handle", "I");
  obj_pointer = (*env)->GetIntField(env, obj, gtkobj_fieldID);
  
  return G_OBJECT((void *)obj_pointer);
}

typedef struct {
  JNIEnv *env;
  jobject obj;  /* object to recieve the signal */
  jclass class;  /* class to recieve the signal for static methods */
  jmethodID methodID;  /* methodID of callback method */
  jobject methodData;  /* user defined data to pass to callback method */
  jboolean isStatic;  /* is the callback a static method? */
  jboolean isBoolReturn; /* does the callback method return a boolean value */
} CallbackData;

typedef struct{
	JNIEnv *env;
	jobject object;
} JGRef;

typedef struct {
  GClosure closure;
  CallbackData *cbdata;
} JGClosure;


static char *
javatype_from_gtktype(GType argtype)
{
  char *javatype = g_malloc(50);
  const char *tmp;

  tmp = g_type_name(argtype);
    
  if (strncmp(tmp, "Gtk", 3) == 0) {
    if (strlen(tmp) > 3)
      tmp = &tmp[3];
    sprintf(javatype, "org/gnu/gtk/%s", tmp);
  }
  else if (strncmp(tmp, "Gdk", 3) == 0) {
    if (strlen(tmp) > 3)
      tmp = &tmp[3];
    sprintf(javatype, "org/gnu/gdk/%s", tmp);
  }
  else if (strncmp(tmp, "Gno", 3) == 0) {
    if (strlen(tmp) > 5)
      tmp = &tmp[5];
    sprintf(javatype, "org/gnu/gnome/%s", tmp);
  }
  else if (strncmp(tmp, "Pan", 3) == 0) {
    if (strlen(tmp) > 5)
      tmp = &tmp[5];
    sprintf(javatype, "org/gnu/pango/%s", tmp);
  }
  else if (strncmp(tmp, "Atk", 3) == 0) {
    if (strlen(tmp) > 3)
      tmp = &tmp[3];
    sprintf(javatype, "org/gnu/atk/%s", tmp);
  }
  else
    sprintf(javatype, "%s", tmp);

  return javatype;
}


/* This method actually recieves all callbacks in the system.  It 
 * recieves a CallbackData struct as its' data.  It then determines
 * what Java method to call and invokes it passing the correct parameters */
static void
jg_signal_cb(GClosure *closure,
	     GValue *return_value,
	     guint n_param_values,
	     const GValue *param_values,
	     gpointer invocation_hint,
	     gpointer marshal_data)
{
  jvalue *jargs;
  JGClosure *jgc = (JGClosure *)closure;
  CallbackData *cbdata;
  int i;
  jmethodID mid;
  jclass clazz;
  jobject localObj;
  jobject globalObj;
  jint peer;
  jfieldID peerfid;
  jthrowable exc;
  gboolean b;
  const gchar* str;
  const gchar* jtype;
  int jargsLen = n_param_values;  
  

  cbdata = jgc->cbdata;
  if (cbdata == NULL) {
    printf("***ERROR***:  Java-GNOME - unable to determine the callback method.\n");
    return;
  }
  
  if (cbdata->methodData != NULL) {
  	jargsLen++;
  }
  
  jargs = alloca(jargsLen * sizeof(jvalue));

  /* build the parameters for the callback */
  for(i = 1; i < n_param_values; i++) {
    switch(G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(&param_values[i]))) {
    case G_TYPE_CHAR:
      jargs[i-1].c = g_value_get_char(&param_values[i]);
      break;
    case G_TYPE_UCHAR:
      jargs[i-1].c = g_value_get_uchar(&param_values[i]);
      break;
    case G_TYPE_BOOLEAN:
      b = g_value_get_boolean(&param_values[i]);
      jargs[i-1].z = (b == TRUE) ? JNI_TRUE : JNI_FALSE;
      break;
    case G_TYPE_INT:
      jargs[i-1].i = g_value_get_int(&param_values[i]);
      break;
    case G_TYPE_UINT:
      jargs[i-1].i = g_value_get_uint(&param_values[i]);
      break;
    case G_TYPE_ENUM:
      jargs[i-1].i = g_value_get_enum(&param_values[i]);
      break;
    case G_TYPE_FLAGS:
      jargs[i-1].i = g_value_get_flags(&param_values[i]);
      break;
    case G_TYPE_LONG:
      jargs[i-1].j = g_value_get_long(&param_values[i]);
      break;
    case G_TYPE_ULONG:
      jargs[i-1].j = g_value_get_ulong(&param_values[i]);
      break;
    case G_TYPE_FLOAT:
      jargs[i-1].f = g_value_get_float(&param_values[i]);
      break;
    case G_TYPE_DOUBLE:
      jargs[i-1].d = g_value_get_double(&param_values[i]);
      break;
    case G_TYPE_STRING:
      str = g_value_get_string(&param_values[i]);
      localObj = (*cbdata->env)->NewStringUTF(cbdata->env, str);
      jargs[i-1].l = localObj;
      break;
    case G_TYPE_BOXED:
      peer = (jint)g_value_get_boxed(&param_values[i]);
      jargs[i-1].i = peer;
      break;
    case G_TYPE_POINTER:
      peer = (jint)g_value_get_pointer(&param_values[i]);
      jargs[i-1].i = peer;
      break;
    default:
      jtype = javatype_from_gtktype(G_VALUE_TYPE(&param_values[i]));
      clazz = (* cbdata->env)->FindClass(cbdata->env, jtype);
      g_free((gpointer)jtype);
      
      if (clazz == NULL) {
	return;
      }
      localObj = (* cbdata->env)->AllocObject(cbdata->env, clazz);
      *(void **) &peer = g_value_get_object(&param_values[i]);
      peerfid = (* cbdata->env)->GetFieldID(cbdata->env, clazz, 
					    "handle", "I");
      (* cbdata->env)->SetIntField(cbdata->env, localObj, peerfid, peer);
      jargs[i-1].l = localObj;
      break;
    }
  }
  
  if (cbdata->methodData != NULL) {
    jargs[jargsLen].l = (jobject)cbdata->methodData;
  }

  mid = cbdata->methodID;

  if (cbdata->isBoolReturn == JNI_TRUE) {
    if (cbdata->isStatic == JNI_FALSE) {
	    b = (* cbdata->env)->CallBooleanMethodA(cbdata->env, 
					      cbdata->obj, 
					      mid, 
					      jargs);
    }
    else {
      b = (* cbdata->env)->CallStaticBooleanMethodA(cbdata->env,
						    cbdata->class,
						    mid,
						    jargs);
    }
    if (return_value) {
      	if (b == JNI_TRUE) {
        	g_value_set_boolean(return_value, TRUE);
      	}
      	else  {
        	g_value_set_boolean(return_value, FALSE);
      	}
    }
  }
  else {
    if (cbdata->isStatic == JNI_FALSE)
      (* cbdata->env)->CallVoidMethodA(cbdata->env, 
				       cbdata->obj, 
				       mid, 
				       jargs);
    else
      (* cbdata->env)->CallStaticVoidMethodA(cbdata->env,
					     cbdata->class,
					     mid,
					     jargs);
  }
  
  exc = (* cbdata->env)->ExceptionOccurred(cbdata->env);
  if (exc) {
    (* cbdata->env)->Throw(cbdata->env, exc);
    (* cbdata->env)->ExceptionClear(cbdata->env);
  }
}

static void
jg_cbdata_destroy(gpointer data, GClosure *closure)
{
  JGClosure *jgclosure = (JGClosure *)closure;
  CallbackData *cbdata = jgclosure->cbdata;
  
  if (cbdata->obj)
    (* cbdata->env)->DeleteGlobalRef(cbdata->env, cbdata->obj);
  if (cbdata->class)
    (* cbdata->env)->DeleteGlobalRef(cbdata->env, cbdata->class);
  
  g_free(data);
}

/* Create a GClosure */
static GClosure *
jg_closure_new()
{
  GClosure *closure;

  closure = g_closure_new_simple(sizeof(JGClosure), NULL);
  g_closure_add_finalize_notifier(closure, NULL, jg_cbdata_destroy);
  g_closure_set_marshal(closure, jg_signal_cb);
  return closure;
}

static void
jg_arg_signature(GString *str, GType argtype)
{
  char *as = NULL;
  char ac = '\0';
  gboolean free_as = FALSE;


  switch(G_TYPE_FUNDAMENTAL(argtype)) {
  case G_TYPE_NONE:
    ac = 'V';
    break;  
  case G_TYPE_CHAR:
  case G_TYPE_UCHAR:
    ac = 'C';
    break;
  case G_TYPE_BOOLEAN:
    ac = 'Z';
    break;
  case G_TYPE_INT:
  case G_TYPE_UINT:
    ac = 'I';
    break;
  case G_TYPE_ENUM:
    ac = 'I';
    break;
  case G_TYPE_FLAGS:
    ac = 'I';
    break;
    case G_TYPE_LONG:
  case G_TYPE_ULONG:
    ac = 'J';
    break;
  case G_TYPE_FLOAT:
    ac = 'F';
    break;
  case G_TYPE_DOUBLE:
    ac = 'D';
    break;
  case G_TYPE_STRING:
    as = "java/lang/String";
    break;
  case G_TYPE_BOXED:
    ac = 'I';
    break;
  case G_TYPE_POINTER:
    ac = 'I';
    break;
  case G_TYPE_OBJECT:
    ac = 'I';
    break;
  default:
    ac = 'I';
    break;
  case G_TYPE_INVALID:
    g_error("Type %d - should not be reached", (gint)argtype);
    break;
  }
  
  if(ac)
    g_string_append_c(str, ac);
  if(as) {
    g_string_append(str, "L");
    g_string_append(str, as);
    g_string_append(str, ";");
  }
  
  if(free_as)
    g_free(as);
}

/* This method determines the Java signature for a callback method */
static char *
jg_signal_signature(GSignalQuery siginfo, jobject method_data)
{
  GString *tmpstr;
  char *retval;
  int i;

  tmpstr = g_string_new(NULL);

  g_string_append_c(tmpstr, '(');

  /* add all of the gtk signal parameters to the string */
  for(i = 0; i < siginfo.n_params; i++) {
    GType argtype = siginfo.param_types[i];
    jg_arg_signature(tmpstr, argtype);
  }

  /* here is where we add the actual method data if it exists */
  if (method_data)
    g_string_append(tmpstr, "Ljava/lang/Object;");

  /* and now, the return type */
  g_string_append_c(tmpstr, ')');
  jg_arg_signature(tmpstr, siginfo.return_type);
  
  retval = tmpstr->str;
  g_string_free(tmpstr, FALSE);
  
  return retval;
}

/* This method constructs the CallbackData struct for a given
 * instance callback */
static CallbackData *
jg_cbdata_new(JNIEnv *env, 
	      jobject obj, 
	      jobject connect_to, 
	      jobject method_data,
	      const char *signame, 
	      const char* sigfunc)
{
  CallbackData *cbdata;
  GObject *gtko;
  GSignalQuery siginfo;
  guint sigid;
  char *sigsig;
  jthrowable exc;
  jclass Exception;
  
  gtko = JGTKO(obj);
  sigid = g_signal_lookup(signame, G_OBJECT_TYPE(gtko));
  g_assert(sigid > 0);
  g_signal_query(sigid, &siginfo);
  
  sigsig = jg_signal_signature(siginfo, method_data);
  cbdata = g_new(CallbackData, 1);

  /* add the target object to the struct */
  cbdata->obj = (*env)->NewGlobalRef(env, connect_to);
  cbdata->class = NULL;
  if (G_TYPE_FUNDAMENTAL(siginfo.return_type) == G_TYPE_BOOLEAN)
    cbdata->isBoolReturn = JNI_TRUE;
  else
    cbdata->isBoolReturn = JNI_FALSE;

  /* add the method data to the struct if it exists */
  if (method_data != NULL)
    cbdata->methodData = (*env)->NewGlobalRef(env, method_data);
  else
    cbdata->methodData = NULL;

  /* add the methodid to the struct */
  cbdata->methodID = 
    (*env)->GetMethodID(env, (*env)->GetObjectClass(env, connect_to), 
			sigfunc, sigsig);
  exc = (*env)->ExceptionOccurred(env);
  if (exc) {
    printf("** WARNING **:  Java-GNOME - cannot find callback method %s in the specified object with signature %s\n",
	   sigfunc, sigsig);
    (*env)->ExceptionClear(env);
	Exception = (* env)->FindClass(env, "java/lang/RuntimeException");
    (* env)->ThrowNew(env, Exception, "" );
    return NULL;
  }

  /* add the env to the struct */
  cbdata->env = env;

  /* set the static flag */
  cbdata->isStatic = JNI_FALSE;

  g_free(sigsig);
  return cbdata;
}


/* This method constructs the CallbackData struct for a given
 * instance callback */
static CallbackData *
jg_static_cbdata_new(JNIEnv *env, 
		     jobject obj, 
		     jclass cbref, 
		     jobject method_data,
		     const char *signame, 
		     const char* sigfunc)
{
  CallbackData *cbdata;
  GObject *gtko;
  GSignalQuery siginfo;
  guint sigid;
  char *sigsig;
  jthrowable exc;
  jclass Exception;
  
  gtko = JGTKO(obj);
  sigid = g_signal_lookup(signame, G_OBJECT_TYPE(gtko));
  g_assert(sigid > 0);
  g_signal_query(sigid, &siginfo);
  
  sigsig = jg_signal_signature(siginfo, method_data);
  
  cbdata = g_new(CallbackData, 1);
  
  /* add the target object to the struct */
  cbdata->class = (*env)->NewGlobalRef(env, cbref);
  cbdata->obj = NULL;
  
  /* add the method data to the struct if it exists */
  if (method_data != NULL)
    cbdata->methodData = (*env)->NewGlobalRef(env, method_data);
  else
    cbdata->methodData = NULL;
  
  /* add the methodid to the struct */
  cbdata->methodID = 
    (*env)->GetStaticMethodID(env, cbref, sigfunc, sigsig);
  
  exc = (*env)->ExceptionOccurred(env);
  if (exc) {
    printf("** WARNING **:  Java-GNOME - cannot find static callback method %s in the specified class with signature %s\n",
	   sigfunc, sigsig);
    (*env)->ExceptionClear(env);
	Exception = (* env)->FindClass(env, "java/lang/RuntimeException");
    (* env)->ThrowNew(env, Exception, "" );
    return NULL;
  }
  
  /* add the env to the struct */
  cbdata->env = env;
  
  /* set the static flag */
  cbdata->isStatic = JNI_TRUE;
  
  g_free(sigsig);
  return cbdata;
}


static jint create_callback(JNIEnv *env, 
                            jobject obj, 
                            jstring sigstring, 
                            jstring func, 
                            jobject cbrecv, 
                            jobject data)
{ 
  jint retval;
  const char *signame;
  const char *sigfunc;
  CallbackData *cbdata;
  GObject *go;
  guint sigid;
  GQuark detail = 0;
  GClosure *closure;

  jclass Exception;

  go = JGTKO(obj);
  signame = (*env)->GetStringUTFChars(env, sigstring, NULL);
  sigfunc = (*env)->GetStringUTFChars(env, func, NULL);
  
  if (!g_signal_parse_name(signame, G_OBJECT_TYPE(go),
			   &sigid, &detail, TRUE)) {
    printf("***ERROR***:  Java-GNOME - unknown signal name %s, function %s, object %s.\n", signame, sigfunc, G_OBJECT_TYPE_NAME(go));
	Exception = (* env)->FindClass(env, "java/lang/RuntimeException");
    (* env)->ThrowNew(env, Exception, "" );
    return 0;
  }

  cbdata = jg_cbdata_new(env, obj, cbrecv, data, signame, sigfunc);

  if (cbdata == NULL)
    return 0;

  closure = jg_closure_new();
  ((JGClosure *)closure)->cbdata = cbdata;

  retval = g_signal_connect_closure_by_id(JGTKO(obj),
					  sigid,
					  detail,
					  closure,
					  FALSE);

  (*env)->ReleaseStringUTFChars(env, sigstring, signame);
  (*env)->ReleaseStringUTFChars(env, func, sigfunc);
    
  return retval;
}


static jint create_static_callback(JNIEnv *env, 
                                   jobject obj, 
                                   jstring sigstring, 
                                   jstring func, 
                                   jclass cbrecv, 
                                   jobject data)
{ 
  jint retval;
  const char *signame;
  const char *sigfunc;
  CallbackData *cbdata;
  GObject *go;
  guint sigid;
  GQuark detail = 0;
  GClosure *closure;
  jclass Exception;
  
  go = JGTKO(obj);
  signame = (*env)->GetStringUTFChars(env, sigstring, NULL);
  sigfunc = (*env)->GetStringUTFChars(env, func, NULL);
  
  if (!g_signal_parse_name(signame, G_OBJECT_TYPE(go),
			   &sigid, &detail, TRUE)) {
    printf("***ERROR***:  Java-GNOME - unknown signal name %s, function %s.\n", signame, sigfunc);
	Exception = (* env)->FindClass(env, "java/lang/RuntimeException");
    (* env)->ThrowNew(env, Exception, "" );
    return 0;
  }

  cbdata = jg_static_cbdata_new(env, obj, cbrecv, data, signame, sigfunc);

  if (cbdata == NULL)
    return 0;
    
  closure = jg_closure_new();
  ((JGClosure *)closure)->cbdata = cbdata;
  retval = g_signal_connect_closure_by_id(JGTKO(obj),
					  sigid,
					  detail,
					  closure,
					  FALSE);
  
  (*env)->ReleaseStringUTFChars(env, sigstring, signame);
  (*env)->ReleaseStringUTFChars(env, func, sigfunc);
    
  return retval;
}


/*
 * Class:     org_gnu_glib_GObject
 * Method:    addEventHandler
 * Signature: (Ljava/lang/String;Lgtk/GtkObject;)I
 */
JNIEXPORT jint JNICALL
Java_org_gnu_glib_GObject_addEventHandler__Ljava_lang_String_2Ljava_lang_Object_2
(JNIEnv *env, jobject obj, jstring sigstring, jobject cbrecv)
{
  return create_callback(env, obj, sigstring, sigstring, cbrecv, NULL);
}

/*
 * Class:     org_gnu_glib_GObject
 * Method:    addEventHandler
 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)I
 */
JNIEXPORT jint JNICALL 
Java_org_gnu_glib_GObject_addEventHandler__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_Object_2 
(JNIEnv *env, jobject obj, jstring sigstring, jstring func, jobject cbrecv)
{ 
  return create_callback(env, obj, sigstring, func, cbrecv, NULL);
}


/*
 * Class:     org_gnu_glib_GObject
 * Method:    addEventHandler
 * Signature: (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)I
 */
JNIEXPORT jint JNICALL 
Java_org_gnu_glib_GObject_addEventHandler__Ljava_lang_String_2Ljava_lang_Object_2Ljava_lang_Object_2
(JNIEnv *env, jobject obj, jstring sigstring, jobject cbrecv, jobject data)
{
  return create_callback(env, obj, sigstring, sigstring, cbrecv, data);
}


/*
 * Class:     org_gnu_glib_GObject
 * Method:    addEventHandler
 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)I
 */
JNIEXPORT jint JNICALL 
Java_org_gnu_glib_GObject_addEventHandler__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_Object_2Ljava_lang_Object_2
(JNIEnv *env, jobject obj, jstring sigstring, jstring func, jobject cbrecv, jobject data)
{ 
  return create_callback(env, obj, sigstring, func, cbrecv, data);
}


/*
 * Class:     org_gnu_glib_GObject
 * Method:    addEventHandler
 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)I
 */
JNIEXPORT jint JNICALL 
Java_org_gnu_glib_GObject_addEventHandler__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_Class_2
(JNIEnv *env, jobject obj, jstring sigstring, jstring func, jclass class)
{
  return create_static_callback(env, obj, sigstring, func, class, NULL);
}

/*
 * Class:     org_gnu_glib_GObject
 * Method:    addEventHandler
 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Object;)I
 */
JNIEXPORT jint JNICALL 
Java_org_gnu_glib_GObject_addEventHandler__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_Class_2Ljava_lang_Object_2
(JNIEnv *env, jobject obj, jstring sigstring, jstring func, jclass class, jobject data)
{
  return create_static_callback(env, obj, sigstring, func, class, data);
}



/*
 * Class:     org_gnu_glib_GObject
 * Method:    removeEventHandler
 * Signature: (I)V
 */
JNIEXPORT void JNICALL
Java_org_org_gnu_glib_GObject_removeEventHandler(JNIEnv *env, jobject obj, jint sigid)
{
  g_signal_handler_disconnect(JGTKO(obj), sigid);
}


/*
 * Class:     org_gnu_glib_GObject/
 * Method:    getData
 * Signature: (ILjava/lang/String;)Ljava/lang/Object;
 */
JNIEXPORT jobject JNICALL Java_org_gnu_glib_GObject_getData
  (JNIEnv *env, jobject obj, jint handle, jstring key)
{
	gchar *k = (gchar*)(*env)->GetStringUTFChars(env, key, NULL);
	JGRef *ref = (JGRef*) g_object_get_data((GObject*)handle, k);
	(*env)->ReleaseStringUTFChars(env, key, k);
	if (ref == NULL){
		return NULL;
	}else{
		return ref->object;
	}
}


static void
jg_gobject_destroy(gpointer data)
{
	JGRef *ref = (JGRef*) data;
	(* ref->env)->DeleteGlobalRef( ref->env, ref->object);
	g_free( ref );
}
/*
 * Class:     org_gnu_glib_GObject
 * Method:    setData
 * Signature: (ILjava/lang/String;Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_org_gnu_glib_GObject_setData
  (JNIEnv *env, jobject obj, jint handle, jstring key, jobject data)
{
	gchar *k = (gchar*)(*env)->GetStringUTFChars(env, key, NULL);
	JGRef *ref = g_new( JGRef, 1 );
	ref->env = env;
	ref->object = (* env)->NewGlobalRef(env, data);
	g_object_set_data_full((GObject*)handle, 
				k, 
				ref,
				jg_gobject_destroy
				) ;
	(*env)->ReleaseStringUTFChars(env, key, k);
}


#ifdef __cplusplus
}

#endif
