

//
// THIS CODE IS UNREADABLE BUT REALY NOT SO COMPLEX AND IT WORKS
// SO TWO WAY :
//
// [1]	make this code more and more unreadable :)
// [2]	you don't need to read this code it's only a wrapper from Java <-> C object, modify absolutly
//		nothing.
//




#include "CvJNILib.h"




// TODO: rewrite this method or create a new one to return object pointer instead of map key name
// and change all code below
// return instances[key];



/**
 * Return the required object Java instance name.
 * Optionaly, if only one element exits in objects list, this name could be retrieve by the first 
 * map key. This option is enable by default to optimize the process.
 *
 * @param env		pointer to native method interface
 * @param obj		the Java object
 * @param fromjava	force to call Java to get the instance name
 *
 * @return the Java object instance name
 */
string javaObjectInstance( JNIEnv *env, jobject obj, bool fromjava )
{
	
	// retrieve the first map element key value
	// -> exit
	if ( !fromjava && instances.size()==1 ) {
		map<string,ofxOpenCV*>::iterator itr = instances.begin();
		return itr->first;
	}
	
	// call java to retrieve the object instance 
	jclass cls = env->GetObjectClass( obj );
	jmethodID mid = env->GetMethodID( cls, "toString", "()Ljava/lang/String;" );
	jstring key = (jstring) env->CallObjectMethod( obj, mid );
	
	return env->GetStringUTFChars( key, JNI_FALSE );
}



/**
 * Return object pointer form C++ side.
 */
ofxOpenCV* whois( JNIEnv *env, jobject obj )
{
	return instances[ javaObjectInstance(env,obj) ];
}



/**
 * Call Java side :
 * update the specified jobject with the given dimensions.
 */
void updateJavaDim( JNIEnv * env, jobject obj, int width, int height )
{
	
	// fill Java width and height properties 
	jclass cls	  = env->GetObjectClass( obj );
	jfieldID wfid = env->GetFieldID( cls, "width", "I");
	jfieldID hfid = env->GetFieldID( cls, "height", "I");
	
	env->SetIntField( obj, wfid, width );
	env->SetIntField( obj, hfid, height );
}





///////////////////////////////////// OPENCV ///////////////////////////////////////////////////////


JNIEXPORT jstring JNICALL Java_hypermedia_video_OpenCV_version
( JNIEnv * env, jclass cls )
{
	return env->NewStringUTF(CV_VERSION);
}







// -------------------------------------------------------------------------------------------------
	#pragma mark setup
// -------------------------------------------------------------------------------------------------

JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_instantiate
( JNIEnv * env, jobject obj )
{
	string key = javaObjectInstance( env, obj, true );
	instances[ key ] = new ofxOpenCV();
	//std::cout << "instantiate cv: "<< key << std::endl;
	//std::cout << "-----> cv map size "<< instances.size() << std::endl;
}

JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_dispose
( JNIEnv * env, jobject obj )
{
	
	// Processing PApplet call automatically this method while exiting the program
	// if all objects are already be deleted by user for some miscalleneous reason
	// -> exit
	if ( instances.empty() ) return;
	
	string key = javaObjectInstance( env, obj, true );
	
	//std::cout << "\ndispose cv: " << key << std::endl;
	if ( instances.count(key)>0 ) {
	
		instances[ key ]->deallocate();
		
		delete instances[ key ];
		instances.erase( key );
		
		//std::cout << "-----> map size "<< instances.size() << std::endl;
	}
}


JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_allocate
( JNIEnv * env, jobject obj, jint width, jint height, jint mode )
{

	string key = javaObjectInstance( env, obj, false );
	instances[ key ]->allocate( (int)width, (int)height, (int)mode );
	
	updateJavaDim( env, obj, instances[ key ]->width, instances[ key ]->height );
}








// -------------------------------------------------------------------------------------------------
	#pragma mark data manipulation
// -------------------------------------------------------------------------------------------------

// method : remember
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_remember
( JNIEnv * env, jobject obj, jint kind, jint mode )
{
	string key = javaObjectInstance( env, obj );
	instances[ key ]->remember( (int)kind, (int)mode );
}

// method : absdiff
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_absDiff
( JNIEnv * env, jobject  obj )
{
	string key = javaObjectInstance( env, obj );
	instances[ key ]->absDiff();
}

// method : threshold
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_threshold
( JNIEnv * env, jobject obj, jfloat value, jfloat max, jint type ) 
{
	string key = javaObjectInstance( env, obj );
	instances[ key ]->threshold( (float)value, (float)max, (int)type );
}


