/*
 * File         : mpi_Request.c
 * Headerfile   : mpi_Request.h 
 * Author       : Sung-Hoon Ko, Xinying Li, Bryan Carpenter
 * Created      : Thu Apr  9 12:22:15 1998
 * Revision     : $Revision: 1.4 $
 * Updated      : $Date: 1999/09/16 17:48:14 $
 * Copyright: Northeast Parallel Architectures Center
 *            at Syracuse University 1998
 */

#include <mpi.h>
#include "mpi_Request.h"

#include "handle.h"
#include "buffer.h"
#include "ID.h"

DECLARE_CLASS("mpi/Request", request_class)
DECLARE_FIELD_ID(request_class, "handle", "J", reqhandleID)
DECLARE_FIELD_ID(request_class, "CbufHandle", "J", reqbufhandleID)
DECLARE_FIELD_ID(request_class, "releaseCbufWhenDone", "I", reqrelbufID)
DECLARE_FIELD_ID(request_class, "commBuf", "Ljava/lang/Object;", reqbufID)
DECLARE_FIELD_ID(request_class, "commOffset", "I", reqoffsetID)
DECLARE_FIELD_ID(request_class, "commType", "Lmpi/Datatype;", reqtypeID)
DECLARE_FIELD_ID(request_class, "commCount", "I", reqcountID)

DECLARE_CLASS("mpi/Status", status_class)
DECLARE_FIELD_ID(status_class, "handle", "J", stathandleID)
DECLARE_FIELD_ID(status_class, "source", "I", sourceID)
DECLARE_FIELD_ID(status_class, "tag", "I", tagID)
DECLARE_FIELD_ID(status_class, "count", "I", countID)
DECLARE_FIELD_ID(status_class, "index", "I", indexID)
DECLARE_METHOD_ID(status_class, "<init>", "()V", statusConstructorID)

DECLARE_CLASS("mpi/Datatype", datatype_class)
DECLARE_FIELD_ID(datatype_class, "handle", "J", DatatypehandleID)
DECLARE_FIELD_ID(datatype_class, "baseType", "I", DatatypebaseTypeID)

void releaseBufPtrMaybe(JNIEnv *env, jobject req)
{
  void *bufptr =
    (void *)jlong_to_handle((*env)->GetLongField(env, req, reqbufhandleID));
  if (bufptr) {
    jobject buf = (*env)->GetObjectField(env, req, reqbufID);
    jobject type = (*env)->GetObjectField(env, req, reqtypeID);
    jint offset = (*env)->GetIntField(env, req, reqoffsetID);
    jint count = (*env)->GetIntField(env, req, reqcountID);
    jint release = (*env)->GetIntField(env, req, reqrelbufID);
    releaseBufPtr(env, buf, type, bufptr, offset, count, release);
    /* (*env)->SetIntField(env, req, reqrelbufID, 0); */
    if (release & BUF_RELEASE) {
      (*env)->SetLongField(env, req, reqbufhandleID, (jlong)NULL);
    }
  }
}

/*
 * Class:     mpi_Status
 * Method:    init
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_mpi_Request_init 
  (JNIEnv *env, jclass thisClass)
{
}                      

/*
 * Class:     mpi_Request
 * Method:    GetReq
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_mpi_Request_GetReq
  (JNIEnv *env, jobject jthis, jint type)
{
  switch (type) {
    case 0:
      (*env)->SetLongField(env,jthis,reqhandleID,(jlong)MPI_REQUEST_NULL);
      break;
	default:
	  break;
  }
}

/*
 * Class:     mpi_Request
 * Method:    Cancel
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_mpi_Request_Cancel
  (JNIEnv *env, jobject jthis)
{
   MPI_Request req;
   req=(MPI_Request)jlong_to_handle((*env)->GetLongField(env,jthis,reqhandleID));
   MPI_Cancel(&req);
}

/*
 * Class:     mpi_Request
 * Method:    Free
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_mpi_Request_Free
  (JNIEnv *env, jobject jthis)
{
   MPI_Request req;
   req=(MPI_Request)jlong_to_handle((*env)->GetLongField(env,jthis,reqhandleID));
   MPI_Request_free(&req);
}

/*
 * Class:     mpi_Request
 * Method:    Is_null
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_mpi_Request_Is_1null
  (JNIEnv *env, jobject jthis)
{
  MPI_Request req;
  req=(MPI_Request)jlong_to_handle((*env)->GetLongField(env,jthis,reqhandleID));
  if(req==MPI_REQUEST_NULL)
    return JNI_TRUE;
  else 
    return JNI_FALSE;
}

/*
 * Class:     mpi_Request
 * Method:    Wait
 * Signature: (Lmpi/Status;)Lmpi/Status;
 */
