/**
 * @file libwrtd.h
 *
 * Copyright (c) 2018-2019 CERN (home.cern)
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */

#ifndef __WRTD_LIB_H__
#define __WRTD_LIB_H__

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <stdbool.h>
#include "wrtd-common.h"

// NOTE: corresponds roughly to IVI ViSession
typedef struct wrtd_dev wrtd_dev;

/**
 * @enum wrtd_status
 * White Rabbit Trigger Distribution error codes.
 *
 * Names and values inspired by IVI-3.2 and IVI-3.15.
 */
typedef enum wrtd_status {
        /** No error. */
        WRTD_SUCCESS = 0,

        /** Same as *IVI_INHERENT_ERROR_BASE*. */
        __WRTD_ERROR_BASE = 0xBFFA0000,
        /** Invalid/unknown attribute. */
        WRTD_ERROR_INVALID_ATTRIBUTE      = __WRTD_ERROR_BASE + 0x0C,
        /** Attempt to write a read-only attribute. */
        WRTD_ERROR_ATTR_NOT_WRITEABLE     = __WRTD_ERROR_BASE + 0x0D,
        /** Attempt to read a write-only attribute. */
        WRTD_ERROR_ATTR_NOT_READABLE      = __WRTD_ERROR_BASE + 0x0E,
        /** Invalid value provided. */
        WRTD_ERROR_INVALID_VALUE          = __WRTD_ERROR_BASE + 0x10,
        /** Device not initialized. */
        WRTD_ERROR_NOT_INITIALIZED        = __WRTD_ERROR_BASE + 0x1D,
        /** Unknown channel name. */
        WRTD_ERROR_UNKNOWN_CHANNEL_NAME   = __WRTD_ERROR_BASE + 0x20,
        /** Device out of memory. */
        WRTD_ERROR_OUT_OF_MEMORY          = __WRTD_ERROR_BASE + 0x56,
        /** Null pointer. */
        WRTD_ERROR_NULL_POINTER           = __WRTD_ERROR_BASE + 0x58,
        /** Unexpected response. */
        WRTD_ERROR_UNEXPECTED_RESPONSE    = __WRTD_ERROR_BASE + 0x59,
        /** Unknown resource. */
        WRTD_ERROR_RESOURCE_UNKNOWN       = __WRTD_ERROR_BASE + 0x60,
        /** Incorrect repeated capability selector. */
        WRTD_ERROR_BADLY_FORMED_SELECTOR  = __WRTD_ERROR_BASE + 0x66,

        /** Same as *IVI_LXISYNC_ERROR_BASE*. */
        __WRTD_LXISYNC_ERROR_BASE = 0xBFFA3000,
        /** The alarm already exists. */
        WRTD_ERROR_ALARM_EXISTS           = __WRTD_LXISYNC_ERROR_BASE + 0x07,
        /** The specified alarm has not been defined. */
        WRTD_ERROR_ALARM_DOES_NOT_EXIST   = __WRTD_LXISYNC_ERROR_BASE + 0x08,

        /** Same as *IVI_VENDOR_SPECIFIC_ERROR_BASE*. */
        __WRTD_SPECIFIC_ERROR_BASE = 0xBFFA6000,
        /** Version mismatch. */
        WRTD_ERROR_VERSION_MISMATCH       = __WRTD_SPECIFIC_ERROR_BASE + 0x00,
        /** Internal error. */
        WRTD_ERROR_INTERNAL               = __WRTD_SPECIFIC_ERROR_BASE + 0x01,
        /** Unknown event log type/reason. */
        WRTD_ERROR_UNKNOWN_LOG_TYPE       = __WRTD_SPECIFIC_ERROR_BASE + 0x02,
        /** Resource is not disabled and cannot be changed.  */
        WRTD_ERROR_RESOURCE_ACTIVE        = __WRTD_SPECIFIC_ERROR_BASE + 0x03,
        /** Attempt to access a global attribute without
            using the global attribute selector. */
        WRTD_ERROR_ATTR_GLOBAL            = __WRTD_SPECIFIC_ERROR_BASE + 0x04,
        /** The device has no more resources to allocate. */
        WRTD_ERROR_OUT_OF_RESOURCES       = __WRTD_SPECIFIC_ERROR_BASE + 0x05,
        /** The rule already exists. */
        WRTD_ERROR_RULE_EXISTS            = __WRTD_SPECIFIC_ERROR_BASE + 0x06,
        /** The specified rule has not been defined. */
        WRTD_ERROR_RULE_DOES_NOT_EXIST    = __WRTD_SPECIFIC_ERROR_BASE + 0x07,

        /** Always last entry in this enum. */
        __WRTD_ERROR_MAX_NUMBER,
} wrtd_status;

