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

#ifndef TCP_IMPL_H_INCLUDED
#define TCP_IMPL_H_INCLUDED

#include "mpid_nem_impl.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include "socksm.h"

/* globals */
extern struct pollfd *MPID_nem_tcp_plfd_tbl;
extern sockconn_t MPID_nem_tcp_g_lstn_sc;
extern struct pollfd MPID_nem_tcp_g_lstn_plfd;

extern MPL_dbg_class MPIDI_NEM_TCP_DBG_DET;

typedef enum { MPID_NEM_TCP_VC_STATE_DISCONNECTED,
    MPID_NEM_TCP_VC_STATE_CONNECTED,
    MPID_NEM_TCP_VC_STATE_ERROR
} MPID_nem_tcp_vc_state_t;

#define MPIDI_NEM_TCP_MAX_CONNECT_RETRIES 100

typedef GENERIC_Q_DECL(struct MPIR_Request) MPIDI_nem_tcp_request_queue_t;

/* The vc provides a generic buffer in which network modules can store
   private fields This removes all dependencies from the VC struction
   on the network module, facilitating dynamic module loading. */
typedef struct {
    struct sockaddr_in sock_id;
    MPID_nem_tcp_vc_state_t state;
    struct MPID_nem_new_tcp_sockconn *sc;
    int send_paused;
    MPIDI_nem_tcp_request_queue_t send_queue;
    MPIDI_nem_tcp_request_queue_t paused_send_queue;
    /* this is a count of how many sc objects refer to this vc */
    int sc_ref_count;
    int connect_retry_count;    /* number of times we've tried to connect */
} MPID_nem_tcp_vc_area;

/* macro for tcp private in VC */
#define VC_TCP(vc) ((MPID_nem_tcp_vc_area *)vc->ch.netmod_area.padding)

#define ASSIGN_SC_TO_VC(vc_tcp_, sc_) do {      \
        (vc_tcp_)->sc = (sc_);                  \
    } while (0)

/* functions */
int MPID_nem_tcp_init(MPIDI_PG_t * pg_p, int pg_rank, char **bc_val_p, int *val_max_sz_p);
int MPID_nem_tcp_finalize(void);

#ifdef ENABLE_CHECKPOINTING
int MPID_nem_tcp_ckpt_pause_send_vc(MPIDI_VC_t * vc);
int MPID_nem_tcp_ckpt_continue_vc(MPIDI_VC_t * vc);
int MPID_nem_tcp_ckpt_restart_vc(MPIDI_VC_t * vc);
int MPID_nem_tcp_ckpt_shutdown(void);
#endif

int MPID_nem_tcp_get_business_card(int my_rank, char **bc_val_p, int *val_max_sz_p);
int MPID_nem_tcp_connect_to_root(const char *business_card, MPIDI_VC_t * new_vc);
int MPID_nem_tcp_vc_init(MPIDI_VC_t * vc);
int MPID_nem_tcp_vc_destroy(MPIDI_VC_t * vc);
int MPID_nem_tcp_vc_terminate(MPIDI_VC_t * vc);

/* completion counter is atomically decremented when operation completes */
int MPID_nem_tcp_get(void *target_p, void *source_p, int source_node, int len, int *completion_ctr);
int MPID_nem_tcp_put(void *target_p, int target_node, void *source_p, int len, int *completion_ctr);

int MPID_nem_tcp_send_init(void);
int MPID_nem_tcp_connect(struct MPIDI_VC *const vc);
int MPID_nem_tcp_connpoll(int in_blocking_poll);
int MPID_nem_tcp_sm_init(void);
int MPID_nem_tcp_sm_finalize(void);
int MPID_nem_tcp_set_sockopts(int fd);
int MPID_nem_tcp_check_sock_status(const struct pollfd *const plfd);
int MPID_nem_tcp_send_finalize(void);
int MPID_nem_tcp_bind(int sockfd);
int MPID_nem_tcp_conn_est(MPIDI_VC_t * vc);
int MPID_nem_tcp_get_conninfo(struct MPIDI_VC *vc, struct sockaddr_in *addr, char **pg_id,
                              int *pg_rank);
int MPID_nem_tcp_get_vc_from_conninfo(char *pg_id, int pg_rank, struct MPIDI_VC **vc);
int MPID_nem_tcp_is_sock_connected(int fd);
int MPID_nem_tcp_disconnect(struct MPIDI_VC *const vc);
int MPID_nem_tcp_cleanup(struct MPIDI_VC *const vc);
int MPID_nem_tcp_cleanup_on_error(MPIDI_VC_t * const vc, int req_errno);
int MPID_nem_tcp_error_out_send_queue(struct MPIDI_VC *const vc, int req_errno);
int MPID_nem_tcp_ckpt_cleanup(void);
int MPID_nem_tcp_state_listening_handler(struct pollfd *const l_plfd, sockconn_t * const l_sc);
int MPID_nem_tcp_send_queued(MPIDI_VC_t * vc, MPIDI_nem_tcp_request_queue_t * send_queue);

int MPID_nem_tcp_iSendContig(MPIDI_VC_t * vc, MPIR_Request * sreq, void *hdr, intptr_t hdr_sz,
                             void *data, intptr_t data_sz);
int MPID_nem_tcp_iStartContigMsg(MPIDI_VC_t * vc, void *hdr, intptr_t hdr_sz, void *data,
                                 intptr_t data_sz, MPIR_Request ** sreq_ptr);
