module builtin

enum SigIndex {
	si_signo  = 0x00
	si_code   = 0x02
	si_pid    = 0x04
	si_uid    = 0x05
	si_status = 0x06
	si_size   = 0x80
}

enum Signo {
	sighup    = 1 // Hangup.
	sigint    = 2 // Interactive attention signal.
	sigquit   = 3 // Quit.
	sigill    = 4 // Illegal instruction.
	sigtrap   = 5 // Trace/breakpoint trap.
	sigabrt   = 6 // Abnormal termination.
	sigbus    = 7
	sigfpe    = 8 // Erroneous arithmetic operation.
	sigkill   = 9 // Killed.
	sigusr1   = 10
	sigsegv   = 11 // Invalid access to memory.
	sigusr2   = 12
	sigpipe   = 13 // Broken pipe.
	sigalrm   = 14 // Alarm clock.
	sigterm   = 15 // Termination request.
	sigstkflt = 16
	sigchld   = 17
	sigcont   = 18
	sigstop   = 19
	sigtstp   = 20
	sigttin   = 21 // Background read from control terminal.
	sigttou   = 22 // Background write to control terminal.
	sigurg    = 23
	sigxcpu   = 24 // CPU time limit exceeded.
	sigxfsz   = 25 // File size limit exceeded.
	sigvtalrm = 26 // Virtual timer expired.
	sigprof   = 27 // Profiling timer expired.
	sigwinch  = 28
	sigpoll   = 29
	sigsys    = 31
}

// List of all the errors returned by syscalls
enum Errno {
	enoerror = 0x00000000
	eperm    = 0x00000001
	enoent   = 0x00000002
	esrch    = 0x00000003
	eintr    = 0x00000004
	eio      = 0x00000005
	enxio    = 0x00000006
	e2big    = 0x00000007
	enoexec  = 0x00000008
	ebadf    = 0x00000009
	echild   = 0x0000000a
	eagain   = 0x0000000b
	enomem   = 0x0000000c
	eacces   = 0x0000000d
	efault   = 0x0000000e
	enotblk  = 0x0000000f
	ebusy    = 0x00000010
	eexist   = 0x00000011
	exdev    = 0x00000012
	enodev   = 0x00000013
	enotdir  = 0x00000014
	eisdir   = 0x00000015
	einval   = 0x00000016
	enfile   = 0x00000017
	emfile   = 0x00000018
	enotty   = 0x00000019
	etxtbsy  = 0x0000001a
	efbig    = 0x0000001b
	enospc   = 0x0000001c
	espipe   = 0x0000001d
	erofs    = 0x0000001e
	emlink   = 0x0000001f
	epipe    = 0x00000020
	edom     = 0x00000021
	erange   = 0x00000022
}

enum MemProt {
	prot_read      = 0x1
	prot_write     = 0x2
	prot_exec      = 0x4
	prot_none      = 0x0
	prot_growsdown = 0x01000000
	prot_growsup   = 0x02000000
}

enum MapFlags {
	map_shared          = 0x01
	map_private         = 0x02
	map_shared_validate = 0x03
	map_type            = 0x0f
	map_fixed           = 0x10
	map_file            = 0x00
	map_anonymous       = 0x20
	map_huge_shift      = 26
	map_huge_mask       = 0x3f
}

