//+build linux, darwin, freebsd, openbsd
package unix

when ODIN_OS == .Darwin {
	foreign import libc "System.framework"
} else  {
	foreign import libc "system:c"
}

import "core:c"

@(default_calling_convention="c")
foreign libc {
	clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> c.int ---
	sleep         :: proc(seconds: c.uint) -> c.int ---
	nanosleep     :: proc(requested, remaining: ^timespec) -> c.int ---
}

timespec :: struct {
	tv_sec:  i64, // seconds
	tv_nsec: i64, // nanoseconds
}

when ODIN_OS == .OpenBSD {
	CLOCK_REALTIME           :: 0
	CLOCK_PROCESS_CPUTIME_ID :: 2
	CLOCK_MONOTONIC          :: 3
	CLOCK_THREAD_CPUTIME_ID  :: 4
	CLOCK_UPTIME             :: 5
	CLOCK_BOOTTIME           :: 6

	// CLOCK_MONOTONIC_RAW doesn't exist, use CLOCK_MONOTONIC
	CLOCK_MONOTONIC_RAW :: CLOCK_MONOTONIC
} else {
	CLOCK_REALTIME           :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
	CLOCK_MONOTONIC          :: 1 // NOTE(tetra): May stand still while system is asleep.
	CLOCK_PROCESS_CPUTIME_ID :: 2
	CLOCK_THREAD_CPUTIME_ID  :: 3
	CLOCK_MONOTONIC_RAW      :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
	CLOCK_REALTIME_COARSE    :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
	CLOCK_MONOTONIC_COARSE   :: 6
	CLOCK_BOOTTIME           :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
	CLOCK_REALTIME_ALARM     :: 8
	CLOCK_BOOTTIME_ALARM     :: 9
}

// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
// I do not know if Darwin programmers are used to the existance of these constants or not, so
// I'm leaving aliases to them for now.
CLOCK_SYSTEM   :: CLOCK_REALTIME
CLOCK_CALENDAR :: CLOCK_MONOTONIC

boot_time_in_nanoseconds :: proc "c" () -> i64 {
	ts_now, ts_boottime: timespec
	clock_gettime(CLOCK_REALTIME, &ts_now)
	clock_gettime(CLOCK_BOOTTIME, &ts_boottime)

	ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec
	return i64(ns)
}

seconds_since_boot :: proc "c" () -> f64 {
	ts_boottime: timespec
	clock_gettime(CLOCK_BOOTTIME, &ts_boottime)
	return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9
}


inline_nanosleep :: proc "c" (nanoseconds: i64) -> (remaining: timespec, res: i32) {
	s, ns := nanoseconds / 1e9, nanoseconds % 1e9
	requested := timespec{tv_sec=s, tv_nsec=ns}
	res = nanosleep(&requested, &remaining)
	return
}

