/*
 * Copyright (C) by Argonne National Laboratory
 *     See COPYRIGHT in top-level directory
 */

#ifndef MPIDI_CH3_PRE_H_INCLUDED
#define MPIDI_CH3_PRE_H_INCLUDED
#include "mpid_nem_pre.h"
#include "mpidu_generic_queue.h"

#if defined(HAVE_NETINET_IN_H)
    #include <netinet/in.h>
#elif defined(HAVE_WINSOCK2_H)
    #include <winsock2.h>
    #include <windows.h>
#endif

/*#define MPID_USE_SEQUENCE_NUMBERS*/
/*#define HAVE_CH3_PRE_INIT*/
/* #define MPIDI_CH3_HAS_NO_DYNAMIC_PROCESS */
#define MPIDI_DEV_IMPLEMENTS_KVS

/* Nemesis packets */
#define MPIDI_CH3_PKT_ENUM                  \
    MPIDI_NEM_PKT_LMT_RTS,                  \
    MPIDI_NEM_PKT_LMT_CTS,                  \
    MPIDI_NEM_PKT_LMT_DONE,                 \
    MPIDI_NEM_PKT_LMT_COOKIE,               \
    MPIDI_NEM_PKT_CKPT_MARKER,              \
    MPIDI_NEM_PKT_NETMOD

typedef struct {
    void *netmod_priv;      /* netmod communicator private data */
} MPIDI_CH3I_CH_comm_t;
    
typedef enum MPIDI_CH3I_VC_state
{
    MPIDI_CH3I_VC_STATE_UNCONNECTED,
    MPIDI_CH3I_VC_STATE_CONNECTING,
    MPIDI_CH3I_VC_STATE_CONNECTED,
    MPIDI_CH3I_VC_STATE_FAILED
}
MPIDI_CH3I_VC_state_t;

/* size of private data area in vc and req for network modules */
#define MPIDI_NEM_VC_NETMOD_AREA_LEN 128
#define MPIDI_NEM_REQ_NETMOD_AREA_LEN 192

/* define functions for access MPID_nem_lmt_rts_queue_t */
typedef GENERIC_Q_DECL(struct MPIR_Request) MPID_nem_lmt_rts_queue_t;
#define MPID_nem_lmt_rtsq_empty(q) GENERIC_Q_EMPTY (q)
#define MPID_nem_lmt_rtsq_head(q) GENERIC_Q_HEAD (q)
#define MPID_nem_lmt_rtsq_enqueue(qp, ep) do {                                          \
        MPL_DBG_MSG_FMT(MPIDI_CH3_DBG_CHANNEL, VERBOSE, (MPL_DBG_FDEST,                         \
                          "MPID_nem_lmt_rtsq_enqueue req=%p (handle=%#x), queue=%p",    \
                          ep, (ep)->handle, qp));                                       \
        GENERIC_Q_ENQUEUE (qp, ep, next);                                           \
    } while (0)
#define MPID_nem_lmt_rtsq_dequeue(qp, epp)  do {                                        \
        GENERIC_Q_DEQUEUE (qp, epp, next);                                          \
        MPL_DBG_MSG_FMT(MPIDI_CH3_DBG_CHANNEL, VERBOSE, (MPL_DBG_FDEST,                         \
                          "MPID_nem_lmt_rtsq_dequeue req=%p (handle=%#x), queue=%p",    \
                          *(epp), *(epp) ? (*(epp))->handle : -1, qp));                 \
    } while (0)
#define MPID_nem_lmt_rtsq_search_remove(qp, req_id, epp) do {                           \
        GENERIC_Q_SEARCH_REMOVE(qp, _e->handle == (req_id), epp,                        \
                struct MPIR_Request, next);                                         \
        MPL_DBG_MSG_FMT(MPIDI_CH3_DBG_CHANNEL, VERBOSE, (MPL_DBG_FDEST,                         \
                    "MPID_nem_lmt_rtsq_search_remove req=%p (handle=%#x), queue=%p",    \
                    *(epp), req_id, qp));                                               \
} while (0)

