/*******************************************************************************
 * 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 "cgal.h"
#include "shotCgal.h"
#include "global.h"
#include "types.h"
#include "Storage.h"


#define CLASS							Java_edu_duke_donaldLab_jdshot_disco_cgal_HalfedgeIterator
#define HalfedgeIterator_init			CLASSFN( CLASS, init )
#define HalfedgeIterator_cleanup		CLASSFN( CLASS, cleanup )
#define HalfedgeIterator_hasNext		CLASSFN( CLASS, hasNext )
#define HalfedgeIterator_next			CLASSFN( CLASS, next )


typedef struct _HalfedgeIterator
{
	Arrangement::Ccb_halfedge_circulator circ;
	Arrangement::Ccb_halfedge_circulator circStart;
	bool isStart;
	jobject halfedgeObj;
	Arrangement::Halfedge halfedge;

	_HalfedgeIterator( )
	{
		// do nothing
		// we just need a no-arg constructor
	}

	_HalfedgeIterator( Arrangement::Ccb_halfedge_circulator _circ )
	{
		circ = _circ;
		circStart = _circ;
		isStart = true;
		halfedgeObj = NULL;
	}
} HalfedgeIterator;


static Storage<HalfedgeIterator> g_halfedgeIterators( CGALCLASS( "HalfedgeIterator" ), true );
static jclass g_halfedgeIteratorClass;
static jfieldID g_halfedgeIteratorFaceId;
static jfieldID g_halfedgeIteratorHoleId;
static jfieldID g_halfedgeIteratorTransientId;


void halfedgeIteratorsCleanup( JNIEnv *jvm )
{
	g_halfedgeIterators.cleanupAll( jvm );

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

Arrangement::Face *getFaceFromHalfedgeIterator( JNIEnv *jvm, jobject self )
{
	// lookup meta info if needed
	if( g_halfedgeIteratorClass == NULL )
	{
		g_halfedgeIteratorClass = (jclass)jvm->NewGlobalRef( jvm->FindClass( CGALCLASS( "HalfedgeIterator" ) ) );
	}
	if( g_halfedgeIteratorFaceId == NULL )
	{
		g_halfedgeIteratorFaceId = jvm->GetFieldID( g_halfedgeIteratorClass, "m_face", "L" CGALCLASS( "Face" ) ";" );
		checkException( jvm );
	}

	jobject face = jvm->GetObjectField( self, g_halfedgeIteratorFaceId );
	checkException( jvm );
	if( face == NULL )
	{
		return NULL;
	}
	return getFace( jvm, face );
}

Arrangement::Ccb_halfedge_circulator *getHoleFromHalfedgeIterator( JNIEnv *jvm, jobject self )
{
	// lookup meta info if needed
	if( g_halfedgeIteratorClass == NULL )
	{
		g_halfedgeIteratorClass = (jclass)jvm->NewGlobalRef( jvm->FindClass( CGALCLASS( "HalfedgeIterator" ) ) );
	}
	if( g_halfedgeIteratorHoleId == NULL )
	{
		g_halfedgeIteratorHoleId = jvm->GetFieldID( g_halfedgeIteratorClass, "m_hole", "L" CGALCLASS( "Hole" ) ";" );
		checkException( jvm );
	}

	jobject hole = jvm->GetObjectField( self, g_halfedgeIteratorHoleId );
	checkException( jvm );
	if( hole == NULL )
	{
		return NULL;
	}
	return getHole( jvm, hole );
}

HalfedgeIterator *getHalfedgeIterator( JNIEnv *jvm, jobject self )
{
	return g_halfedgeIterators.get( jvm, self );
}

bool isTransient( JNIEnv *jvm, jobject self )
{
	// lookup meta info if needed
	if( g_halfedgeIteratorClass == NULL )
	{
		g_halfedgeIteratorClass = (jclass)jvm->NewGlobalRef( jvm->FindClass( CGALCLASS( "HalfedgeIterator" ) ) );
	}
	if( g_halfedgeIteratorTransientId == NULL )
	{
		g_halfedgeIteratorTransientId = jvm->GetFieldID( g_halfedgeIteratorClass, "m_isTransient", "Z" );
		checkException( jvm );
	}

	jboolean isTransient = jvm->GetBooleanField( self, g_halfedgeIteratorTransientId );
	checkException( jvm );
	return (bool)isTransient;
}


JNIEXPORT void JNICALL HalfedgeIterator_init( JNIEnv *jvm, jobject self )
{
	START_SIGNAL_HANDLING
	{
		// are we iterating over a face or hole?
		Arrangement::Face *pFace = getFaceFromHalfedgeIterator( jvm, self );
		Arrangement::Ccb_halfedge_circulator *pHole = getHoleFromHalfedgeIterator( jvm, self );
		HalfedgeIterator *pIter = NULL;
		if( pFace != NULL )
		{
			pIter = new HalfedgeIterator( pFace->outer_ccb() );
		}
		else if( pHole != NULL )
		{
			pIter = new HalfedgeIterator( *pHole );
		}
		g_halfedgeIterators.add( jvm, self, pIter );

		if( isTransient( jvm, self ) )
		{
			pIter->halfedgeObj = jvm->NewGlobalRef( newHalfedge( jvm, &pIter->halfedge ) );
		}
	}
	STOP_SIGNAL_HANDLING
}

JNIEXPORT void JNICALL HalfedgeIterator_cleanup( JNIEnv *jvm, jclass c, jint id )
{
	START_SIGNAL_HANDLING
	{
		HalfedgeIterator *pIter = g_halfedgeIterators.get( id );
		if( pIter->halfedgeObj != NULL )
		{
			jvm->DeleteGlobalRef( pIter->halfedgeObj );
			pIter->halfedgeObj = NULL;
		}
		if( !g_halfedgeIterators.cleanup( id ) )
		{
			throwException( jvm, "HalfedgeIterator cleanup failure!" );
		}
	}
	STOP_SIGNAL_HANDLING
}

JNIEXPORT jboolean JNICALL HalfedgeIterator_hasNext( JNIEnv *jvm, jobject self )
{
	START_SIGNAL_HANDLING
	{
		HalfedgeIterator *pIter = getHalfedgeIterator( jvm, self );
		return pIter->isStart || pIter->circ != pIter->circStart;
	}
	STOP_SIGNAL_HANDLING
	return false;
}

JNIEXPORT jobject JNICALL HalfedgeIterator_next( JNIEnv *jvm, jobject self )
{
	START_SIGNAL_HANDLING
	{
		HalfedgeIterator *pIter = getHalfedgeIterator( jvm, self );

		jobject halfedge = NULL;
		if( !isTransient( jvm, self ) )
		{
			// by default, make a new halfedge
			halfedge = newHalfedge( jvm, &*(pIter->circ) );
		}
		else
		{
			// if transient, reuse memory for all halfedges in this circulator
			halfedge = pIter->halfedgeObj;
			Arrangement::Halfedge *pHalfedge = getHalfedge( jvm, halfedge );
			*pHalfedge = *(pIter->circ);
		}

		// advance the circulator
		pIter->circ++;
		pIter->isStart = false;

		return halfedge;
	}
	STOP_SIGNAL_HANDLING
	return NULL;
}
