/* 
 * JGimp - A Java extension for the GIMP
 * Copyright (C) 2002  Georgia Tech Research Corp.
 * Written by Michael Terry, mterry@cc.gatech.edu
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 * This file contains all the native code for the JGimpPlugInProxy class
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <jni.h>

#include "JGimpPlugInProxy.h"
#include "jgimp_data_conversions.h"
#include "jgimp_gimp_adapter.h"
#include "jgimp_constants.h"
#include "jgimp_jni_utils.h"
#include "jgimp_plugin_handler.h"

/*
 * Formats for reading and writing pixels from the drawable
 */
typedef enum 
{
	FORMAT_NATIVE_BYTE = 0,
	FORMAT_NATIVE_INT,
	FORMAT_JAVA_INT
} PixelFormat;

jlong common_pixel_read(JNIEnv* env,
		JGimpDrawableInfo* drawable,
		gint x,
		gint y,
		gint width,
		gint height,
		guchar* buffer,
		gint buffer_offset,
		gint buffer_length,
		PixelFormat buffer_format);
jlong common_pixel_write(JNIEnv* env,
		JGimpDrawableInfo* drawable,
		gint x,
		gint y,
		gint width,
		gint height,
		guchar* buffer,
		gint buffer_offset,
		gint buffer_length,
		PixelFormat buffer_format);

/* Native functions */

/*
 * Class:     org_gimp_jgimp_proxy_JGimpPlugInProxy
 * Method:    _unloadPlugInOrExtension
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_org_gimp_jgimp_proxy_JGimpPlugInProxy__1unloadPlugInOrExtension
  (JNIEnv *env, jobject proxy, jstring pluginName)
{
    gchar *plugin_name = NULL;
    plugin_name = jstring_to_c_string(env, pluginName);
    gimp_uninstall_temp_proc(plugin_name);
    g_free(plugin_name);
    plugin_name = NULL;
}
/*
 * Installs a Java-based plug-in
 */
/*
 * Class:     org_gimp_jgimp_proxy_JGimpPlugInProxy
 * Method:    _installPlugInNatively
 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I[Lorg/gimp/jgimp/plugin/JGimpParamDescriptor;[Lorg/gimp/jgimp/plugin/JGimpParamDescriptor;)I
 */
JNIEXPORT jint JNICALL Java_org_gimp_jgimp_proxy_JGimpPlugInProxy__1installPlugInNatively
  (JNIEnv * env,
   jclass calling_class,
   jstring name,
   jstring blurb,
   jstring help,
   jstring author,
   jstring copyright,
   jstring date,
   jstring menupath,
   jstring image_types,
   jobjectArray param_descriptions,
   jobjectArray returnvalue_descriptions)
{
#ifdef JGIMP_COMPILE_AS_PLUGIN
	gchar *c_name             = NULL;
	gchar *c_blurb            = NULL;
	gchar *c_help             = NULL;
	gchar *c_author           = NULL;
	gchar *c_copyright        = NULL;
	gchar *c_date             = NULL;
	gchar *c_menupath         = NULL;
	gchar *c_image_types      = NULL;
	gint   nparams            = 0;
	gint   nreturn_vals       = 0;
	GimpParamDef *params      = NULL;
	GimpParamDef *return_vals = NULL;
	int i                     = 0;
	
	/* Check to make sure the plug-in to install is not the same name as the jgimp server */
	c_name = jstring_to_c_string(env, name);
	if ((strcmp(c_name, JGIMP_EXTENSION_SERVER) == 0) || (strcmp(c_name, JGIMP_PLUGIN_SERVER) == 0))
	{
		fprintf(stderr, "Couldn't install plug-in %s, because it is the same name as the JGimp server\n", c_name);
		return -1;
	}
	c_blurb       = jstring_to_c_string(env, blurb);
	c_help        = jstring_to_c_string(env, help);
	c_author      = jstring_to_c_string(env, author);
	c_copyright   = jstring_to_c_string(env, copyright);
	c_date        = jstring_to_c_string(env, date);
	c_menupath    = jstring_to_c_string(env, menupath);
	c_image_types = jstring_to_c_string(env, image_types);

	nparams = (*env)->GetArrayLength(env, param_descriptions);
	params = g_new(GimpParamDef, nparams);
	for (i = 0; i < nparams; i++)
	{
		convert_jgimp_param_descriptor(env, (*env)->GetObjectArrayElement(env, param_descriptions, i), &(params[i]));
	}

	if (returnvalue_descriptions == NULL)
	{
		nreturn_vals = 0;
		return_vals  = NULL;
	}
	else
	{
		nreturn_vals = (*env)->GetArrayLength(env, returnvalue_descriptions);
		if (nreturn_vals == 0)
		{
			return_vals = NULL;
		}
		else
		{
			return_vals = g_new(GimpParamDef, nreturn_vals);
			for (i = 0; i < nreturn_vals; i++)
			{
				convert_jgimp_param_descriptor(env, (*env)->GetObjectArrayElement(env, returnvalue_descriptions, i), &(return_vals[i]));
			}
		}
	}

	adapter_install_temp_proc(c_name,
                           c_blurb,
						   c_help,
						   c_author,
						   c_copyright,
						   c_date,
						   c_menupath,
						   c_image_types,
						   GIMP_TEMPORARY,
						   nparams,
						   nreturn_vals,
						   params,
						   return_vals,
						   handle_java_plug_in);

	g_free(c_name);        c_name = NULL;
	g_free(c_blurb);       c_blurb = NULL;
	g_free(c_help);        c_help = NULL;
	g_free(c_author);      c_author = NULL;
	g_free(c_copyright);   c_copyright = NULL;
	g_free(c_date);        c_date = NULL;
	g_free(c_menupath);    c_menupath = NULL;
	g_free(c_image_types); c_image_types = NULL;

	for (i = 0; i < nparams; i++)
	{
		g_free(params[i].name);
		g_free(params[i].description);
	}
	g_free(params); params = NULL;
	for (i = 0; i < nreturn_vals; i++)
	{
		g_free(return_vals[i].name);
		g_free(return_vals[i].description);
	}
	g_free(return_vals); return_vals = NULL;

	return 0;
#else
fprintf(stderr, "Stub: don't handle plug-ins in embedded version\n");
	return -1;
#endif /* JGIMP_COMPILE_AS_PLUGIN */
}