/**
 * @enum wrtd_attr
 * White Rabbit Trigger Distribution attributes.
 *
 * Names and values inspired by IVI-3.2 and IVI-3.15.
 */
typedef enum wrtd_attr {
        /** Same as *IVI_INSTR_SPECIFIC_ATTR_BASE*. */
        __WRTD_ATTR_BASE = 1150000,

        /** `RO` `bool` `global` True if the Event Log is empty. */
        WRTD_ATTR_EVENT_LOG_EMPTY           = __WRTD_ATTR_BASE + 0x00,
        /** `RW` `bool` `global` Enable/disable the Event Log. */
        WRTD_ATTR_EVENT_LOG_ENABLED         = __WRTD_ATTR_BASE + 0x01,
        /** `RO` `bool` `global` True if the device is synchronized to White Rabbit time. */
        WRTD_ATTR_IS_TIME_SYNCHRONIZED      = __WRTD_ATTR_BASE + 0x02,
        /** `RO` `tstamp` `global` Current system time. */
        WRTD_ATTR_SYS_TIME                  = __WRTD_ATTR_BASE + 0x03,

        /** `RO` `int32` `global` Number of defined Alarms. */
        WRTD_ATTR_ALARM_COUNT               = __WRTD_ATTR_BASE + 0x10,
        /** `RW` `bool` `alarm` Enable/disable an Alarm. */
        WRTD_ATTR_ALARM_ENABLED             = __WRTD_ATTR_BASE + 0x11,
        /** `RW` `tstamp` `alarm` Specifies at what time to send an Alarm event. This is
            typically set to a moment earlier than #WRTD_ATTR_ALARM_TIME, to allow for
            the event to reach its destination(s) before the #WRTD_ATTR_ALARM_TIME moment. */
        WRTD_ATTR_ALARM_SETUP_TIME          = __WRTD_ATTR_BASE + 0x12,
        /** `RW` `tstamp` `alarm` Specifies at what time to trigger an Alarm. */
        WRTD_ATTR_ALARM_TIME                = __WRTD_ATTR_BASE + 0x13,
        /** `RW` `tstamp` `alarm` Specifies the Alarm period. 0 means no repetitions. */
        WRTD_ATTR_ALARM_PERIOD              = __WRTD_ATTR_BASE + 0x14,
        /** `RW` `int32` `alarm` Specifies the number of times an Alarm will occur at the
            period specified by #WRTD_ATTR_ALARM_PERIOD, before becoming automatically disabled.
            0 means infinite repetitions. 1 means that the alarm will occur only once.
            When read, it returns the remaining repetitions. */
        WRTD_ATTR_ALARM_REPEAT_COUNT        = __WRTD_ATTR_BASE + 0x15,

        /** `RO` `int32` `global` Number of defined Rules. */
        WRTD_ATTR_RULE_COUNT                = __WRTD_ATTR_BASE + 0x20,
        /** `RW` `bool` `rule` Enable/disable a Rule. */
        WRTD_ATTR_RULE_ENABLED              = __WRTD_ATTR_BASE + 0x21,
        /** `RW` `int32` `rule` Specifies the number of times a Rule will fire before becoming
            automatically disabled. 0 means infinite. 1 means that the Rule will fire only once.
            When read, it returns the remaining repetitions. */
        WRTD_ATTR_RULE_REPEAT_COUNT         = __WRTD_ATTR_BASE + 0x22,
        /** `RW` `string` `rule` Get/set Rule source. Rule sources can be:
            - Local source Event IDs (in the form of **LC-I<x>**)
            - Alarm IDs (any ID with an **alarm** prefix)
            - Any other string which will be interpreted as a network message Event ID. */
        WRTD_ATTR_RULE_SOURCE               = __WRTD_ATTR_BASE + 0x23,
        /** `RW` `string` `rule` Get/set Rule destinations. Rule destinations can be:
            - Local destination Event IDs (in the form of **LC-O<x>**)
            - Any other string which will be interpreted as a network message Event ID. */
        WRTD_ATTR_RULE_DESTINATION          = __WRTD_ATTR_BASE + 0x24,
        /** `RW` `bool` `rule` If true, events that arrive late (with a timestamp in
            the past) will still be executed; otherwise they are logged and discarded. */
        WRTD_ATTR_RULE_SEND_LATE            = __WRTD_ATTR_BASE + 0x25,
        /** `RW` `tstamp` `rule` Specifies the delay to add to the timestamp of the source
            Event before forwarding it to its destination. The provided value must be less
            than 1 second. */
        WRTD_ATTR_RULE_DELAY                = __WRTD_ATTR_BASE + 0x26,
        /** `RW` `tstamp` `rule` Specifies the "dead time" between two source Events. Any
            new event that arrives during this time, will be logged and discarded. The provided
            value must be less than 1 second. */
        WRTD_ATTR_RULE_HOLDOFF              = __WRTD_ATTR_BASE + 0x27,
        /** `RW` `tstamp` `rule` Re-align the source Event timestamp to a multiple
            of this value. As an example, if the Event timestamp is **00:00:12.000.123.456**
            and RESYNC_PERIOD is **00:00:00.000.005.000** (5us), the Event timestamp will
            be re-aligned to **00:00:12.000.125.000**. This calculation is done after applying
            the delay defined by #WRTD_ATTR_RULE_DELAY. The provided value must be less than
            1 second. */
        WRTD_ATTR_RULE_RESYNC_PERIOD        = __WRTD_ATTR_BASE + 0x28,
        /** `RW` `int32` `rule` Further re-align the source Event timestamp to a multiple
            of #WRTD_ATTR_RULE_RESYNC_PERIOD. As an example, if RESYNC_PERIOD
            is 5us and the provided value is 10, 50us will be added to the Event timestamp,
            after applying #WRTD_ATTR_RULE_DELAY and re-aligning it to the RESYNC_PERIOD. */
        WRTD_ATTR_RULE_RESYNC_FACTOR        = __WRTD_ATTR_BASE + 0x29,

        /** `RO` `int32` `rule` Number of received Events for a Rule. */
        WRTD_ATTR_STAT_RULE_RX_EVENTS       = __WRTD_ATTR_BASE + 0x30,
        /** `RO` `tstamp` `rule` Timestamp of most recently received Event for a Rule. */
        WRTD_ATTR_STAT_RULE_RX_LAST         = __WRTD_ATTR_BASE + 0x31,
        /** `RO` `int32` `rule` Number of transmitted Events for a Rule. */
        WRTD_ATTR_STAT_RULE_TX_EVENTS       = __WRTD_ATTR_BASE + 0x32,
        /** `RO` `tstamp` `rule` Timestamp of most recently transmitted Event for a Rule. */
        WRTD_ATTR_STAT_RULE_TX_LAST         = __WRTD_ATTR_BASE + 0x33,
        /** `RO` `int32` `rule` Number of received Events that arrived too late.
            See also #WRTD_ATTR_RULE_SEND_LATE. */
        WRTD_ATTR_STAT_RULE_MISSED_LATE     = __WRTD_ATTR_BASE + 0x34,
        /** `RO` `int32` `rule` Number of received Events that arrived during hold-off.
            See also #WRTD_ATTR_RULE_HOLDOFF. */
        WRTD_ATTR_STAT_RULE_MISSED_HOLDOFF  = __WRTD_ATTR_BASE + 0x35,
        /** `RO` `int32` `rule` Number of received Events that were discarded because the
            device was not synchronized to White Rabbit.
            See also #WRTD_ATTR_IS_TIME_SYNCHRONIZED. */
        WRTD_ATTR_STAT_RULE_MISSED_NOSYNC   = __WRTD_ATTR_BASE + 0x36,
        /** `RO` `int32` `rule` Number of received Events that were discarded because of
            internal buffer overflows. This may happen if the Event rate is too high. */
        WRTD_ATTR_STAT_RULE_MISSED_OVERFLOW = __WRTD_ATTR_BASE + 0x37,
        /** `RO` `tstamp` `rule` Timestamp of most recently missed Event. */
        WRTD_ATTR_STAT_RULE_MISSED_LAST     = __WRTD_ATTR_BASE + 0x38,
        /** `RO` `tstamp` `rule` Minimum latency between the Event timestamp and its
            reception by WRTD. */
        WRTD_ATTR_STAT_RULE_RX_LATENCY_MIN  = __WRTD_ATTR_BASE + 0x39,
        /** `RO` `tstamp` `rule` Maximum latency between the Event timestamp and its
            reception by WRTD. */
        WRTD_ATTR_STAT_RULE_RX_LATENCY_MAX  = __WRTD_ATTR_BASE + 0x3A,
        /** `RO` `tstamp` `rule` Average latency between the Event timestamp and its
            reception by WRTD. */
        WRTD_ATTR_STAT_RULE_RX_LATENCY_AVG  = __WRTD_ATTR_BASE + 0x3B,

        /** `RO` `int32` `global` Number of separate WRTD firmware applications
            running on the device. */
        WRTD_ATTR_FW_COUNT                  = __WRTD_ATTR_BASE + 0x80,
        /** `RO` `int32` `application` Major part of the firmware's version. */
        WRTD_ATTR_FW_MAJOR_VERSION          = __WRTD_ATTR_BASE + 0x81,
        /** `RO` `int32` `application` Major part of the firmware's version. */
        WRTD_ATTR_FW_MINOR_VERSION          = __WRTD_ATTR_BASE + 0x82,
        /** `RO` `int32` `application` Major part of WRTD version supported by the firmware.
            It must be equal to the major version of the WRTD library in use. */
        WRTD_ATTR_FW_MAJOR_VERSION_REQUIRED = __WRTD_ATTR_BASE + 0x83,
        /** `RO` `int32` `application` Minor part of WRTD version supported by the firmware.
            It must be less than or equal to the minor version of the WRTD library in use. */
        WRTD_ATTR_FW_MINOR_VERSION_REQUIRED = __WRTD_ATTR_BASE + 0x84,
        /** `RO` `int32` `application` Max number of rules allowed by this firmware. */
        WRTD_ATTR_FW_MAX_RULES              = __WRTD_ATTR_BASE + 0x85,
        /** `RO` `int32` `application` Max number of alarms allowed by this firmware. */
        WRTD_ATTR_FW_MAX_ALARMS             = __WRTD_ATTR_BASE + 0x86,
        /** `RO` `int32` `application` Event TX/RX capabilities of this firmware. This is
            a bit-field with the following meaning per bit:
            - **bit 0:** if set, the firmware can receive network events.
            - **bit 1:** if set, the firmware can create network events.
            - **bit 2:** if set, the firmware can receive local events.
            - **bit 3:** if set, the firmware can create local events. */
        WRTD_ATTR_FW_CAPABILITIES           = __WRTD_ATTR_BASE + 0x88,
        /** `RO` `int32` `application` Number of available local input channels. */
        WRTD_ATTR_FW_LOCAL_INPUTS           = __WRTD_ATTR_BASE + 0x8A,
        /** `RO` `int32` `application` Number of available local output channels. */
        WRTD_ATTR_FW_LOCAL_OUTPUTS          = __WRTD_ATTR_BASE + 0x8B,

        /** Always last entry in this enum */
        __WRTD_ATTR_MAX_NUMBER,
}wrtd_attr;

