// vi:syntax=yggdrasil-abi:

extern {
    type Duration = core::time::Duration;
    type AtomicU32 = core::sync::atomic::AtomicU32;

    type SystemTime = yggdrasil_abi::time::SystemTime;
    #[thin]
    type ClockType = yggdrasil_abi::time::ClockType;

    // TODO "tagged union" enums are not yet implemented
    type MappingSource;

    type SignalEntryData = yggdrasil_abi::process::SignalEntryData;
    type MutexOperation = yggdrasil_abi::process::MutexOperation;
    #[thin]
    type ExitCode = yggdrasil_abi::process::ExitCode;
    #[thin]
    type ThreadEvent = yggdrasil_abi::process::ThreadEvent;
    type ProcessWait = yggdrasil_abi::process::ProcessWait;

    type FileMetadataUpdate = yggdrasil_abi::io::FileMetadataUpdate;
    type Rename = yggdrasil_abi::io::Rename;

    type MessageHeader = yggdrasil_abi::net::MessageHeader;
    type MessageHeaderMut = yggdrasil_abi::net::MessageHeaderMut;

    type DebugOperation = yggdrasil_abi::debug::DebugOperation;
    type DebugFrame = yggdrasil_abi::debug::DebugFrame;

    #[thin]
    type SeekFrom = yggdrasil_abi::io::SeekFrom;
}

// abi::net

enum SocketType(u32) {
    /// Raw packet socket
    RawPacket = 0,
    /// TCP stream socket
    TcpStream = 1,
    /// UDP packet socket
    UdpPacket = 2,
    /// Local packet socket
    LocalPacket = 200,
    /// Network configuration interface
    NetConfig = 1000,
}

bitfield SocketShutdown(u32) {
    /// Stop reception on a socket
    READ: 0,
    /// Stop transmission on a socket
    WRITE: 1,
}

// abi::mem

bitfield MappingFlags(u32) {
    /// Process write access
    WRITE: 0,
    /// Executable access
    EXEC: 1,
}

enum TraceLevel(u32) {
    Debug = 0,
    Info = 1,
    Warn = 2,
    Error = 3,
}

// TODO allow splitting types into separate modules

