/* SPDX-License-Identifier: GPL-2.0 */

#ifndef __PARAMS_H
#define __PARAMS_H

#include <getopt.h>
#include <stdbool.h>
#include <stdlib.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/if_ether.h>
#include <bpf/libbpf.h>

enum option_type {
                  OPT_NONE,
                  OPT_BOOL,
                  OPT_FLAGS,
                  OPT_STRING,
                  OPT_U16,
                  OPT_U32,
                  OPT_MACADDR,
                  OPT_IFNAME,
                  OPT_IPADDR,
                  OPT_ENUM,
                  OPT_MULTISTRING,
                  __OPT_MAX
};

struct prog_option {
	enum option_type type;
	size_t cfg_size;
	size_t cfg_offset;
	size_t opt_size;
	char *name;
	char short_opt;
	char *help;
	char *metavar;
	void *typearg;
	bool required;
	bool positional;
	bool was_set;
};

struct flag_val {
        const char *flagstring;
        unsigned int flagval;
};

struct enum_val {
        const char *name;
        unsigned int value;
};

struct multistring {
        const char **strings;
        size_t num_strings;
};

struct iface {
        char *ifname;
        int ifindex;
};

struct ip_addr {
        int af;
        union {
                struct in_addr addr4;
                struct in6_addr addr6;
        } addr;
};

struct mac_addr {
        unsigned char addr[ETH_ALEN];
};

#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))

#define DEFINE_OPTION(_name, _type, _cfgtype, _cfgmember, ...) {	\
		.cfg_size = sizeof(_cfgtype),				\
		.opt_size = sizeof_field(_cfgtype, _cfgmember),	\
		.cfg_offset = offsetof(_cfgtype, _cfgmember),		\
		.name = _name,						\
		.type = _type,						\
		__VA_ARGS__}

#define END_OPTIONS 	{}

#define FOR_EACH_OPTION(_options, _opt)                 \
        for (_opt = _options; _opt->type != OPT_NONE; _opt++)

struct prog_command {
	const char *name;
	int (*func)(const void *cfg, const char *pin_root_path);
	struct prog_option *options;
	const void *default_cfg;
	char *doc;
	bool no_cfg;
};

#define DEFINE_COMMAND(_name, _doc) {					\
		.name = textify(_name),				\
		.func = do_##_name,					\
		.options = _name ##_options,				\
		.default_cfg = &defaults_##_name,			\
		.doc = _doc}
#define DEFINE_COMMAND_NODEF(_name, _doc) {				\
		.name = textify(_name),				\
		.func = do_##_name,					\
		.options = _name ##_options,				\
		.doc = _doc}

#define END_COMMANDS {}

const char* get_enum_name(const struct enum_val *vals, unsigned int value);
void print_flags(char *buf, size_t buf_len, const struct flag_val *flags,
                 unsigned long flags_val);
void print_addr(char *buf, size_t buf_len, const struct ip_addr *addr);
void print_macaddr(char *buf, size_t buf_len, const struct mac_addr *addr);
bool is_prefix(const char *prefix, const char *string);
void usage(const char *prog_name, const char *doc,
           const struct prog_option *long_options, bool full);

int parse_cmdline_args(int argc, char **argv,
                       struct prog_option *long_options,
                       void *cfg, const char *prog, const char *doc,
		       const void *defaults);

int dispatch_commands(const char *argv0, int argc, char **argv,
		      const struct prog_command *cmds, size_t cfg_size,
		      const char *prog_name);

#endif /* __COMMON_PARAMS_H */