// method : adaptiveThreshold
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_adaptiveThreshold                               
( JNIEnv * env, jobject obj, jfloat max, jint method, jint type, jint block_size, jfloat param1 ) 
{                                                                                                      
	string key = javaObjectInstance( env, obj );
	instances[ key ]->adaptiveThreshold( (float)max, (int)method, (int)type, (int)block_size, (float)param1 );
}                                                                                                      
                                                                                                       

// method : convert
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_convert
( JNIEnv * env, jobject obj, jint type )
{
	string key = javaObjectInstance( env, obj );
	instances[ key ]->convert( (int)type );
}


// method : flip
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_flip
( JNIEnv * env, jobject obj, jint mode ) 
{
	string key = javaObjectInstance( env, obj );
	instances[ key ]->flip( (int)mode );
	
}

// method : pixels
JNIEXPORT jintArray JNICALL Java_hypermedia_video_OpenCV_pixels
( JNIEnv * env, jobject obj, jint type )
{	
	
	
	string key = javaObjectInstance( env, obj );
	
	
	
	/*// DON'T CALL OBJECT METHOD AND  WORK HERE TO CONVERT 
	// PIXELS VALUE DIRECTLY WITHOUT A DOUBLE CONVERSION IplImage -> unsigned char -> int[]
	unsigned char * data = instances[ key ]->pixels( (int)type );
	
	jintArray jpixels;
	int size = instances[ key ]->width*instances[ key ]->height;
	jpixels  = env->NewIntArray(size);
	
	
	jint pix[1];
	for( int i=0; i<size; i++ ) {
		pix[0] = 0xff000000 | data[i+2] << 16 | data[i+1] << 8 | data[i];
		env->SetIntArrayRegion( jpixels, i, 1, pix );
	}
	
	return jpixels;*/
	
	
	jintArray jpixels;
	int size = instances[ key ]->width*instances[ key ]->height;
	jpixels  = env->NewIntArray(size);
	jint data[1];
	
	
	
	// nothing to fix
	if ( (int)type==OFX_OPENCV_SOURCE && instances[ key ]->source==NULL ) return jpixels;
	if ( (int)type==OFX_OPENCV_BUFFER && instances[ key ]->buffer==NULL ) return jpixels;
	if ( (int)type==OFX_OPENCV_MEMORY && instances[ key ]->memory==NULL ) return jpixels;
	
	
	IplImage * src;
	switch( (int)type ) {
		case OFX_OPENCV_MEMORY	: src = cvCloneImage(instances[ key ]->memory); break;
		case OFX_OPENCV_SOURCE	: src = cvCloneImage(instances[ key ]->source); break;
		default	: src = cvCloneImage(instances[ key ]->buffer); break;
	}
	
	
	for( int i=0; i<size; i++ ) {
		
		int x = i%instances[ key ]->width;
		int y = i/instances[ key ]->width;
		int p = ( x*src->nChannels ) + ( y*src->widthStep );
		
		unsigned char b = src->imageData[ p ];
		unsigned char g = src->nChannels==1 ? b : src->imageData[ p+1 ];
		unsigned char r = src->nChannels==1 ? b : src->imageData[ p+2 ];
		
		data[0] = 0xff000000 | r << 16 | g << 8 | b;
		env->SetIntArrayRegion( jpixels, i, 1, data );
	}
	
	cvReleaseImage( &src );
	
	return jpixels;	
}

// method : copy Cv
JNIEXPORT jboolean JNICALL Java_hypermedia_video_OpenCV_copy__Lhypermedia_video_OpenCV_2II
( JNIEnv * env, jobject obj, jobject source, jint srctype, jint desttype )
{	
	
	string srcId  = javaObjectInstance( env, source );
	string destId = javaObjectInstance( env, obj );
	
	IplImage * srcImg;
	IplImage * destImg;
	
	switch( srctype ) {
		case OFX_OPENCV_MEMORY	: srcImg = cvCloneImage( instances[ srcId ]->memory ); break;
		case OFX_OPENCV_SOURCE	: srcImg = cvCloneImage( instances[ srcId ]->source ); break;
		default					: srcImg = cvCloneImage( instances[ srcId ]->buffer ); break;
	}
	switch( desttype ) {
		case OFX_OPENCV_MEMORY	: destImg = cvCloneImage( instances[ destId ]->memory ); break;
		case OFX_OPENCV_SOURCE	: destImg = cvCloneImage( instances[ destId ]->source ); break;
		default					: destImg = cvCloneImage( instances[ destId ]->buffer ); break;
	}
	
	instances[ destId ]->copy( srcImg, 0, 0, srcImg->width, srcImg->height, 0, 0, destImg->width, destImg->height );
	
	// free memory
	cvReleaseImage( &srcImg );
	cvReleaseImage( &destImg );
	
	return true;
}

