/*
 * utils.h - utility functions header
 *
 * Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 */

#ifndef UTILS_H
#define UTILS_H

#include <libubox/list.h>
#include <libubox/blob.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>

struct json_object;

#define _S(v)	#v
#define S(v)	_S(v)

#define hwaddr_hash(a)	(a[0] ^ a[1] ^ a[2] ^ a[3] ^ a[4] ^ a[5])

#ifndef BIT
#define BIT(x)  (1 << (x))
#endif

#define BIT1(m, x)      ((x >> m) & 0x0001)

#define foreach_token_r(t, tokens, saveptr, delim)	\
	for ((t = strtok_r(tokens, delim, &saveptr)); t; (t = strtok_r(NULL, delim, &saveptr)))

unsigned char *hwaddr_aton(const char *macstr, unsigned char *mac);
char *hwaddr_ntoa(const unsigned char *mac, char *macstr);
int hwaddr_from_ip(char *ifname, char *ipstr, unsigned char *hw);

/* bytes from-to hexstring helper functions */
int hex2byte(const char *hex);

/* utility wrappers over json-c functions */
int json_get_bool(struct json_object *object, const char *key);
int json_get_int(struct json_object *object, const char *key);
const char *json_get_string(struct json_object *object, const char *key);


/* list utility functions and macros */
#define BUF_SIZE 256

#define dbg_list_print(label, h, type, l, member)					\
do {											\
	type *e;									\
	char _bl[BUF_SIZE] = {0};							\
	size_t _bl_used = 0;								\
	bool _bl_trunc = false;								\
        int ret;									\
								                        \
	ret = snprintf(_bl, BUF_SIZE, "%s = [", label);					\
	if (ret < 0 || ret >= BUF_SIZE) {						\
		_bl_trunc = true;							\
		break;									\
	}										\
	_bl_used += ret;								\
	list_for_each_entry(e, h, l) {							\
		char _mstr[18] = {0};							\
											\
		if (BUF_SIZE - _bl_used < (18 + 4)) {					\
			_bl_trunc = true;						\
			break;								\
		}									\
		hwaddr_ntoa(e->member, _mstr);						\
		ret = snprintf(_bl + _bl_used, BUF_SIZE - _bl_used, "%s ", _mstr);	\
		if (ret < 0 || ret >= BUF_SIZE - _bl_used) {				\
			_bl_trunc = true;						\
			break;								\
		}									\
		_bl_used += ret;							\
	}										\
	snprintf(_bl + _bl_used, BUF_SIZE - _bl_used, "%s\n", (_bl_trunc) ? "T" : "]");	\
	fprintf(stderr, "%s", _bl);							\
} while (0)



#define list_flush(head, type, member)					\
do {									\
	type *__p, *__tmp;						\
									\
	if (!list_empty(head))						\
		list_for_each_entry_safe(__p, __tmp, head, member) {	\
			list_del(&__p->member);				\
			free(__p);					\
		}							\
} while (0)

/**
 * list_func - pointer to private list function type for list manipulation
 */
typedef int (*list_func)(struct list_head *a, struct list_head *b);

/**
 * list_join - joins two sorted lists
 * @a:		head of the first list
 * @b:		head of second list
 * @join:	private join function of type @list_func, which defines the
 *		criteria how the final list is going to be ordered.
 *		The resultant list is stored in @a
 */
#define list_join(a, b, join)						\
do {									\
	struct list_head *p, *q, *t1, *t2;				\
	typeof((list_func) join) __join = (join);			\
									\
	list_for_each_safe(p, t1, a) {					\
		list_for_each_safe(q, t2, b) {				\
			if (__join && __join(p, q) <= 0)		\
				list_move(q, p->prev);			\
		}							\
	}								\
	if (!list_empty(b))						\
		list_splice_tail(b, a);					\
} while (0)

/**
 * list_uniq - remove duplicate entries from a list
 * @a:		head of the list
 * @match:	private match function of type @list_func for duplicate checking
 * @merge:	private merge function of type @list_func for merging duplicate
 *		entries
 */
#define list_uniq(a, match, merge)				\
do {								\
	struct list_head *e, *p, *t, *n;			\
	typeof((list_func) match) __match = (match);		\
	typeof((list_func) merge) __merge = (merge);		\
								\
	list_for_each(e, a) {					\
		p = e;						\
		list_for_each_safe(n, t, p) {			\
			if (__match && __match(e, n)) {		\
				list_del(n);			\
				if (__merge)			\
					__merge(e, n);		\
				/* break; */			\
			}					\
		}						\
	}							\
} while (0)

int list_join_uniq(void *priv, struct list_head *a, struct list_head *b,
		struct list_head *out,
		int (*match)(void *priv, struct list_head *a, struct list_head *b),
		struct list_head *(*create_jentry)(void *priv, struct list_head *a, struct list_head *b),
		void (*free_jentry)(void *priv, struct list_head *),
		void (*free_entry_a)(struct list_head *),
		void (*free_entry_b)(struct list_head *));


int list_dup(struct list_head *h, struct list_head *new,
		void *(*alloc_entry)(void),
		void (*free_entry)(struct list_head *n),
		void (*copy_entry)(struct list_head *from, struct list_head *to));


int set_sighandler(int sig, void (*handler)(int));
int unset_sighandler(int sig);
void do_daemonize(const char *pidfile);
uint8_t wifi_band_to_ieee1905band(uint8_t band);
uint8_t wifi_bandstr_to_band(char *bandstr);
const char *band_to_str(uint32_t band);
uint32_t int_to_band(uint32_t band);
uint32_t band_to_int(uint32_t band);
uint8_t get_device_num_from_name(char *device);

bool is_package_available(const char *package);
bool is_process_running(const char *process);
bool is_local_cntlr_available(void);
bool is_local_cntlr_running(void);

int writeto_configfile(const char *filename, void *in, size_t len);
int readfrom_configfile(const char *filename, uint8_t **out, size_t *olen);
uint8_t *blobattrtob(struct blob_attr *attr, uint8_t *out, int size);

int get_ethportslist(int *num, char ifs[][16]);

int remove_uint8_duplicates(uint8_t *arr, int length);

#define TS_VID_INVALID 0x0FFF
bool is_vid_valid(unsigned int vid);

#define blobmsg_add_macaddr(b, f, v)	\
do {					\
	char _vstr[18] = {0};		\
	hwaddr_ntoa(v, _vstr);		\
	blobmsg_add_string(b, f, _vstr);\
} while (0)

#endif /* UTILS_H */
