#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix

import "core:c"

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

// sys/sem.h - XSI semaphore facility

foreign lib {
	/*
	Provides various semaphore control operation as specified by cmd.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/semctl.html ]]
	*/
	@(link_name=LSEMCTL)
	semctl :: proc(semid: FD, semnum: c.int, cmd: Sem_Cmd, #c_vararg args: ..semun) -> c.int ---

	/*
	Returns the semaphore identifier associated with key.

	Returns: -1 (setting errno) on failure, a semaphore file descriptor otherwise

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/semget.html ]]
	*/
	semget :: proc(key: key_t, nsems: c.int, semflg: IPC_Flags) -> FD ---

	/*
	Perform atomically a user-defined array of semaphore operations in array order on the set of
	semaphores associated with the semaphore identifier specified by the argument semid.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/semop.html ]]
	*/
	semop :: proc(semid: FD, sops: [^]sembuf, nsops: c.size_t) -> result ---
}

Sem_Cmd :: enum c.int {
	RMID    = IPC_RMID,
	SET     = IPC_SET,
	STAT    = IPC_STAT,
	// Returns the value of semncnt.
	GETNCNT = GETNCNT,
	// Returns the value of sempid.
	GETPID  = GETPID,
	// Return the value of semval.
	GETVAL  = GETVAL,
	// Returns the value of semval for each semaphore in the semaphore set.
	GETALL  = GETALL,
	// Returns the value of semzcnt.
	GETZCNT = GETZCNT,
	// Sets the value of semval to arg.val.
	SETVAL  = SETVAL,
	// Sets the value of semval for each semaphore in the set.
	SETALL  = SETALL,
}

semun :: struct #raw_union {
	val:   c.int,
	buf:   ^semid_ds,
	array: [^]c.ushort,
}

when ODIN_OS == .NetBSD {
	@(private) LSEMCTL :: "__semctl50"
} else {
	@(private) LSEMCTL :: "semctl"
}

when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {

	SEM_UNDO :: 0o10000

	GETNCNT :: 3
	GETPID  :: 4
	GETVAL  :: 5
	GETALL  :: 6
	GETZCNT :: 7
	SETVAL  :: 8
	SETALL  :: 9

	when ODIN_OS == .Darwin {
		semid_ds :: struct #max_field_align(4) {
			sem_perm:  ipc_perm,     /* [PSX] operation permission structure */
			sem_base:  c.int32_t,    /* 32 bit base ptr for semaphore set */
			sem_nsems: c.ushort,     /* [PSX] number of semaphores in set */
			sem_otime: time_t,       /* [PSX] last semop() */
			sem_pad1:  c.int32_t,
			sem_ctime: time_t,       /* [PSX] last time changed by semctl() */
			sem_pad2:  c.int32_t,
			sem_pad3:  [4]c.int32_t,
		}
	} else when ODIN_OS == .FreeBSD {
		semid_ds :: struct {
			sem_perm:  ipc_perm, /* [PSX] operation permission structure */
			sem_base:  rawptr,   /* 32 bit base ptr for semaphore set */
			sem_nsems: c.ushort, /* [PSX] number of semaphores in set */
			sem_otime: time_t,   /* [PSX] last semop() */
			sem_ctime: time_t,   /* [PSX] last time changed by semctl() */
		}
	} else when ODIN_OS == .NetBSD {
		semid_ds :: struct {
			sem_perm:  ipc_perm, /* [PSX] operation permission structure */
			sem_nsems: c.ushort, /* [PSX] number of semaphores in set */
			sem_otime: time_t,   /* [PSX] last semop() */
			sem_ctime: time_t,   /* [PSX] last time changed by semctl() */
			_sem_base: rawptr,   /* 32 bit base ptr for semaphore set */
		}
	} else when ODIN_OS == .OpenBSD {
		semid_ds :: struct {
			sem_perm:  ipc_perm, /* [PSX] operation permission structure */
			sem_nsems: c.ushort, /* [PSX] number of semaphores in set */
			sem_otime: time_t,   /* [PSX] last semop() */
			sem_pad1:  c.long,
			sem_ctime: time_t,   /* [PSX] last time changed by semctl() */
			sem_pad2:  c.long,
			sem_pad3:  [4]c.long,
		}
	}

	sembuf :: struct {
		sem_num: c.ushort, /* [PSX] semaphore number */
		sem_op:  c.short,  /* [PSX] semaphore operation */
		sem_flg: c.short,  /* [PSX] operation flags */
	}

} else when ODIN_OS == .Linux {

	SEM_UNDO :: 0x1000 // undo the operation on exit

	// Commands for `semctl'.
	GETPID  :: 11
	GETVAL  :: 12
	GETALL  :: 13
	GETNCNT :: 14
	GETZCNT :: 15
	SETVAL  :: 16
	SETALL  :: 17

	when ODIN_ARCH == .arm64 {
		semid_ds :: struct {
			sem_perm:  ipc_perm,  // [PSX] operation permission structure
			sem_otime: time_t,    // [PSX] last semop()
			sem_ctime: time_t,    // [PSX] last time changed by semctl()
			sem_nsems: c.ulong, // [PSX] number of semaphores in set
			__glibc_reserved3: c.ulong,
			__glibc_reserved4: c.ulong,
		}
	} else {
		semid_ds :: struct {
			sem_perm:  ipc_perm,  // [PSX] operation permission structure
			sem_otime: time_t,    // [PSX] last semop()
			__sem_otime_high: c.ulong,
			sem_ctime: time_t,    // [PSX] last time changed by semctl()
			__sem_ctime_high: c.ulong,
			sem_nsems: c.ulong, // [PSX] number of semaphores in set
			__glibc_reserved3: c.ulong,
			__glibc_reserved4: c.ulong,
		}
	}

	sembuf :: struct {
		sem_num: c.ushort, /* [PSX] semaphore number */
		sem_op:  c.short,  /* [PSX] semaphore operation */
		sem_flg: c.short,  /* [PSX] operation flags */
	}

} else when ODIN_OS == .Haiku {

	SEM_UNDO :: 10 // undo the operation on exit

	// Commands for `semctl'.
	GETPID  :: 3
	GETVAL  :: 4
	GETALL  :: 5
	GETNCNT :: 6
	GETZCNT :: 7
	SETVAL  :: 8
	SETALL  :: 9

	semid_ds :: struct {
		sem_perm:  ipc_perm,  // [PSX] operation permission structure
		sem_nsems: c.ushort, // [PSX] number of semaphores in set
		sem_otime: time_t,    // [PSX] last semop()
		sem_ctime: time_t,    // [PSX] last time changed by semctl()
	}

	sembuf :: struct {
		sem_num: c.ushort, /* [PSX] semaphore number */
		sem_op:  c.short,  /* [PSX] semaphore operation */
		sem_flg: c.short,  /* [PSX] operation flags */
	}

}
