/*
 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
 *                         University Research and Technology
 *                         Corporation.  All rights reserved.
 * Copyright (c) 2004-2005 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
 *                         University of Stuttgart.  All rights reserved.
 * Copyright (c) 2004-2005 The Regents of the University of California.
 *                         All rights reserved.
 * Copyright (c) 2014-2020 Intel, Inc.  All rights reserved.
 * Copyright (c) 2015      Mellanox Technologies, Inc.
 *                         All rights reserved.
 * Copyright (c) 2019      Research Organization for Information Science
 *                         and Technology (RIST).  All rights reserved.
 * Copyright (c) 2021-2022 Nanook Consulting.  All rights reserved.
 * $COPYRIGHT$
 *
 * Additional copyrights may follow
 *
 * $HEADER$
 */

#ifndef PMIX_TYPES_H
#define PMIX_TYPES_H

#include "src/include/pmix_config.h"
#include "pmix_common.h"

#ifdef HAVE_STDINT_H
#    include <stdint.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#    include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#    include <sys/socket.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#    include <sys/select.h>
#endif
#ifdef HAVE_NETINET_IN_H
#    include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#    include <arpa/inet.h>
#endif
#include <event.h>
#if !PMIX_HAVE_LIBEV
#    include <event2/thread.h>
#endif

#if PMIX_ENABLE_DEBUG
#    include "src/util/pmix_output.h"
#endif

#include <pthread.h>

/*
 * portable assignment of pointer to int
 */

typedef union {
    uint64_t lval;
    uint32_t ival;
    void *pval;
    struct {
        uint32_t uval;
        uint32_t lval;
    } sval;
} pmix_ptr_t;

/*
 * handle differences in iovec
 */

#if defined(__APPLE__) || defined(__WINDOWS__)
typedef char *pmix_iov_base_ptr_t;
#    define PMIX_IOVBASE char
#else
#    define PMIX_IOVBASE void
typedef void *pmix_iov_base_ptr_t;
#endif

/*
 * handle differences in socklen_t
 */

#if defined(HAVE_SOCKLEN_T)
typedef socklen_t pmix_socklen_t;
#else
typedef int pmix_socklen_t;
#endif

#define pmix_htons htons
#define pmix_ntohs ntohs

/*
 * Convert a 64 bit value to network byte order.
 */
static inline uint64_t pmix_hton64(uint64_t val) __pmix_attribute_const__;
static inline uint64_t pmix_hton64(uint64_t val)
{
#ifdef HAVE_UNIX_BYTESWAP
    union {
        uint64_t ll;
        uint32_t l[2];
    } w, r;

    /* platform already in network byte order? */
    if (htonl(1) == 1L)
        return val;
    w.ll = val;
    r.l[0] = htonl(w.l[1]);
    r.l[1] = htonl(w.l[0]);
    return r.ll;
#else
    return val;
#endif
}

/*
 * Convert a 64 bit value from network to host byte order.
 */

static inline uint64_t pmix_ntoh64(uint64_t val) __pmix_attribute_const__;
static inline uint64_t pmix_ntoh64(uint64_t val)
{
#ifdef HAVE_UNIX_BYTESWAP
    union {
        uint64_t ll;
        uint32_t l[2];
    } w, r;

    /* platform already in network byte order? */
    if (htonl(1) == 1L)
        return val;
    w.ll = val;
    r.l[0] = ntohl(w.l[1]);
    r.l[1] = ntohl(w.l[0]);
    return r.ll;
#else
    return val;
#endif
}

/* Convert size_t value from host to network byte order and back */
#if SIZEOF_SIZE_T == 4

#    define pmix_htonsizet(x) htonl(x)
#    define pmix_ntohsizet(x) ntohl(x)

#elif SIZEOF_SIZE_T == 8

#    define pmix_htonsizet(x) pmix_hton64(x)
#    define pmix_ntohsizet(x) pmix_ntoh64(x)

#endif

/**
 * Convert between a local representation of pointer and a 64 bits value.
 */
static inline uint64_t pmix_ptr_ptol(void *ptr) __pmix_attribute_const__;
static inline uint64_t pmix_ptr_ptol(void *ptr)
{
    return (uint64_t)(uintptr_t) ptr;
}

static inline void *pmix_ptr_ltop(uint64_t value) __pmix_attribute_const__;
static inline void *pmix_ptr_ltop(uint64_t value)
{
#if SIZEOF_VOID_P == 4 && PMIX_ENABLE_DEBUG
    if (value > ((1ULL << 32) - 1ULL)) {
        pmix_output(0, "Warning: truncating value in pmix_ptr_ltop");
    }
#endif
    return (void *) (uintptr_t) value;
}

#if defined(WORDS_BIGENDIAN) || !defined(HAVE_UNIX_BYTESWAP)
static inline uint16_t pmix_swap_bytes2(uint16_t val) __pmix_attribute_const__;
static inline uint16_t pmix_swap_bytes2(uint16_t val)
{
    union {
        uint16_t bigval;
        uint8_t arrayval[2];
    } w, r;

    w.bigval = val;
    r.arrayval[0] = w.arrayval[1];
    r.arrayval[1] = w.arrayval[0];

    return r.bigval;
}

