/*
 *  OpenVPN -- An application to securely tunnel IP networks
 *             over a single TCP/UDP port, with support for SSL/TLS-based
 *             session authentication and key exchange,
 *             packet encryption, packet authentication, and
 *             packet compression.
 *
 *  Copyright (C) 2002-2026 OpenVPN Inc <sales@openvpn.net>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2
 *  as published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef EVENT_H
#define EVENT_H

#include "win32.h"
#include "sig.h"

/*
 * rwflags passed to event_ctl and returned by
 * struct event_set_return.
 */
#define READ_SHIFT  0
#define WRITE_SHIFT 1

#define EVENT_UNDEF 4
#define EVENT_READ  (1u << READ_SHIFT)
#define EVENT_WRITE (1u << WRITE_SHIFT)

/* event flags returned by io_wait.
 *
 * All these events are defined as bits in a bitfield.
 * Each event 'type' owns two bits in the bitfield: one for the READ
 * event and one for the WRITE event.
 *
 * For this reason, the specific event bit is calculated by adding
 * the event type identifier (always a multiple of 2, as defined
 * below) to 0 for READ and 1 for WRITE.
 *
 * E.g.
 * MANAGEMENT_SHIFT = 6;  <---- event type identifier
 * MANAGEMENT_READ = (1 << (6 + 0)),  <---- READ event
 * MANAGEMENT_WRITE = (1 << (6 + 1))  <---- WRITE event
 *
 * 'error' and 'file_close' are special and use read/write for different
 * signals.
 */

#define SOCKET_SHIFT     0
#define SOCKET_READ      (1 << (SOCKET_SHIFT + READ_SHIFT))
#define SOCKET_WRITE     (1 << (SOCKET_SHIFT + WRITE_SHIFT))
#define TUN_SHIFT        2
#define TUN_READ         (1 << (TUN_SHIFT + READ_SHIFT))
#define TUN_WRITE        (1 << (TUN_SHIFT + WRITE_SHIFT))
#define ERR_SHIFT        4
#define ES_ERROR         (1 << (ERR_SHIFT + READ_SHIFT))
#define ES_TIMEOUT       (1 << (ERR_SHIFT + WRITE_SHIFT))
#define MANAGEMENT_SHIFT 6
#define MANAGEMENT_READ  (1 << (MANAGEMENT_SHIFT + READ_SHIFT))
#define MANAGEMENT_WRITE (1 << (MANAGEMENT_SHIFT + WRITE_SHIFT))
#define DCO_SHIFT        10
#define DCO_READ         (1 << (DCO_SHIFT + READ_SHIFT))
#define DCO_WRITE        (1 << (DCO_SHIFT + WRITE_SHIFT))

/*
 * Initialization flags passed to event_set_init
 */
#define EVENT_METHOD_US_TIMEOUT (1 << 0)
#define EVENT_METHOD_FAST       (1 << 1)

/*
 * The following constant is used as boundary between integer value
 * and real addresses when passing arguments to event handlers as (void *)
 */
#define MULTI_N ((void *)16) /* upper bound on MTCP_x */

#ifdef _WIN32

typedef const struct rw_handle *event_t;

#define UNDEFINED_EVENT (NULL)

#else /* ifdef _WIN32 */

typedef int event_t;

#define UNDEFINED_EVENT (-1)

#endif

struct event_set;
struct event_set_return;

struct event_set_functions
{
    void (*free)(struct event_set *es);
    void (*reset)(struct event_set *es);
    void (*del)(struct event_set *es, event_t event);
    void (*ctl)(struct event_set *es, event_t event, unsigned int rwflags, void *arg);

    /*
     * Return status for wait:
     * -1 on signal or error
     * 0 on timeout
     * length of event_set_return if at least 1 event is returned
     */
    int (*wait)(struct event_set *es, const struct timeval *tv, struct event_set_return *out,
                int outlen);
};

struct event_set_return
{
    unsigned int rwflags;
    void *arg;
};

struct event_set
{
    struct event_set_functions func;
};

typedef enum
{
    EVENT_ARG_MULTI_INSTANCE = 0,
    EVENT_ARG_LINK_SOCKET,
} event_arg_t;

/* generic event argument object to pass to event_ctl() */
struct event_arg
{
    event_arg_t type;
    union
    {
        struct multi_instance *mi; /* if type = EVENT_ARG_MULTI_INSTANCE */
        struct link_socket *sock;  /* if type = EVENT_ARG_LINK_SOCKET */
    } u;
};

/*
 * maxevents on input:  desired max number of event_t descriptors
 *                      simultaneously set with event_ctl
 * maxevents on output: may be modified down, depending on limitations
 *                      of underlying API
 * flags:               EVENT_METHOD_x flags
 */
struct event_set *event_set_init(int *maxevents, unsigned int flags);

static inline void
event_free(struct event_set *es)
{
    if (es)
    {
        (*es->func.free)(es);
    }
}

static inline void
event_reset(struct event_set *es)
{
    (*es->func.reset)(es);
}

static inline void
event_del(struct event_set *es, event_t event)
{
    (*es->func.del)(es, event);
}

static inline void
event_ctl(struct event_set *es, event_t event, unsigned int rwflags, void *arg)
{
    (*es->func.ctl)(es, event, rwflags, arg);
}

static inline int
event_wait(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen)
{
    int ret;
    ret = (*es->func.wait)(es, tv, out, outlen);
    return ret;
}

static inline void
event_set_return_init(struct event_set_return *esr)
{
    esr->rwflags = 0;
    esr->arg = NULL;
}

#ifdef _WIN32

static inline void
wait_signal(struct event_set *es, void *arg)
{
    if (HANDLE_DEFINED(win32_signal.in.read))
    {
        event_ctl(es, &win32_signal.in, EVENT_READ, arg);
    }
}

#else /* ifdef _WIN32 */

static inline void
wait_signal(struct event_set *es, void *arg)
{
}

#endif

#endif /* ifndef EVENT_H */