// method : copy pixels
JNIEXPORT jboolean JNICALL Java_hypermedia_video_OpenCV_copy___3IIIIIIIIII
( JNIEnv * env, jobject obj, jintArray pixels, jint step, jint srcx, jint srcy, jint srcw, jint srch, jint dstx, jint dsty, jint dstw, jint dsth )
{
	
	string key = javaObjectInstance( env, obj );
	
	
	// -> exit : without buffer, outside the scope, or without source's region
	if ( !instances[key]->buffer || dstx>instances[key]->width-1 || dsty>instances[key]->height-1 || srcw<=0 || srch<=0 ) {
		printf( "buffer does not exists or to small" );
		return false;
	}
	
	
	// access pixel's values and
	// check in case the VM tried to make a copy.
	int * data = (int*) env->GetPrimitiveArrayCritical( pixels, 0 );
	if ( data==NULL ) std::cout << "out of memory"  << std::endl;
	
	// data length
	int srcsize = (int) env->GetArrayLength( pixels );
	
	// the destination region size
	int size = step*(srcsize/step);
	
	
	
	// scaled image
	IplImage * src  = cvCreateImage( cvSize(step,srcsize/step), IPL_DEPTH_8U, instances[key]->buffer->nChannels );
	
	
	// add data
	for( int i=0; i<size; i++ ) {
		
		int x = i%step;
		int y = i/step;
		
		// retrieve scaled source and destination pixel's index
		int srci = x+(y*step);
		int dsti = ( x*src->nChannels )+( y*src->widthStep );
		
		// get RGB values
		unsigned char b = data[ srci ] & 0xff;
		unsigned char g = data[ srci ] >> 8 & 0xff;
		unsigned char r = data[ srci ] >> 16 & 0xff;
		
		
		// set RGB pixel's value
		if ( src->nChannels==3 ) {
			src->imageData[ dsti ]		= b;
			src->imageData[ dsti+1 ]	= g;
			src->imageData[ dsti+2 ]	= r;
		}
		// or convert to gray
		else src->imageData[ dsti ] = (b*0.114) + (g*0.587) + (r*0.299);
		
	}	
	
	bool done = instances[ key ]->copy( src, srcx, srcy, srcw, srch, dstx, dsty, dstw, dsth );
	
	// free memory
	cvReleaseImage( &src );
	env->ReleasePrimitiveArrayCritical( pixels, data, 0 );
	
	return done;
	
}

// method : copy file
JNIEXPORT jboolean JNICALL Java_hypermedia_video_OpenCV_copy__Ljava_lang_String_2IIIIIIII
( JNIEnv * env, jobject obj, jstring file, jint srcx, jint srcy, jint srcw, jint srch, jint dstx, jint dsty, jint dstw, jint dsth )
{

	string key  = javaObjectInstance( env, obj );

	// -> exit : without buffer or outside the scope
	if ( !instances[key]->buffer || dstx>instances[key]->width-1 || dsty>instances[key]->height-1 ) {
		printf( "buffer does not exists or to small" );
		return false;
	}
	

	// retrieve file absolute path
	jclass cls 	  = env->GetObjectClass( obj );
	jmethodID mid = env->GetMethodID( cls, "absolutePath", "(Ljava/lang/String;)Ljava/lang/String;" );
	jstring path  = (jstring) env->CallObjectMethod( obj, mid, file );
	
	// convert jstring
	const char * usrfile = env->GetStringUTFChars( file, JNI_FALSE );
	const char * abspath = env->GetStringUTFChars( path, JNI_FALSE );
	
	
	bool loaded = instances[key]->copy( string(abspath), (int)srcx, (int)srcy, (int)srcw, (int)srch, (int)dstx, (int)dsty, (int)dstw, (int)dsth );
	if ( !loaded ) {
		fprintf( stderr, "[opencv] the image file '%s' cannot be found in folders or loaded\n", usrfile );
		fprintf( stderr, "[opencv] try with the full path instead.\n" );
	}
	
	// free memory
	env->ReleaseStringUTFChars( file, usrfile );
	env->ReleaseStringUTFChars( path, abspath );
	
	return loaded;
	
}