static inline uint32_t pmix_swap_bytes4(uint32_t val) __pmix_attribute_const__;
static inline uint32_t pmix_swap_bytes4(uint32_t val)
{
    union {
        uint32_t bigval;
        uint8_t arrayval[4];
    } w, r;

    w.bigval = val;
    r.arrayval[0] = w.arrayval[3];
    r.arrayval[1] = w.arrayval[2];
    r.arrayval[2] = w.arrayval[1];
    r.arrayval[3] = w.arrayval[0];

    return r.bigval;
}

static inline uint64_t pmix_swap_bytes8(uint64_t val) __pmix_attribute_const__;
static inline uint64_t pmix_swap_bytes8(uint64_t val)
{
    union {
        uint64_t bigval;
        uint8_t arrayval[8];
    } w, r;

    w.bigval = val;
    r.arrayval[0] = w.arrayval[7];
    r.arrayval[1] = w.arrayval[6];
    r.arrayval[2] = w.arrayval[5];
    r.arrayval[3] = w.arrayval[4];
    r.arrayval[4] = w.arrayval[3];
    r.arrayval[5] = w.arrayval[2];
    r.arrayval[6] = w.arrayval[1];
    r.arrayval[7] = w.arrayval[0];

    return r.bigval;
}

#else
#    define pmix_swap_bytes2 htons
#    define pmix_swap_bytes4 htonl
#    define pmix_swap_bytes8 hton64
#endif /* WORDS_BIGENDIAN || !HAVE_UNIX_BYTESWAP */

#define PMIX_EV_TIMEOUT EV_TIMEOUT
#define PMIX_EV_READ    EV_READ
#define PMIX_EV_WRITE   EV_WRITE
#define PMIX_EV_SIGNAL  EV_SIGNAL
/* Persistent event: won't get removed automatically when activated. */
#define PMIX_EV_PERSIST EV_PERSIST

#define PMIX_EVLOOP_ONCE     EVLOOP_ONCE     /**< Block at most once. */
#define PMIX_EVLOOP_NONBLOCK EVLOOP_NONBLOCK /**< Do not block. */

#define PMIX_EVENT_SIGNAL(ev) pmix_event_get_signal(ev)

typedef struct event_base pmix_event_base_t;
typedef struct event pmix_event_t;

#define pmix_event_base_create() event_base_new()

#define pmix_event_base_free(b) event_base_free(b)

#if PMIX_HAVE_LIBEV
#    define pmix_event_use_threads()
#    define pmix_event_free(b)       free(b)
#    define pmix_event_get_signal(x) (x)->ev_fd
#else

/* thread support APIs */
#    define pmix_event_use_threads() evthread_use_pthreads()
#    define pmix_event_free(x)       event_free(x)
#    define pmix_event_get_signal(x) event_get_signal(x)
#endif

/* Basic event APIs */
#define pmix_event_enable_debug_mode() event_enable_debug_mode()

PMIX_EXPORT int pmix_event_assign(struct event *ev, pmix_event_base_t *evbase, int fd, short arg,
                                  event_callback_fn cbfn, void *cbd);

#define pmix_event_set(b, x, fd, fg, cb, arg) \
    pmix_event_assign((x), (b), (fd), (fg), (event_callback_fn)(cb), (arg))

#if PMIX_HAVE_LIBEV
PMIX_EXPORT int pmix_event_add(struct event *ev, struct timeval *tv);
PMIX_EXPORT int pmix_event_del(struct event *ev);
PMIX_EXPORT void pmix_event_active(struct event *ev, int res, short ncalls);
PMIX_EXPORT void pmix_event_base_loopexit(pmix_event_base_t *b);
#else
#    define pmix_event_add(ev, tv)      event_add((ev), (tv))
#    define pmix_event_del(ev)          event_del((ev))
#    define pmix_event_active(x, y, z)  event_active((x), (y), (z))
#    define pmix_event_base_loopexit(b) event_base_loopexit(b, NULL)
#endif

PMIX_EXPORT pmix_event_t *pmix_event_new(pmix_event_base_t *b, int fd, short fg,
                                         event_callback_fn cbfn, void *cbd);

#define pmix_event_loop(b, fg) event_base_loop((b), (fg))

#define pmix_event_evtimer_new(b, cb, arg) pmix_event_new((b), -1, 0, (cb), (arg))

#define pmix_event_evtimer_add(x, tv) pmix_event_add((x), (tv))

#define pmix_event_evtimer_set(b, x, cb, arg) \
    pmix_event_assign((x), (b), -1, 0, (event_callback_fn)(cb), (arg))

#define pmix_event_evtimer_del(x) pmix_event_del((x))

#define pmix_event_signal_set(b, x, fd, cb, arg) \
    pmix_event_assign((x), (b), (fd), EV_SIGNAL | EV_PERSIST, (event_callback_fn)(cb), (arg))

#endif /* PMIX_TYPES_H */
