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

#pragma once

#include "metrics.h"
#include "vec.h"

#include <gr_infra.h>

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

struct iface_mac {
	uint8_t refcnt;
	bool hardware;
	struct rte_ether_addr mac;
};

struct __rte_cache_aligned iface {
	BASE(__gr_iface_base);

	vec struct iface **subinterfaces;
	char *name;
	char *description;
	int cp_id; // Control plane (Linux) port ID
	int cp_fd; // control plane fd
	struct event *cp_ev; // libevent to poll cp_fd
	unsigned promisc;
	bool user_promisc;
	vec struct iface_mac *macs;
	alignas(alignof(void *)) uint8_t info[/* size depends on type */];
};

#define GR_IFACE_INFO(type_id, type_name, fields)                                                  \
	struct type_name fields __attribute__((__may_alias__, aligned(alignof(void *))));          \
	static inline struct type_name *type_name(const struct iface *iface) {                     \
		assert(iface->type == type_id);                                                    \
		return (struct type_name *)iface->info;                                            \
	}

#define IFACE_SET_ALL UINT64_C(0xffffffffffffffff)

struct iface_type {
	uint16_t id;
	size_t pub_size;
	size_t priv_size;
	int (*init)(struct iface *, const void *api_info);
	int (*reconfig)(
		struct iface *,
		uint64_t set_attrs,
		const struct gr_iface *,
		const void *api_info
	);
	int (*fini)(struct iface *);
	int (*attach_domain)(struct iface *domain, struct iface *iface);
	int (*detach_domain)(struct iface *domain, struct iface *iface);
	int (*get_eth_addr)(const struct iface *, struct rte_ether_addr *);
	int (*set_eth_addr)(struct iface *, const struct rte_ether_addr *);
	int (*add_eth_addr)(struct iface *, struct iface_mac *);
	int (*del_eth_addr)(struct iface *, struct iface_mac *);
	int (*set_up_down)(struct iface *, bool up);
	int (*set_mtu)(struct iface *, uint16_t mtu);
	int (*set_promisc)(struct iface *, bool enabled);
	void (*to_api)(void *api_info, const struct iface *);
	void (*metrics_collect)(struct metrics_ctx *, const struct iface *);
};

void iface_type_register(const struct iface_type *);
const struct iface_type *iface_type_get(gr_iface_type_t type_id);
struct iface *iface_create(const struct gr_iface *conf, const void *api_info);
int iface_reconfig(
	uint16_t ifid,
	uint64_t set_attrs,
	const struct gr_iface *conf,
	const void *api_info
);
int iface_destroy(struct iface *);
struct iface *iface_from_id(uint16_t ifid);
void iface_add_subinterface(struct iface *parent, struct iface *sub);
void iface_del_subinterface(struct iface *parent, struct iface *sub);
int iface_get_eth_addr(const struct iface *, struct rte_ether_addr *);
int iface_set_eth_addr(struct iface *, const struct rte_ether_addr *);
int iface_add_eth_addr(struct iface *, const struct rte_ether_addr *);
int iface_del_eth_addr(struct iface *, const struct rte_ether_addr *);
int iface_set_mtu(struct iface *, uint16_t mtu);
int iface_set_up_down(struct iface *, bool up);
int iface_set_promisc(struct iface *, bool enabled);
uint16_t ifaces_count(gr_iface_type_t type_id);
struct iface *iface_next(gr_iface_type_t type_id, const struct iface *prev);

uint16_t vrf_default_get_or_create(void);

// Get the VRF interface.
// vrf_id is the VRF interface ID.
struct iface *get_vrf_iface(uint16_t vrf_id);

// Register a reserved interface name.
// If prefix is true, any name starting with the string is reserved.
// If prefix is false, only the exact name is reserved.
void iface_name_reserve(const char *name, bool prefix);

struct __rte_cache_aligned iface_stats {
	uint64_t rx_packets;
	uint64_t rx_bytes;
	uint64_t tx_packets;
	uint64_t tx_bytes;
	uint64_t cp_rx_packets;
	uint64_t cp_rx_bytes;
	uint64_t cp_tx_packets;
	uint64_t cp_tx_bytes;
};

extern struct iface_stats iface_stats[GR_MAX_IFACES][RTE_MAX_LCORE];
static inline struct iface_stats *iface_get_stats(uint16_t lcore_id, uint16_t ifid) {
	return &iface_stats[ifid][lcore_id];
}
