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

#ifndef MPIDU_SCHED_H_INCLUDED
#define MPIDU_SCHED_H_INCLUDED

//#include "mpidu_pre.h"
/* FIXME open questions:
 * - Should the schedule hold a pointer to the nbc request and the nbc request
 *   hold a pointer to the schedule?  This could cause MT issues.
 */
#include "mpidu_pre.h"

enum MPIDU_Sched_entry_type {
    MPIDU_SCHED_ENTRY_INVALID_LB = 0,
    MPIDU_SCHED_ENTRY_SEND,
    /* _SEND_DEFER is easily implemented via _SEND */
    MPIDU_SCHED_ENTRY_RECV,
    /* _RECV_STATUS is easily implemented via _RECV */
    MPIDU_SCHED_ENTRY_REDUCE,
    MPIDU_SCHED_ENTRY_COPY,
    MPIDU_SCHED_ENTRY_NOP,
    MPIDU_SCHED_ENTRY_CB,
    MPIDU_SCHED_ENTRY_INVALID_UB
};

struct MPIDU_Sched_send {
    const void *buf;
    MPI_Aint count;
    const MPI_Aint *count_p;
    MPI_Datatype datatype;
    int dest;
    struct MPIR_Comm *comm;
    struct MPIR_Request *sreq;
    int is_sync;                /* TRUE iff this send is an ssend */
};

struct MPIDU_Sched_recv {
    void *buf;
    MPI_Aint count;
    MPI_Datatype datatype;
    int src;
    struct MPIR_Comm *comm;
    struct MPIR_Request *rreq;
    MPI_Status *status;
};

struct MPIDU_Sched_reduce {
    const void *inbuf;
    void *inoutbuf;
    MPI_Aint count;
    MPI_Datatype datatype;
    MPI_Op op;
};

struct MPIDU_Sched_copy {
    const void *inbuf;
    MPI_Aint incount;
    MPI_Datatype intype;
    void *outbuf;
    MPI_Aint outcount;
    MPI_Datatype outtype;
};

/* nop entries have no args, so no structure is needed */

enum MPIDU_Sched_cb_type {
    MPIDU_SCHED_CB_TYPE_1 = 0,  /* single state arg type --> MPIR_Sched_cb_t */
    MPIDU_SCHED_CB_TYPE_2       /* double state arg type --> MPIR_Sched_cb2_t */
};

struct MPIDU_Sched_cb {
    enum MPIDU_Sched_cb_type cb_type;
    union {
        MPIR_Sched_cb_t *cb_p;
        MPIR_Sched_cb2_t *cb2_p;
    } u;
    void *cb_state;
    void *cb_state2;            /* unused for single-param callbacks */
};

enum MPIDU_Sched_entry_status {
    MPIDU_SCHED_ENTRY_STATUS_NOT_STARTED = 0,
    MPIDU_SCHED_ENTRY_STATUS_STARTED,
    MPIDU_SCHED_ENTRY_STATUS_COMPLETE,
    MPIDU_SCHED_ENTRY_STATUS_FAILED,    /* indicates a failure occurred while executing the entry */
    MPIDU_SCHED_ENTRY_STATUS_INVALID    /* indicates an invalid entry, or invalid status value */
};

/* Use a tagged union for schedule entries.  Not always space optimal, but saves
 * lots of error-prone pointer arithmetic and makes scanning the schedule easy. */
struct MPIDU_Sched_entry {
    enum MPIDU_Sched_entry_type type;
    enum MPIDU_Sched_entry_status status;
    int is_barrier;
    union {
        struct MPIDU_Sched_send send;
        struct MPIDU_Sched_recv recv;
        struct MPIDU_Sched_reduce reduce;
        struct MPIDU_Sched_copy copy;
        /* nop entries have no args */
        struct MPIDU_Sched_cb cb;
    } u;
};

struct MPIDU_Sched {
    size_t size;                /* capacity (in entries) of the entries array */
    size_t idx;                 /* index into entries array of first yet-outstanding entry */
    int num_entries;            /* number of populated entries, num_entries <= size */
    int tag;
    struct MPIR_Request *req;   /* really needed? could cause MT problems... */
    struct MPIDU_Sched_entry *entries;

    struct MPIDU_Sched *next;   /* linked-list next pointer */
    struct MPIDU_Sched *prev;   /* linked-list next pointer */
};

/* prototypes */
int MPIDU_Sched_progress(int *made_progress);
int MPIDU_Sched_are_pending(void);
int MPIDU_Sched_next_tag(struct MPIR_Comm *comm_ptr, int *tag);
int MPIDU_Sched_create(MPIR_Sched_t * sp);
int MPIDU_Sched_clone(MPIR_Sched_t orig, MPIR_Sched_t * cloned);
int MPIDU_Sched_start(MPIR_Sched_t * sp, struct MPIR_Comm *comm, int tag,
                      struct MPIR_Request **req);
int MPIDU_Sched_send(const void *buf, MPI_Aint count, MPI_Datatype datatype, int dest,
                     struct MPIR_Comm *comm, MPIR_Sched_t s);
int MPIDU_Sched_recv(void *buf, MPI_Aint count, MPI_Datatype datatype, int src,
                     struct MPIR_Comm *comm, MPIR_Sched_t s);
int MPIR_Sched_ssend(const void *buf, MPI_Aint count, MPI_Datatype datatype, int dest,
                     struct MPIR_Comm *comm, MPIR_Sched_t s);
int MPIR_Sched_reduce(const void *inbuf, void *inoutbuf, MPI_Aint count, MPI_Datatype datatype,
                      MPI_Op op, MPIR_Sched_t s);
int MPIDU_Sched_copy(const void *inbuf, MPI_Aint incount, MPI_Datatype intype, void *outbuf,
                     MPI_Aint outcount, MPI_Datatype outtype, MPIR_Sched_t s);
int MPIDU_Sched_barrier(MPIR_Sched_t s);

#endif /* MPIDU_SCHED_H_INCLUDED */