/*
 * Class:     org_gimp_jgimp_proxy_JGimpPlugInProxy
 * Method:    _runPDBProcedureImpl
 * Signature: (Ljava/lang/String;[Lorg/gimp/jgimp/JGimpData;)[Lorg/gimp/jgimp/JGimpData;
 */
JNIEXPORT jobjectArray JNICALL Java_org_gimp_jgimp_proxy_JGimpPlugInProxy__1runPDBProcedureImpl
  (JNIEnv *env, jclass calling_class, jstring proc_name, jobjectArray jgimp_proc_args)
{
	char               *c_proc_name             = NULL;
	GimpParam          *args                    = NULL;
	int                 num_args                = 0;
	struct ConvertedGimpParam **converted_array = NULL;
	GimpParam          *return_vals             = NULL;
	int                 num_return_vals         = 0;
	jobject             return_object_array     = NULL;
	jclass              jgimp_data_class        = NULL;
    int                 i                       = 0;
    int                 j                       = 0;
	int                 last_int32_value        = 0;

	/* Convert the procedure's name to a c-string */
	c_proc_name = jstring_to_c_string(env, proc_name);

	/* Initialize the number of arguments to 0 */
	num_args = 0;

	/* Convert the arguments to GimpParam types */
	if (jgimp_proc_args != NULL)
	{
		num_args = (*env)->GetArrayLength(env, jgimp_proc_args);

		/* Convert the arguments, put the pointer to the converted params in
		 * an array, copy the params to the GimpParam array
		 */
		if (num_args > 0)
		{
			/* Create an array of pointers to converted params */
			converted_array = g_new(struct ConvertedGimpParam*, num_args);
			if (!converted_array)
			{
				fprintf(stderr, "JGimp out of memory in _runPDBProcedureImpl\n");
				return NULL;
			}
			/* Create an array of GimpParam objects (our parameters) */
			args = g_new(GimpParam, num_args);
			if (!args)
			{
				fprintf(stderr, "JGimp out of memory in _runPDBProcedureImpl\n");
				g_free(converted_array);
				converted_array = NULL;
				return NULL;
			}

			/* Iterate through the Java objects, converting and copying the params */
			for (i = 0; i < num_args; i++)
			{
				jobject param_object = (*env)->GetObjectArrayElement(env, jgimp_proc_args, i);
				if (param_object == NULL)
				{
					fprintf(stderr, "JGimp internal error: Couldn't retrieve param object number %d in _runPDBProcedureImpl\n", i);
					g_free(args);
					args = NULL;
					
					j = i - 1;
					while (j >= 0)
					{
						free_ConvertedGimpParam(converted_array[j]);
						converted_array[j] = NULL;
						j--;
					}
					g_free(converted_array);
					converted_array = NULL;
					return NULL;
				}

				/* Convert the object */
				converted_array[i] = JGimpData_to_GimpParam(env, param_object);
				if (converted_array[i] == NULL)
				{
					fprintf(stderr, "JGimp internal error: Couldn't convert param object number %d in _runPDBProcedureImpl\n", i);
					g_free(args);
					args = NULL;
					
					j = i - 1;
					while (j >= 0)
					{
						free_ConvertedGimpParam(converted_array[j]);
						converted_array[j] = NULL;
						j--;
					}
					g_free(converted_array);
					converted_array = NULL;
					(*env)->DeleteLocalRef(env, param_object);
					param_object = NULL;
					return NULL;
				}

				/* Copy the GimpParam into our argument array */
				args[i] = (converted_array[i])->param;

				/* Delete the ref to the object (an optimization) */
				(*env)->DeleteLocalRef(env, param_object);
				param_object = NULL;
			}
		}
	}

	/* Call the procedure */
#ifdef JGIMP_COMPILE_AS_PLUGIN
	return_vals = adapter_run_pdb_procedure(c_proc_name, &num_return_vals, num_args, args);
#else
	return_vals = (GimpParam*)adapter_run_pdb_procedure(c_proc_name, &num_return_vals, num_args, (GPParam*)args);
#endif /* JGIMP_COMPILE_AS_PLUGIN */
	g_free(c_proc_name);
	c_proc_name = NULL;

	/* Free up the memory for the args */
	if (args)
	{
		g_free(args);
		args = NULL;
	}
	for (i = 0; i < num_args; i++)
	{
		free_ConvertedGimpParam(converted_array[i]);
		converted_array[i] = NULL;
	}
	g_free(converted_array);
	converted_array = NULL;
	
	if (return_vals == NULL)
	{
		fprintf(stderr, "JGimp internal error: NULL return_vals in _runPDBProcedureImpl\n");
		return NULL;
	}
	/* Convert the return params */
	jgimp_data_class = (*env)->FindClass(env, JGIMP_DATA_CLASS);
	if (jgimp_data_class == NULL)
	{
		fprintf(stderr, "JGimp internal error: Couldn't find class ID for JGimpData in _runPDBProcedureImpl\n");
#ifdef JGIMP_COMPILE_AS_PLUGIN
		adapter_destroy_pdb_procedure_return_vals(return_vals, num_return_vals);
#else
		adapter_destroy_pdb_procedure_return_vals((GPParam*)return_vals, num_return_vals);
#endif /* JGIMP_COMPILE_AS_PLUGIN */
		return_vals = NULL;
		return NULL;
	}

	return_object_array = (*env)->NewObjectArray(env, num_return_vals, jgimp_data_class, NULL);
	if (return_object_array == NULL)
	{
		fprintf(stderr, "JGimp internal error: Couldn't allocate return object array in _runPDBProcedureImpl\n");
#ifdef JGIMP_COMPILE_AS_PLUGIN
		adapter_destroy_pdb_procedure_return_vals(return_vals, num_return_vals);
#else
		adapter_destroy_pdb_procedure_return_vals((GPParam*)return_vals, num_return_vals);
#endif /* JGIMP_COMPILE_AS_PLUGIN */
		return_vals = NULL;
		return NULL;
	}
	last_int32_value = 0;
	for (i = 0; i < num_return_vals; i++)
	{
		/* OK, here's where we have some magic and a leap of faith, of sorts...
		 * Since C-arrays don't have a length encoded in them, we have to trust that
		 * developers use the following protocol: pass back the length of the array, 
		 * followed by the array. For that reason, we _always_ copy the last INT32
		 * and copy it as the array length when performing a conversion. If
		 * this protocol is not observed, then this conversion will fail...
		 */
		jobject this_object = GimpParam_to_JGimpData(env, &(return_vals[i]), last_int32_value);

		if (this_object == NULL)
		{
			fprintf(stderr, "JGimp internal error: Couldn't allocate return object in _runPDBProcedureImpl\n");
#ifdef JGIMP_COMPILE_AS_PLUGIN
			adapter_destroy_pdb_procedure_return_vals(return_vals, num_return_vals);
#else
			adapter_destroy_pdb_procedure_return_vals((GPParam*)return_vals, num_return_vals);
#endif /* JGIMP_COMPILE_AS_PLUGIN */
			return_vals = NULL;
			(*env)->DeleteLocalRef(env, return_object_array);
			return_object_array = NULL;
			return NULL;
		}

		(*env)->SetObjectArrayElement(env, return_object_array, i, this_object);
		(*env)->DeleteLocalRef(env, this_object);
		this_object = NULL;

		/* Update, in case this param was the length of an upcoming array... */
		last_int32_value = return_vals[i].data.d_int32;
	}

	/* Free up the memory for the return args */
#ifdef JGIMP_COMPILE_AS_PLUGIN
	adapter_destroy_pdb_procedure_return_vals(return_vals, num_return_vals);
#else
	adapter_destroy_pdb_procedure_return_vals((GPParam*)return_vals, num_return_vals);
#endif /* JGIMP_COMPILE_AS_PLUGIN */
	return_vals = NULL;

	/* Return the new params */
	return return_object_array;
}

