/*
 *                         Vortex OpenSplice
 *
 *   This software and documentation are Copyright 2006 to TO_YEAR ADLINK
 *   Technology Limited, its affiliated companies and licensors. All rights
 *   reserved.
 *
 *   Licensed under the ADLINK Software License Agreement Rev 2.7 2nd October
 *   2014 (the "License"); you may not use this file except in compliance with
 *   the License.
 *   You may obtain a copy of the License at:
 *                      $OSPL_HOME/LICENSE
 *
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */
#ifndef Q_GLOBALS_H
#define Q_GLOBALS_H

#include <stdio.h>

#include "os_defs.h"
#include "os_mutex.h"
#include "os_rwlock.h"
#include "os_cond.h"
#include "os_socket.h"
#include "os_time.h"

#include "ut_fibheap.h"

#if !LITE
#include "c_base.h"
#include "kernelModuleI.h"
#endif

#include "q_plist.h"
#include "q_protocol.h"
#include "q_nwif.h"
#include "q_sockwaitset.h"

#ifdef DDSI_INCLUDE_ENCRYPTION
#include "q_security.h" /* for q_securityDecoderSet */
#endif /* DDSI_INCLUDE_ENCRYPTION */

#if defined (__cplusplus)
extern "C" {
#endif

#ifdef OSPL_BUILD_DDSI2
#define OS_API OS_API_EXPORT
#else
#define OS_API OS_API_IMPORT
#endif

struct nn_xmsgpool;
struct serstatepool;
struct nn_dqueue;
struct nn_reorder;
struct nn_defrag;
struct addrset;
struct xeventq;
struct gcreq_queue;
struct ephash;
struct lease;
struct ddsi_tran_conn;
struct ddsi_tran_listener;
struct ddsi_tran_factory;
struct ut_thread_pool_s;

typedef struct ospl_in_addr_node {
   os_sockaddr_storage addr;
   struct ospl_in_addr_node *next;
} ospl_in_addr_node;

enum recvips_mode {
  RECVIPS_MODE_ALL,             /* all MC capable interfaces */
  RECVIPS_MODE_ANY,             /* kernel-default interface */
  RECVIPS_MODE_PREFERRED,       /* selected interface only */
  RECVIPS_MODE_NONE,            /* no interfaces at all */
  RECVIPS_MODE_SOME             /* explicit list of interfaces; only one requiring recvips */
};

#define N_LEASE_LOCKS_LG2 4
#define N_LEASE_LOCKS ((int) (1 << N_LEASE_LOCKS_LG2))

struct q_globals {
  volatile int terminate;
  volatile int exception;
  volatile int deaf;
  volatile int mute;

#if ! LITE
  /* OpenSplice base & kernel pointers, QoS type */
  c_base ospl_base;
  v_kernel ospl_kernel;
  c_collectionType ospl_qostype;
  c_collectionType ospl_eotgroup_tidlist_type;
#endif

  /* Hash tables for participants, readers, writers, proxy
     participants, proxy readers and proxy writers by GUID
     (guid_hash) */
  struct ephash *guid_hash;

  /* Timed events admin */
  struct xeventq *xevents;

  /* Queue for garbage collection requests */
  struct gcreq_queue *gcreq_queue;
  struct nn_servicelease *servicelease;

  /* Lease junk */
  os_mutex leaseheap_lock;
  os_mutex lease_locks[N_LEASE_LOCKS];
  ut_fibheap_t leaseheap;

  /* Transport factory */

  struct ddsi_tran_factory * m_factory;

  /* Connections for multicast discovery & data, and those that correspond
     to the one DDSI participant index that the DDSI2 service uses. The
     DCPS participant of DDSI2 itself will be mirrored in a DDSI
     participant, and in multi-socket mode that one gets its own
     socket. */

  struct ddsi_tran_conn * disc_conn_mc;
  struct ddsi_tran_conn * data_conn_mc;
  struct ddsi_tran_conn * disc_conn_uc;
  struct ddsi_tran_conn * data_conn_uc;

  /* TCP listener */

  struct ddsi_tran_listener * listener;

  /* Thread pool */

  struct ut_thread_pool_s * thread_pool;

  /* Receive thread triggering: must have a socket per receive thread
     because all receive threads must be triggered, even though each
     receive thread takes the trigger message from the socket. With one
     trigger socket, we can only have one receive thread (which enables
     other optimisations that we don't currently do). */
  os_sockWaitset waitset;

  /* In many sockets mode, the receive threads maintain a local array
     with participant GUIDs and sockets, participant_set_generation is
     used to notify them. */
  pa_uint32_t participant_set_generation;

  /* nparticipants is used primarily for limiting the number of active
     participants, but also during shutdown to determine when it is
     safe to stop the GC thread. */
  os_mutex participant_set_lock;
  os_cond participant_set_cond;
  os_uint32 nparticipants;

  /* For participants without (some) built-in writers, we fall back to
     this participant, which is the first one created with all
     built-in writers present.  It MUST be created before any in need
     of it pops up! */
  struct participant *privileged_pp;
  os_mutex privileged_pp_lock;

#if LITE
  /* GUID to be used in next call to new_participant; also protected
     by privileged_pp_lock */
  struct nn_guid next_ppguid;
#endif

  /* number of up, non-loopback, IPv4/IPv6 interfaces, the index of
     the selected/preferred one, and the discovered interfaces. */
  int n_interfaces;
  int selected_interface;
  struct nn_interface interfaces[MAX_INTERFACES];

#if OS_SOCKET_HAS_IPV6
  /* whether we're using an IPv6 link-local address (and therefore
     only listening to multicasts on that interface) */
  int ipv6_link_local;
#endif

  /* Addressing: actual own (preferred) IP address, IP address
     advertised in discovery messages (so that an external IP address on
     a NAT may be advertised), and the DDSI multi-cast address. */
  enum recvips_mode recvips_mode;
  struct ospl_in_addr_node *recvips;
  struct in_addr extmask;

  os_sockaddr_storage ownip;
  os_sockaddr_storage extip;

  /* InterfaceNo that the OwnIP is tied to */
  os_uint interfaceNo;

  /* Locators */

  nn_locator_t loc_spdp_mc;
  nn_locator_t loc_meta_mc;
  nn_locator_t loc_meta_uc;
  nn_locator_t loc_default_mc;
  nn_locator_t loc_default_uc;

  /*
    Initial discovery address set, and the current discovery address
    set. These are the addresses that SPDP pings get sent to. The
    as_disc_group is an FT group (only use first working).
  */
  struct addrset *as_disc;
  struct addrset *as_disc_group;

  /* qoslock serializes QoS changes, probably not strictly necessary,
     but a lot more straightforward that way */
  os_rwlock qoslock;

  os_mutex lock;

#if LITE
  /* guarantees serialisation of attach/detach operations on waitsets
     -- a single global lock is a bit coarse, but these operations are
     rare and at initialisation time anyway */
  os_mutex attach_lock;
#endif

  /* Receive thread. (We can only has one for now, cos of the signal
     trigger socket.) Receive buffer pool is per receive thread,
     practical considerations led to it being a global variable
     TEMPORARILY. */
  struct thread_state1 *recv_ts;
  struct nn_rbufpool *rbufpool;

  /* Listener thread for connection based transports */
  struct thread_state1 *listen_ts;

  /* Flag cleared when stopping (receive threads). FIXME. */
  int rtps_keepgoing;

  /* Startup mode causes data to be treated as transient-local with
     depth 1 (i.e., stored in the WHCs and regurgitated on request) to
     cover the start-up delay of the discovery protocols. Because all
     discovery data is shared, this is strictly a start-up issue of the
     service. */
  int startup_mode;

  /* Start time of the DDSI2 service, for logging relative time stamps,
     should I ever so desire. */
  nn_wctime_t tstart;

  /* Default QoSs for participant, readers and writers (needed for
     eliminating default values in outgoing discovery packets, and for
     supplying values for missing QoS settings in incoming discovery
     packets); plus the actual QoSs needed for the builtin
     endpoints. */
  nn_plist_t default_plist_pp;
  nn_xqos_t default_xqos_rd;
  nn_xqos_t default_xqos_wr;
  nn_xqos_t default_xqos_wr_nad;
  nn_xqos_t default_xqos_tp;
  nn_xqos_t default_xqos_sub;
  nn_xqos_t default_xqos_pub;
  nn_xqos_t spdp_endpoint_xqos;
  nn_xqos_t builtin_endpoint_xqos_rd;
  nn_xqos_t builtin_endpoint_xqos_wr;

  /* SPDP packets get very special treatment (they're the only packets
     we accept from writers we don't know) and have their very own
     do-nothing defragmentation and reordering thingummies, as well as a
     global mutex to in lieu of the proxy writer lock. */
  os_mutex spdp_lock;
  struct nn_defrag *spdp_defrag;
  struct nn_reorder *spdp_reorder;

  /* Built-in stuff other than SPDP gets funneled through the builtins
     delivery queue; currently just SEDP and PMD */
  struct nn_dqueue *builtins_dqueue;

  /* Connection used by general timed-event queue for transmitting data */

  struct ddsi_tran_conn * tev_conn;

#ifndef DDSI_INCLUDE_NETWORK_CHANNELS
  os_uint32 networkQueueId;
  struct thread_state1 *channel_reader_ts;

  /* Application data gets its own delivery queue */
  struct nn_dqueue *user_dqueue;
#endif

  /* Transmit side: pools for the serializer & transmit messages and a
     transmit queue*/
  struct serstatepool *serpool;
  struct nn_xmsgpool *xmsgpool;

  /* Network ID needed by v_groupWrite -- FIXME: might as well pass it
     to the receive thread instead of making it global (and that would
     remove the need to include kernelModule.h) */
  os_uint32 myNetworkId;

#if ! LITE
  /* Shared memory exhaustion warnings */
  pa_uint32_t last_threshold_warning_sec;
  pa_uint32_t memory_shortage_dropcount;
#endif

#ifdef DDSI_INCLUDE_ENCRYPTION
  /* Codecs needed for decoding incoming encrypted messages
     FIXME: should be a property of the receiver thread, and pass down
     while processing messages. For now made global */
  q_securityDecoderSet recvSecurityCodec;
#endif /* DDSI_INCLUDE_ENCRYPTION */

  /* File for dumping captured packets, NULL if disabled */
  FILE *pcap_fp;
  os_mutex pcap_lock;

  /* Data structure to capture power events */
  os_timePowerEvents powerEvents;

  /* Static log buffer, for those rare cases a thread calls nn_vlogb
     without having its own log buffer (happens during config file
     processing and for listeners, &c. */
  int static_logbuf_lock_inited;
  os_mutex static_logbuf_lock;
  struct logbuf static_logbuf;
};

extern struct q_globals OS_API gv;
#undef OS_API
#if defined (__cplusplus)
}
#endif

#endif /* Q_GLOBALS_H */
