/*
 * File         : Request.java
 * Author       : Sang Lim, Xinying Li, Bryan Carpenter
 * Created      : Thu Apr  9 12:22:15 1998
 * Revision     : $Revision: 1.9 $
 * Updated      : $Date: 1999/09/14 20:50:19 $
 * Copyright: Northeast Parallel Architectures Center
 *            at Syracuse University 1998
 */

/*
 * Note: in a send request for a buffer containing objects, the primary
 * `MPI_Request' referenced by `handle' is the request to send the data.
 * The request to send the header is in the secondary field, `hdrReq'.
 * Conversely, in a *receive* request for a buffer containing objects
 * the primary `MPI_Request' is the request to send the header.
 * The receive of the data is not initiated until a `wait' or `test'
 * operation succeeds.
 */

package mpi;

public class Request {
  protected final static int NULL = 0;

  protected final static int OP_NORMAL      = 0;
  protected final static int OP_SEND_OBJECT = 1;
  protected final static int OP_RECV_OBJECT = 2;

  protected Request hdrReq ;
  protected int opTag    = OP_NORMAL   ;
  protected int mode ;
  protected Object buf;
  protected int offset;
  protected int count;
  protected Datatype type;
  protected int dest;
  protected int src;
  protected int tag;
  protected Comm comm;
  protected int[] length_buf;


  private static native void init();
 
  private native void GetReq(int Type);


  protected Request() {}

  protected Request(int Type) { GetReq(Type); }

  protected Request(long _handle) { handle = _handle;}

  /**
   * Constructor used by <tt>Isend</tt>, etc.
   */

  protected Request(Request hdrReq) {

    opTag  = Request.OP_SEND_OBJECT ;

    this.hdrReq = hdrReq ;
  }

  /**
   * Constructor used by <tt>Irecv</tt>.
   */

  protected Request(Object buf, int offset, int count, Datatype type,
                    int source, int tag, Comm comm,
                    int [] length_buf) {

    opTag  = Request.OP_RECV_OBJECT ;

    this.buf    = buf;
    this.offset = offset;
    this.count  = count;
    this.type   = type;
    this.src    = source;
    this.tag    = tag;
    this.comm   = comm;

    this.length_buf = length_buf;
  }


  /**
   * Set the request object to be void.
   * Java binding of the MPI operation <tt>MPI_REQUEST_FREE</tt>.
   */

  public native void Free() throws MPIException ;

  /**
   * Mark a pending nonblocking communication for cancellation.
   * Java binding of the MPI operation <tt>MPI_CANCEL</tt>.
   */

  public native void Cancel() throws MPIException ;

  /**
   * Test if request object is void.
   * <p>
   * <table>
   * <tr><td><em> returns: </em></td><td> true if the request object is void,
   *                                      false otherwise </tr>
   * </table>
   */

  public native boolean Is_null();

  /*
   * After initial wait succeeds with some status, complete as necessary.
   */

  private Status complete(Status status) throws MPIException {
    switch (opTag) {
      case OP_NORMAL :
        break;
      case OP_SEND_OBJECT :
        hdrReq.Wait(new Status()) ;  // Data has already gone, but must
                                     // still do `wait' on header send.
        break;
      case OP_RECV_OBJECT :

        int index = status.index ;

        // Header has arrived, now read the actual data.

        byte[] byte_buf = new byte[length_buf[0]];
        status = comm.Recv(byte_buf, 0, length_buf[0], MPI.BYTE, src, tag) ;
        comm.Object_Deserialize(buf, byte_buf, offset, length_buf[1], type);

        status.object_count = length_buf[1];
        status.index        = index ;

        break;
    }
    return status ;
  }

  /**
   * Blocks until the operation identified by the request is complete.
   * <p>
   * <table>
   * <tr><td><em> returns: </em></td><td> status object </tr>
   * </table>
   * <p>
   * Java binding of the MPI operation <tt>MPI_WAIT</tt>.
   * <p>
   * After the call returns, the request object becomes inactive.
   */

  public Status Wait() throws MPIException {
    Status result = new Status();
    Wait(result);

    return complete(result) ;
  }

  private native Status Wait(Status stat);

  /**
   * Returns a status object if the operation identified by the request
   * is complete, or a null handle otherwise.
   * <p>
   * <table>
   * <tr><td><em> returns: </em></td><td> status object or null handle </tr>
   * </table>
   * <p>
   * Java binding of the MPI operation <tt>MPI_TEST</tt>.
   * <p>
   * After the call, if the operation is complete (ie, if the return value
   * is non-null), the request object becomes inactive.
   */

  public Status Test() throws MPIException {

    Status result = new Status();
    if (Test(result) == null)
      return null;
    else
      return complete(result) ;
  }

  private native Status Test(Status stat);

  /**
   * Blocks until one of the operations associated with the active
   * requests in the array has completed.
   * <p>
   * <table>
   * <tr><td><tt> array_of_requests </tt></td><td> array of requests </tr>
   * <tr><td><em> returns:          </em></td><td> status object </tr>
   * </table>
   * <p>
   * Java binding of the MPI operation <tt>MPI_WAITANY</tt>.
   * <p>
   * The index in <tt>array_of_requests</tt> for the request that completed
   * can be obtained from the returned status object through the
   * <tt>Status.index</tt> field.  The corresponding element
   * of <tt>array_of_requests</tt> becomes inactive.
   */

  public static Status Waitany(Request [] array_of_request)
                                                      throws MPIException {
    Status result = new Status();
    Waitany(array_of_request, result);
    
    if(result == null)
      return null;
    else
      return array_of_request[result.index].complete(result) ; 
  }