/*
 * Class:     org_gimp_jgimp_proxy_JGimpPlugInProxy
 * Method:    _readPixelRegionInNativeByteFormat
 * Signature: (IIIII[BI)J
 */
JNIEXPORT jlong JNICALL Java_org_gimp_jgimp_proxy_JGimpPlugInProxy__1readPixelRegionInNativeByteFormat
  (JNIEnv *env, jclass calling_class, jint drawable_ID, jint x, jint y, jint width, jint height, jbyteArray buffer, jint offset)
{
	JGimpDrawableInfo drawable         = adapter_get_drawable_info(drawable_ID);
	jbyte            *java_byte_array  = NULL;
	jlong             num_bytes_copied = -1;

	/* Get the drawable info */
	if (!drawable.is_valid_drawable)
	{
		jni_utils_throw_invalid_drawable_exception(env, drawable_ID);
		return -1;
	}

	/* Get a copy of the byte array */
	java_byte_array = (*env)->GetByteArrayElements(env, buffer, NULL);
	if (!java_byte_array)
	{
		fprintf(stderr, "JGimp out of memory error: couldn't get copy of byte array");
		return -1;
	}

	num_bytes_copied = common_pixel_read(env,
			&drawable,
			x,
			y,
			width,
			height,
			java_byte_array,
			offset,
			(*env)->GetArrayLength(env, buffer),
			FORMAT_NATIVE_BYTE);

	/* Copy the array back to the object */
	(*env)->ReleaseByteArrayElements(env, buffer, java_byte_array, 0);

	return num_bytes_copied;
}

