#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <netlink/route/link.h>

#include "easy.h"

static int hex2num(char c)
{
	if (c >= '0' && c <= '9')
		return c - '0';
	if (c >= 'a' && c <= 'f')
		return c - 'a' + 10;
	if (c >= 'A' && c <= 'F')
		return c - 'A' + 10;
	return -1;
}

static int hex2byte(const char *hex)
{
	int a, b;
	if((a = hex2num(*hex++)) < 0)
		return -1;
	if((b = hex2num(*hex++)) < 0)
		return -1;

	return (a << 4) | b;
}

LIBEASY_API uint8_t *strtob(char *str, int len, uint8_t *bytes)
{
	size_t slen;
	int i;

	if (!str || !bytes)
		return NULL;

	slen = strlen(str);
	if (!slen || slen % 2 || str[slen] != '\0')
		return NULL;

	slen >>= 1;
	if (len > slen)
		len = (int)slen;

	for (i = 0; i < len; i++) {
		int a;

		if ((a = hex2byte(str)) < 0)
			return NULL;

		str += 2;
		bytes[i] = (uint8_t)a;
	}

	return bytes;
}

LIBEASY_API char *btostr(uint8_t *bytes, int len, char *str)
{
	size_t i;

	if (!str || !bytes)
		return NULL;

	for (i = 0; i < len; i++)
		sprintf(str + strlen(str), "%02x", bytes[i] & 0xff);

	return str;
}

LIBEASY_API uint8_t *hwaddr_aton(const char *macstr, uint8_t *mac)
{
	size_t i;

	for (i = 0; i < 6; i++) {
		int a;

		if((a = hex2byte(macstr)) < 0)
			return NULL;

		macstr += 2;
		mac[i] = (uint8_t)a;
		if (i < 6 - 1 && *macstr++ != ':')
			return NULL;
	}
	return mac;
}

LIBEASY_API char *hwaddr_ntoa(const uint8_t *mac, char *macstr)
{
	sprintf(macstr, "%02x:%02x:%02x:%02x:%02x:%02x",
			mac[0]&0xff, mac[1]&0xff,
			mac[2]&0xff, mac[3]&0xff,
			mac[4]&0xff, mac[5]&0xff);

	return macstr;
}

int LIBEASY_API uuid_strtob(char *uuidstr, uint8_t *uuid)
{
	uint8_t elen[] = {8, 4, 4, 4, 12};
	char *ptr, *pos, *end;
	uint8_t *ret;
	int idx = 0;
	int i = 0;


	if (!uuidstr || !uuid)
		return -1;

	ptr = uuidstr;
	end = uuidstr + strlen(uuidstr);

	while (ptr < end) {
		char tmp[32] = {0};

		pos = ptr;
		ptr = strchr(pos, '-');
		if (!ptr) {
			if (i == 4) {
				ptr = end;
				if (ptr - pos != elen[i])
					return -1;

				strncpy(tmp, pos, elen[i]);
				ret = strtob(tmp, elen[i] / 2, &uuid[idx]);
				return !ret ? -1 : 0;
			}

			return -1;
		}

		if (ptr - pos != elen[i])
			return -1;

		strncpy(tmp, pos, elen[i]);
		ret = strtob(tmp, elen[i] / 2, &uuid[idx]);
		if (!ret)
			return -1;

		ptr++;
		idx += elen[i] / 2;
		i++;
	}

	return 0;
}

int LIBEASY_API uuid_btostr(uint8_t *uuid, char *uuidstr)
{
	if (!uuidstr || !uuid)
		return -1;

	sprintf(uuidstr,
		"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
		uuid[0], uuid[1], uuid[2], uuid[3],
		uuid[4], uuid[5], uuid[6], uuid[7],
		uuid[8], uuid[9], uuid[10], uuid[11],
		uuid[12], uuid[13], uuid[14], uuid[15]);

	return 0;
}

int LIBEASY_API get_ifstatus(const char *ifname, ifstatus_t *f)
{
	struct ifreq ifr;
	int ret = 0;
	int s;

	s = socket(AF_INET, SOCK_STREAM, 0);
	if (s < 0)
		return -errno;

	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, ifname, 16);
	if (ioctl(s, SIOCGIFFLAGS, &ifr) != 0) {
		ret = errno;
		close(s);
		return -ret;
	}

	*f = (ifstatus_t)ifr.ifr_flags;
	close(s);
	return 0;
}

