/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
/* vim: set ts=8 sts=4 sw=4 noexpandtab : */
/*
 *
 */



#include "mpid_nem_impl.h"
#include "llc_impl.h"

//#define MPID_NEM_LLC_DEBUG_PROBE
#ifdef MPID_NEM_LLC_DEBUG_PROBE
#define dprintf printf
#else
#define dprintf(...)
#endif


#undef FUNCNAME
#define FUNCNAME MPID_nem_llc_probe
#undef FCNAME
#define FCNAME MPL_QUOTE(FUNCNAME)
int MPID_nem_llc_probe(MPIDI_VC_t * vc, int source, int tag, MPIR_Comm * comm, int context_offset,
                       MPI_Status * status)
{
    int mpi_errno = MPI_SUCCESS;

    MPIR_FUNC_VERBOSE_STATE_DECL(MPID_STATE_MPID_NEM_LLC_PROBE);
    MPIR_FUNC_VERBOSE_ENTER(MPID_STATE_MPID_NEM_LLC_PROBE);
    dprintf("llc_probe,source=%d,tag=%d\n", source, tag);

    /* NOTE : This function is not used. Because 'vc->comm_ops->probe()' is not used */
  fn_exit:
    MPIR_FUNC_VERBOSE_EXIT(MPID_STATE_MPID_NEM_LLC_PROBE);
    return mpi_errno;
  fn_fail:
    goto fn_exit;
}

#undef FUNCNAME
#define FUNCNAME MPID_nem_llc_iprobe
#undef FCNAME
#define FCNAME MPL_QUOTE(FUNCNAME)
int MPID_nem_llc_iprobe(MPIDI_VC_t * vc, int source, int tag, MPIR_Comm * comm, int context_offset,
                        int *flag, MPI_Status * status)
{
    int mpi_errno = MPI_SUCCESS, llc_errno;
    int rank;
    LLC_tag_t _tag;
    LLC_probe_t probe;
    LLC_match_mask_t mask;

    MPIR_FUNC_VERBOSE_STATE_DECL(MPID_STATE_MPID_NEM_LLC_IPROBE);
    MPIR_FUNC_VERBOSE_ENTER(MPID_STATE_MPID_NEM_LLC_IPROBE);
    dprintf("llc_iprobe,source=%d,tag=%d\n", source, tag);

    mask.rank = ~0;
    mask.tag = ~0;

    if (tag == MPI_ANY_TAG) {
        *(int32_t *) ((uint8_t *) & _tag) = LLC_ANY_TAG;
        *(int32_t *) ((uint8_t *) & mask.tag) = 0;
    }
    else {
        *(int32_t *) ((uint8_t *) & _tag) = tag;
    }

    *(MPIR_Context_id_t *) ((uint8_t *) & _tag + sizeof(int32_t)) =
        comm->recvcontext_id + context_offset;
    memset((uint8_t *) & _tag + sizeof(int32_t) + sizeof(MPIR_Context_id_t),
           0, sizeof(LLC_tag_t) - sizeof(int32_t) - sizeof(MPIR_Context_id_t));

    if (source == MPI_ANY_SOURCE) {
        rank = LLC_ANY_SOURCE;
        mask.rank = 0;
    }
    else {
        MPIR_Assert(vc);
        rank = VC_FIELD(vc, remote_endpoint_addr);
    }

    llc_errno = LLC_probe(LLC_COMM_MPICH, rank, _tag, &mask, &probe);
    if (llc_errno == LLC_SUCCESS) {
        *flag = 1;
        status->MPI_ERROR = MPI_SUCCESS;
        if (source != MPI_ANY_SOURCE) {
            status->MPI_SOURCE = source;
        }
        else {
            int found = 0;
            found = convert_rank_llc2mpi(comm, probe.rank, &status->MPI_SOURCE);
            MPIR_Assert(found);
        }
        status->MPI_TAG = probe.tag & 0xffffffff;
        MPIR_STATUS_SET_COUNT(*status, probe.len);
    }
    else {
        *flag = 0;

        MPID_Progress_poke();   /* do LLC_poll */
    }

  fn_exit:
    MPIR_FUNC_VERBOSE_EXIT(MPID_STATE_MPID_NEM_LLC_IPROBE);
    return mpi_errno;
  fn_fail:
    goto fn_exit;
}