typedef struct MPIDI_CH3I_VC
{
    int pg_rank;
    struct MPIR_Request *recv_active;

    int is_local;
    unsigned short send_seqno;
    MPID_nem_fastbox_t *fbox_out;
    MPID_nem_fastbox_t *fbox_in;
    MPID_nem_queue_ptr_t recv_queue;
    MPID_nem_queue_ptr_t free_queue;

#ifdef ENABLE_CHECKPOINTING
    intptr_t ckpt_msg_len;
    void *ckpt_msg_buf;
#endif

    /* temp buffer to store partially received header */
    intptr_t pending_pkt_len;
    union MPIDI_CH3_Pkt *pending_pkt;

    /* can be used by netmods to put this vc on a send queue or list */
    struct MPIDI_VC *next;
    struct MPIDI_VC *prev;

    /* contig function pointers.  Netmods should set these. */
    /* iStartContigMsg -- sends a message consisting of a header (hdr) and contiguous data (data), possibly of 0 size.  If the
       message cannot be sent immediately, the function should create a request and return a pointer in sreq_ptr.  The network
       module should complete the request once the message has been completely sent. */
    int (* iStartContigMsg)(struct MPIDI_VC *vc, void *hdr, intptr_t hdr_sz, void *data, intptr_t data_sz,
                            struct MPIR_Request **sreq_ptr);
    /* iSentContig -- sends a message consisting of a header (hdr) and contiguous data (data), possibly of 0 size.  The
       network module should complete the request once the message has been completely sent. */
    int (* iSendContig)(struct MPIDI_VC *vc, struct MPIR_Request *sreq, void *hdr, intptr_t hdr_sz,
                        void *data, intptr_t data_sz);
    /* iSendIov -- sends a message consisting of multiple iovs, possibly of 0 n_iov.
       n_iov should not exceed MPL_IOV_LIMIT - 1. network module should complete the request once the
       message has been completely sent. */
    int (* iSendIov)(struct MPIDI_VC *vc, struct MPIR_Request *sreq, void *hdr, intptr_t hdr_sz,
                     struct iovec *iov, int n_iov);

#ifdef ENABLE_CHECKPOINTING
    /* ckpt_pause_send -- netmod should stop sending on this vc and queue messages to be sent after ckpt_continue()*/
    int (* ckpt_pause_send_vc)(struct MPIDI_VC *vc);
    /* ckpt_continue -- Notify remote side to start sending again. */
    int (* ckpt_continue_vc)(struct MPIDI_VC *vc);
    /* ckpt_restart -- similar to ckpt_continue, except that the process has been restarted */
    int (* ckpt_restart_vc)(struct MPIDI_VC *vc);
#endif

    /* LMT function pointers */
    int (* lmt_initiate_lmt)(struct MPIDI_VC *vc, union MPIDI_CH3_Pkt *rts_pkt, struct MPIR_Request *req);
    int (* lmt_start_recv)(struct MPIDI_VC *vc, struct MPIR_Request *req, struct iovec s_cookie);
    int (* lmt_start_send)(struct MPIDI_VC *vc, struct MPIR_Request *sreq, struct iovec r_cookie);
    int (* lmt_handle_cookie)(struct MPIDI_VC *vc, struct MPIR_Request *req, struct iovec cookie);
    int (* lmt_done_send)(struct MPIDI_VC *vc, struct MPIR_Request *req);
    int (* lmt_done_recv)(struct MPIDI_VC *vc, struct MPIR_Request *req);
    int (* lmt_vc_terminated)(struct MPIDI_VC *vc);

    /* LMT shared memory copy-buffer ptr */
    struct MPID_nem_copy_buf *lmt_copy_buf;
    MPL_shm_hnd_t lmt_copy_buf_handle;
    MPL_shm_hnd_t lmt_recv_copy_buf_handle;
    int lmt_buf_num;
    intptr_t lmt_surfeit;
    struct {struct MPID_nem_lmt_shm_wait_element *head, *tail;} lmt_queue;
    struct MPID_nem_lmt_shm_wait_element *lmt_active_lmt;
    int lmt_enqueued; /* FIXME: used for debugging */
    MPID_nem_lmt_rts_queue_t lmt_rts_queue;

    /* Pointer to per-vc packet handlers */
    MPIDI_CH3_PktHandler_Fcn **pkt_handler;
    int num_pkt_handlers;
    
    union
    {
        char padding[MPIDI_NEM_VC_NETMOD_AREA_LEN];

        /* Temporary helper field for ticket #1679.  Should force proper pointer
         * alignment on finnicky platforms like SPARC.  Proper fix is to stop
         * this questionable type aliasing altogether. */
        void *align_helper;
    } netmod_area;
    

    /* FIXME: ch3 assumes there is a field called sendq_head in the ch
       portion of the vc.  This is unused in nemesis and should be set
       to NULL */
    void *sendq_head;
} MPIDI_CH3I_VC;