  private static native Status Waitany(Request [] array_of_request, 
                                       Status stat);

  /**
   * Tests for completion of either one or none of the operations associated
   * with active requests.
   * <p>
   * <table>
   * <tr><td><tt> array_of_requests </tt></td><td> array of requests </tr>
   * <tr><td><em> returns:          </em></td><td> status object or
   *                                               null handle </tr>
   * </table>
   * <p>
   * Java binding of the MPI operation <tt>MPI_TESTANY</tt>.
   * <p>
   * If some request completed, the index in <tt>array_of_requests</tt>
   * for that request can be obtained from the returned status object
   * through the.  The corresponding element of <tt>array_of_requests</tt>
   * becomes inactive.
   * If no request completed, <tt>Testany</tt> returns a null handle.
   */

  public static Status Testany(Request [] array_of_request)
                                                      throws MPIException {
 
    Status result = new Status();
    result = Testany(array_of_request, result);
    
    if(result == null)
      return null;
    else
      return array_of_request[result.index].complete(result) ; 
  }

  private static native Status Testany(Request [] array_of_request, 
                                       Status     stat);

  /**
   * Blocks until all of the operations associated with the active
   * requests in the array have completed.
   * <p>
   * <table>
   * <tr><td><tt> array_of_requests </tt></td><td> array of requests </tr>
   * <tr><td><em> returns:          </em></td><td> array of status objects </tr>
   * </table>
   * <p>
   * Java binding of the MPI operation <tt>MPI_WAITALL</tt>.
   * <p>
   * The result array will be the same size as <tt>array_of_requests</tt>.
   * On exit, requests become inactive.  If the <em>input</em> value of
   * <tt>arrayOfRequests</tt> contains inactive requests, corresponding
   * elements of the result array will contain null status handles.
   */

  public static Status[] Waitall (Request [] array_of_request)
                                                      throws MPIException {
    Status result[] = waitall(array_of_request);
            
    for (int i = 0 ; i < array_of_request.length ; i++)
      result [i] = array_of_request [i].complete(result [i]) ; 

    return result;
  }

  private static native Status[] waitall(Request [] array_of_request);

  /**
   * Tests for completion of <em>all</em> of the operations associated
   * with active requests.
   * <p>
   * <table>
   * <tr><td><tt> array_of_requests </tt></td><td> array of requests </tr>
   * <tr><td><em> returns:          </em></td><td> array of status objects </tr>
   * </table>
   * <p>
   * Java binding of the MPI operation <tt>MPI_TESTALL</tt>.
   * <p>
   * If all operations have completed, the exit value of the argument array
   * and the result array are as for <tt>Waitall</tt>.  If any
   * operation has not completed, the result value is null and no
   * element of the argument array is modified.
   */

  public static Status[] Testall(Request [] array_of_request)
                                                      throws MPIException {
    Status result[] = testall(array_of_request);

    if (result == null)
      return null;
    else {
      for (int i = 0 ; i < array_of_request.length ; i++)
        result [i] = array_of_request [i].complete(result [i]) ; 

      return result;
    }
  }

  private static native Status[] testall(Request [] array_of_request);

  /**
   * Blocks until at least one of the operations associated with the active
   * requests in the array has completed.
   * <p>
   * <table>
   * <tr><td><tt> array_of_requests </tt></td><td> array of requests </tr>
   * <tr><td><em> returns:          </em></td><td> array of status objects </tr>
   * </table>
   * <p>
   * Java binding of the MPI operation <tt>MPI_WAITSOME</tt>.
   * <p>
   * The size of the result array will be the number of operations that
   * completed.  The index in <tt>array_of_requests</tt> for each request that
   * completed can be obtained from the returned status objects through the
   * <tt>Status.index</tt> field.  The corresponding element in
   * <tt>array_of_requests</tt> becomes inactive.
   */

  public static Status[] Waitsome(Request [] array_of_request)
                                                      throws MPIException {
    Status result[] = waitsome(array_of_request);                         
            
    for (int i = 0 ; i < result.length ; i++)
      result [i] = array_of_request [result [i].index].complete(result [i]) ; 

    return result;    
  }

  private static native Status[] waitsome(Request [] array_of_request);

  /**
   * Behaves like <tt>Waitsome</tt>, except that it returns immediately.
   * <p>
   * <table>
   * <tr><td><tt> array_of_requests </tt></td><td> array of requests </tr>
   * <tr><td><em> returns:          </em></td><td> array of status objects </tr>
   * </table>
   * <p>
   * Java binding of the MPI operation <tt>MPI_TESTSOME</tt>.
   * <p>
   * If no operation has completed, <tt>TestSome</tt> returns an array of
   * length zero and elements of <tt>array_of_requests</tt> are unchanged.
   * Otherwise, arguments and return value are as for <tt>Waitsome</tt>.
   */

  public static Status[] Testsome(Request [] array_of_request)
                                                      throws MPIException {
    Status result[] = testsome(array_of_request);

    if (result  == null)
      return null;
    else {
      for (int i = 0 ; i < result.length ; i++)
        result [i] = array_of_request [result [i].index].complete(result [i]) ; 

      return result;    
    }
  }

  private static native Status[] testsome(Request [] array_of_request);

  protected long handle;
  protected long CbufHandle;
  protected int releaseCbufWhenDone;
  protected Object commBuf;
  protected int commOffset;
  protected Datatype commType;
  protected int commCount;

  public native void finalize();

  static {
    init();
  }          
}

// Things to do
//
//   What happens to `Cancel' in the object case?

