// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package linux

import (
	"math"
	"time"
)

const (
	// ClockTick is the length of time represented by a single clock tick, as
	// used by times(2) and /proc/[pid]/stat.
	ClockTick = time.Second / CLOCKS_PER_SEC

	// CLOCKS_PER_SEC is the number of ClockTicks per second.
	//
	// Linux defines this to be 100 on most architectures, irrespective of
	// CONFIG_HZ. Userspace obtains the value through sysconf(_SC_CLK_TCK),
	// which uses the AT_CLKTCK entry in the auxiliary vector if one is
	// provided, and assumes 100 otherwise (glibc:
	// sysdeps/posix/sysconf.c:__sysconf() =>
	// sysdeps/unix/sysv/linux/getclktck.c, elf/dl-support.c:_dl_aux_init()).
	//
	// Not to be confused with POSIX CLOCKS_PER_SEC, as used by clock(3); "XSI
	// requires that [POSIX] CLOCKS_PER_SEC equals 1000000 independent of the
	// actual resolution" - clock(3).
	CLOCKS_PER_SEC = 100
)

// CPU clock types for use with clock_gettime(2) et al.
//
// The 29 most significant bits of a 32 bit clock ID are either a PID or a FD.
//
// Bits 1 and 0 give the type: PROF=0, VIRT=1, SCHED=2, or FD=3.
//
// Bit 2 indicates whether a cpu clock refers to a thread or a process.
const (
	CPUCLOCK_PROF  = 0
	CPUCLOCK_VIRT  = 1
	CPUCLOCK_SCHED = 2
	CPUCLOCK_MAX   = 3
	CLOCKFD        = CPUCLOCK_MAX

	CPUCLOCK_CLOCK_MASK     = 3
	CPUCLOCK_PERTHREAD_MASK = 4
)

// Clock identifiers for use with clock_gettime(2), clock_getres(2),
// clock_nanosleep(2).
const (
	CLOCK_REALTIME           = 0
	CLOCK_MONOTONIC          = 1
	CLOCK_PROCESS_CPUTIME_ID = 2
	CLOCK_THREAD_CPUTIME_ID  = 3
	CLOCK_MONOTONIC_RAW      = 4
	CLOCK_REALTIME_COARSE    = 5
	CLOCK_MONOTONIC_COARSE   = 6
	CLOCK_BOOTTIME           = 7
	CLOCK_REALTIME_ALARM     = 8
	CLOCK_BOOTTIME_ALARM     = 9
)

// Flags for clock_nanosleep(2).
const (
	TIMER_ABSTIME = 1
)

// Flags for timerfd syscalls (timerfd_create(2), timerfd_settime(2)).
const (
	// TFD_CLOEXEC is a timerfd_create flag.
	TFD_CLOEXEC = O_CLOEXEC

	// TFD_NONBLOCK is a timerfd_create flag.
	TFD_NONBLOCK = O_NONBLOCK

	// TFD_TIMER_ABSTIME is a timerfd_settime flag.
	TFD_TIMER_ABSTIME = 1
)

// The safe number of seconds you can represent by int64.
const maxSecInDuration = math.MaxInt64 / int64(time.Second)

// TimeT represents time_t in <time.h>. It represents time in seconds.
//
// +marshal
type TimeT int64

// NsecToTimeT translates nanoseconds to TimeT (seconds).
func NsecToTimeT(nsec int64) TimeT {
	return TimeT(nsec / 1e9)
}

// Timespec represents struct timespec in <time.h>.
//
// +marshal slice:TimespecSlice
type Timespec struct {
	Sec  int64
	Nsec int64
}

// Unix returns the second and nanosecond.
func (ts Timespec) Unix() (sec int64, nsec int64) {
	return int64(ts.Sec), int64(ts.Nsec)
}

// ToTime returns the Go time.Time representation.
func (ts Timespec) ToTime() time.Time {
	return time.Unix(ts.Sec, ts.Nsec)
}

// ToNsec returns the nanosecond representation.
func (ts Timespec) ToNsec() int64 {
	return int64(ts.Sec)*1e9 + int64(ts.Nsec)
}

// ToNsecCapped returns the safe nanosecond representation.
func (ts Timespec) ToNsecCapped() int64 {
	if ts.Sec > maxSecInDuration {
		return math.MaxInt64
	}
	return ts.ToNsec()
}