/*
 * Class:     org_gimp_jgimp_proxy_JGimpPlugInProxy
 * Method:    _readPixelRegionInNativeIntFormat
 * Signature: (IIIII[II)J
 */
JNIEXPORT jlong JNICALL Java_org_gimp_jgimp_proxy_JGimpPlugInProxy__1readPixelRegionInNativeIntFormat
  (JNIEnv *env, jclass calling_class, jint drawable_ID, jint x, jint y, jint width, jint height, jintArray buffer, jint offset)
{
	JGimpDrawableInfo drawable         = adapter_get_drawable_info(drawable_ID);
	jint             *java_int_array   = NULL;
	jlong             num_bytes_copied = -1;

	/* Get the drawable info */
	if (!drawable.is_valid_drawable)
	{
		jni_utils_throw_invalid_drawable_exception(env, drawable_ID);
		return -1;
	}

	/* Get a copy of the int array */
	java_int_array = (*env)->GetIntArrayElements(env, buffer, NULL);
	if (!java_int_array)
	{
		fprintf(stderr, "JGimp out of memory error: couldn't get copy of int array");
		return -1;
	}

	num_bytes_copied = common_pixel_read(env,
			&drawable,
			x,
			y,
			width,
			height,
			(guchar*)java_int_array,
			offset * 4,
			(*env)->GetArrayLength(env, buffer) * 4,
			FORMAT_NATIVE_INT);

	/* Copy the array back to the object */
	(*env)->ReleaseIntArrayElements(env, buffer, java_int_array, 0);

	return num_bytes_copied;
}

/*
 * Class:     org_gimp_jgimp_proxy_JGimpPlugInProxy
 * Method:    _readPixelRegionInJavaIntFormat
 * Signature: (IIIII[II)J
 */
JNIEXPORT jlong JNICALL Java_org_gimp_jgimp_proxy_JGimpPlugInProxy__1readPixelRegionInJavaIntFormat
  (JNIEnv *env, jclass calling_class, jint drawable_ID, jint x, jint y, jint width, jint height, jintArray buffer, jint offset)
{
	JGimpDrawableInfo drawable         = adapter_get_drawable_info(drawable_ID);
	jint             *java_int_array   = NULL;
	jlong             num_bytes_copied = -1;

	/* Get the drawable info */
	if (!drawable.is_valid_drawable)
	{
printf("Invalid drawable %d\n", drawable_ID);
		jni_utils_throw_invalid_drawable_exception(env, drawable_ID);
		return -1;
	}

	/* Get a copy of the int array */
	java_int_array = (*env)->GetIntArrayElements(env, buffer, NULL);
	if (!java_int_array)
	{
		fprintf(stderr, "JGimp out of memory error: couldn't get copy of int array");
		return -1;
	}

	num_bytes_copied = common_pixel_read(env,
			&drawable,
			x,
			y,
			width,
			height,
			(guchar*)java_int_array,
			offset * 4,
			(*env)->GetArrayLength(env, buffer) * 4,
			FORMAT_JAVA_INT);

	/* Copy the array back to the object */
	(*env)->ReleaseIntArrayElements(env, buffer, java_int_array, 0);

	return num_bytes_copied;
}




/*
 * Class:     org_gimp_jgimp_proxy_JGimpPlugInProxy
 * Method:    _writePixelRegionInNativeByteFormat
 * Signature: (IIIII[BI)J
 */