// method : interpolation
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_interpolation
( JNIEnv * env, jobject obj, jint method )
{
	string key = javaObjectInstance( env, obj );
	instances[ key ]->interpolation( (int)method );
}


JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_restore
( JNIEnv * env, jobject obj, jint type ) 
{
	string key = javaObjectInstance( env, obj );
	instances[ key ]->restore( (int)type );
}




// -------------------------------------------------------------------------------------------------
	#pragma mark capture and movie file
// -------------------------------------------------------------------------------------------------

// method : capture
JNIEXPORT jboolean JNICALL Java_hypermedia_video_OpenCV_capture
( JNIEnv * env, jobject obj, jint w, jint h, jint idx, jfloat fps )
{	
	string key  = javaObjectInstance( env, obj );
	bool opened = instances[ key ]->capture( (int)w, (int)h, (int)idx, (float)fps );
	if ( opened ) updateJavaDim( env, obj, instances[ key ]->width, instances[ key ]->height );
	else {
		fprintf( stderr, "[opencv] error while starting capture : device %i\n", (int)idx );
	}
	
	return opened;
}

// method : movie
JNIEXPORT jboolean JNICALL Java_hypermedia_video_OpenCV_movie
( JNIEnv * env, jobject obj, jstring file, jint w, jint h, jfloat fps )
{

	// retrieve file absolute path
	jclass cls 	  = env->GetObjectClass( obj );
	jmethodID mid = env->GetMethodID( cls, "absolutePath", "(Ljava/lang/String;)Ljava/lang/String;" );
	jstring path  = (jstring) env->CallObjectMethod( obj, mid, file );
	
	// convert jstring
	const char * usrfile = env->GetStringUTFChars( file, JNI_FALSE );
	const char * abspath = env->GetStringUTFChars( path, JNI_FALSE );
	
	
	
	string key  = javaObjectInstance( env, obj );
	bool loaded = instances[ key ]->movie( string(abspath), (int)w, (int)h, (float)fps );
	if ( loaded ) {
		updateJavaDim( env, obj, instances[ key ]->width, instances[ key ]->height );
		if ( instances[ key ]->width==0 && instances[ key ]->width==0 ) {
			printf( "[opencv] the movie file '%s' is successfully loaded", usrfile );
			printf( "[opencv] but can not retrieve movie dimensions" );
			printf( "[opencv] please specify movie size by hand" );
		}
	}
	else {
		fprintf( stderr, "[opencv] the movie file '%s' cannot be found in folders or loaded\n", usrfile );
		fprintf( stderr, "[opencv] try with the full path instead.\n" );
	}
	
	// free memory
	env->ReleaseStringUTFChars( file, usrfile );
	env->ReleaseStringUTFChars( path, abspath );
	
	return loaded;
}

// method : start
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_start
( JNIEnv * env, jobject obj )
{
	string key  = javaObjectInstance( env, obj );
	instances[ key ]->start();
}