int MPID_nem_tcp_iStartContigMsg_paused(MPIDI_VC_t * vc, void *hdr, intptr_t hdr_sz, void *data,
                                        intptr_t data_sz, MPIR_Request ** sreq_ptr);
int MPID_nem_tcp_SendNoncontig(MPIDI_VC_t * vc, MPIR_Request * sreq, void *header, intptr_t hdr_sz,
                               struct iovec * hdr_iov, int n_hdr_iov);
int MPID_nem_tcp_iSendIov(MPIDI_VC_t * vc, MPIR_Request * sreq, void *hdr, intptr_t hdr_sz,
                          struct iovec * iov, int n_iov);
int MPID_nem_tcp_get_addr_port_from_bc(const char *business_card, struct in_addr *addr,
                                       in_port_t * port);

void MPID_nem_tcp_vc_dbg_print_sendq(FILE * stream, MPIDI_VC_t * vc);

int MPID_nem_tcp_socksm_finalize(void);
int MPID_nem_tcp_socksm_init(void);
int MPID_nem_tcp_vc_terminated(MPIDI_VC_t * vc);
int MPID_nem_tcp_get_ordering(int *ordering);


int MPID_nem_tcp_pkt_unpause_handler(MPIDI_VC_t * vc, MPIDI_CH3_Pkt_t * pkt,
                                     void *data ATTRIBUTE((unused)),
                                     intptr_t * buflen, MPIR_Request ** rreqp);


/* Macros */

/* system call wrapper -- This retries the syscall each time it is interrupted.
   Example usage:  instead of writing "ret = write(fd, buf, len);"
   use: "CHECK_EINTR(ret, write(fd, buf, len));
 Caution:
 (1) Some of the system calls have value-result parameters. Those system calls
 should not be used within CHECK_EINTR macro or should be used with CARE.
 For eg. accept, the last parameter (addrlen) is a value-result one. So, even if the
 system call is interrupted, addrlen should be initialized to appropriate value before
 calling it again.

 (2) connect should not be called within a loop. In case, the connect is interrupted after
 the TCP handshake is initiated, calling connect again will only fail. So, select/poll
 should be called to check the status of the socket.
 I don't know what will happen, if a connect is interrupted even before the system call
 tries to initiate TCP handshake. No book/manual doesn't seem to explain this scenario.
*/
#define CHECK_EINTR(var, func) do {             \
        (var) = (func);                         \
    } while ((var) == -1 && errno == EINTR)

#define Q_EMPTY(q) GENERIC_Q_EMPTY (q)
#define Q_HEAD(q) GENERIC_Q_HEAD (q)
#define Q_ENQUEUE_EMPTY(qp, ep) GENERIC_Q_ENQUEUE_EMPTY (qp, ep, next)
#define Q_ENQUEUE(qp, ep) GENERIC_Q_ENQUEUE (qp, ep, next)
#define Q_ENQUEUE_EMPTY_MULTIPLE(qp, ep0, ep1) GENERIC_Q_ENQUEUE_EMPTY_MULTIPLE (qp, ep0, ep1, next)
#define Q_ENQUEUE_MULTIPLE(qp, ep0, ep1) GENERIC_Q_ENQUEUE_MULTIPLE (qp, ep0, ep1, next)
#define Q_DEQUEUE(qp, ep) GENERIC_Q_DEQUEUE (qp, ep, next)
#define Q_REMOVE_ELEMENTS(qp, ep0, ep1) GENERIC_Q_REMOVE_ELEMENTS (qp, ep0, ep1, next)


/* VC list macros */
#define VC_L_EMPTY(q) GENERIC_L_EMPTY (q)
#define VC_L_HEAD(q) GENERIC_L_HEAD (q)
#define SET_PLFD(vc_tcp)   MPID_nem_tcp_plfd_tbl[(vc_tcp)->sc->index].events |= POLLOUT
#define UNSET_PLFD(vc_tcp) MPID_nem_tcp_plfd_tbl[(vc_tcp)->sc->index].events &= ~POLLOUT

/* stack macros */
#define S_EMPTY(s) GENERIC_S_EMPTY (s)
#define S_TOP(s) GENERIC_S_TOP (s)
#define S_PUSH(sp, ep) GENERIC_S_PUSH (sp, ep, next)
#define S_PUSH_MULTIPLE(sp, ep0, ep1) GENERIC_S_PUSH_MULTIPLE (sp, ep0, ep1, next)
#define S_POP(sp, ep) GENERIC_S_POP (sp, ep, next)

/* Keys for business cards */
#define MPIDI_CH3I_PORT_KEY "port"
#define MPIDI_CH3I_HOST_DESCRIPTION_KEY "description"
#define MPIDI_CH3I_IFNAME_KEY "ifname"



/* tcp-local packet types */

typedef enum MPIDI_nem_tcp_pkt_type {
#ifdef ENABLE_CHECKPOINTING
    MPIDI_NEM_TCP_PKT_UNPAUSE,
#endif
    MPIDI_NEM_TCP_PKT_NUM_TYPES,
    MPIDI_NEM_TCP_PKT_INVALID = -1      /* force signed, to avoid warnings */
} MPIDI_nem_tcp_pkt_type_t;

#ifdef ENABLE_CHECKPOINTING
typedef struct MPIDI_nem_tcp_pkt_unpause {
    MPIDI_CH3_Pkt_type_t type;
    unsigned subtype;
} MPIDI_nem_tcp_pkt_unpause_t;
#endif



#endif /* TCP_IMPL_H_INCLUDED */