JNIEXPORT jobject JNICALL Java_mpi_Request_Wait
  (JNIEnv *env, jobject jthis, jobject stat)
{
  MPI_Request req;
  MPI_Status *status =
    (MPI_Status*)jlong_to_handle((*env)->GetLongField(env,stat,stathandleID));
  void *bufptr;

/*
  jclass status_class = (*env)->GetObjectClass(env, stat);
  sourceID=(*env)->GetFieldID(env,status_class,"source","I");
  tagID=(*env)->GetFieldID(env,status_class,"tag","I");
*/

  req=(MPI_Request)jlong_to_handle((*env)->GetLongField(env,jthis,reqhandleID));

  MPI_Wait(&req, status);

  (*env)->SetLongField(env,jthis,reqhandleID,handle_to_jlong(req));

  (*env)->SetIntField(env,stat, sourceID,status->MPI_SOURCE);
  (*env)->SetIntField(env,stat, tagID,status->MPI_TAG);

  releaseBufPtrMaybe(env, jthis);

  return stat;
}

/*
 * Class:     mpi_Request
 * Method:    Test
 * Signature: (Lmpi/Status;)Lmpi/Status;
 */
JNIEXPORT jobject JNICALL Java_mpi_Request_Test
  (JNIEnv *env, jobject jthis, jobject stat)
{
  int flag;
  MPI_Request req;
  MPI_Status *status =
    (MPI_Status*)jlong_to_handle((*env)->GetLongField(env,stat,stathandleID));

/*
  jclass status_class = (*env)->GetObjectClass(env, stat);
  sourceID=(*env)->GetFieldID(env,status_class,"source","I");
  tagID=(*env)->GetFieldID(env,status_class,"tag","I");
*/

  req=(MPI_Request)jlong_to_handle((*env)->GetLongField(env,jthis,reqhandleID));
  
  MPI_Test(&req, &flag, status);

  (*env)->SetLongField(env,jthis,reqhandleID,handle_to_jlong(req));

  (*env)->SetIntField(env,stat, sourceID,status->MPI_SOURCE);
  (*env)->SetIntField(env,stat, tagID,status->MPI_TAG);

  if(flag!=0) {
    releaseBufPtrMaybe(env, jthis);

    return stat;
  } else {
    return NULL;
  }
}

/*
 * Class:     mpi_Request
 * Method:    Waitany
 * Signature: ([Lmpi/Request;Lmpi/Status;)Lmpi/Status;
 */
JNIEXPORT jobject JNICALL Java_mpi_Request_Waitany
  (JNIEnv *env, jclass jthis, jobjectArray array_of_request, jobject stat)
{
  int i,index;
  int count=(*env)->GetArrayLength(env,array_of_request);
  MPI_Request *reqs=(MPI_Request*)calloc(count, sizeof(MPI_Request));
  MPI_Status *status =
    (MPI_Status*)jlong_to_handle((*env)->GetLongField(env,stat,stathandleID));

/*
  jclass status_class = (*env)->GetObjectClass(env, stat);
  sourceID=(*env)->GetFieldID(env,status_class,"source","I");
  tagID=(*env)->GetFieldID(env,status_class,"tag","I");
  indexID=(*env)->GetFieldID(env,status_class,"index","I");
*/

  for(i=0; i<count; i++) {
    reqs[i]=(MPI_Request)jlong_to_handle((*env)->GetLongField(env,
	  (*env)->GetObjectArrayElement(env,array_of_request,i),
          reqhandleID) );
   }

  MPI_Waitany(count, reqs, &index, status);

  for(i=0; i<count; i++) {
    (*env)->SetLongField( env,
              (*env)->GetObjectArrayElement(env,array_of_request,i),
              reqhandleID, handle_to_jlong( reqs[i]) );
	}

  (*env)->SetIntField(env,stat, sourceID,status->MPI_SOURCE);
  (*env)->SetIntField(env,stat, tagID,status->MPI_TAG);
  (*env)->SetIntField(env,stat, indexID,index);
  releaseBufPtrMaybe(env,
		     (*env)->GetObjectArrayElement(env, array_of_request, index));

  free(reqs);

  return stat;
}