/*
 * Class:     hypermedia_video_OpenCV
 * Method:    playing
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_hypermedia_video_OpenCV_playing
( JNIEnv *env, jobject obj )
{
	string key  = javaObjectInstance( env, obj );
	return instances[ key ]->playing();
}

// method : pause
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_pause
( JNIEnv * env, jobject obj )
{
	string key  = javaObjectInstance( env, obj );
	instances[ key ]->pause();
}

// method : stop
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_stop
( JNIEnv * env, jobject obj )
{
	string key  = javaObjectInstance( env, obj );
	instances[ key ]->stop();
}
  
// method : image
JNIEXPORT jboolean JNICALL Java_hypermedia_video_OpenCV_loadImage
( JNIEnv * env, jobject obj, jstring file, jint w, jint h )
{

	// retrieve file absolute path
	jclass cls 	  = env->GetObjectClass( obj );
	jmethodID mid = env->GetMethodID( cls, "absolutePath", "(Ljava/lang/String;)Ljava/lang/String;" );
	jstring path  = (jstring) env->CallObjectMethod( obj, mid, file );
	
	// convert jstring
	const char * usrfile = env->GetStringUTFChars( file, JNI_FALSE );
	const char * abspath = env->GetStringUTFChars( path, JNI_FALSE );
	
	
	string key  = javaObjectInstance( env, obj );
	bool loaded = instances[ key ]->image( string(abspath), (int)w, (int)h );
	if ( loaded ) updateJavaDim( env, obj, instances[ key ]->width, instances[ key ]->height );
	else {
		fprintf( stderr, "[opencv] the image file '%s' cannot be found in folders or loaded\n", usrfile );
		fprintf( stderr, "[opencv] try with the full path instead.\n" );
	}
	
	// free memory
	env->ReleaseStringUTFChars( file, usrfile );
	env->ReleaseStringUTFChars( path, abspath );
	
	return loaded;
}


// method : (CvPlayer) read
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_read
( JNIEnv * env, jobject obj )
{	
	string key = javaObjectInstance( env, obj );
	instances[ key ]->read();
}

// method : (CvPlayer) read
JNIEXPORT jboolean JNICALL Java_hypermedia_video_OpenCV_available
( JNIEnv * env, jobject obj )
{	
	string key = javaObjectInstance( env, obj );
	return instances[ key ]->available();
}

// method : (CvPlayer) jump
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_jump
( JNIEnv * env, jobject obj, jfloat value, jint type )
{
	string key = javaObjectInstance( env, obj );
	instances[ key ]->jump( (double)value, (int)type );
}

// method : (CvPlayer) property
JNIEXPORT jfloat JNICALL Java_hypermedia_video_OpenCV_property
( JNIEnv * env, jobject obj, jint which )
{
	string key = javaObjectInstance( env, obj );
	return (jfloat)instances[ key ]->property( (int)which );
}


///////////////////////////// Region of interest ///////////////////////////////////////////////////

// -- method : ROI
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_ROI
( JNIEnv * env, jobject obj, jint x, jint y , jint w, jint h )
{
	string key = javaObjectInstance( env, obj );
	instances[ key ]->ROI( (int)x, (int)y , (int)w, (int)h );
}


// -- method : invert
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_invert
( JNIEnv * env, jobject obj )
{
	string key = javaObjectInstance( env, obj );
	instances[ key ]->invert();
}

// -- method : blur
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_blur
( JNIEnv * env, jobject obj, jint type, jint param1, jint param2, jfloat param3, jfloat param4
)
{
	string key = javaObjectInstance( env, obj );
	instances[ key ]->blur( (int)type, (int)param1, (int)param2, (float)param3, (float)param4 );
}

// -- method : brightness ~ contrast
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_brightnessContrast
( JNIEnv * env, jobject obj, jint brightness, jint contrast )
{
	string key = javaObjectInstance( env, obj );
	instances[ key ]->brightnessContrast( (int)brightness, (int)contrast );
}







// -------------------------------------------------------------------------------------------------
	#pragma mark detection
// -------------------------------------------------------------------------------------------------

// method : cascade
JNIEXPORT jboolean JNICALL Java_hypermedia_video_OpenCV_cascade
( JNIEnv * env, jobject obj, jstring file )
{
	
	
	// get file absolute path
	jclass cls 	  = env->GetObjectClass( obj );
	jmethodID mid = env->GetMethodID( cls, "absolutePath", "(Ljava/lang/String;)Ljava/lang/String;" );
	jstring path  = (jstring) env->CallObjectMethod( obj, mid, file );
	
	
	// convert file name and file path
	const char * usrfile = env->GetStringUTFChars( file, JNI_FALSE );
	const char * abspath = env->GetStringUTFChars( path, JNI_FALSE );
	
	// load
	string key  = javaObjectInstance( env, obj );
	bool loaded = instances[ key ]->cascade( string(abspath) );
	if ( !loaded ) fprintf( stderr, "[opencv] try with the full path instead.\n\n" );
	
	
	// free memory
	env->ReleaseStringUTFChars( file, usrfile );
	env->ReleaseStringUTFChars( path, abspath );
	
	return loaded;
}

// -- method : detect
JNIEXPORT jobjectArray JNICALL Java_hypermedia_video_OpenCV_detect
( JNIEnv * env, jobject obj, jfloat scale, jint neighbors, jint flags, jint w, jint h )
{

	vector<ofRectangle> list;
	string key = javaObjectInstance( env, obj );
	int count  = instances[ key ]->detect( list, scale, (int)neighbors, (int)flags, (int)w, (int)h );
	
	// Find the Java Rectangle class object and constructor method
	jclass cls = env->FindClass( "java/awt/Rectangle" );
	jmethodID method = env->GetMethodID( cls, "<init>", "(IIII)V" );
	
	// create Java output list
	jobjectArray out = env->NewObjectArray( count, cls, NULL );
	int i = 0;
	for( vector<ofRectangle>::iterator itr=list.begin(); itr!=list.end(); itr++ ) {
		// Construct a new Java Rectangle
		jobject elem = env->NewObject( cls, method, (int)itr->x, (int)itr->y, (int)itr->width, (int)itr->height );
		// place the new element in the output list
		env->SetObjectArrayElement( out, i++, elem );
	}
	
	// return the result
	list.clear();
	return out;

}

// method : blobs detect
// TODO: looping looping looping -> proceed blob detect here or store Blob object in C++ side
JNIEXPORT jobjectArray JNICALL Java_hypermedia_video_OpenCV_blobs
( JNIEnv * env, jobject obj, jint minArea, jint maxArea, jint maxBlobs, jboolean findHoles, jboolean approx )
{
	vector<ofxOpenCvBlob> list;
	string key = javaObjectInstance( env, obj );
	int count = instances[ key ]->blobs( list, (int)minArea, (int)maxArea, (int)maxBlobs, (bool)findHoles, (bool)approx );
	
	
	// search for the hypermedia.video.CvBlob class
	jclass bcls = env->FindClass( "hypermedia/video/CvBlob" );
		
	// define the output list
	jobjectArray out = env->NewObjectArray( count, bcls, NULL );
	if ( count>0 ) {
		
		// search for constructor and associated fields
		jmethodID bmid = env->GetMethodID( bcls, "<init>", "()V" );
		jfieldID bfid_area = env->GetFieldID( bcls, "area", "F" );
		jfieldID bfid_length = env->GetFieldID( bcls, "length", "F" );
		jfieldID bfid_centroid = env->GetFieldID( bcls, "centroid", "Lprocessing/core/PVector;" );
		jfieldID bfid_rectangle = env->GetFieldID( bcls, "rectangle", "Ljava/awt/Rectangle;" );
		jfieldID bfid_points = env->GetFieldID( bcls, "points", "[Lprocessing/core/PVector;" );
		jfieldID bfid_ishole = env->GetFieldID( bcls, "isHole", "Z" );
		
		// search for Java object class and constructor
		jclass pcls = env->FindClass( "processing/core/PVector" );
		jclass rcls = env->FindClass( "java/awt/Rectangle" );
		jmethodID pmid = env->GetMethodID( pcls, "<init>", "(FF)V" );
		jmethodID rmid = env->GetMethodID( rcls, "<init>", "(IIII)V" );
		
		
		
		// create Java object
		int i = 0;
		for( vector<ofxOpenCvBlob>::iterator itr=list.begin(); itr!=list.end(); itr++ ) {
			
		
			// create Java objects : hypermedia.video.CvBlob, Rectangle, PVector, PVector array
			jobject blob = env->NewObject( bcls, bmid );
			jobject centroid = env->NewObject( pcls, pmid, itr->centroid.x, itr->centroid.y );
			jobject rectangle = env->NewObject( rcls, rmid, itr->rectangle.x, itr->rectangle.y, itr->rectangle.width, itr->rectangle.height );
			jobjectArray points = env->NewObjectArray( itr->points.size(), pcls, NULL );
			
			
			
			// list contour points
			for( int j=0; j<(int)itr->points.size(); j++ ) {
				env->SetObjectArrayElement( points, j, env->NewObject( pcls, pmid, itr->points[j].x, itr->points[j].y ) );
			}
			
			// set java Blob's values
			env->SetFloatField( blob, bfid_area, itr->area );
			env->SetFloatField( blob, bfid_length, itr->length );
			env->SetObjectField( blob, bfid_centroid, centroid );
			env->SetObjectField( blob, bfid_rectangle, rectangle );
			env->SetObjectField( blob, bfid_points, points );
			env->SetBooleanField( blob, bfid_ishole, itr->isHole );
			
			// append the new Blob in the output list
			env->SetObjectArrayElement( out, i++, blob );
		}
		
		
	}
	
	// return the result
	list.clear();
	return out;

}



// -------------------------------------------------------------------------------------------------
	#pragma mark draw
// -------------------------------------------------------------------------------------------------

/*
 * Class:     hypermedia_video_OpenCV
 * Method:    draw
 */
JNIEXPORT void JNICALL Java_hypermedia_video_OpenCV_draw
( JNIEnv * env, jobject obj, jfloat x, jfloat y, jint type )
{
	string key = javaObjectInstance( env, obj );
	instances[ key ]->draw( (float)x, (float)y );
}