JNIEXPORT jlong JNICALL Java_org_gimp_jgimp_proxy_JGimpPlugInProxy__1writePixelRegionInNativeByteFormat
  (JNIEnv *env, jclass calling_class, jint drawable_ID, jint x, jint y, jint width, jint height, jbyteArray buffer, jint offset)
{
	JGimpDrawableInfo drawable         = adapter_get_drawable_info(drawable_ID);
	jbyte            *java_byte_array  = NULL;
	jlong             num_bytes_copied = -1;

	/* Get the drawable info */
	if (!drawable.is_valid_drawable)
	{
		jni_utils_throw_invalid_drawable_exception(env, drawable_ID);
		return -1;
	}

	/* Get a copy of the byte array */
	java_byte_array = (*env)->GetByteArrayElements(env, buffer, NULL);
	if (!java_byte_array)
	{
		fprintf(stderr, "JGimp out of memory error: couldn't get copy of int array");
		return -1;
	}

	num_bytes_copied = common_pixel_write(env,
			&drawable,
			x,
			y,
			width,
			height,
			(guchar*)java_byte_array,
			offset,
			(*env)->GetArrayLength(env, buffer),
			FORMAT_NATIVE_BYTE);

	/* Release the array */
	(*env)->ReleaseByteArrayElements(env, buffer, java_byte_array, JNI_ABORT); /* JNI_ABORT means don't copy back any changes */

	return num_bytes_copied;
}

/*
 * Class:     org_gimp_jgimp_proxy_JGimpPlugInProxy
 * Method:    _writePixelRegionInNativeIntFormat
 * Signature: (IIIII[II)J
 */
JNIEXPORT jlong JNICALL Java_org_gimp_jgimp_proxy_JGimpPlugInProxy__1writePixelRegionInNativeIntFormat
  (JNIEnv *env, jclass calling_class, jint drawable_ID, jint x, jint y, jint width, jint height, jintArray buffer, jint offset)
{
	JGimpDrawableInfo drawable         = adapter_get_drawable_info(drawable_ID);
	jint             *java_int_array   = NULL;
	jlong             num_bytes_copied = -1;

	/* Get the drawable info */
	if (!drawable.is_valid_drawable)
	{
		jni_utils_throw_invalid_drawable_exception(env, drawable_ID);
		return -1;
	}

	/* Get a copy of the int array */
	java_int_array = (*env)->GetIntArrayElements(env, buffer, NULL);
	if (!java_int_array)
	{
		fprintf(stderr, "JGimp out of memory error: couldn't get copy of int array");
		return -1;
	}

	num_bytes_copied = common_pixel_write(env,
			&drawable,
			x,
			y,
			width,
			height,
			(guchar*)java_int_array,
			offset * 4,
			(*env)->GetArrayLength(env, buffer) * 4,
			FORMAT_NATIVE_INT);

	/* Release the array */
	(*env)->ReleaseIntArrayElements(env, buffer, java_int_array, JNI_ABORT); /* JNI_ABORT means don't copy back any changes */

	return num_bytes_copied;
}

/*
 * Class:     org_gimp_jgimp_proxy_JGimpPlugInProxy
 * Method:    _writePixelRegionInJavaIntFormat
 * Signature: (IIIII[II)J
 */
JNIEXPORT jlong JNICALL Java_org_gimp_jgimp_proxy_JGimpPlugInProxy__1writePixelRegionInJavaIntFormat
  (JNIEnv *env, jclass calling_class, jint drawable_ID, jint x, jint y, jint width, jint height, jintArray buffer, jint offset)
{
	JGimpDrawableInfo drawable         = adapter_get_drawable_info(drawable_ID);
	jint             *java_int_array   = NULL;
	jlong             num_bytes_copied = -1;

	/* Get the drawable info */
	if (!drawable.is_valid_drawable)
	{
		jni_utils_throw_invalid_drawable_exception(env, drawable_ID);
		return -1;
	}

	/* Get a copy of the int array */
	java_int_array = (*env)->GetIntArrayElements(env, buffer, NULL);
	if (!java_int_array)
	{
		fprintf(stderr, "JGimp out of memory error: couldn't get copy of int array");
		return -1;
	}

	num_bytes_copied = common_pixel_write(env,
			&drawable,
			x,
			y,
			width,
			height,
			(guchar*)java_int_array,
			offset * 4,
			(*env)->GetArrayLength(env, buffer) * 4,
			FORMAT_JAVA_INT);

	/* Release the array */
	(*env)->ReleaseIntArrayElements(env, buffer, java_int_array, JNI_ABORT); /* JNI_ABORT means don't copy back any changes */

	return num_bytes_copied;
}

