#ifndef JNIUTIL_H
#define JNIUTIL_H

#include <jni.h>
#include <string.h>
#include <malloc.h>

/* http://java.sun.com/docs/books/jni/html/exceptions.html#26050 */
void JNU_ThrowByName(JNIEnv *, const char *, const char *);

/* http://java.sun.com/docs/books/jni/html/other.html#26021 */
jstring JNU_NewStringNative(JNIEnv *, const char *);

/* http://java.sun.com/docs/books/jni/html/other.html#26043 */
char *JNU_GetStringNativeChars(JNIEnv *, jstring);

void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg)
{
	jclass cls = env->FindClass(name);
	/* if cls is NULL, an exception has already been thrown */
	if (cls != NULL)
	{
		env->ThrowNew(cls, msg);
	}
	/* free the local ref */
	env->DeleteLocalRef(cls);
}

jmethodID jmethod_str = 0;

jstring JNU_NewStringNative(JNIEnv *env, const char *str)
{
	jclass jcls_str;
	jstring result;
	jbyteArray bytes;
	int len;
	if (str == NULL)
	{
		return NULL;
	}

	jcls_str = env->FindClass("java/lang/String");
	if (!jmethod_str)
		jmethod_str = env->GetMethodID(jcls_str, "<init>", "([B)V");

	bytes = 0;

	if (env->EnsureLocalCapacity(2) < 0)
	{
		return NULL; /* out of memory error */
	}
	len = strlen(str);
	bytes = env->NewByteArray(len);
	if (bytes != NULL)
	{
		env->SetByteArrayRegion(bytes, 0, len, (jbyte *) str);
		result = (jstring) env->NewObject(jcls_str, jmethod_str, bytes);
		env->DeleteLocalRef(bytes);
		return result;
	} /* else fall through */
	return NULL;
}

jmethodID MID_String_getBytes = 0;

char *JNU_GetStringNativeChars(JNIEnv *env, jstring jstr)
{
	jbyteArray bytes = 0;
	char *result = 0;
	jthrowable exc;

	if (env->EnsureLocalCapacity(2) < 0)
	{
		return 0; /* out of memory error */
	}

	if (!MID_String_getBytes)
		MID_String_getBytes = env->GetMethodID(env->FindClass(
				"java/lang/String"), "getBytes", "()[B");

	bytes = (jbyteArray) env->CallObjectMethod(jstr, MID_String_getBytes);
	exc = env->ExceptionOccurred();

	if (!exc)
	{
		jint len = env->GetArrayLength(bytes);
		result = new char[len + 1];
		if (result == 0)
		{
			JNU_ThrowByName(env, "java/lang/OutOfMemoryError", 0);
			env->DeleteLocalRef(bytes);
			return 0;
		}

		env->GetByteArrayRegion(bytes, 0, len, (jbyte *) result);
		result[len] = 0; /* NULL-terminate */
	}
	else
	{
		env->DeleteLocalRef(exc);
	}

	env->DeleteLocalRef(bytes);

	return result;
}

#endif