/** A repeated capability identifier for global attributes. */
#define WRTD_GLOBAL_REP_CAP_ID "WGRCI"

/** Size (in characters, including null termination) of an event log enty. */
#define WRTD_LOG_ENTRY_SIZE 120

/** Size (in characters, including null termination) of minimum error message bugger. */
#define WRTD_ERR_MSG_BUF_SIZE 256

/* ------------------------------------------------------------------- */
/* Function prototypes for the official WRTD API. Documented in wrtd.c */
/* ------------------------------------------------------------------- */

/* Initialisation */

wrtd_status wrtd_get_node_count(uint32_t *count);

wrtd_status wrtd_get_node_id(uint32_t index, uint32_t *node_id);

wrtd_status wrtd_init(uint32_t node_id,
                      bool reset,
                      const char *options_str,
                      wrtd_dev **wrtd);

wrtd_status wrtd_close(wrtd_dev *wrtd);

wrtd_status wrtd_reset(wrtd_dev *wrtd);

/* Errors */

wrtd_status wrtd_get_error(wrtd_dev *wrtd,
                           wrtd_status *error_code,
                           int32_t error_description_buffer_size,
                           char *error_description);

wrtd_status wrtd_error_message(wrtd_dev *wrtd,
                               wrtd_status err_code,
                               char *err_message);