#define MPIDI_CH3_VC_DECL struct MPIDI_CH3I_VC ch;

struct MPIDI_CH3I_Request
{
    struct MPIDI_VC     *vc;
    int                  noncontig;
    intptr_t       header_sz;

    MPI_Request          lmt_req_id;     /* request id of remote side */
    struct MPIR_Request *lmt_req;        /* pointer to original send/recv request */
    intptr_t       lmt_data_sz;    /* data size to be transferred, after checking for truncation */
    struct iovec             lmt_tmp_cookie; /* temporary storage for received cookie */
    void                *s_cookie;       /* temporary storage for the cookie data in case the packet can't be sent immediately */

    union
    {
        char padding[MPIDI_NEM_REQ_NETMOD_AREA_LEN];

        /* Temporary helper field for ticket #1679.  Should force proper pointer
         * alignment on finnicky platforms like SPARC.  Proper fix is to stop
         * this questionable type aliasing altogether. */
        void *align_helper;
    } netmod_area;
};

/*
 * MPIDI_CH3_REQUEST_DECL (additions to MPIR_Request)
 */
#define MPIDI_CH3_REQUEST_DECL struct MPIDI_CH3I_Request ch;


#if 0
#define DUMP_REQUEST(req) do {                                          \
        int i;                                                          \
        MPL_DBG_MSG_P(MPIDI_CH3_DBG_OTHER, TERSE, "request %p\n", (req));        \
        MPL_DBG_MSG_D(MPIDI_CH3_DBG_OTHER, TERSE, "  handle = %d\n", (req)->handle); \
        MPL_DBG_MSG_D(MPIDI_CH3_DBG_OTHER, TERSE, "  ref_count = %d\n", (req)->ref_count); \
        MPL_DBG_MSG_D(MPIDI_CH3_DBG_OTHER, TERSE, "  cc = %d\n", (req)->cc);     \
        for (i = 0; i < (req)->iov_count; ++i)                          \
            MPL_DBG_MSG_FMT(MPIDI_CH3_DBG_OTHER, TERSE, (MPL_DBG_FDEST, "  dev.iov[%d] = (%p, %d)\n", i, \
                                                (req)->dev.iov[i+(req)->dev.iov_offset].iov_base, \
                                                (req)->dev.iov[i+(req)->dev.iov_offset].iov_len)); \
        MPL_DBG_MSG_D(MPIDI_CH3_DBG_OTHER, TERSE, "  dev.iov_count = %d\n",      \
                       (req)->dev.iov_count);                           \
        MPL_DBG_MSG_FMT(MPIDI_CH3_DBG_OTHER, TERSE, (MPL_DBG_FDEST, "  dev.state = 0x%x\n", (req)->dev.state)); \
        MPL_DBG_MSG_D(MPIDI_CH3_DBG_OTHER, TERSE, "    type = %d\n",             \
                       MPIDI_Request_get_type(req));                    \
    } while (0)
#else
#define DUMP_REQUEST(req) do { } while (0)
#endif


#define MPIDI_POSTED_RECV_ENQUEUE_HOOK(req) MPIDI_CH3I_Posted_recv_enqueued(req)
#define MPIDI_POSTED_RECV_DEQUEUE_HOOK(req) MPIDI_CH3I_Posted_recv_dequeued(req)

/*
 * MPID_Progress_state - device/channel dependent state to be passed between
 * MPID_Progress_{start,wait,end}
 *
 */
typedef struct MPIDI_CH3I_Progress_state
{
    int completion_count;
}
MPIDI_CH3I_Progress_state;

#define MPIDI_CH3_PROGRESS_STATE_DECL MPIDI_CH3I_Progress_state ch;

extern MPL_atomic_int_t MPIDI_CH3I_progress_completion_count;

#define MPIDI_CH3I_INCR_PROGRESS_COMPLETION_COUNT do {                                  \
        MPL_atomic_write_barrier();                                                     \
        MPL_atomic_fetch_add_int(&MPIDI_CH3I_progress_completion_count, 1);             \
        MPL_DBG_MSG_D(MPIDI_CH3_DBG_PROGRESS,VERBOSE,                                   \
                       "just incremented MPIDI_CH3I_progress_completion_count=%d",      \
                       MPL_atomic_relaxed_load_int(&MPIDI_CH3I_progress_completion_count)); \
    } while(0)

#endif /* MPIDI_CH3_PRE_H_INCLUDED */

