/*
 * timeutils.c - time utility functions.
 *
 * Copyright (C) 2025 Genexis Sweden AB. All rights reserved.
 *
 * See LICENSE file for license related information.
 *
 */

#define _DEFAULT_SOURCE
#define _XOPEN_SOURCE

#include "timeutils.h"

#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>

//#include <easy/easy.h>

void timestamp_update(struct timespec *ts)
{
	if (clock_gettime(CLOCK_REALTIME, ts) < 0) {
		ts->tv_sec = 0;
		ts->tv_nsec = 0;
	}
}

int timestamp_invalid(struct timespec *ts)
{
	return ts->tv_sec == 0 && ts->tv_nsec == 0;
}

/* Time difference in seconds */
uint32_t timestamp_elapsed_sec(const struct timespec *ts)
{
	struct timespec now;
	uint32_t elapsed;

	if (ts->tv_sec == 0)
		return -1;

	/* seconds and nanoseconds since the Epoch */
	if (clock_gettime(CLOCK_REALTIME, &now) < 0)
		now.tv_sec = 0;

	elapsed = now.tv_sec - ts->tv_sec;

	return (elapsed > 0) ? elapsed : 0;
}

/* check if timestamp expired */
int timestamp_expired(struct timespec *a, unsigned int tmo_ms)
{
	struct timespec now;
	unsigned long diff_ns = 0, diff_s = 0;

	/* seconds and nanoseconds since the Epoch */
	if (clock_gettime(CLOCK_REALTIME, &now) < 0)
		return -1;

	diff_s = now.tv_sec - a->tv_sec;
	diff_ns = now.tv_nsec - a->tv_nsec;
	if ((long)diff_ns < 0) {
		diff_ns += 1000000000UL;
		diff_s--;
	}

	if (diff_s * 1000 + diff_ns / 1000000 >= tmo_ms)
		return 1;

	return 0;
}

char *time_to_timestamp(const time_t *t, char *tsp)
{
	char tmpbuf[64] = {0};
	struct tm res;
	char sign;
	long int toff, toff_hour, toff_min;

	if (!tsp)
		return NULL;

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

	localtime_r(t, &res);
	tzset();
	toff = timezone;
	sign = toff > 0 ? '-' : '+';
	toff *= -1L;

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

	snprintf(tmpbuf, sizeof(tmpbuf), "%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);

	snprintf(tsp, 64, "%s", tmpbuf);
	return tsp;
}

/* get time adjustment seconds (time(tzone) - time(UTC) secs) */
static long int timestamp_get_off_sec(const char *tsp)
{
	char *tzone;
	long int toff = 0, sign;
	int toff_hour, toff_min;

	/* Example timestamp: "2019-02-11T06:42:31-08:00" */

	tzone = strchr(tsp, '+');
	if (!tzone) {
		tzone = strrchr(tsp, '-'); /* last occurence */
		sign = -1L;
	} else {
		sign = +1L;
	}

	if (tzone) {
		sscanf(tzone+1, "%02d:%02d", &toff_hour, &toff_min);
		toff = toff_hour * 3600 + toff_min * 60; // seconds
		toff *= -sign;
	}

	return toff;
}

/* Returns time alligned to UTC+0 */
time_t timestamp_to_time(const char *tsp)
{
	struct tm tm_time;
	time_t res;

	/* Example timestamp: "2019-02-11T06:42:31-08:00" */
	memset(&tm_time, 0, sizeof(tm_time));
	strptime(tsp, "%Y-%m-%dT%H:%M:%S", &tm_time);

	tzset();
	res = mktime(&tm_time);

	/* Allign by toff to get UTC+0 */
	res += timestamp_get_off_sec(tsp);

	return res;
}

struct timespec time_to_timespec(time_t t)
{
	struct timespec res = {};

	res.tv_sec = t;
	res.tv_nsec = 0;

	return res;
}

/* converts timestamp string to timespec struct
 * adj_rtime true: adjust for realtime (ignore off time)
 */
struct timespec timestamp_to_timespec(const char *tsp, bool adj_rtime)
{
	time_t tt = 0;

	tt = timestamp_to_time(tsp);
	if (adj_rtime)
		tt -= timestamp_get_off_sec(tsp);

	return time_to_timespec(tt);
}

bool timestamp_less_than(const struct timespec *lhs, const struct timespec *rhs)
{
	if (lhs->tv_sec == rhs->tv_sec)
		return lhs->tv_nsec < rhs->tv_nsec;
	else
		return lhs->tv_sec < rhs->tv_sec;
}

bool timestamp_greater_than(const struct timespec *lhs, const struct timespec *rhs)
{
	return timestamp_less_than(rhs, lhs);
}

bool timestamp_greater_or_equal(const struct timespec *lhs, const struct timespec *rhs)
{
	return !timestamp_less_than(lhs, rhs);
}

bool timestamp_less_or_equal(const struct timespec *lhs, const struct timespec *rhs)
{
	return !timestamp_less_than(rhs, lhs);
}