/* Attributes */

wrtd_status wrtd_set_attr_bool(wrtd_dev *wrtd,
                               const char *rep_cap_id,
                               wrtd_attr id,
                               bool value);

wrtd_status wrtd_get_attr_bool(wrtd_dev *wrtd,
                               const char *rep_cap_id,
                               wrtd_attr id,
                               bool *value);

wrtd_status wrtd_set_attr_int32(wrtd_dev *wrtd,
                                const char *rep_cap_id,
                                wrtd_attr id,
                                int32_t value);

wrtd_status wrtd_get_attr_int32(wrtd_dev *wrtd,
                                const char *rep_cap_id,
                                wrtd_attr id,
                                int32_t *value);

wrtd_status wrtd_set_attr_string(wrtd_dev *wrtd,
                                 const char *rep_cap_id,
                                 wrtd_attr id,
                                 const char *value);

wrtd_status wrtd_get_attr_string(wrtd_dev *wrtd,
                                 const char *rep_cap_id,
                                 wrtd_attr id,
                                 int32_t value_buf_size,
                                 char *value);

wrtd_status wrtd_set_attr_tstamp(wrtd_dev *wrtd,
                                 const char *rep_cap_id,
                                 wrtd_attr id,
                                 wrtd_tstamp *value);

wrtd_status wrtd_get_attr_tstamp(wrtd_dev *wrtd,
                                 const char *rep_cap_id,
                                 wrtd_attr id,
                                 wrtd_tstamp *value);

