// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Robin Jarry

#pragma once

#include "control_queue.h"
#include "graph.h"
#include "mbuf.h"

#include <gr_infra.h>
#include <gr_net_types.h>

#include <rte_build_config.h>
#include <rte_graph.h>
#include <rte_spinlock.h>

#include <stddef.h>
#include <stdint.h>

#define RX_NODE_BASE "port_rx"
#define RX_NODE_FMT RX_NODE_BASE "-p%uq%u"
#define TX_NODE_BASE "port_tx"
#define TX_NODE_FMT TX_NODE_BASE "-p%uq%u"

struct port_queue {
	uint16_t port_id;
	uint16_t queue_id;
};

GR_NODE_CTX_TYPE(rx_node_ctx, {
	const struct iface *iface;
	struct port_queue rxq;
	uint16_t burst_size;
});

GR_NODE_CTX_TYPE(tx_node_ctx, {
	struct port_queue txq;
	rte_spinlock_t *lock;
});

struct port_output_edges {
	rte_edge_t edges[RTE_MAX_ETHPORTS];
};

GR_MBUF_PRIV_DATA_TYPE(iface_mbuf_data, {
	uint16_t vlan_id;
	struct l3_addr vtep;
});

int rxtx_trace_format(char *buf, size_t len, const void *data, size_t /*data_len*/);

void iface_input_mode_register(gr_iface_mode_t, const char *next_node);

void iface_output_type_register(gr_iface_type_t, const char *next_node);

void iface_cp_tx(void *obj, uintptr_t priv, const struct control_queue_drain *);
int iface_output_send(struct rte_mbuf *);

void rx_burst_histogram_get(uint16_t port_id, uint64_t *histogram, unsigned slots);
void rx_burst_histogram_reset(void);

typedef enum : uint16_t {
	RXTX_F_VLAN_OFFLOAD = GR_BIT16(0),
	RXTX_F_TXQ_SHARED = GR_BIT16(1),
	RXTX_F_BOND = GR_BIT16(2),
	RXTX_F_VIRTIO = GR_BIT16(3),
} rxtx_flags_t;

struct rxtx_trace_data {
	rxtx_flags_t func_flags;
	uint64_t mbuf_ol_flags;
};

uint16_t rx_virtio_process(struct rte_graph *, struct rte_node *, void **, uint16_t);
uint16_t rx_offload_process(struct rte_graph *, struct rte_node *, void **, uint16_t);
uint16_t rx_process(struct rte_graph *, struct rte_node *, void **, uint16_t);
uint16_t rx_bond_virtio_process(struct rte_graph *, struct rte_node *, void **, uint16_t);
uint16_t rx_bond_offload_process(struct rte_graph *, struct rte_node *, void **, uint16_t);
uint16_t rx_bond_process(struct rte_graph *, struct rte_node *, void **, uint16_t);

uint16_t tx_process(struct rte_graph *, struct rte_node *, void **, uint16_t);
uint16_t tx_shared_process(struct rte_graph *, struct rte_node *, void **, uint16_t);

#define IFACE_STATS_VARS(dir, acc)                                                                 \
	struct iface_stats *dir##_##acc##_stats;                                                   \
	uint16_t dir##_##acc##_last_iface_id = GR_IFACE_ID_UNDEF;                                  \
	uint16_t dir##_##acc##_packets = 0;                                                        \
	uint64_t dir##_##acc##_bytes = 0;

#define IFACE_STATS_INC(dir, acc, mbuf, iface)                                                     \
	do {                                                                                       \
		if (iface->id != dir##_##acc##_last_iface_id) {                                    \
			if (dir##_##acc##_packets != 0) {                                          \
				dir##_##acc##_stats = iface_get_stats(                             \
					rte_lcore_id(), dir##_##acc##_last_iface_id                \
				);                                                                 \
				dir##_##acc##_stats->dir##_packets += dir##_##acc##_packets;       \
				dir##_##acc##_stats->dir##_bytes += dir##_##acc##_bytes;           \
				dir##_##acc##_packets = 0;                                         \
				dir##_##acc##_bytes = 0;                                           \
			}                                                                          \
			dir##_##acc##_last_iface_id = iface->id;                                   \
		}                                                                                  \
		dir##_##acc##_packets += 1;                                                        \
		dir##_##acc##_bytes += rte_pktmbuf_pkt_len(mbuf);                                  \
	} while (0)

#define IFACE_STATS_FLUSH(dir, acc)                                                                \
	do {                                                                                       \
		if (dir##_##acc##_packets != 0) {                                                  \
			dir##_##acc##_stats = iface_get_stats(                                     \
				rte_lcore_id(), dir##_##acc##_last_iface_id                        \
			);                                                                         \
			dir##_##acc##_stats->dir##_packets += dir##_##acc##_packets;               \
			dir##_##acc##_stats->dir##_bytes += dir##_##acc##_bytes;                   \
		}                                                                                  \
	} while (0)
