/*
 * delm_utils.c
 * WiFi Data Elements utility functions
 *
 * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 * See LICENSE file for license related information.
 *
 */
#include "utils.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>

const char *i1905_media_type_to_str(uint16_t media_type)
{
	switch (media_type) {
	case 0x0000:  return "IEEE 802.3u";
	case 0x0001:  return "IEEE 802.3ab";
#ifdef IEEE1905_ETH_MEDIA_EXTENSION
	case 0x0002: return "IEEE 802.3bz";
	case 0x0003: return "IEEE 802.3bz";
	case 0x0004: return "IEEE 802.3ae";
#endif
	case 0x0100:  return "IEEE 802.11b";
	case 0x0101:  return "IEEE 802.11g";
	case 0x0102:  return "IEEE 802.11a";
	case 0x0103:  return "IEEE 802.11n 2.4";
	case 0x0104:  return "IEEE 802.11n 5.0";
	case 0x0105:  return "IEEE 802.11ac";
	case 0x0106:  return "IEEE 802.11ad";
	case 0x0107:  return "IEEE 802.11af";
	case 0x0108:  return "IEEE 802.11ax";
	case 0x0109:  return "IEEE 802.11be";
	default: return "Other";
	}
}

/* Get UTC date in ISO 8601 format */
char *get_date(time_t *t, char *tbuf)
{
	char tmpbuf[32] = {0};
	const time_t now = time(t);
	struct tm timeinfo;

	if (!tbuf)
		return NULL;

	gmtime_r(&now, &timeinfo);
	strftime(tmpbuf, sizeof(tmpbuf), "%Y-%m-%dT%H:%M:%SZ", &timeinfo);
	snprintf(tbuf, 32, "%s", tmpbuf);
	if (tbuf[0] == '\0') {
		fprintf(stderr, "Error: get_date() failed\n");
		return NULL;
	}
	tbuf[31] = '\0';

	return tbuf;
}

char *get_timestamp(const time_t *t, char *timestamp_str, size_t size)
{
	struct tm res;
	char sign;
	long int toff, toff_hour, toff_min;
	time_t to_convert;

	if (!timestamp_str)
		return "";

	if (t)
		to_convert = *t;
	else
		to_convert = time(NULL);

	/* E.g. "2019-02-11T06:42:31-08:00" */

	localtime_r(&to_convert, &res);
	tzset();
	toff = timezone;
	sign = toff > 0 ? '-' : '+';

	toff = labs(toff);
	toff_hour = toff / 3600;
	toff_min = (toff % 3600) / 60;

	snprintf(timestamp_str, size, "%04d-%02d-%02dT%02d:%02d:%02d%c%02ld:%02ld",
					res.tm_year + 1900,
					res.tm_mon + 1,
					res.tm_mday,
					res.tm_hour,
					res.tm_min,
					res.tm_sec,
					sign,
					toff_hour,
					toff_min);

	return timestamp_str;
}

int getcurrtime(struct timeval *out)
{
	struct timespec nowts = { 0 };
	struct timeval now = { 0 };
	int ret;

	ret = clock_gettime(CLOCK_REALTIME, &nowts);
	if (!ret) {
		now.tv_sec = nowts.tv_sec;
		now.tv_usec = nowts.tv_nsec / 1000;
	} else {
		ret = gettimeofday(&now, NULL);
	}

	now.tv_usec = (now.tv_usec / 1000) * 1000;
	out->tv_sec = now.tv_sec;
	out->tv_usec = now.tv_usec;

	return ret;
}

int timeradd_msecs(struct timeval *a, unsigned long msecs, struct timeval *res)
{
	if (res) {
		struct timeval t = { 0 };

		if (msecs > 1000) {
			t.tv_sec += msecs / 1000;
			t.tv_usec = (msecs % 1000) * 1000;
		} else {
			t.tv_usec = msecs * 1000;
		}

		timeradd(a, &t, res);
		return 0;
	}

	return -1;
}

void do_daemonize(const char *pidfile)
{
	char buf[128] = {0};
	int flags;
	int f;

	if (daemon(0, 0)) {
		fprintf(stderr, "Can't becomes a daemon. "
				"Continue as foreground process...\n");
	}

	if (!pidfile)
		return;

	f = open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
	if (f >= 0) {
		flags = fcntl(f, F_GETFD);
		if (flags != -1) {
			flags |= FD_CLOEXEC;
			fcntl(f, F_SETFD, flags);
		}
		if (lockf(f, F_TLOCK, 0) < 0) {
			fprintf(stderr, "File '%s' exists. Aborting...\n",
				pidfile);
			exit(-1);
		}
		if (ftruncate(f, 0)) {
			fprintf(stderr, "Continue with invalid pid file %s\n",
					pidfile);
			return;
		}
		snprintf(buf, sizeof(buf), "%ld\n", (long)getpid());
		if (write(f, buf, strlen(buf)) != strlen(buf)) {
			fprintf(stderr, "Continue with invalid pid file %s\n",
					pidfile);
			return;
		}
	}
}

size_t list_count(const struct list_head *list)
{
	const struct list_head *element;
	size_t cnt = 0;

	list_for_each(element, list)
		++cnt;

	return cnt;
}

uint8_t rssi_to_rcpi(int rssi)
{
	if (!rssi)
		return 255;
	else if (rssi < -110)
		return 0;
	else if (rssi > 0)
		return 220;
	else
		return (rssi + 110) * 2;
}