/* Event Log */

wrtd_status wrtd_clear_event_log_entries(wrtd_dev *wrtd);

wrtd_status wrtd_get_next_event_log_entry(wrtd_dev *wrtd,
                                          int32_t log_entry_buffer_size,
                                          char *log_entry);

/* Alarms */

wrtd_status wrtd_add_alarm(wrtd_dev *wrtd,
                           const char *rep_cap_id);

wrtd_status wrtd_disable_all_alarms(wrtd_dev *wrtd);

wrtd_status wrtd_remove_alarm(wrtd_dev *wrtd,
                              const char *rep_cap_id);

wrtd_status wrtd_remove_all_alarms(wrtd_dev *wrtd);

wrtd_status wrtd_get_alarm_name(wrtd_dev *wrtd,
                                int32_t index,
                                int32_t name_buffer_size,
                                char *name);

/* Rules */

wrtd_status wrtd_add_rule(wrtd_dev *wrtd,
                          const char *rep_cap_id);

wrtd_status wrtd_disable_all_rules(wrtd_dev *wrtd);

wrtd_status wrtd_remove_rule(wrtd_dev *wrtd,
                             const char *rep_cap_id);

wrtd_status wrtd_remove_all_rules(wrtd_dev *wrtd);

wrtd_status wrtd_get_rule_name(wrtd_dev *wrtd,
                               int32_t index,
                               int32_t name_buffer_size,
                               char *name);

wrtd_status wrtd_reset_rule_stats(wrtd_dev *wrtd,
                                  const char *rep_cap_id);

/* Firmware applications */

wrtd_status wrtd_get_fw_name(wrtd_dev *wrtd,
                             int32_t index,
                             int32_t name_buffer_size,
                             char *name);

#ifdef __cplusplus
};
#endif

#endif