int LIBEASY_API get_ifoperstatus(const char *ifname, ifopstatus_t *opstatus)
{
	struct rtnl_link *link;
	struct nl_sock *sk;
	int ret = 0;

	sk = nl_socket_alloc();
	if (sk == NULL) {
		ret = -errno;
		return ret;
	}

	nl_connect(sk, NETLINK_ROUTE);
	link = rtnl_link_alloc();
	if (link == NULL) {
		ret = -errno;
		nl_socket_free(sk);
		return ret;
	}

	if (rtnl_link_get_kernel(sk, 0, ifname, &link) < 0) {
		ret = -1;
		goto out;
	}

	*opstatus = rtnl_link_get_operstate(link);

out:
	rtnl_link_put(link);
	nl_socket_free(sk);
	return ret;
}

int LIBEASY_API set_sighandler(int sig, void (*handler)(int))
{
	struct sigaction sa;

	memset(&sa, 0, sizeof(sa));
	sa.sa_flags = 0;
	sigemptyset(&sa.sa_mask);
	sa.sa_handler = handler;
	if (sigaction(sig, &sa, NULL) < 0) {
		libeasy_err("Error sigaction %d\n", sig);
		return -1;
	}

	return 0;
}

int LIBEASY_API unset_sighandler(int sig)
{
	struct sigaction sa;

	memset(&sa, 0, sizeof(sa));
	sa.sa_flags = 0;
	sigemptyset(&sa.sa_mask);
	sa.sa_handler = SIG_DFL;

	return sigaction(sig, &sa, NULL);
}

#define QD_LINE_MAX 2048
LIBEASY_API char *trim(char *str)
{
	size_t start = 0, end = 0;

	if (!str)
		return NULL;

	if (strlen(str) == 0)
		return str;

	/* trim at the end */
	end = strlen(str);
	while (end > 0 && isspace(str[end - 1]))
		end--;
	str[end] = 0;

	/* trim at the start */
	start = 0;
	while (start < end && isspace(str[start]))
		start++;
	memmove(str, str + start, end - start + 1);

	return str;
}

void LIBEASY_API remove_newline(char *buf)
{
	size_t len;

	len = strlen(buf);
	if (len > 0 && buf[len - 1] == '\n')
		buf[len - 1] = 0;
}

int vsnsystemf(char *output, size_t output_size, const char *format, va_list ap)
{
	int n, rv = -1;
	size_t cmdline_size = 256;
	char *cmdline = NULL, *new_cmdline;

	cmdline = (char *)malloc(cmdline_size);
	if (!cmdline)
		goto out;

	while (1) {
		memset(cmdline, 0, cmdline_size);

		n = vsnprintf(cmdline, cmdline_size, format, ap);

		if (n < 0)
			goto out_cmdline;
		if (n < cmdline_size)
			break; /* good */

		/* else try again with more space */
		cmdline_size += 32;
		new_cmdline = (char *) realloc(cmdline,
						cmdline_size * sizeof(char));
		if (!new_cmdline)
			goto out_cmdline;
		cmdline = new_cmdline;
	}

	FILE *stream;
	char *line;

	stream = popen(cmdline, "r");	/* Flawfinder: ignore */
	if (!stream)
		goto out_stream;

	if (!output || !(output_size > 0))
		goto out_no_output;

	line = (char *) malloc(QD_LINE_MAX * sizeof(char));
	if (!line)
		goto out_line;

	memset(output, 0, output_size);
	while (fgets(line, QD_LINE_MAX, stream)) {
		int remaining = (int)output_size - (int)strlen(output) - 1;

		if (remaining <= 0)
			break;
		strncat(output, line, (size_t)remaining);
	}
	output[output_size - 1] = 0;

out_line:
	free(line);
out_no_output:
	rv = pclose(stream);
	/* Check pclose() status */
	if (WIFEXITED(rv))
		rv = WEXITSTATUS(rv);
	else if (WIFSIGNALED(rv))
		rv = WTERMSIG(rv);
	else if (WIFSTOPPED(rv))
		rv = WSTOPSIG(rv);
out_stream:
out_cmdline:
	free(cmdline);
out:
	return rv;
}

int vsystemf(const char *format, va_list ap)
{
	int rv;

	rv = vsnsystemf(NULL, 0, format, ap);

	return rv;
}


/* runCmd is an alias for systemf, calls directly vsystemf */
void LIBEASY_API runCmd(const char *format, ...)
{
	va_list ap;

	va_start(ap, format);
	vsystemf(format, ap);
	va_end(ap);
}

/* chrCmd is an alias for snsystemf, calls directly vsnsystemf */
LIBEASY_API char *chrCmd(char *output, size_t output_size, const char *format, ...)
{
	va_list ap;

	va_start(ap, format);
	vsnsystemf(output, output_size, format, ap);
	va_end(ap);

	trim(output);

	return output;
}

LIBEASY_API int Cmd(char *output, size_t output_size, const char *format, ...)
 {
	int rv;
	va_list ap;

	va_start(ap, format);

	rv = vsnsystemf(output, output_size, format, ap);
	va_end(ap);

	trim(output);

	return rv;
 }