// ToDuration returns the safe nanosecond representation as time.Duration.
func (ts Timespec) ToDuration() time.Duration {
	return time.Duration(ts.ToNsecCapped())
}

// Valid returns whether the timespec contains valid values.
func (ts Timespec) Valid() bool {
	return !(ts.Sec < 0 || ts.Nsec < 0 || ts.Nsec >= int64(time.Second))
}

// NsecToTimespec translates nanoseconds to Timespec.
func NsecToTimespec(nsec int64) (ts Timespec) {
	ts.Sec = nsec / 1e9
	ts.Nsec = nsec % 1e9
	return
}

// DurationToTimespec translates time.Duration to Timespec.
func DurationToTimespec(dur time.Duration) Timespec {
	return NsecToTimespec(dur.Nanoseconds())
}

// SizeOfTimeval is the size of a Timeval struct in bytes.
const SizeOfTimeval = 16

// Timeval represents struct timeval in <time.h>.
//
// +marshal slice:TimevalSlice
type Timeval struct {
	Sec  int64
	Usec int64
}

// ToNsecCapped returns the safe nanosecond representation.
func (tv Timeval) ToNsecCapped() int64 {
	if tv.Sec > maxSecInDuration {
		return math.MaxInt64
	}
	return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3
}

// ToDuration returns the safe nanosecond representation as a time.Duration.
func (tv Timeval) ToDuration() time.Duration {
	return time.Duration(tv.ToNsecCapped())
}

// ToTime returns the Go time.Time representation.
func (tv Timeval) ToTime() time.Time {
	return time.Unix(tv.Sec, tv.Usec*1e3)
}

// NsecToTimeval translates nanosecond to Timeval.
func NsecToTimeval(nsec int64) (tv Timeval) {
	nsec += 999 // round up to microsecond
	tv.Sec = nsec / 1e9
	tv.Usec = nsec % 1e9 / 1e3
	return
}

// DurationToTimeval translates time.Duration to Timeval.
func DurationToTimeval(dur time.Duration) Timeval {
	return NsecToTimeval(dur.Nanoseconds())
}

// Itimerspec represents struct itimerspec in <time.h>.
//
// +marshal
type Itimerspec struct {
	Interval Timespec
	Value    Timespec
}

// ItimerVal mimics the following struct in <sys/time.h>
//   struct itimerval {
//     struct timeval it_interval; /* next value */
//     struct timeval it_value;    /* current value */
//   };
//
// +marshal
type ItimerVal struct {
	Interval Timeval
	Value    Timeval
}

// ClockT represents type clock_t.
//
// +marshal
type ClockT int64

// ClockTFromDuration converts time.Duration to clock_t.
func ClockTFromDuration(d time.Duration) ClockT {
	return ClockT(d / ClockTick)
}

// Tms represents struct tms, used by times(2).
//
// +marshal
type Tms struct {
	UTime  ClockT
	STime  ClockT
	CUTime ClockT
	CSTime ClockT
}

// TimerID represents type timer_t, which identifies a POSIX per-process
// interval timer.
//
// +marshal
type TimerID int32

// StatxTimestamp represents struct statx_timestamp.
//
// +marshal
type StatxTimestamp struct {
	Sec  int64
	Nsec uint32
	_    int32
}

// ToNsec returns the nanosecond representation.
func (sxts StatxTimestamp) ToNsec() int64 {
	return int64(sxts.Sec)*1e9 + int64(sxts.Nsec)
}

// ToNsecCapped returns the safe nanosecond representation.
func (sxts StatxTimestamp) ToNsecCapped() int64 {
	if sxts.Sec > maxSecInDuration {
		return math.MaxInt64
	}
	return sxts.ToNsec()
}

// NsecToStatxTimestamp translates nanoseconds to StatxTimestamp.
func NsecToStatxTimestamp(nsec int64) (ts StatxTimestamp) {
	return StatxTimestamp{
		Sec:  nsec / 1e9,
		Nsec: uint32(nsec % 1e9),
	}
}

// Utime represents struct utimbuf used by utimes(2).
//
// +marshal
type Utime struct {
	Actime  int64
	Modtime int64
}
