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

#include <stddef.h>
#include <errno.h>
#include "libwrtd.h"
#include "libwrtd-private.h"
#include "mockturtle/libmockturtle.h"

enum wrtd_status wrtd_attr_get_fw_major_version(struct wrtd_dev *wrtd,
                                                const char *rep_cap_id,
                                                int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_fw(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = RT_VERSION_MAJ(wrtd->fw_version[idx].rt_version);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_fw_minor_version(struct wrtd_dev *wrtd,
                                                const char *rep_cap_id,
                                                int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_fw(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = RT_VERSION_MIN(wrtd->fw_version[idx].rt_version);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_fw_major_version_required(struct wrtd_dev *wrtd,
                                                         const char *rep_cap_id,
                                                         int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_fill_roots(wrtd, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_find_fw(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = wrtd->roots[idx].ver_major;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_fw_minor_version_required(struct wrtd_dev *wrtd,
                                                         const char *rep_cap_id,
                                                         int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_fill_roots(wrtd, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_find_fw(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = wrtd->roots[idx].ver_minor;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_fw_max_rules(struct wrtd_dev *wrtd,
                                            const char *rep_cap_id,
                                            int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_fill_roots(wrtd, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_find_fw(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = wrtd->roots[idx].nbr_rules;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_fw_max_alarms(struct wrtd_dev *wrtd,
                                             const char *rep_cap_id,
                                             int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_fill_roots(wrtd, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_find_fw(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = wrtd->roots[idx].nbr_alarms;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_fw_capabilities(struct wrtd_dev *wrtd,
                                               const char *rep_cap_id,
                                               int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_fill_roots(wrtd, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_find_fw(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = wrtd->roots[idx].capabilities;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_fw_local_inputs(struct wrtd_dev *wrtd,
                                               const char *rep_cap_id,
                                               int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;
        int32_t count;
        int i;

        status = wrtd_fill_roots(wrtd, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_find_fw(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        for (count = 0, i = 0; i < wrtd->roots[idx].nbr_devices; i++)
                if (wrtd->roots[idx].devices_chs_dir[i] == WRTD_CH_DIR_IN)
                        count += wrtd->roots[idx].devices_nbr_chs[i];

        *value = count;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_fw_local_outputs(struct wrtd_dev *wrtd,
                                                const char *rep_cap_id,
                                                int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;
        int32_t count;
        int i;

        status = wrtd_fill_roots(wrtd, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_find_fw(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        for (count = 0, i = 0; i < wrtd->roots[idx].nbr_devices; i++)
                if (wrtd->roots[idx].devices_chs_dir[i] == WRTD_CH_DIR_OUT)
                        count += wrtd->roots[idx].devices_nbr_chs[i];

        *value = count;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_alarm_count(struct wrtd_dev *wrtd,
                                           int32_t *value)
{
        enum wrtd_status status;
        unsigned i;
        int32_t res;

        status = wrtd_fill_alarms(wrtd, __func__);
        WRTD_RETURN_IF_ERROR(status);

        res = 0;
        for (i = 0; i < wrtd->nbr_alarms; i++)
                if (!wrtd_id_null(&wrtd->alarms[i].alarm.event.id))
                        res++;
        *value = res;
        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_alarm_period(struct wrtd_dev *wrtd,
                                            const char *rep_cap_id,
                                            const struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        if (value->seconds != 0)
                return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
                                         "Invalid value (%d) for function %s, "
                                         "parameter value.seconds",
                                         value->seconds, __func__);

        status = wrtd_find_alarm(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_alarm_check_disabled(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        wrtd->alarms[idx].alarm.period_ns = value->ns;

        status = wrtd_write_alarm(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_alarm_period(struct wrtd_dev *wrtd,
                                            const char *rep_cap_id,
                                            struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_alarm(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        memset(value, 0, sizeof(struct wrtd_tstamp));
        wrtd_ts_add_ns(value, wrtd->alarms[idx].alarm.period_ns);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_alarm_setup_time(struct wrtd_dev *wrtd,
                                                const char *rep_cap_id,
                                                const struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_alarm(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_alarm_check_disabled(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        wrtd->alarms[idx].alarm.setup_time = *value;

        status = wrtd_write_alarm(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_alarm_setup_time(struct wrtd_dev *wrtd,
                                                const char *rep_cap_id,
                                                struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_alarm(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = wrtd->alarms[idx].alarm.setup_time;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_alarm_time(struct wrtd_dev *wrtd,
                                          const char *rep_cap_id,
                                          const struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_alarm(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_alarm_check_disabled(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        wrtd->alarms[idx].alarm.event.ts = *value;

        status = wrtd_write_alarm(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_alarm_time(struct wrtd_dev *wrtd,
                                          const char *rep_cap_id,
                                          struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_alarm(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = wrtd->alarms[idx].alarm.event.ts;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_alarm_enable(struct wrtd_dev *wrtd,
                                            const char *rep_cap_id,
                                            bool value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_alarm(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        wrtd->alarms[idx].alarm.enabled = value;

        status = wrtd_write_alarm(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_alarm_enable(struct wrtd_dev *wrtd,
                                            const char *rep_cap_id,
                                            bool *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_alarm(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = wrtd->alarms[idx].alarm.enabled != 0;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_alarm_repeat_count(struct wrtd_dev *wrtd,
                                                  const char *rep_cap_id,
                                                  int32_t value)
{
        enum wrtd_status status;
        unsigned int idx;

        if (value < 0)
                return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
                                         "Invalid value (%d) for function %s, "
                                         "parameter value", value, __func__);

        status = wrtd_find_alarm(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_alarm_check_disabled(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        wrtd->alarms[idx].alarm.repeat_count = value;

        status = wrtd_write_alarm(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_alarm_repeat_count(struct wrtd_dev *wrtd,
                                                  const char *rep_cap_id,
                                                  int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_alarm(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = wrtd->alarms[idx].alarm.repeat_count;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_rule_repeat_count(struct wrtd_dev *wrtd,
                                                 const char *rep_cap_id,
                                                 int32_t value)
{
        enum wrtd_status status;
        unsigned int idx;

        if (value < 0)
                return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
                                         "Invalid value (%d) for function %s, "
                                         "parameter value", value, __func__);

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_rule_check_disabled(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        wrtd->rules[idx].conf.repeat_count = value;

        status = wrtd_write_rule_conf(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_rule_repeat_count(struct wrtd_dev *wrtd,
                                                 const char *rep_cap_id,
                                                 int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = wrtd->rules[idx].conf.repeat_count;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_log_enable(struct wrtd_dev *wrtd,
                                          bool value)
{
        enum wrtd_status status;
        unsigned int cpu;

        status = wrtd_fill_roots(wrtd, __func__);
        WRTD_RETURN_IF_ERROR(status);

        for (cpu = 0; cpu < wrtd->nbr_cpus; cpu++) {
                wrtd->roots[cpu].log_flags = value ? 0xff : 0;
                status = wrtd_write_root_flags(wrtd, cpu, __func__);
                WRTD_RETURN_IF_ERROR(status);
        }

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_log_enable(struct wrtd_dev *wrtd,
                                          bool *value)
{
        enum wrtd_status status;

        status = wrtd_fill_roots(wrtd, __func__);
        WRTD_RETURN_IF_ERROR(status);

        /* all cpus have the same flags, it's enough to read one */
        *value = wrtd->roots[0].log_flags != 0;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_log_empty(struct wrtd_dev *wrtd,
                                         bool *value)
{
        struct polltrtl p[WRTD_MAX_CPUS];
        int ret, i;

        for (i = 0; i < wrtd->nbr_cpus; ++i) {
                p[i].trtl = wrtd->trtl;
                p[i].idx_hmq = WRTD_HMQ;
                p[i].idx_cpu = i;
                p[i].events = POLLIN;
        }

        ret = trtl_msg_poll(p, wrtd->nbr_cpus, 0);
        if (ret < 0)
                return wrtd_return_error
                        (wrtd, WRTD_ERROR_INTERNAL,
                         "%s: %s", __func__, trtl_strerror(errno));

        *value = (ret == 0);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_time_sync(struct wrtd_dev *wrtd,
                                         bool *value)
{
        struct wrtd_config_msg msg;
        enum wrtd_status status;

        status = wrtd_msg_get_config(wrtd, 0, &msg, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = (msg.sync_flag != 0);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_rule_count(struct wrtd_dev *wrtd,
                                          int32_t *value)
{
        enum wrtd_status status;
        unsigned i;
        int32_t res;

        status = wrtd_fill_rules(wrtd, __func__);
        WRTD_RETURN_IF_ERROR(status);

        res = 0;
        for (i = 0; i < wrtd->nbr_rules; i++)
                if (!wrtd_id_null(&wrtd->rules[i].conf.id))
                        res++;
        *value = res;
        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_rule_source(struct wrtd_dev *wrtd,
                                           const char *rep_cap_id,
                                           const char *src)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_validate_id(wrtd, src, __func__);
        WRTD_RETURN_IF_ERROR(status);

        /* If the source is an alarm, check that it exists */
        if ((strncmp(src, "alarm", 5) == 0) ||
            (strncmp(src, "ALARM", 5) == 0)) {
                status = wrtd_find_alarm(wrtd, src, &idx, __func__);
                WRTD_RETURN_IF_ERROR(status);
        }

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_rule_check_disabled(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        wrtd_id_build(&wrtd->rules[idx].conf.source_id, src);

        status = wrtd_write_rule_conf(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_rule_source(struct wrtd_dev *wrtd,
                                           const char *rep_cap_id,
                                           int32_t buffer_size,
                                           char *dst)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_id_copy_buf(wrtd, dst, buffer_size,
                                  wrtd->rules[idx].conf.source_id.c,
                                  __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_rule_destination(struct wrtd_dev *wrtd,
                                                const char *rep_cap_id,
                                                const char *dst)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_validate_id(wrtd, dst, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_rule_check_disabled(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        wrtd_id_build(&wrtd->rules[idx].conf.dest_id, dst);

        status = wrtd_write_rule_conf(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_rule_destination(struct wrtd_dev *wrtd,
                                                const char *rep_cap_id,
                                                int32_t buffer_size,
                                                char *dst)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_id_copy_buf(wrtd, dst, buffer_size,
                                  wrtd->rules[idx].conf.dest_id.c,
                                  __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_rule_delay(struct wrtd_dev *wrtd,
                                          const char *rep_cap_id,
                                          const struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        if (value->seconds != 0)
                return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
                                         "Invalid value (%d) for function %s, "
                                         "parameter value.seconds",
                                         value->seconds, __func__);

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_rule_check_disabled(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        wrtd->rules[idx].conf.delay_ns = value->ns;

        status = wrtd_write_rule_conf(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_rule_delay(struct wrtd_dev *wrtd,
                                          const char *rep_cap_id,
                                          struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        memset(value, 0, sizeof(struct wrtd_tstamp));
        wrtd_ts_add_ns(value, wrtd->rules[idx].conf.delay_ns);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_rule_holdoff(struct wrtd_dev *wrtd,
                                            const char *rep_cap_id,
                                            const struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        if (value->seconds != 0)
                return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
                                         "Invalid value (%d) for function %s, "
                                         "parameter value.seconds",
                                         value->seconds, __func__);

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_rule_check_disabled(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        wrtd->rules[idx].conf.hold_off_ns = value->ns;

        status = wrtd_write_rule_conf(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_rule_holdoff(struct wrtd_dev *wrtd,
                                            const char *rep_cap_id,
                                            struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        memset(value, 0, sizeof(struct wrtd_tstamp));
        wrtd_ts_add_ns(value, wrtd->rules[idx].conf.hold_off_ns);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_rule_resync_period(struct wrtd_dev *wrtd,
                                                  const char *rep_cap_id,
                                                  const struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        if (value->seconds != 0)
                return wrtd_return_error(wrtd, WRTD_ERROR_INVALID_VALUE,
                                         "Invalid value (%d) for function %s, "
                                         "parameter value.seconds",
                                         value->seconds, __func__);

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_rule_check_disabled(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        wrtd->rules[idx].conf.resync_period_ns = value->ns;

        status = wrtd_write_rule_conf(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_rule_resync_period(struct wrtd_dev *wrtd,
                                                  const char *rep_cap_id,
                                                  struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        memset(value, 0, sizeof(struct wrtd_tstamp));
        wrtd_ts_add_ns(value, wrtd->rules[idx].conf.resync_period_ns);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_stat_rule_rx_last(struct wrtd_dev *wrtd,
                                                 const char *rep_cap_id,
                                                 struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return wrtd_read_rule_stat
          (wrtd, idx,
           offsetof(struct wrtd_rule_stats, rx_last),
           sizeof(struct wrtd_tstamp), value, __func__);
}

enum wrtd_status wrtd_attr_get_stat_rule_tx_last(struct wrtd_dev *wrtd,
                                                 const char *rep_cap_id,
                                                 struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return wrtd_read_rule_stat
          (wrtd, idx,
           offsetof(struct wrtd_rule_stats, tx_last),
           sizeof(struct wrtd_tstamp), value, __func__);
}

enum wrtd_status wrtd_attr_get_stat_rule_missed_last(struct wrtd_dev *wrtd,
                                                     const char *rep_cap_id,
                                                     struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return wrtd_read_rule_stat
          (wrtd, idx,
           offsetof(struct wrtd_rule_stats, miss_last),
           sizeof(struct wrtd_tstamp), value, __func__);
}

enum wrtd_status wrtd_attr_get_stat_rule_rx_latency_min(struct wrtd_dev *wrtd,
                                                        const char *rep_cap_id,
                                                        struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;
        uint32_t val;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_read_rule_stat
          (wrtd, idx,
           offsetof(struct wrtd_rule_stats, lat_min_ns), sizeof(int32_t),
           &val, __func__);
        WRTD_RETURN_IF_ERROR(status);

        memset(value, 0, sizeof(struct wrtd_tstamp));
        wrtd_ts_add_ns(value, val);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_stat_rule_rx_latency_max(struct wrtd_dev *wrtd,
                                                        const char *rep_cap_id,
                                                        struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;
        uint32_t val;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_read_rule_stat
          (wrtd, idx,
           offsetof(struct wrtd_rule_stats, lat_max_ns), sizeof(int32_t),
           &val, __func__);
        WRTD_RETURN_IF_ERROR(status);

        memset(value, 0, sizeof(struct wrtd_tstamp));
        wrtd_ts_add_ns(value, val);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_stat_rule_rx_latency_avg(struct wrtd_dev *wrtd,
                                                        const char *rep_cap_id,
                                                        struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        unsigned int idx;
        uint64_t latency;
        struct wrtd_rule_stats stats;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_read_rule_stat
          (wrtd, idx, 0, sizeof(stats), &stats, __func__);
        WRTD_RETURN_IF_ERROR(status);

        memset(value, 0, sizeof(struct wrtd_tstamp));

        if (stats.lat_nbr != 0) {
                latency   = stats.lat_hi_ns;
                latency <<= 32;
                latency  |= stats.lat_lo_ns;
                latency  /= stats.lat_nbr;
                wrtd_ts_add_ns(value, latency & 0xffffffff);
        }

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_rule_resync_factor(struct wrtd_dev *wrtd,
                                                  const char *rep_cap_id,
                                                  int32_t value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_rule_check_disabled(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        wrtd->rules[idx].conf.resync_factor = value;

        status = wrtd_write_rule_conf(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_rule_resync_factor(struct wrtd_dev *wrtd,
                                                  const char *rep_cap_id,
                                                  int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = wrtd->rules[idx].conf.resync_factor;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_stat_rule_rx_events(struct wrtd_dev *wrtd,
                                                   const char *rep_cap_id,
                                                   int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return wrtd_read_rule_stat
          (wrtd, idx,
           offsetof(struct wrtd_rule_stats, rx_events), sizeof(int32_t),
           value, __func__);
}

enum wrtd_status wrtd_attr_get_stat_rule_tx_events(struct wrtd_dev *wrtd,
                                                   const char *rep_cap_id,
                                                   int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return wrtd_read_rule_stat
          (wrtd, idx,
           offsetof(struct wrtd_rule_stats, tx_events), sizeof(int32_t),
           value, __func__);
}

enum wrtd_status wrtd_attr_get_stat_rule_missed_late(struct wrtd_dev *wrtd,
                                                     const char *rep_cap_id,
                                                     int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return wrtd_read_rule_stat
          (wrtd, idx,
           offsetof(struct wrtd_rule_stats, miss_late), sizeof(int32_t),
           value, __func__);
}

enum wrtd_status wrtd_attr_get_stat_rule_missed_holdoff(struct wrtd_dev *wrtd,
                                                        const char *rep_cap_id,
                                                        int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return wrtd_read_rule_stat
          (wrtd, idx,
           offsetof(struct wrtd_rule_stats, miss_holdoff), sizeof(int32_t),
           value, __func__);
}

enum wrtd_status wrtd_attr_get_stat_rule_missed_nosync(struct wrtd_dev *wrtd,
                                                       const char *rep_cap_id,
                                                       int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return wrtd_read_rule_stat
          (wrtd, idx,
           offsetof(struct wrtd_rule_stats, miss_nosync), sizeof(int32_t),
           value, __func__);
}

enum wrtd_status wrtd_attr_get_stat_rule_missed_overflow(struct wrtd_dev *wrtd,
                                                         const char *rep_cap_id,
                                                         int32_t *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        return wrtd_read_rule_stat
          (wrtd, idx,
           offsetof(struct wrtd_rule_stats, miss_overflow), sizeof(int32_t),
           value, __func__);
}

enum wrtd_status wrtd_attr_set_rule_enable(struct wrtd_dev *wrtd,
                                           const char *rep_cap_id,
                                           bool value)
{
        enum wrtd_status status;
        unsigned int idx;
        struct wrtd_lib_rule *rule;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        rule = &wrtd->rules[idx];

        status = wrtd_validate_id(wrtd, rule->conf.source_id.c, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_validate_id(wrtd, rule->conf.dest_id.c, __func__);
        WRTD_RETURN_IF_ERROR(status);

        if (rule->conf.enabled != value) {
                rule->conf.enabled = value;
                rule->modified = 1;

                status = wrtd_reconfigure(wrtd, __func__);
                WRTD_RETURN_IF_ERROR(status);
        }

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_rule_enable(struct wrtd_dev *wrtd,
                                           const char *rep_cap_id,
                                           bool *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = wrtd->rules[idx].conf.enabled != 0;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_set_rule_send_late(struct wrtd_dev *wrtd,
                                              const char *rep_cap_id,
                                              bool value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_rule_check_disabled(wrtd, idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        if (wrtd->rules[idx].conf.send_late != value) {
                wrtd->rules[idx].conf.send_late = value;

                status = wrtd_write_rule_conf(wrtd, idx, __func__);
                WRTD_RETURN_IF_ERROR(status);
        }

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_rule_send_late(struct wrtd_dev *wrtd,
                                              const char *rep_cap_id,
                                              bool *value)
{
        enum wrtd_status status;
        unsigned int idx;

        status = wrtd_find_rule(wrtd, rep_cap_id, &idx, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = wrtd->rules[idx].conf.send_late != 0;

        return WRTD_SUCCESS;
}

enum wrtd_status wrtd_attr_get_sys_time(struct wrtd_dev *wrtd,
                                        struct wrtd_tstamp *value)
{
        enum wrtd_status status;
        struct wrtd_config_msg msg;

        if (wrtd == NULL)
                return WRTD_ERROR_NOT_INITIALIZED;

        /* Always use CPU #0 (it does exist!).  */
        status = wrtd_msg_get_config(wrtd, 0, &msg, __func__);
        WRTD_RETURN_IF_ERROR(status);

        *value = msg.now;

        return WRTD_SUCCESS;
}

/**
 *@} End group Time
 */

enum wrtd_status wrtd_attr_global(struct wrtd_dev *wrtd,
                                  const char *rep_cap_id)
{
        if (strcmp(rep_cap_id, WRTD_GLOBAL_REP_CAP_ID))
                return wrtd_return_error(wrtd, WRTD_ERROR_ATTR_GLOBAL,
                                         "Global attributes require the "
                                         "global repeated capability ID");
        return WRTD_SUCCESS;
}
