/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2024 Brett Sheffield <bacs@librecast.net> */

/* librecast/router.h - librecast router API */

#ifndef _LIBRECAST_ROUTER_H
#define _LIBRECAST_ROUTER_H 1

#include <librecast/types.h>
#include <librecast/net.h>
#include <sys/uio.h>

/* Router API declarations */

#define ROUTER_THREADS 2 /* minimum router threads */

typedef enum {
	LC_ROUTER_FLAG_FIXED   = 0x01, /* ports not resizable */
	LC_ROUTER_FLAG_VISIT   = 0x02, /* visited, used in searches */
	LC_ROUTER_FLAG_RUNNING = 0x04, /* router is running */
} lc_router_flags_t;

typedef enum {
	LC_PORT_UP    = 1, /* port enabled */
	LC_PORT_STP   = 2, /* STP running on this port */
	LC_PORT_FWD   = 4, /* forwarding enabled */
	LC_PORT_FLOOD = 8, /* port flooding - bypass OIL filter checks */
} lc_portstatus_t;

typedef enum {
	LC_TOPO_NONE,	   /* do not connect anything */
	LC_TOPO_CHAIN,	   /* routers are daisy-chained */
	LC_TOPO_RING,      /* ring topology */
	LC_TOPO_TREE,	   /* loop-free spanning tree */
	LC_TOPO_STAR,	   /* root router is connected to all others */
	LC_TOPO_MESH,	   /* full mesh (all routers directly connected) */
	LC_TOPO_RANDOM,    /* surprise me */
	LC_TOPO_BUTTERFLY, /* a fixed topology of 6 routers */
	LC_TOPO_HONEYCOMB, /* 3 routers inside n rings of hexagons */
} lc_topology_t;

lc_router_t *lc_router_new(lc_ctx_t *ctx, unsigned int ports, int flags);

/* return number of routers in ctx */
int lc_router_count(lc_ctx_t *ctx);

/* create n routers, connecting them with topology t */
int lc_router_net(lc_ctx_t *ctx, lc_router_t *r[], int n, lc_topology_t t,
		unsigned int ports, int flags);

int lc_router_connect(lc_router_t *r1, lc_router_t *r2);

void lc_router_free(lc_router_t *router);
int lc_router_start(lc_router_t *router);
int lc_router_stop(lc_router_t *router);
int lc_router_socket_add(lc_router_t *router, lc_socket_t *sock);
int lc_router_socket_del(lc_router_t *router, lc_socket_t *sock);
int lc_router_socket_get(lc_router_t *router, unsigned int port);
int lc_router_socket_set(lc_router_t *router, unsigned int port, int status);

/* callbacks */
int lc_router_onready(lc_router_t *r, void *(*cb)(void *), void *arg);

/* copy chan and bind to port on router */
int lc_router_channel_bind(lc_router_t *router, unsigned int port, lc_channel_t *chan);
int lc_router_channel_unbind(lc_router_t *router, unsigned int port, lc_channel_t *chan);

/* set/unset portstatus for router port. If port == -1 => all ports */
int lc_router_port_set(lc_router_t *router, unsigned int port, int status);
int lc_router_port_unset(lc_router_t *router, unsigned int port, int status);

/* up/down router port. If port == -1 => all ports */
int lc_router_port_down(lc_router_t *router, unsigned int port);
int lc_router_port_up(lc_router_t *router, unsigned int port);

/* return -1 if r1 has path to r2, 0 if not. r[] is the array of routers, n is
 * the number of routers */
int lc_router_has_path(lc_router_t *r[], int n, lc_router_t *r1, lc_router_t *r2);

/* return -1 if routing loop exists, 0 if not */
int lc_router_net_hasloop(lc_router_t *r[], int n);

/* lock/unlock router mutex */
int lc_router_lock(lc_router_t *r);
int lc_router_trylock(lc_router_t *r);
int lc_router_unlock(lc_router_t *r);

/* lock a pair of routers */
int lc_router_lockpair(lc_router_t *r1, lc_router_t *r2);

/* acquire read-lock on object. Increments reference count */
lc_router_t *lc_router_acquire(lc_router_t *r);
void lc_router_release(lc_router_t *r);

#ifdef _LIBRECAST_PRIVATE
/* Private declarations - for librecast internal use only */

#define MAX_MTU 1500

struct pkt_s {
	char data[MAX_MTU];
	size_t len;
};

struct lc_router_s {
	lc_router_t *next;
	lc_ctx_t *ctx;
	struct pkt_s *buf;
	unsigned int bufs;
	unsigned int ridx;
	unsigned int widx;
	pthread_t recver; /* router recv thread */
	pthread_mutex_t mtx;
	/* callbacks */
	void *(*onready)(void *);
	void *onready_arg;
#if HAVE_EPOLL_CREATE1
	int epollfd;
#endif
	int readers; /* reference count (readers) */
	int deleted; /* marked for deletion */
	int flags;
	unsigned int ports;
	lc_socket_t **port;
};

#endif /* _LIBRECAST_PRIVATE */

#endif /* _LIBRECAST_ROUTER_H */
