/*
 * Copyright (C) 2025 Jakub Kruszona-Zawadzki, Saglabs SA
 * 
 * This file is part of MooseFS.
 * 
 * MooseFS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 2 (only).
 * 
 * MooseFS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MooseFS; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA
 * or visit http://www.gnu.org/licenses/gpl-2.0.html
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#else
#  if defined(__APPLE__)
#    define HAVE_MACH_MACH_H 1
#    define HAVE_MACH_MACH_TIME_H 1
#    define HAVE_MACH_ABSOLUTE_TIME 1
#    define HAVE_MACH_TIMEBASE_INFO 1
#  endif
#  if defined(__linux__) || defined(__FreeBSD__)
#    define HAVE_CLOCK_GETTIME 1
#  endif
#  if defined(__posix__)
#    define HAVE_SYS_TIME_H 1
#    define HAVE_GETTIMEOFDAY 1
#  endif
#  if defined(WIN32)
#    define HAVE_WINDOWS_H 1
#  endif
#endif

#include <time.h>

#if defined(HAVE_SYS_TIME_H)
#  include <sys/time.h>
#endif
#if defined(HAVE_MACH_MACH_H) && defined(HAVE_MACH_MACH_TIME_H)
#  include <mach/mach.h>
#  include <mach/mach_time.h>
#endif
#if defined(HAVE_WINDOWS_H)
#  include <windows.h>
#endif

#include <inttypes.h>

#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)

double monotonic_seconds(void) {
	struct timespec ts;
	clock_gettime(CLOCK_MONOTONIC,&ts);
	return ts.tv_sec + (ts.tv_nsec * 0.000000001);
}

uint64_t monotonic_nseconds(void) {
	struct timespec ts;
	clock_gettime(CLOCK_MONOTONIC,&ts);
	return (uint64_t)(ts.tv_sec)*UINT64_C(1000000000)+(uint64_t)(ts.tv_nsec);
}

uint64_t monotonic_useconds(void) {
	return monotonic_nseconds()/1000;
}

const char* monotonic_method(void) {
	return "clock_gettime";
}

uint32_t monotonic_speed(void) {
	uint32_t i;
	uint64_t st,en;
	i = 0;
	st = monotonic_nseconds() + 10000000;
	do {
		en = monotonic_nseconds();
		i++;
	} while (en < st);
	return i;
}

#elif defined(HAVE_MACH_ABSOLUTE_TIME) && defined(HAVE_MACH_TIMEBASE_INFO)

double monotonic_seconds(void) {
	uint64_t c;
	static double coef = 0.0;

	c = mach_absolute_time();
	if (coef==0.0) {
		mach_timebase_info_data_t sti;
		mach_timebase_info(&sti);
		coef = (double)(sti.numer);
		coef /= (double)(sti.denom);
		coef /= 1000000000.0;
	}
	return c * coef;
}

uint64_t monotonic_nseconds(void) {
	uint64_t c;
	static uint8_t i = 0;
	static mach_timebase_info_data_t sti;

	c = mach_absolute_time();
	if (i==0) {
		mach_timebase_info(&sti);
		i = 1;
	}
	return c * sti.numer / sti.denom;
}

uint64_t monotonic_useconds(void) {
	return monotonic_nseconds()/1000;
}

const char* monotonic_method(void) {
	(void)monotonic_seconds(); // init static variables
	(void)monotonic_nseconds(); // init static variables
	return "mach_absolute_time";
}

uint32_t monotonic_speed(void) {
	uint32_t i;
	uint64_t st,en;
	i = 0;
	st = monotonic_nseconds() + 10000000;
	do {
		en = monotonic_nseconds();
		i++;
	} while (en < st);
	return i;
}

#elif defined(WIN32)

double monotonic_seconds(void) {
	return (double)GetTickCount64() / 1000.0;
}

uint64_t monotonic_useconds(void) {
	return GetTickCount64() * 1000;
}

uint64_t monotonic_nseconds(void) {
	return monotonic_useconds() * 1000;
}

const char* monotonic_method(void) {
	return "GetTickCount64";
}

uint32_t monotonic_speed(void) {
	uint32_t i;
	uint64_t st, en;
	i = 0;
	st = monotonic_useconds() + 10000;
	do {
		en = monotonic_useconds();
		i++;
	} while (en < st);
	return i;
}

#elif defined(HAVE_GETTIMEOFDAY)

double monotonic_seconds(void) {
	struct timeval tv;
	gettimeofday(&tv,NULL);
	return tv.tv_sec + (tv.tv_usec * 0.000001);
}

uint64_t monotonic_useconds(void) {
	struct timeval tv;
	gettimeofday(&tv,NULL);
	return (uint64_t)(tv.tv_sec)*UINT64_C(1000000)+(uint64_t)(tv.tv_usec);
}

uint64_t monotonic_nseconds(void) {
	return monotonic_useconds()*1000;
}

const char* monotonic_method(void) {
	return "gettimeofday";
}

uint32_t monotonic_speed(void) {
	uint32_t i;
	uint64_t st,en;
	i = 0;
	st = monotonic_useconds() + 10000;
	do {
		en = monotonic_useconds();
		i++;
	} while (en < st);
	return i;
}

#else
#error "Can't find valid time function"
#endif

#if 0
#include <unistd.h>
#include <stdio.h>
int main(void) {
	double st,en;
	uint64_t stusec,enusec;
	uint64_t stnsec,ennsec;

	printf("used method: %s\n",monotonic_method());
	st = monotonic_seconds();
	stusec = monotonic_useconds();
	stnsec = monotonic_nseconds();
	sleep(1);
	en = monotonic_seconds();
	enusec = monotonic_useconds();
	ennsec = monotonic_nseconds();
	printf("%.6lf ; %"PRIu64" ; %"PRIu64"\n",en-st,enusec-stusec,ennsec-stnsec);
}
#endif
