/*******************************************************************************
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 * 
 * Contact Info:
 * 	Bruce Donald
 * 	Duke University
 * 	Department of Computer Science
 * 	Levine Science Research Center (LSRC)
 * 	Durham
 * 	NC 27708-0129 
 * 	USA
 * 	brd@cs.duke.edu
 * 
 * Copyright (C) 2011 Jeffrey W. Martin and Bruce R. Donald
 * 
 * <signature of Bruce Donald>, April 2011
 * Bruce Donald, Professor of Computer Science
 ******************************************************************************/


#include <jni.h>

#include "shotCgal.h"
#include "global.h"


AbortSignal g_abortSignal;

static jclass g_abstractCleanableClass = NULL;
static jfieldID g_idFieldId = NULL;
static jfieldID g_pointerFieldId = NULL;
static jclass g_vectorClass = NULL;
static jmethodID g_vectorConstructorId = NULL;


void checkException( JNIEnv *jvm )
{
	if( jvm->ExceptionCheck() )
	{
		// bail out of C++ code so we can handle the exception in java
		throw g_abortSignal;
	}
}

void throwException( JNIEnv *jvm, const char *message )
{
	jvm->ThrowNew( jvm->FindClass( "java/lang/Exception" ), message );
	throw g_abortSignal;
}

void throwIllegalArgumentException( JNIEnv *jvm, const char *message )
{
	jvm->ThrowNew( jvm->FindClass( "java/lang/IllegalArgumentException" ), message );
	throw g_abortSignal;
}

int getId( JNIEnv *jvm, jobject self )
{
	// lookup meta info if needed
	if( g_abstractCleanableClass == NULL )
	{
		g_abstractCleanableClass = (jclass)jvm->NewGlobalRef( jvm->FindClass( CGALCLASS( "AbstractCleanable" ) ) );
	}
	if( g_idFieldId == NULL )
	{
		g_idFieldId = jvm->GetFieldID( g_abstractCleanableClass, "m_id", "I" );
		checkException( jvm );
	}

	// get the id
	int id = jvm->GetIntField( self, g_idFieldId );
	checkException( jvm );

	// make sure the id is valid
	if( id < 0 )
	{
		throwException( jvm, "Inavlid Id!" );
	}

	return id;
}

void setId( JNIEnv *jvm, jobject self, int id )
{
	// lookup meta info if needed
	if( g_abstractCleanableClass == NULL )
	{
		g_abstractCleanableClass = (jclass)jvm->NewGlobalRef( jvm->FindClass( CGALCLASS( "AbstractCleanable" ) ) );
	}
	if( g_idFieldId == NULL )
	{
		g_idFieldId = jvm->GetFieldID( g_abstractCleanableClass, "m_id", "I" );
		checkException( jvm );
	}

	// set the id
	jvm->SetIntField( self, g_idFieldId, id );
	checkException( jvm );
}

void *getPointer( JNIEnv *jvm, jobject self )
{
	// lookup meta info if needed
	if( g_abstractCleanableClass == NULL )
	{
		g_abstractCleanableClass = (jclass)jvm->NewGlobalRef( jvm->FindClass( CGALCLASS( "AbstractCleanable" ) ) );
	}
	if( g_pointerFieldId == NULL )
	{
		g_pointerFieldId = jvm->GetFieldID( g_abstractCleanableClass, "m_pointer", "J" );
		checkException( jvm );
	}

	// get the pointer
	void *p = (void *)jvm->GetLongField( self, g_pointerFieldId );
	checkException( jvm );
	return p;
}

void setPointer( JNIEnv *jvm, jobject self, void *p )
{
	// lookup meta info if needed
	if( g_abstractCleanableClass == NULL )
	{
		g_abstractCleanableClass = (jclass)jvm->NewGlobalRef( jvm->FindClass( CGALCLASS( "AbstractCleanable" ) ) );
	}
	if( g_pointerFieldId == NULL )
	{
		g_pointerFieldId = jvm->GetFieldID( g_abstractCleanableClass, "m_pointer", "J" );
		checkException( jvm );
	}

	// set the id
	jvm->SetLongField( self, g_pointerFieldId, (jlong)p );
	checkException( jvm );
}

jobject newVector2( JNIEnv *jvm, double x, double y )
{
	// lookup meta info if needed
	if( g_vectorClass == NULL )
	{
		g_vectorClass = (jclass)jvm->NewGlobalRef( jvm->FindClass( GEOMCLASS( "Vector2" ) ) );
	}
	if( g_vectorConstructorId == NULL )
	{
		g_vectorConstructorId = jvm->GetMethodID( g_vectorClass, "<init>", "(DD)V" );
		checkException( jvm );
	}

	// return the vector
	jobject vector = jvm->NewObject( g_vectorClass, g_vectorConstructorId, x, y );
	checkException( jvm );
	return vector;
}


// prototypes for the cleanup functions
void arrangementsCleanup( JNIEnv *jvm );
void faceIteratorsCleanup( JNIEnv *jvm );
void facesCleanup( JNIEnv *jvm );
void halfedgeIteratorsCleanup( JNIEnv *jvm );
void halfedgesCleanup( JNIEnv *jvm );
void circlesCleanup( JNIEnv *jvm );
void verticesCleanup( JNIEnv *jvm );
void boxesCleanup( JNIEnv *jvm );


JNIEXPORT void JNICALL Java_edu_duke_donaldLab_jdshot_disco_cgal_ShotCgal_nativeCleanup( JNIEnv *jvm, jclass self )
{
	// release all the resources used by the various sub-components
	arrangementsCleanup( jvm );
	faceIteratorsCleanup( jvm );
	facesCleanup( jvm );
	halfedgeIteratorsCleanup( jvm );
	halfedgesCleanup( jvm );
	circlesCleanup( jvm );
	verticesCleanup( jvm );
	boxesCleanup( jvm );

	if( g_abstractCleanableClass != NULL )
	{
		jvm->DeleteGlobalRef( g_abstractCleanableClass );
		g_abstractCleanableClass = NULL;
	}
	if( g_vectorClass != NULL )
	{
		jvm->DeleteGlobalRef( g_vectorClass );
		g_vectorClass = NULL;
	}
}