syscall get_random(buffer: &mut [u8]);
syscall get_clock(clock: ClockType, out: &mut MaybeUninit<SystemTime>) -> Result<()>;
syscall set_clock(clock: ClockType, value: &SystemTime) -> Result<()>;
syscall mount(opts: &MountOptions<'_>) -> Result<()>;
syscall unmount(opts: &UnmountOptions) -> Result<()>;
syscall load_module(path: &str) -> Result<()>;

syscall filesystem_control(fd: Option<RawFd>, option: u32, value: &mut [u8], len: usize) -> Result<usize>;
syscall system_control(option: u32, value: &mut [u8], len: usize) -> Result<usize>;

syscall get_system_info(option: u32, value: &mut [u8]) -> Result<usize>;

// Memory management
syscall map_memory(
    hint: Option<NonZeroUsize>,
    len: usize,
    flags: MappingFlags,
    source: &MappingSource
) -> Result<usize>;
syscall unmap_memory(address: usize, len: usize) -> Result<()>;

// Process/thread management
syscall create_process_group() -> ProcessGroupId;
syscall get_process_group_id() -> ProcessGroupId;

syscall exit_process(code: ExitCode) -> !;
syscall spawn_process(options: &SpawnOptions<'_>) -> Result<ProcessId>;
syscall wait_process(wait: &ProcessWait, status: &mut ExitCode, flags: WaitFlags) -> Result<ProcessId>;
syscall get_pid() -> ProcessId;
syscall get_tid() -> u32;

// TODO use ThreadId
syscall spawn_thread(options: &ThreadSpawnOptions) -> Result<u32>;
syscall exit_thread() -> !;
syscall wait_thread(tid: u32, event: &mut MaybeUninit<ThreadEvent>) -> Result<()>;
syscall get_thread_option(option: u32, value: &mut [u8]) -> Result<usize>;
syscall set_thread_option(option: u32, value: &[u8]) -> Result<()>;
syscall get_process_option(process: Option<ProcessId>, option: u32, value: &mut [u8]) -> Result<usize>;
syscall set_process_option(process: Option<ProcessId>, option: u32, value: &[u8]) -> Result<()>;

syscall nanosleep(duration: &Duration, remaining: &mut MaybeUninit<Duration>) -> Result<()>;

syscall exit_signal(frame: &SignalEntryData) -> !;
syscall send_signal(target: ProcessId, signal: Signal) -> Result<()>;

syscall mutex(mutex: &AtomicU32, op: &MutexOperation) -> Result<()>;

syscall start_session() -> Result<()>;

// I/O
syscall open(at: Option<RawFd>, path: &str, opts: OpenOptions, mode: FileMode) -> Result<RawFd>;
syscall check_access(at: Option<RawFd>, path: &str, access: AccessMode) -> Result<()>;
syscall close(fd: RawFd) -> Result<()>;
syscall write(fd: RawFd, data: &[u8]) -> Result<usize>;
syscall read(fd: RawFd, data: &mut [u8]) -> Result<usize>;
syscall seek(fd: RawFd, pos: SeekFrom, output: &mut u64) -> Result<()>;
syscall truncate(fd: RawFd, size: u64) -> Result<()>;
syscall fsync(fd: RawFd, what: FileSync) -> Result<()>;
syscall read_at(fd: RawFd, pos: u64, data: &mut [u8]) -> Result<usize>;
syscall write_at(fd: RawFd, pos: u64, data: &[u8]) -> Result<usize>;
syscall get_file_option(fd: RawFd, option: u32, data: &mut [u8]) -> Result<usize>;
syscall set_file_option(fd: RawFd, option: u32, data: &[u8]) -> Result<()>;

syscall open_directory(at: Option<RawFd>, path: &str) -> Result<RawFd>;
syscall read_directory_entries(fd: RawFd, entries: &mut [MaybeUninit<DirectoryEntry>]) -> Result<usize>;
syscall create_directory(at: Option<RawFd>, path: &str, mode: FileMode) -> Result<()>;
syscall create_symlink(at: Option<RawFd>, target: &str, path: &str) -> Result<()>;

syscall read_link(at: Option<RawFd>, path: &str, buf: &mut [u8]) -> Result<usize>;
syscall remove(at: Option<RawFd>, path: &str, flags: RemoveFlags) -> Result<()>;
syscall rename(rename: &Rename<'_>) -> Result<()>;
syscall clone_fd(source: RawFd, target: Option<RawFd>) -> Result<RawFd>;
syscall update_metadata(at: Option<RawFd>, path: &str, update: &FileMetadataUpdate) -> Result<()>;
syscall get_metadata(at: Option<RawFd>, path: &str, metadata: &mut MaybeUninit<FileAttr>, follow: bool) -> Result<()>;
syscall device_request(fd: RawFd, option: u32, value: &mut [u8], len: usize) -> Result<usize>;

// Misc I/O
syscall create_timer(options: TimerOptions) -> Result<RawFd>;
syscall create_pid(target: &ProcessWait, flags: WaitFlags) -> Result<RawFd>;
syscall create_pty(opts: &TerminalOptions, size: &TerminalSize, fds: &mut [MaybeUninit<RawFd>; 2]) -> Result<()>;
syscall create_shared_memory(size: usize) -> Result<RawFd>;
syscall create_poll_channel() -> Result<RawFd>;
syscall create_pipe(fds: &mut [MaybeUninit<RawFd>; 2]) -> Result<()>;

syscall poll_channel_wait(
    poll_fd: RawFd,
    timeout: &Option<Duration>,
    retain: bool,
    output: &mut Option<(RawFd, Result<()>)>
) -> Result<()>;
syscall poll_channel_control(poll_fd: RawFd, ctl: PollControl, fd: RawFd) -> Result<()>;

// Network

syscall create_socket(ty: SocketType) -> Result<RawFd>;
syscall bind(sock_fd: RawFd, local: &[u8]) -> Result<()>;
syscall listen(sock_fd: RawFd) -> Result<()>;
syscall connect(sock_fd: RawFd, remote: &[u8]) -> Result<()>;
syscall accept(sock_fd: RawFd, remote: Option<&mut [u8]>) -> Result<RawFd>;
syscall shutdown(sock_fd: RawFd, how: SocketShutdown) -> Result<()>;
syscall send_to(sock_fd: RawFd, data: &[u8], address: Option<&[u8]>) -> Result<usize>;
syscall receive_from(sock_fd: RawFd, data: &mut [u8], address: Option<&mut [u8]>) -> Result<usize>;
syscall get_socket_option(sock_fd: RawFd, option: u32, value: &mut [u8]) -> Result<usize>;
syscall set_socket_option(sock_fd: RawFd, option: u32, value: &[u8]) -> Result<()>;

syscall send_message(sock_fd: RawFd, message: &MessageHeader<'_>) -> Result<usize>;
syscall receive_message(sock_fd: RawFd, message: &mut MessageHeaderMut<'_>) -> Result<usize>;

// C compat
syscall fork() -> Result<ProcessId>;
syscall execve(options: &ExecveOptions<'_>) -> Result<()>;

// Debugging
syscall debug_trace(level: TraceLevel, message: &str);
syscall debug_control(tid: u32, option: u32, buffer: &mut [u8], len: usize) -> Result<usize>;