/*
 * Class:     mpi_Request
 * Method:    Testany
 * Signature: ([Lmpi/Request;Lmpi/Status;)Lmpi/Status;
 */
JNIEXPORT jobject JNICALL Java_mpi_Request_Testany
  (JNIEnv *env, jclass jthis, jobjectArray array_of_request, jobject stat)
{
  int i,flag,index;
  int count=(*env)->GetArrayLength(env,array_of_request);
  MPI_Request *reqs=(MPI_Request*)calloc(count, sizeof(MPI_Request));
  MPI_Status *status =
    (MPI_Status*)jlong_to_handle((*env)->GetLongField(env,stat,stathandleID));

/*
  jclass status_class = (*env)->GetObjectClass(env, stat);
  sourceID=(*env)->GetFieldID(env,status_class,"source","I");
  tagID=(*env)->GetFieldID(env,status_class,"tag","I");
  indexID=(*env)->GetFieldID(env,status_class,"index","I");
*/

  for(i=0; i<count; i++) {
    reqs[i]=(MPI_Request)jlong_to_handle( (*env)->GetLongField(env,
        (*env)->GetObjectArrayElement(env,array_of_request,i),
        reqhandleID) );
  }

  MPI_Testany(count, reqs, &index,&flag, status);
  
  for(i=0; i<count; i++) {
    (*env)->SetLongField( env,
                (*env)->GetObjectArrayElement(env,array_of_request,i),
                reqhandleID, handle_to_jlong( reqs[i]) );
  }

  (*env)->SetIntField(env,stat, sourceID,status->MPI_SOURCE);
  (*env)->SetIntField(env,stat, tagID,status->MPI_TAG);
  (*env)->SetIntField(env,stat, indexID,index);

  free(reqs);
  if(flag!=0) {
    releaseBufPtrMaybe(env,
	    (*env)->GetObjectArrayElement(env, array_of_request, index));
    return stat;
  } else {
    return NULL;
  }
}

/*
 * Class:     mpi_Request
 * Method:    waitall
 * Signature: ([Lmpi/Request;)[Lmpi/Status;
 */
JNIEXPORT jobjectArray JNICALL Java_mpi_Request_waitall
  (JNIEnv *env, jclass jthis, jobjectArray array_of_request)
{
  int i;
  int count=(*env)->GetArrayLength(env,array_of_request);
  MPI_Request *reqs=(MPI_Request*)calloc(count, sizeof(MPI_Request));
  MPI_Status *stas=(MPI_Status*)calloc(count, sizeof(MPI_Status));

  jobjectArray array_of_status = 
    (*env)->NewObjectArray(env,count,status_class,NULL);

  for(i=0; i<count; i++) {
    reqs[i]=(MPI_Request)jlong_to_handle((*env)->GetLongField( env,
        (*env)->GetObjectArrayElement(env,array_of_request,i),
        reqhandleID) );
  }

  MPI_Waitall(count, reqs, stas);

  for(i=0; i<count; i++) {
    jobject req = (*env)->GetObjectArrayElement(env,array_of_request,i);
    (*env)->SetLongField( env, req,
             reqhandleID, handle_to_jlong( reqs[i]) );
    releaseBufPtrMaybe(env, req);
  }
    
  for(i=0; i<count; i++) { 
    jobject jstas=(*env)->NewObject(env,status_class,statusConstructorID);
    MPI_Status *status =
      (MPI_Status*)jlong_to_handle((*env)->GetLongField(env,jstas,stathandleID));

/*
    sourceID=(*env)->GetFieldID(env,status_class,"source","I");
    tagID=(*env)->GetFieldID(env,status_class,"tag","I");
*/

    *status = stas [i] ;
    (*env)->SetIntField(env,jstas,sourceID,status->MPI_SOURCE);
    (*env)->SetIntField(env,jstas,tagID,status->MPI_TAG);   

    (*env)->SetObjectArrayElement(env,array_of_status,i,jstas);
  }

  free(reqs);
  free(stas);

  return array_of_status;
}


/*
 * Class:     mpi_Request
 * Method:    testall
 * Signature: ([Lmpi/Request;)[Lmpi/Status;
 */
