/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
/*
 *  (C) 2012 by Argonne National Laboratory.
 *      See COPYRIGHT in top-level directory.
 */

#include "mpidimpl.h"

#undef FUNCNAME
#define FUNCNAME MPID_Imrecv
#undef FCNAME
#define FCNAME MPL_QUOTE(FUNCNAME)
int MPID_Imrecv(void *buf, int count, MPI_Datatype datatype,
                MPID_Request *message, MPID_Request **rreqp)
{
    int mpi_errno = MPI_SUCCESS;
    MPID_Request *rreq;
    MPID_Comm *comm;
    MPIDI_VC_t *vc = NULL;

    /* message==NULL is equivalent to MPI_MESSAGE_NO_PROC being passed at the
     * upper level */
    if (message == NULL)
    {
        MPIDI_Request_create_null_rreq(rreq, mpi_errno, goto fn_fail);
        MV2_INC_NUM_POSTED_RECV();
        *rreqp = rreq;
        goto fn_exit;
    }

    MPIU_Assert(message != NULL);
    MPIU_Assert(message->kind == MPID_REQUEST_MPROBE);

    /* promote the request object to be a "real" recv request */
    message->kind = MPID_REQUEST_RECV;
    MV2_INC_NUM_POSTED_RECV();

    *rreqp = rreq = message;

    comm = rreq->comm;

    /* the following code was adapted from FDU_or_AEP and MPID_Irecv */
    /* comm was already added at FDU (mprobe) time */
    rreq->dev.user_buf = buf;
    rreq->dev.user_count = count;
    rreq->dev.datatype = datatype;

#ifdef ENABLE_COMM_OVERRIDES
    MPIDI_Comm_get_vc(comm, rreq->status.MPI_SOURCE, &vc);
    if (vc->comm_ops && vc->comm_ops->imrecv) {
        MPID_THREAD_CS_ENTER(POBJ, vc->pobj_mutex);
        vc->comm_ops->imrecv(vc, rreq);
        MPID_THREAD_CS_EXIT(POBJ, vc->pobj_mutex);
        goto fn_exit;
    }
#endif

    if (MPIDI_Request_get_msg_type(rreq) == MPIDI_REQUEST_EAGER_MSG)
    {
        int recv_pending;

        /* This is an eager message */
        MPIU_DBG_MSG(CH3_OTHER,VERBOSE,"eager message in the request");

        /* If this is an eager synchronous message, then we need to send an
           acknowledgement back to the sender. */
        if (MPIDI_Request_get_sync_send_flag(rreq))
        {
            MPIDI_Comm_get_vc_set_active(comm, rreq->dev.match.parts.rank, &vc);
            mpi_errno = MPIDI_CH3_EagerSyncAck(vc, rreq);
            if (mpi_errno) MPIR_ERR_POP(mpi_errno);
        }

        /* the request was found in the unexpected queue, so it has a
           recv_pending_count of at least 1 */
        MPIDI_Request_decr_pending(rreq);
        MPIDI_Request_check_pending(rreq, &recv_pending);

        if (MPID_Request_is_complete(rreq)) {
            /* is it ever possible to have (cc==0 && recv_pending>0) ? */
            MPIU_Assert(!recv_pending);

            /* All of the data has arrived, we need to copy the data and
               then free the buffer. */
            if (rreq->dev.recv_data_sz > 0)
            {
                MPIDI_CH3U_Request_unpack_uebuf(rreq);
                MPIU_Free(rreq->dev.tmpbuf);
            }

            mpi_errno = rreq->status.MPI_ERROR;
            goto fn_exit;
        }
        else
        {
            /* there should never be outstanding completion events for an unexpected
             * recv without also having a "pending recv" */
            MPIU_Assert(recv_pending);
            /* The data is still being transfered across the net.  We'll
               leave it to the progress engine to handle once the
               entire message has arrived. */
            if (HANDLE_GET_KIND(datatype) != HANDLE_KIND_BUILTIN)
            {
                MPID_Datatype_get_ptr(datatype, rreq->dev.datatype_ptr);
                MPID_Datatype_add_ref(rreq->dev.datatype_ptr);
            }

        }
    }
    else if (MPIDI_Request_get_msg_type(rreq) == MPIDI_REQUEST_RNDV_MSG)
    {
        MPIDI_Comm_get_vc_set_active(comm, rreq->dev.match.parts.rank, &vc);

        mpi_errno = vc->rndvRecv_fn(vc, rreq);
        if (mpi_errno) MPIR_ERR_POP(mpi_errno);
        if (HANDLE_GET_KIND(datatype) != HANDLE_KIND_BUILTIN)
        {
            MPID_Datatype_get_ptr(datatype, rreq->dev.datatype_ptr);
            MPID_Datatype_add_ref(rreq->dev.datatype_ptr);
        }
    }
    else if (MPIDI_Request_get_msg_type(rreq) == MPIDI_REQUEST_SELF_MSG)
    {
        mpi_errno = MPIDI_CH3_RecvFromSelf(rreq, buf, count, datatype);
        if (mpi_errno) MPIR_ERR_POP(mpi_errno);
    }
    else
    {
        /* --BEGIN ERROR HANDLING-- */
#ifdef HAVE_ERROR_CHECKING
        int msg_type = MPIDI_Request_get_msg_type(rreq);
#endif
        MPID_Request_release(rreq);
        rreq = NULL;
        MPIR_ERR_SETANDJUMP1(mpi_errno,MPI_ERR_INTERN, "**ch3|badmsgtype",
                             "**ch3|badmsgtype %d", msg_type);
        /* --END ERROR HANDLING-- */
    }

fn_exit:
    return mpi_errno;
fn_fail:
    goto fn_exit;
}

