static int CCONV
error_handler(CPhidgetHandle h, void *arg, int l, const char *v)
{
	JNIEnv *env;
	jobject obj;
	jobject errorEvent;
	jobject eobj;
	jstring edesc;

	if ((*ph_vm)->AttachCurrentThread(ph_vm, (void **)&env, NULL))
		JNI_ABORT_STDERR("Couldn't AttachCurrentThread");

	obj = (jobject)arg;

	if (!(edesc = (*env)->NewStringUTF(env, v)))
		return -1;

	if (!(eobj = (*env)->NewObject(env, ph_exception_class, ph_exception_cons, l, edesc)))
		return -1;

	if (!(errorEvent = (*env)->NewObject(env, errorEvent_class, errorEvent_cons, obj, eobj)))
		return -1;

	(*env)->CallVoidMethod(env, obj, fireError_mid, errorEvent);
	(*env)->DeleteLocalRef(env, errorEvent);
	(*ph_vm)->DetachCurrentThread(ph_vm);

	return 0;
}
static int CCONV
serverDisconnect_handler(CPhidgetManagerHandle h, void *arg)
{
	JNIEnv *env;
	jobject obj;
	jobject serverDisconnectEvent;
	jint result;

	result = (*ph_vm)->GetEnv(ph_vm, (void **)&env, JNI_VERSION_1_4);
	
	if(result == JNI_EDETACHED)
	{
		if ((*ph_vm)->AttachCurrentThread(ph_vm, (JNIEnvPtr)&env, NULL))
			JNI_ABORT_STDERR("Couldn't AttachCurrentThread");
	}

	obj = (jobject)arg;

	if (!(serverDisconnectEvent = (*env)->NewObject(env, serverDisconnectEvent_class, serverDisconnectEvent_cons,
	  obj)))
		return -1;
	(*env)->CallVoidMethod(env, obj, fireServerDisconnect_mid, serverDisconnectEvent);
	(*env)->DeleteLocalRef(env, serverDisconnectEvent);
	(*ph_vm)->DetachCurrentThread(ph_vm);

	return 0;
}
static int CCONV
detach_handler(CPhidgetHandle h, void *arg)
{
	JNIEnv *env;
	jobject obj;
	jobject device;
	jobject detachEvent;
	jlong devhandle;

	if ((*ph_vm)->AttachCurrentThread(ph_vm, (JNIEnvPtr)&env, NULL))
		JNI_ABORT_STDERR("Couldn't AttachCurrentThread");

	obj = (jobject)arg;
	devhandle = (jlong)(uintptr_t)h;

	if (!(device = (*env)->NewObject(env, phidget_class, phidget_cons,
	  devhandle)))
		return -1;

	if (!(detachEvent = (*env)->NewObject(env, detachEvent_class, detachEvent_cons,
	  device)))
		return -1;
	(*env)->CallVoidMethod(env, obj, fireDetach_mid, detachEvent);
	(*env)->DeleteLocalRef(env, device);
	(*env)->DeleteLocalRef(env, detachEvent);
	(*ph_vm)->DetachCurrentThread(ph_vm);

	return 0;
}
static int CCONV
serverConnect_handler(CPhidgetDictionaryHandle h, void *arg)
{
    JNIEnv *env;
    jobject obj;
    jobject serverConnectEvent;

    if ((*ph_vm)->AttachCurrentThread(ph_vm, (JNIEnvPtr)&env, NULL))
        JNI_ABORT_STDERR("Couldn't AttachCurrentThread");

    obj = (jobject)arg;

    if (!(serverConnectEvent = (*env)->NewObject(env, serverConnectEvent_class, serverConnectEvent_cons,
                               obj)))
        return -1;
    (*env)->CallVoidMethod(env, obj, fireServerConnect_mid, serverConnectEvent);
    (*env)->DeleteLocalRef(env, serverConnectEvent);

    return 0;
}
void
com_phidgets_Dictionary_OnLoad(JNIEnv *env)
{
    //Dictionary
    if (!(dictionary_class = (*env)->FindClass(env, "com/phidgets/Dictionary")))
        JNI_ABORT_STDERR("Couldn't FindClass com/phidgets/Dictionary");
    if (!(dictionary_class = (jclass)(*env)->NewGlobalRef(env, dictionary_class)))
        JNI_ABORT_STDERR("Couldn't create global ref dictionary_class");

    if (!(dictionary_handle_fid = (*env)->GetFieldID(env, dictionary_class, "handle", "J")))
        JNI_ABORT_STDERR("Couldn't get Field ID handle from dictionary_class");
    if (!(nativeServerConnectHandler_fid = (*env)->GetFieldID(env, dictionary_class, "nativeServerConnectHandler", "J")))
        JNI_ABORT_STDERR("Couldn't get Field ID nativeServerConnectHandler from dictionary_class");
    if (!(nativeServerDisconnectHandler_fid = (*env)->GetFieldID(env, dictionary_class, "nativeServerDisconnectHandler", "J")))
        JNI_ABORT_STDERR("Couldn't get Field ID nativeServerDisconnectHandler from dictionary_class");

    if (!(fireServerConnect_mid = (*env)->GetMethodID(env, dictionary_class, "fireServerConnect", "(Lcom/phidgets/event/ServerConnectEvent;)V")))
        JNI_ABORT_STDERR("Couldn't get method ID fireServerConnect from dictionary_class");
    if (!(fireServerDisconnect_mid = (*env)->GetMethodID(env, dictionary_class, "fireServerDisconnect", "(Lcom/phidgets/event/ServerDisconnectEvent;)V")))
        JNI_ABORT_STDERR("Couldn't get method ID fireServerDisconnect from dictionary_class");
}
} static int CCONV
tagLoss_handler (CPhidgetRFIDHandle h, void *arg, char *v, CPhidgetRFID_Protocol proto)
{
	JNIEnv *env;
	jobject obj;
	jobject tagLossEv;
	jstring jb;

	if ((*ph_vm)->AttachCurrentThread(ph_vm, (JNIEnvPtr)&env, NULL))
		JNI_ABORT_STDERR("Couldn't AttachCurrentThread");
	
	obj = (jobject) arg;

	jb=(*env)->NewStringUTF(env, v);

	if (!(tagLossEv = (*env)->NewObject (env, tagLossEvent_class, tagLossEvent_cons, obj, jb, (int)proto)))
		return -1;
	(*env)->DeleteLocalRef (env, jb);

	(*env)->CallVoidMethod (env, obj, fireTagLoss_mid, tagLossEv);
	(*env)->DeleteLocalRef (env, tagLossEv);
	return 0;
}
static int CCONV
attach_handler(CPhidgetHandle h, void *arg)
{
	JNIEnv *env;
	jobject obj;
	jobject device;
	jobject attachEvent;
	jlong devhandle;

	//could pass this to AttachCurrentThread for named threads.
	//Test first!
	JavaVMAttachArgs args;
	args.version = JNI_VERSION_1_2;
	args.name = "Central Thread";
	args.group = NULL;

	if ((*ph_vm)->AttachCurrentThread(ph_vm, (JNIEnvPtr)&env, NULL))
		JNI_ABORT_STDERR("Couldn't AttachCurrentThread");

	obj = (jobject)arg;
	devhandle = (jlong)(uintptr_t)h;

	if (!(device = (*env)->NewObject(env, phidget_class, phidget_cons,
	  devhandle)))
		return -1;

	if (!(attachEvent = (*env)->NewObject(env, attachEvent_class, attachEvent_cons,
	  device)))
		return -1;
	(*env)->CallVoidMethod(env, obj, fireAttach_mid, attachEvent);
	(*env)->DeleteLocalRef(env, device);
	(*env)->DeleteLocalRef(env, attachEvent);
	(*ph_vm)->DetachCurrentThread(ph_vm);

	return 0;
}
jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved)
{
	JNIEnv *env;
	jint version = 0;

	ph_vm = vm;

	// Don't set thread security when we're using Java -  this break events.
	#ifdef _WINDOWS
		useThreadSecurity = PFALSE;
		destroyThreadSecurityAttributes();
	#endif
	
	if((*ph_vm)->GetEnv(ph_vm, (void **)&env, JNI_VERSION_1_4) != JNI_OK)
	{
		return -1;
	}
	
	if(!(version = (*env)->GetVersion(env)))
		JNI_ABORT_STDERR("Couldn't get version");
	LOG(PHIDGET_LOG_DEBUG,"JNI Version: %08x",version);

	//Load all Phidget classes, and needed methods and fields...
	//Phidget
	if (!(phidget_class = (*env)->FindClass(env, "com/phidgets/Phidget")))
		JNI_ABORT_STDERR("");
	if (!(phidget_class = (jclass)(*env)->NewGlobalRef(env, phidget_class)))
		JNI_ABORT_STDERR("");
	if (!(phidget_cons = (*env)->GetMethodID(env, phidget_class, "<init>", "(J)V")))
		JNI_ABORT_STDERR("");

	//PhidgetException
	if (!(ph_exception_class = (*env)->FindClass(env, "com/phidgets/PhidgetException")))
		JNI_ABORT_STDERR("Coulnd't FindClass com/phidgets/PhidgetException");
	if (!(ph_exception_class = (jclass)(*env)->NewGlobalRef(env, ph_exception_class)))
		JNI_ABORT_STDERR("Couldn't create global ref ph_exception_class");
	if (!(ph_exception_cons = (*env)->GetMethodID(env, ph_exception_class, "<init>", "(ILjava/lang/String;)V")))
		JNI_ABORT_STDERR("Couldn't get Method ID <init> from ph_exception_class");

	//AttachEvent
	if (!(attachEvent_class = (*env)->FindClass(env, "com/phidgets/event/AttachEvent")))
		JNI_ABORT_STDERR("Coulnd't FindClass com/phidgets/AttachEvent");
	if (!(attachEvent_class = (jclass)(*env)->NewGlobalRef(env, attachEvent_class)))
		JNI_ABORT_STDERR("Couldn't create global ref attachEvent_class");
	if (!(attachEvent_cons = (*env)->GetMethodID(env, attachEvent_class, "<init>", "(Lcom/phidgets/Phidget;)V")))
		JNI_ABORT_STDERR("Couldn't get method ID <init> from attachEvent_class");

	//DetachEvent
	if (!(detachEvent_class = (*env)->FindClass(env, "com/phidgets/event/DetachEvent")))
		JNI_ABORT_STDERR("Coulnd't FindClass com/phidgets/DetachEvent");
	if (!(detachEvent_class = (jclass)(*env)->NewGlobalRef(env, detachEvent_class)))
		JNI_ABORT_STDERR("Couldn't create global ref detachEvent_class");
	if (!(detachEvent_cons = (*env)->GetMethodID(env, detachEvent_class, "<init>", "(Lcom/phidgets/Phidget;)V")))
		JNI_ABORT_STDERR("Couldn't get method ID <init> from detachEvent_class");

	//ErrorEvent
	if (!(errorEvent_class = (*env)->FindClass(env, "com/phidgets/event/ErrorEvent")))
		JNI_ABORT_STDERR("Coulnd't FindClass com/phidgets/ErrorEvent");
	if (!(errorEvent_class = (jclass)(*env)->NewGlobalRef(env, errorEvent_class)))
		JNI_ABORT_STDERR("Couldn't create global ref errorEvent_class");
	if (!(errorEvent_cons = (*env)->GetMethodID(env, errorEvent_class, "<init>", "(Lcom/phidgets/Phidget;Lcom/phidgets/PhidgetException;)V")))
		JNI_ABORT_STDERR("Couldn't get method ID <init> from errorEvent_class");

	//ServerConnectEvent
	if (!(serverConnectEvent_class = (*env)->FindClass(env, "com/phidgets/event/ServerConnectEvent")))
		JNI_ABORT_STDERR("Couldn't FindClass com/phidgets/ServerConnectEvent");
	if (!(serverConnectEvent_class = (jclass)(*env)->NewGlobalRef(env, serverConnectEvent_class)))
		JNI_ABORT_STDERR("Couldn't create global ref serverConnectEvent_class");
	if (!(serverConnectEvent_cons = (*env)->GetMethodID(env, serverConnectEvent_class, "<init>", "(Ljava/lang/Object;)V")))
		JNI_ABORT_STDERR("Couldn't get method ID <init> from serverConnectEvent_class");

	//ServerDisconnectEvent
	if (!(serverDisconnectEvent_class = (*env)->FindClass(env, "com/phidgets/event/ServerDisconnectEvent")))
		JNI_ABORT_STDERR("Couldn't FindClass com/phidgets/ServerDisconnectEvent");
	if (!(serverDisconnectEvent_class = (jclass)(*env)->NewGlobalRef(env, serverDisconnectEvent_class)))
		JNI_ABORT_STDERR("Couldn't create global ref serverDisconnectEvent_class");
	if (!(serverDisconnectEvent_cons = (*env)->GetMethodID(env, serverDisconnectEvent_class, "<init>", "(Ljava/lang/Object;)V")))
		JNI_ABORT_STDERR("Couldn't get method ID <init> from serverDisconnectEvent_class");

	com_phidgets_Phidget_OnLoad(env);
	com_phidgets_Manager_OnLoad(env);
	com_phidgets_Dictionary_OnLoad(env);
	com_phidgets_DictionaryKeyListener_OnLoad(env);
	com_phidgets_AccelerometerPhidget_OnLoad(env);
	com_phidgets_AdvancedServoPhidget_OnLoad(env);
	com_phidgets_AnalogPhidget_OnLoad(env);
	com_phidgets_BridgePhidget_OnLoad(env);
	com_phidgets_EncoderPhidget_OnLoad(env);
    com_phidgets_FrequencyCounterPhidget_OnLoad(env);
    com_phidgets_GPSPhidget_OnLoad(env);
	com_phidgets_InterfaceKitPhidget_OnLoad(env);
	com_phidgets_IRPhidget_OnLoad(env);
	com_phidgets_LEDPhidget_OnLoad(env);
	com_phidgets_MotorControlPhidget_OnLoad(env);
	com_phidgets_PHSensorPhidget_OnLoad(env);
	com_phidgets_RFIDPhidget_OnLoad(env);
	com_phidgets_ServoPhidget_OnLoad(env);
	com_phidgets_SpatialPhidget_OnLoad(env);
	com_phidgets_StepperPhidget_OnLoad(env);
	com_phidgets_TemperatureSensorPhidget_OnLoad(env);
	com_phidgets_TextLCDPhidget_OnLoad(env);
	com_phidgets_TextLEDPhidget_OnLoad(env);
	com_phidgets_WeightSensorPhidget_OnLoad(env);

	//So the main library can detach threads from java before they exit.
	fptrJavaDetachCurrentThread = detachCurrentThreadFromJavaVM;
	
#ifdef _ANDROID
	if(com_phidgets_usb_Phidget_OnLoad(env) && com_phidgets_usb_Manager_OnLoad(env))
		AndroidUsbJarAvailable = PTRUE;
	else
		AndroidUsbJarAvailable = PFALSE;
#endif

	return JNI_VERSION_1_4;
}
void
com_phidgets_Manager_OnLoad(JNIEnv *env)
{
	//Manager
	if (!(manager_class = (*env)->FindClass(env, "com/phidgets/Manager")))
		JNI_ABORT_STDERR("");
	if (!(manager_class = (jclass)(*env)->NewGlobalRef(env, manager_class)))
		JNI_ABORT_STDERR("");

	if (!(manager_handle_fid = (*env)->GetFieldID(env, manager_class, "handle", "J")))
		JNI_ABORT_STDERR("");
	if (!(nativeAttachHandler_fid = (*env)->GetFieldID(env, manager_class, "nativeAttachHandler", "J")))
		JNI_ABORT_STDERR("");
	if (!(nativeDetachHandler_fid = (*env)->GetFieldID(env, manager_class, "nativeDetachHandler", "J")))
		JNI_ABORT_STDERR("");
	if (!(nativeServerConnectHandler_fid = (*env)->GetFieldID(env, manager_class, "nativeServerConnectHandler", "J")))
		JNI_ABORT_STDERR("");
	if (!(nativeServerDisconnectHandler_fid = (*env)->GetFieldID(env, manager_class, "nativeServerDisconnectHandler", "J")))
		JNI_ABORT_STDERR("");

	if (!(fireAttach_mid = (*env)->GetMethodID(env, manager_class, "fireAttach", "(Lcom/phidgets/event/AttachEvent;)V")))
		JNI_ABORT_STDERR("");
	if (!(fireDetach_mid = (*env)->GetMethodID(env, manager_class, "fireDetach", "(Lcom/phidgets/event/DetachEvent;)V")))
		JNI_ABORT_STDERR("");
	if (!(fireServerConnect_mid = (*env)->GetMethodID(env, manager_class, "fireServerConnect", "(Lcom/phidgets/event/ServerConnectEvent;)V")))
		JNI_ABORT_STDERR("");
	if (!(fireServerDisconnect_mid = (*env)->GetMethodID(env, manager_class, "fireServerDisconnect", "(Lcom/phidgets/event/ServerDisconnectEvent;)V")))
		JNI_ABORT_STDERR("");
}
void
com_phidgets_Phidget_OnLoad(JNIEnv *env)
{
	if (!(handle_fid = (*env)->GetFieldID(env, phidget_class, "handle", "J")))
		JNI_ABORT_STDERR("Couldn't get Field ID handle from phidget_class");
	if (!(nativeAttachHandler_fid = (*env)->GetFieldID(env, phidget_class, "nativeAttachHandler", "J")))
		JNI_ABORT_STDERR("Couldn't get Field ID nativeAttachHandler from phidget_class");
	if (!(nativeDetachHandler_fid = (*env)->GetFieldID(env, phidget_class, "nativeDetachHandler", "J")))
		JNI_ABORT_STDERR("Couldn't get Field ID nativeDetachHandler from phidget_class");
	if (!(nativeErrorHandler_fid = (*env)->GetFieldID(env, phidget_class, "nativeErrorHandler", "J")))
		JNI_ABORT_STDERR("Couldn't get Field ID nativeErrorHandler from phidget_class");
	if (!(nativeServerConnectHandler_fid = (*env)->GetFieldID(env, phidget_class, "nativeServerConnectHandler", "J")))
		JNI_ABORT_STDERR("Couldn't get Field ID nativeServerConnectHandler from phidget_class");
	if (!(nativeServerDisconnectHandler_fid = (*env)->GetFieldID(env, phidget_class, "nativeServerDisconnectHandler", "J")))
		JNI_ABORT_STDERR("Couldn't get Field ID nativeServerDisconnectHandler from phidget_class");

	if (!(fireAttach_mid = (*env)->GetMethodID(env, phidget_class, "fireAttach", "(Lcom/phidgets/event/AttachEvent;)V")))
		JNI_ABORT_STDERR("Couldn't get method ID fireAttach from phidget_class");
	if (!(fireDetach_mid = (*env)->GetMethodID(env, phidget_class, "fireDetach", "(Lcom/phidgets/event/DetachEvent;)V")))
		JNI_ABORT_STDERR("Couldn't get method ID fireDetach from phidget_class");
	if (!(fireError_mid = (*env)->GetMethodID(env, phidget_class, "fireError", "(Lcom/phidgets/event/ErrorEvent;)V")))
		JNI_ABORT_STDERR("Couldn't get method ID fireError from phidget_class");
	if (!(fireServerConnect_mid = (*env)->GetMethodID(env, phidget_class, "fireServerConnect", "(Lcom/phidgets/event/ServerConnectEvent;)V")))
		JNI_ABORT_STDERR("Couldn't get method ID fireServerConnect from phidget_class");
	if (!(fireServerDisconnect_mid = (*env)->GetMethodID(env, phidget_class, "fireServerDisconnect", "(Lcom/phidgets/event/ServerDisconnectEvent;)V")))
		JNI_ABORT_STDERR("Couldn't get method ID fireServerDisconnect from phidget_class");
}