jlong common_pixel_read(JNIEnv* env,
		JGimpDrawableInfo* drawable,
		gint x,
		gint y,
		gint width,
		gint height,
		guchar* buffer,
		gint buffer_offset,
		gint buffer_length,
		PixelFormat buffer_format)
{
	guint          dest_bpp                    = 0;
	guint32        needed_dest_array_size      = 0;
	guint32        actual_dest_array_size      = 0;
	jlong          num_bytes_copied            = 0;
	gboolean       buffer_overflow             = 0;
	guint32        adjusted_height             = 0;

	if (buffer_format < 0)
	{
		fprintf(stderr, "Internal jgimp error: invalid pixel buffer_format specified for common_pixel_read\n");
		return -1;
	}
	if (buffer == NULL)
	{
		fprintf(stderr, "Internal jgimp error: NULL buffer passed to common_pixel_read\n");
		return -1;
	}

	/* Check to make sure we're in bounds */
	/* Throw an exception if we're not in bounds */
	if ( (x < 0) || (y < 0) || (width < 0) || (height < 0) || ((x + width) > drawable->width) || ((y + height) > drawable->height) )
	{
		jni_utils_throw_invalid_pixel_region_exception(env, x, y, width, height, drawable->width, drawable->height);
		return -1;
	}

	/* Calculate array size needed as well as the real array size we have */
	switch (buffer_format)
	{
		case FORMAT_NATIVE_BYTE:
		case FORMAT_NATIVE_INT:
			dest_bpp = drawable->bpp;
			break;

		default:
			dest_bpp = 4;
	}

	needed_dest_array_size = width * height * dest_bpp;
	actual_dest_array_size = buffer_length - buffer_offset;

	/* Calculate the number of pixels to copy */
	if (needed_dest_array_size > actual_dest_array_size)
	{
		guint32 row_size = -1;

		row_size = width * dest_bpp;
		adjusted_height = actual_dest_array_size / row_size;
		buffer_overflow = 1;
	}
	else
	{
		adjusted_height  = height;
	}

	num_bytes_copied = width * adjusted_height * dest_bpp;
	if (num_bytes_copied < 1)
	{
		return num_bytes_copied;
	}

	if (buffer_format != FORMAT_JAVA_INT)
	{
		/* read directly into the buffer */
		num_bytes_copied = adapter_get_pixel_rgn(drawable, x, y, width, adjusted_height, buffer, buffer_offset, buffer_length);
	}
	else
	{
		/*
		 * STUB: Could try reading in on tile boundaries to see if it would be any faster
		 * Might go a little something like this:

		guint start_x = x / drawable->tile_width;
		guint start_y = y / drawable->tile_height;
		guint cur_x   = -1;
		guint cur_y   = -1;
		guint end_x   = x + width;
		guint end_y   = y + adjusted_height;

		Get a lock on the read buffer
		lock_temp_read_buffer();
		if (temp_read_buffer == NULL) <- a globally-defined buffer
		{
			temp_read_buffer = g_new(guchar, drawable->tile_width * drawable->tile_height * 4);
			if (temp_read_buffer == NULL)
			{
				fprintf(stderr, "jgimp error: couldn't allocate temp_read_buffer in common_pixel_read\n");
				return -1;
			}
		}

		for (cur_y = start_y; cur_y < end_y; cur_y += drawable->tile_height)
		{
			for (cur_x = start_x; cur_x < end_x; cur_x += drawable->tile_width)
			{
				Get next tile, copy the bits to the java array through the conversion...
			}
		}
		unlock_temp_read_buffer();
		*/

		guint   num_pixels_to_copy = width * adjusted_height;
		guint   pixel_buf_length   = num_pixels_to_copy * drawable->bpp;
		guchar* pixel_buf          = NULL;
		guchar* pixel_buf_iterator = NULL;
		jint*   dest_buf_iterator  = NULL;
		jint*   end_point          = NULL;

		/* Allocate a buffer for the pixels */
		pixel_buf = g_new(guchar, pixel_buf_length);
		if (!pixel_buf)
		{
			fprintf(stderr, "JGimp out of memory error: couldn't allocate pixel buffer in common_pixel_read");
			return -1;
		}

		/* Grab the pixels */
		adapter_get_pixel_rgn(drawable, x, y, width, adjusted_height, pixel_buf, 0, pixel_buf_length);

		/* Set up iterators into the buffers we're copying from/to */
		pixel_buf_iterator = pixel_buf;
		dest_buf_iterator  = (jint*)(buffer + buffer_offset);
		end_point          = dest_buf_iterator + num_pixels_to_copy;

		/* Copy the pixels to the dest array */
		if (drawable->is_rgb)
		{
			register jint this_pixel_value = -1;

			if (drawable->has_alpha)
			{
				if (drawable->bpp == 4)
				{
					while (dest_buf_iterator != end_point)
					{
						this_pixel_value       = (*pixel_buf_iterator) << 16; pixel_buf_iterator++; /* Red   */
						this_pixel_value      |= (*pixel_buf_iterator) << 8 ; pixel_buf_iterator++; /* Green */
						this_pixel_value      |= (*pixel_buf_iterator)      ; pixel_buf_iterator++; /* Blue  */
						this_pixel_value      |= (*pixel_buf_iterator) << 24; pixel_buf_iterator++; /* Alpha */
						(*dest_buf_iterator)   = this_pixel_value           ; dest_buf_iterator++;
					}
				}
				else
				{
printf("Stub for transferring rgb with alpha to Java buffer at byte count different than 4\n");
				}
			}
			else
			{
				if (drawable->bpp == 3) 
				{
					/* ARGB byte order, with alpha initialized to full on */
					while (dest_buf_iterator != end_point)
					{
						this_pixel_value       = (*pixel_buf_iterator) << 16  ; pixel_buf_iterator++;  /* Red   */
						this_pixel_value      |= (*pixel_buf_iterator) <<  8  ; pixel_buf_iterator++;  /* Green */
						this_pixel_value      |= (*pixel_buf_iterator)        ; pixel_buf_iterator++;  /* Blue  */
						(*dest_buf_iterator)   = this_pixel_value | 0xFF000000; dest_buf_iterator++; /* OR in the Alpha as a solid */
					}
				}
				else
				{
printf("Stub for transferring rgb without alpha to Java buffer at byte count different than 3\n");
				}
			}
		}
		else if (drawable->is_gray || drawable->is_indexed)
		{
			register jint this_pixel_value = -1;

			if (drawable->has_alpha)
			{
				if (drawable->bpp == 2) 
				{
					/* AXXX - Alpha, Gray/Indexed, Gray/Indexed, Gray/Indexed byte order */
					while (dest_buf_iterator != end_point)
					{
						this_pixel_value       = (*pixel_buf_iterator)      ; pixel_buf_iterator++;   /* Gray value  */
						this_pixel_value      |= this_pixel_value      << 8 ;                         /* Gray value  */
						this_pixel_value      |= this_pixel_value      << 16;                         /* Gray value  */
						this_pixel_value      |= (*pixel_buf_iterator) << 24; pixel_buf_iterator++;   /* Alpha value */
						(*dest_buf_iterator)   = this_pixel_value           ; dest_buf_iterator++;
					}
				}
				else
				{
					fprintf(stderr, "Unexpected byte count for grayscale/indexed image with alpha channel: %d\n", drawable->bpp);
					jni_utils_throw_proxy_exception(env, "Unexpected byte count for grayscale/indexed image with alpha channel");
					num_bytes_copied = -1;
				}
			}
			else
			{
				if (drawable->bpp == 1)
				{
					/* AXXX - Alpha, Gray/Indexed, Gray/Indexed, Gray/Indexed byte order */
					while (dest_buf_iterator != end_point)
					{
						this_pixel_value      = (*pixel_buf_iterator)         ; pixel_buf_iterator++;  /* Gray value   */
						this_pixel_value     |= this_pixel_value      <<  8   ;                        /* Gray value */
						this_pixel_value     |= this_pixel_value      << 16   ;                        /* Gray value  */
						(*dest_buf_iterator)  = this_pixel_value | 0xFF000000 ; dest_buf_iterator++; /* OR in the alpha value to solid */
					}
				}
				else
				{
					fprintf(stderr, "Unexpected byte count for grayscale/indexed image without alpha channel: %d\n", drawable->bpp);
					jni_utils_throw_proxy_exception(env, "Unexpected byte count for grayscale image without alpha channel");
					num_bytes_copied = -1;
				}
			}
		}
		else
		{
			jni_utils_throw_proxy_exception(env, "Unknown image type in readPixelRegion");
			num_bytes_copied = -1;
		}

		/* Release our buffer */
		g_free(pixel_buf);
		pixel_buf = NULL;
	}

	/* Check for buffer overflow, throw error if necessary */
	if (buffer_overflow)
	{
		jni_utils_throw_buffer_overflow_exception(env, x, y, width, height, width, adjusted_height, drawable->bpp, actual_dest_array_size);
	}
	return num_bytes_copied;
}
jlong common_pixel_write(JNIEnv* env,
		JGimpDrawableInfo* drawable,
		gint x,
		gint y,
		gint width,
		gint height,
		guchar* buffer,
		gint buffer_offset,
		gint buffer_length,
		PixelFormat buffer_format)
{
	guint          src_bpp                 = 0;
	guint32        expected_src_array_size = 0;
	guint32        actual_src_array_size   = 0;
	jlong          num_bytes_copied        = -1;
	guint32        adjusted_height         = 0;

	if (buffer_format < 0)
	{
		fprintf(stderr, "Internal jgimp error: invalid pixel buffer_format specified for common_pixel_write\n");
		return -1;
	}
	if (buffer == NULL)
	{
		fprintf(stderr, "Internal jgimp error: NULL buffer passed to common_pixel_write\n");
		return -1;
	}

	/* Check to make sure we're in bounds */
	/* Throw an exception if we're not in bounds */
	if ( (x < 0) || (y < 0) || (width < 0) || (height < 0) || ((x + width) > drawable->width) || ((y + height) > drawable->height) )
	{
		jni_utils_throw_invalid_pixel_region_exception(env, x, y, width, height, drawable->width, drawable->height);
		return -1;
	}

	/* Calculate array size needed as well as the real array size we have */
	switch (buffer_format)
	{
		case FORMAT_NATIVE_BYTE:
		case FORMAT_NATIVE_INT:
			src_bpp = drawable->bpp;
			break;

		default:
			src_bpp = 4;
	}

	expected_src_array_size = width * height * src_bpp;
	actual_src_array_size   = buffer_length - buffer_offset;

	/* Calculate the number of pixels to copy */
	if (expected_src_array_size > actual_src_array_size)
	{
		guint32 row_size = -1;

		row_size = width * src_bpp;
		adjusted_height = actual_src_array_size / row_size;
	}
	else
	{
		adjusted_height  = height;
	}
	
	num_bytes_copied = width * adjusted_height * src_bpp;
	if (num_bytes_copied > 0)
	{
		if (buffer_format != FORMAT_JAVA_INT)
		{
			num_bytes_copied = adapter_set_pixel_rgn(drawable, x, y, width, adjusted_height, buffer, buffer_offset, buffer_length, TRUE, TRUE, TRUE);
		}
		else
		{
			guint   num_pixels_to_copy = width * adjusted_height;
			guint   pixel_buf_length   = num_pixels_to_copy * drawable->bpp;
			guchar* pixel_buf          = NULL;
			guchar* pixel_buf_iterator = NULL;
			jint*   src_buf_iterator   = NULL;
			jint*   end_point          = NULL;

			/* Allocate a buffer for the pixels */
			pixel_buf = g_new(guchar, pixel_buf_length);
			if (!pixel_buf)
			{
				fprintf(stderr, "JGimp out of memory error: couldn't allocate pixel buffer in common_pixel_write");
				return -1;
			}

			/* Set up our array iterators */
			pixel_buf_iterator = pixel_buf;
			src_buf_iterator   = (jint*)(buffer + buffer_offset);
			end_point          = src_buf_iterator + num_pixels_to_copy;

			/* Copy the pixels from the int array to the byte array */
			if (drawable->is_rgb)
			{
				register int this_pixel_value = -1;

				if (drawable->has_alpha)
				{
					if (drawable->bpp == 4)
					{
						while (src_buf_iterator != end_point)
						{
							this_pixel_value       = (*src_buf_iterator)            ; src_buf_iterator++;
							(*pixel_buf_iterator)  = (this_pixel_value >> 24) & 0xFF; pixel_buf_iterator++;  /* Red   */
							(*pixel_buf_iterator)  = (this_pixel_value >> 16) & 0xFF; pixel_buf_iterator++;  /* Green */
							(*pixel_buf_iterator)  = (this_pixel_value >>  8) & 0xFF; pixel_buf_iterator++;  /* Blue  */
							(*pixel_buf_iterator)  =  this_pixel_value        & 0xFF; pixel_buf_iterator++;  /* Alpha */
						}
					}
					else
					{
						num_bytes_copied = -1;
printf("Stub for transferring rgb with alpha from Java buffer at byte count different than 4\n");
					}
				}
				else
				{
					if (drawable->bpp == 3) 
					{
						while (src_buf_iterator != end_point)
						{
							this_pixel_value       = (*src_buf_iterator)            ; src_buf_iterator++;
							(*pixel_buf_iterator)  = (this_pixel_value >> 24) & 0xFF; pixel_buf_iterator++;  /* Red   */
							(*pixel_buf_iterator)  = (this_pixel_value >> 16) & 0xFF; pixel_buf_iterator++;  /* Green */
							(*pixel_buf_iterator)  = (this_pixel_value >>  8) & 0xFF; pixel_buf_iterator++;  /* Blue  */
						}
					}
					else
					{
						num_bytes_copied = -1;
printf("Stub for transferring rgb without alpha from Java buffer at byte count different than 3\n");
					}
				}
			}
			else if (drawable->is_gray || drawable->is_indexed)
			{
				register int this_pixel_value = -1;

				if (drawable->has_alpha)
				{
					if (drawable->bpp == 2) 
					{
						while (src_buf_iterator != end_point)
						{
							this_pixel_value       = (*src_buf_iterator)           ; src_buf_iterator++;
							(*pixel_buf_iterator)  = (this_pixel_value >> 8) & 0xFF; pixel_buf_iterator++;
							(*pixel_buf_iterator)  =  this_pixel_value       & 0xFF; pixel_buf_iterator++;
						}
					}
					else
					{
						fprintf(stderr, "Unexpected byte count for grayscale or indexed image with alpha channel: %d\n", drawable->bpp);
						jni_utils_throw_proxy_exception(env, "Unexpected byte count for grayscale image with alpha channel");
						num_bytes_copied = -1;
					}
				}
				else
				{
					if (drawable->bpp == 1)
					{
						while (src_buf_iterator != end_point)
						{
							(*pixel_buf_iterator) = (*src_buf_iterator);
							src_buf_iterator++;
							pixel_buf_iterator++;
						}
					}
					else
					{
						fprintf(stderr, "Unexpected byte count for grayscale or indexed image without alpha channel: %d\n", drawable->bpp);
						jni_utils_throw_proxy_exception(env, "Unexpected byte count for grayscale image without alpha channel");
						num_bytes_copied = -1;
					}
				}
			}
			
			/* Write out our contents and free the buffer */
			if (num_bytes_copied > 0)
			{
				num_bytes_copied = adapter_set_pixel_rgn(drawable, x, y, width, adjusted_height, pixel_buf, 0, pixel_buf_length, TRUE, TRUE, TRUE);
			}
			g_free(pixel_buf);
			pixel_buf = NULL;
		}
	}

	return num_bytes_copied;
}