//   const (
// 	fcntlf_dupfd         = 0x00000000
// 	fcntlf_exlck         = 0x00000004
// 	fcntlf_getfd         = 0x00000001
// 	fcntlf_getfl         = 0x00000003
// 	fcntlf_getlk         = 0x00000005
// 	fcntlf_getlk64       = 0x0000000c
// 	fcntlf_getown        = 0x00000009
// 	fcntlf_getowner_uids = 0x00000011
// 	fcntlf_getown_ex     = 0x00000010
// 	fcntlf_getsig        = 0x0000000b
// 	fcntlf_ofd_getlk     = 0x00000024
// 	fcntlf_ofd_setlk     = 0x00000025
// 	fcntlf_ofd_setlkw    = 0x00000026
// 	fcntlf_owner_pgrp    = 0x00000002
// 	fcntlf_owner_pid     = 0x00000001
// 	fcntlf_owner_tid     = 0x00000000
// 	fcntlf_rdlck         = 0x00000000
// 	fcntlf_setfd         = 0x00000002
// 	fcntlf_setfl         = 0x00000004
// 	fcntlf_setlk         = 0x00000006
// 	fcntlf_setlk64       = 0x0000000d
// 	fcntlf_setlkw        = 0x00000007
// 	fcntlf_setlkw64      = 0x0000000e
// 	fcntlf_setown        = 0x00000008
// 	fcntlf_setown_ex     = 0x0000000f
// 	fcntlf_setsig        = 0x0000000a
// 	fcntlf_shlck         = 0x00000008
// 	fcntlf_unlck         = 0x00000002
// 	fcntlf_wrlck         = 0x00000001
// 	fcntllock_ex         = 0x00000002
// 	fcntllock_mand       = 0x00000020
// 	fcntllock_nb         = 0x00000004
// 	fcntllock_read       = 0x00000040
// 	fcntllock_rw         = 0x000000c0
// 	fcntllock_sh         = 0x00000001
// 	fcntllock_un         = 0x00000008
// 	fcntllock_write      = 0x00000080
// 	fcntlo_accmode       = 0x00000003
// 	fcntlo_append        = 0x00000400
// 	fcntlo_cloexec       = 0x00080000
// 	fcntlo_creat         = 0x00000040
// 	fcntlo_direct        = 0x00004000
// 	fcntlo_directory     = 0x00010000
// 	fcntlo_dsync         = 0x00001000
// 	fcntlo_excl          = 0x00000080
// 	fcntlo_largefile     = 0x00008000
// 	fcntlo_ndelay        = 0x00000800
// 	fcntlo_noatime       = 0x00040000
// 	fcntlo_noctty        = 0x00000100
// 	fcntlo_nofollow      = 0x00020000
// 	fcntlo_nonblock      = 0x00000800
// 	fcntlo_path          = 0x00200000
// 	fcntlo_rdonly        = 0x00000000
// 	fcntlo_rdwr          = 0x00000002
// 	fcntlo_trunc         = 0x00000200
// 	fcntlo_wronly        = 0x00000001
// )

/*
Paraphrased from "man 2 waitid" on Linux

	Upon successful return, waitid() fills in the
	following fields of the siginfo_t structure
	pointed to by infop:

	si_pid, offset 0x10, int index 0x04:
		The process ID of the child.

	si_uid: offset 0x14, int index 0x05
		The real user ID of the child.

	si_signo: offset 0x00, int index 0x00
		Always set to SIGCHLD.

	si_status: ofset 0x18, int index 0x06
		1 the exit status of the child, as given to _exit(2)
			(or exit(3)) (sc_sys.cld_exited)
		2 the signal that caused the child to terminate, stop,
			or continue.
		3 The si_code field can be used to determine how to
			interpret this field.

	si_code, set to one of (enum Wi_si_code), offset 0x08, int index 0x02:
		CLD_EXITED (child called _exit(2));
		CLD_KILLED (child killed by signal);
		CLD_DUMPED (child  killed by signal, and dumped core);
		CLD_STOPPED (child stopped by signal);
		CLD_TRAPPED (traced child has trapped);
		CLD_CONTINUED (child continued by SIGCONT).
*/

const wp_sys_wnohang = u64(0x00000001)
const wp_sys_wuntraced = u64(0x00000002)
const wp_sys_wstopped = u64(0x00000002)
const wp_sys_wexited = u64(0x00000004)
const wp_sys_wcontinued = u64(0x00000008)
const wp_sys_wnowait = u64(0x01000000) // don't reap, just poll status.

const wp_sys___wnothread = u64(0x20000000) // don't wait on children of other threads in this group

const wp_sys___wall = u64(0x40000000) // wait on all children, regardless of type

const wp_sys___wclone = u64(0x80000000)

// First argument to waitid:
enum WiWhich {
	p_all  = 0
	p_pid  = 1
	p_pgid = 2
}

enum WiSiCode {
	cld_exited    = 1 // child has exited
	cld_killed    = 2 // child was killed
	cld_dumped    = 3 // child terminated abnormally
	cld_trapped   = 4 // traced child has trapped
	cld_stopped   = 5 // child has stopped
	cld_continued = 6 // stopped child has continued
}

fn split_int_errno(rc_in u64) (i64, Errno) {
	rc := i64(rc_in)
	if rc < 0 {
		return i64(-1), unsafe { Errno(-rc) }
	}
	return rc, Errno.enoerror
}

// 0 sys_read
fn sys_read(fd i64, buf &u8, count u64) (i64, Errno) {
	return split_int_errno(sys_call3(0, u64(fd), u64(buf), count))
}

// 1 sys_write
pub fn sys_write(fd i64, buf &u8, count u64) (i64, Errno) {
	return split_int_errno(sys_call3(1, u64(fd), u64(buf), count))
}

// 2 sys_open
fn sys_open(filename &u8, flags i64, mode int) (i64, Errno) {
	return split_int_errno(sys_call3(2, u64(filename), u64(flags), u64(mode)))
}