#undef FUNCNAME
#define FUNCNAME MPID_nem_llc_improbe
#undef FCNAME
#define FCNAME MPL_QUOTE(FUNCNAME)
int MPID_nem_llc_improbe(MPIDI_VC_t * vc, int source, int tag, MPIR_Comm * comm, int context_offset,
                         int *flag, MPIR_Request ** message, MPI_Status * status)
{
    int mpi_errno = MPI_SUCCESS;
    int rank;
    LLC_tag_t _tag;
    LLC_probe_t probe;
    LLC_match_mask_t mask;
    LLC_cmd_t *msg = NULL;

    MPIR_FUNC_VERBOSE_STATE_DECL(MPID_STATE_MPID_NEM_LLC_IMPROBE);
    MPIR_FUNC_VERBOSE_ENTER(MPID_STATE_MPID_NEM_LLC_IMPROBE);
    dprintf("llc_improbe,source=%d,tag=%d\n", source, tag);

    mask.rank = ~0;
    mask.tag = ~0;

    if (tag == MPI_ANY_TAG) {
        *(int32_t *) ((uint8_t *) & _tag) = LLC_ANY_TAG;
        *(int32_t *) ((uint8_t *) & mask.tag) = 0;
    }
    else {
        *(int32_t *) ((uint8_t *) & _tag) = tag;
    }

    *(MPIR_Context_id_t *) ((uint8_t *) & _tag + sizeof(int32_t)) =
        comm->recvcontext_id + context_offset;
    memset((uint8_t *) & _tag + sizeof(int32_t) + sizeof(MPIR_Context_id_t),
           0, sizeof(LLC_tag_t) - sizeof(int32_t) - sizeof(MPIR_Context_id_t));

    if (source == MPI_ANY_SOURCE) {
        rank = LLC_ANY_SOURCE;
        mask.rank = 0;
    }
    else {
        MPIR_Assert(vc);
        rank = VC_FIELD(vc, remote_endpoint_addr);
    }

    msg = LLC_mprobe(LLC_COMM_MPICH, rank, _tag, &mask, &probe);
    if (msg) {
        MPIR_Request *req;

        *flag = 1;

        req = MPIR_Request_create(MPIR_REQUEST_KIND__MPROBE);
        MPIR_Object_set_ref(req, 2);
        req->comm = comm;
        MPIR_Comm_add_ref(comm);
        req->ch.vc = vc;

        MPIDI_Request_set_msg_type(req, MPIDI_REQUEST_EAGER_MSG);
        req->dev.recv_pending_count = 1;
        req->status.MPI_ERROR = MPI_SUCCESS;
        if (source != MPI_ANY_SOURCE) {
            req->status.MPI_SOURCE = source;
        }
        else {
            int found = 0;
            found = convert_rank_llc2mpi(comm, probe.rank, &req->status.MPI_SOURCE);
            MPIR_Assert(found);
        }
        req->status.MPI_TAG = probe.tag & 0xffffffff;
        req->dev.recv_data_sz = probe.len;
        MPIR_STATUS_SET_COUNT(req->status, req->dev.recv_data_sz);
        req->dev.tmpbuf = MPL_malloc(req->dev.recv_data_sz, MPL_MEM_BUFFER);
        MPIR_Assert(req->dev.tmpbuf);

        /* receive message in req->dev.tmpbuf */
        LLC_cmd_t *cmd = LLC_cmd_alloc2(1, 1, 1);

        cmd[0].opcode = 0;      // not use
        cmd[0].comm = LLC_COMM_MPICH;
        cmd[0].req_id = (uint64_t) cmd;
        cmd[0].rank = msg->rank;
        // cmd[0].tag = 0; // not use

        cmd[0].iov_local[0].addr = (uint64_t) req->dev.tmpbuf;
        cmd[0].iov_local[0].length = req->dev.recv_data_sz;
        cmd[0].niov_local = 1;

        cmd[0].iov_remote[0].addr = 0;
        cmd[0].iov_remote[0].length = req->dev.recv_data_sz;
        cmd[0].niov_remote = 1;

        ((struct llc_cmd_area *) cmd[0].usr_area)->cbarg = req;
        if (source == MPI_ANY_SOURCE) {
            ((struct llc_cmd_area *) cmd[0].usr_area)->raddr = MPI_ANY_SOURCE;
        }
        else {
            ((struct llc_cmd_area *) cmd[0].usr_area)->raddr = VC_FIELD(vc, remote_endpoint_addr);
        }

        LLC_recv_msg(cmd, msg);

        /* Wait until the reception of data is completed */
        do {
            mpi_errno = MPID_nem_llc_poll(0);
        } while (!MPIR_Request_is_complete(req));

//        MPID_Request_complete(req); // This operation is done in llc_poll.

        *message = req;

        /* TODO : Should we change status ? */
        //status->MPI_ERROR = MPI_SUCCESS;
        status->MPI_SOURCE = req->status.MPI_SOURCE;
        status->MPI_TAG = req->status.MPI_TAG;
        MPIR_STATUS_SET_COUNT(*status, req->dev.recv_data_sz);
    }
    else {
        *flag = 0;
        *message = NULL;

        MPID_Progress_poke();   /* do LLC_poll */
    }

  fn_exit:
    MPIR_FUNC_VERBOSE_EXIT(MPID_STATE_MPID_NEM_LLC_IMPROBE);
    return mpi_errno;
  fn_fail:
    goto fn_exit;
}