JNIEXPORT jobjectArray JNICALL Java_mpi_Request_testall
  (JNIEnv *env, jclass jthis, jobjectArray array_of_request)
{
  int i,flag;
  int count=(*env)->GetArrayLength(env,array_of_request);
  MPI_Status *stas=(MPI_Status*)calloc(count, sizeof(MPI_Status));
  MPI_Request *reqs=(MPI_Request*)calloc(count, sizeof(MPI_Request));

  jobjectArray array_of_status = 
    (*env)->NewObjectArray(env,count,status_class,NULL);

  for(i=0; i<count; i++) {
    reqs[i]=( MPI_Request)jlong_to_handle((*env)->GetLongField(env,
        (*env)->GetObjectArrayElement(env,array_of_request,i),
        reqhandleID) );
  }

  MPI_Testall(count, reqs, &flag, stas);

  for(i=0; i<count; i++)
    (*env)->SetLongField( env,
		(*env)->GetObjectArrayElement(env,array_of_request,i),
                reqhandleID, handle_to_jlong( reqs[i]) );
  free(reqs);

  if(flag!=0) {
    for(i=0; i<count; i++) {
      jobject jstas=(*env)->NewObject(env,status_class,statusConstructorID);
      MPI_Status *status =
	(MPI_Status*)jlong_to_handle((*env)->GetLongField(env,jstas,stathandleID));

/*
      sourceID=(*env)->GetFieldID(env,status_class,"source","I");
      tagID=(*env)->GetFieldID(env,status_class,"tag","I");
*/

      *status = stas [i] ;
      (*env)->SetIntField(env,jstas,sourceID,status->MPI_SOURCE);
      (*env)->SetIntField(env,jstas,tagID,status->MPI_TAG);  

      (*env)->SetObjectArrayElement(env,array_of_status,i,jstas);
      releaseBufPtrMaybe(env,
	    (*env)->GetObjectArrayElement(env, array_of_request, i));
    }
  }

  free(stas);

  if(flag!=0)
    return array_of_status;
  else
    return NULL;
}

/*
 * Class:     mpi_Request
 * Method:    waitsome
 * Signature: ([Lmpi/Request;)[Lmpi/Status;
 */
JNIEXPORT jobjectArray JNICALL Java_mpi_Request_waitsome
  (JNIEnv *env, jclass jthis, jobjectArray array_of_request)
{
  int i;
  int incount=(*env)->GetArrayLength(env,array_of_request);
  MPI_Request *reqs=(MPI_Request*)calloc(incount, sizeof(MPI_Request));
  MPI_Status *stas=(MPI_Status*)calloc(incount, sizeof(MPI_Status));
  int *array_of_indices=(int*)calloc(incount,sizeof(int));
  int outcount;

  jobjectArray array_of_status;

  for(i=0; i<incount; i++) {
    reqs[i] = 
      (MPI_Request)jlong_to_handle( (*env)->GetLongField(env,
	   (*env)->GetObjectArrayElement(env,array_of_request,i),
           reqhandleID) );
  }

  MPI_Waitsome(incount, reqs, &outcount, array_of_indices, stas); 

  if(outcount!=MPI_UNDEFINED) {
    for(i=0; i<outcount; i++) {
      jobject req = (*env)->GetObjectArrayElement( env,
                  array_of_request,array_of_indices[i] );
      (*env)->SetLongField( env, req,
                  reqhandleID,handle_to_jlong( reqs[array_of_indices[i]]) );
      releaseBufPtrMaybe(env, req);
    }

    array_of_status=(*env)->NewObjectArray(env,outcount,status_class,NULL);

    for(i=0; i<outcount; i++) {
      jobject jstas=(*env)->NewObject(env,status_class,statusConstructorID);
      MPI_Status *status =
	(MPI_Status*)jlong_to_handle((*env)->GetLongField(env,jstas,stathandleID));

/*
	sourceID=(*env)->GetFieldID(env,status_class,"source","I");
	tagID=(*env)->GetFieldID(env,status_class,"tag","I");
	indexID=(*env)->GetFieldID(env,status_class,"index","I");
*/

      *status = stas [i] ;
      (*env)->SetIntField(env,jstas,sourceID,status->MPI_SOURCE);
      (*env)->SetIntField(env,jstas,tagID,status->MPI_TAG);   
      (*env)->SetIntField(env,jstas,indexID,array_of_indices[i]);

      (*env)->SetObjectArrayElement(env,array_of_status,i,jstas);
    }
  }

  free(reqs);
  free(stas);
  free(array_of_indices);

  if(outcount==MPI_UNDEFINED)
    return NULL;
  else
    return array_of_status;
}