// 3 sys_close
fn sys_close(fd i64) Errno {
	return unsafe { Errno(-i64(sys_call1(3, u64(fd)))) }
}

// 9 sys_mmap
fn sys_mmap(addr &u8, len u64, prot MemProt, flags MapFlags, fildes i64, off u64) (&u8, Errno) {
	rc := sys_call6(9, u64(addr), len, u64(prot), u64(flags), fildes, off)
	a, e := split_int_errno(rc)
	return &u8(a), e
}

// 11 sys_munmap
fn sys_munmap(addr voidptr, len u64) Errno {
	return unsafe { Errno(-sys_call2(11, u64(addr), len)) }
}

// 25 sys_mremap
fn sys_mremap(old_addr voidptr, old_len u64, new_len u64, flags u64) (&u8, Errno) {
	rc := sys_call4(25, u64(old_addr), old_len, new_len, flags)
	a, e := split_int_errno(rc)
	return &u8(a), e
}

// 22  sys_pipe
fn sys_pipe(filedes &int) Errno {
	return unsafe { Errno(sys_call1(22, u64(filedes))) }
}

// 24 sys_sched_yield
fn sys_sched_yield() Errno {
	return unsafe { Errno(sys_call0(24)) }
}

// 28 sys_madvise
fn sys_madvise(addr voidptr, len u64, advice int) Errno {
	return unsafe { Errno(sys_call3(28, u64(addr), len, u64(advice))) }
}

// 39 sys_getpid
fn sys_getpid() int {
	return int(sys_call0(39))
}

// 57 sys_fork
fn sys_fork() int {
	return int(sys_call0(57))
}

// 58 sys_vfork
fn sys_vfork() int {
	return int(sys_call0(58))
}

// 33  sys_dup2
fn sys_dup2(oldfd int, newfd int) (i64, Errno) {
	return split_int_errno(sys_call2(33, u64(oldfd), u64(newfd)))
}

// 59  sys_execve
fn sys_execve(filename &u8, argv []&u8, envp []&u8) int {
	return int(sys_call3(59, u64(filename), argv.data, envp.data))
}

// 60 sys_exit
@[noreturn]
fn sys_exit(ec int) {
	sys_call1(60, u64(ec))
	for {}
}

// 102 sys_getuid
fn sys_getuid() int {
	return int(sys_call0(102))
}

// 247 sys_waitid
fn sys_waitid(which WiWhich, pid int, infop &int, options int, ru voidptr) Errno {
	return unsafe {
		Errno(sys_call5(247, u64(which), u64(pid), u64(infop), u64(options), u64(ru)))
	}
}

fn sys_call0(scn u64) u64 {
	mut res := u64(0)
	asm amd64 {
		syscall
		; =a (res)
		; a (scn)
	}
	return res
}

fn sys_call1(scn u64, arg1 u64) u64 {
	mut res := u64(0)
	asm amd64 {
		syscall
		; =a (res)
		; a (scn)
		  D (arg1)
	}
	return res
}

fn sys_call2(scn u64, arg1 u64, arg2 u64) u64 {
	mut res := u64(0)
	asm amd64 {
		syscall
		; =a (res)
		; a (scn)
		  D (arg1)
		  S (arg2)
	}
	return res
}

fn sys_call3(scn u64, arg1 u64, arg2 u64, arg3 u64) u64 {
	mut res := u64(0)
	asm amd64 {
		syscall
		; =a (res)
		; a (scn)
		  D (arg1)
		  S (arg2)
		  d (arg3)
	}
	return res
}

fn sys_call4(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64) u64 {
	mut res := u64(0)
	asm amd64 {
		mov r10, arg4
		syscall
		; =a (res)
		; a (scn)
		  D (arg1)
		  S (arg2)
		  d (arg3)
		  r (arg4)
		; r10
	}
	return res
}

fn sys_call5(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64) u64 {
	mut res := u64(0)
	asm amd64 {
		mov r10, arg4
		mov r8, arg5
		syscall
		; =a (res)
		; a (scn)
		  D (arg1)
		  S (arg2)
		  d (arg3)
		  r (arg4)
		  r (arg5)
		; r10
		  r8
	}
	return res
}

fn sys_call6(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 i64, arg6 u64) u64 {
	mut res := u64(0)
	asm amd64 {
		mov r10, arg4
		mov r8, arg5
		mov r9, arg6
		syscall
		; =a (res)
		; a (scn)
		  D (arg1)
		  S (arg2)
		  d (arg3)
		  r (arg4)
		  r (arg5)
		  r (arg6)
		; r10
		  r8
		  r9
	}
	return res
}

asm amd64 {
	.globl _start
	_start:
	call main
	mov rax, 60
	xor rdi, rdi
	syscall
	ret
}