/*
 * Class:     mpi_Request
 * Method:    testsome
 * Signature: ([Lmpi/Request;)[Lmpi/Status;
 */
JNIEXPORT jobjectArray JNICALL Java_mpi_Request_testsome
 (JNIEnv *env, jclass jthis, jobjectArray array_of_request)
{
  int i;
  int incount=(*env)->GetArrayLength(env,array_of_request);
  MPI_Request *reqs=(MPI_Request*)calloc(incount, sizeof(MPI_Request));
  MPI_Status *stas=(MPI_Status*)calloc(incount, sizeof(MPI_Status));
  int *array_of_indices=(int*)calloc(incount,sizeof(int));
  int outcount;

  jobjectArray array_of_status;

  for(i=0; i<incount; i++) {
    reqs[i]=(MPI_Request)jlong_to_handle( (*env)->GetLongField(env,
        (*env)->GetObjectArrayElement(env,array_of_request,i),
        reqhandleID) );
  }

  MPI_Testsome(incount,reqs,&outcount,array_of_indices, stas);

  if(outcount!=MPI_UNDEFINED) {  
    for(i=0; i<outcount; i++) {
      jobject req = (*env)->GetObjectArrayElement(env,
                            array_of_request,array_of_indices[i]);
      (*env)->SetLongField( env, req,
		   reqhandleID,
                   handle_to_jlong( reqs[array_of_indices[i]]) );
      releaseBufPtrMaybe(env, req);
    }

    array_of_status = (*env)->NewObjectArray(env,outcount,status_class,NULL);
    for(i=0; i<outcount; i++) {
      jobject jstas=(*env)->NewObject(env,status_class,statusConstructorID);
      MPI_Status *status =
	(MPI_Status*)jlong_to_handle((*env)->GetLongField(env,jstas,stathandleID));

/*
      sourceID=(*env)->GetFieldID(env,status_class,"source","I");
      tagID=(*env)->GetFieldID(env,status_class,"tag","I");
      indexID=(*env)->GetFieldID(env,status_class,"index","I");
*/

      *status = stas [i] ;
      (*env)->SetIntField(env,jstas,sourceID,status->MPI_SOURCE);
      (*env)->SetIntField(env,jstas,tagID,status->MPI_TAG);   
      (*env)->SetIntField(env,jstas,indexID,array_of_indices[i]);

      (*env)->SetObjectArrayElement(env,array_of_status,i,jstas);
    }
  }

  free(reqs);
  free(stas);
  free(array_of_indices);
  
  if(outcount==MPI_UNDEFINED)
    return NULL;
  else
    return array_of_status;
}

/*
 * Class:     mpi_Request
 * Method:    finalize
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_mpi_Request_finalize
  (JNIEnv *env, jobject jthis) {
   MPI_Request req =
     (MPI_Request)jlong_to_handle((*env)->GetLongField(env,jthis,reqhandleID));
   void *bufptr = (void *)jlong_to_handle((*env)->GetLongField(env, jthis, reqbufhandleID));
   if (req != MPI_REQUEST_NULL) {
       fprintf(stderr, "WARNING: mpi.Request finalized before communication completes.\n");
     MPI_Request_free(&req);
   }
   if (bufptr != NULL) {
     /* This is not an error for Prequest. */
     jobject buf = (*env)->GetObjectField(env, jthis, reqbufID);
     jobject type = (*env)->GetObjectField(env, jthis, reqtypeID);
     jint offset = (*env)->GetIntField(env, jthis, reqoffsetID);
     jint count = (*env)->GetIntField(env, jthis, reqcountID);
     jint release = (*env)->GetIntField(env, jthis, reqrelbufID);
     if (release) {
	 /* sorry I change the message... YAMAUCHI */
	 fprintf(stderr, "WARNING: mpi.Request finalized before communication completes\n");
     }
     /* If communication is still going on, releasing buffer may cause
	memory corruption.  So, we just write back what we have now and
	let the memory leak. */
     releaseBufPtr(env, buf, type, bufptr, offset, count, release & (~BUF_RELEASE));
   }
}

/*
 * Things to do:
 *
 *    Initialize `handleConstructorID' in consistent way with `sourceId', etc.
 *
 */

