const builtin = @import("builtin");
const std = @import("../std.zig");
const wasi = std.os.wasi;

extern threadlocal var errno: c_int;

pub fn _errno() *c_int {
    return &errno;
}

pub const mode_t = u32;
pub const time_t = i64;

pub const timespec = extern struct {
    tv_sec: time_t,
    tv_nsec: isize,

    pub fn fromTimestamp(tm: wasi.timestamp_t) timespec {
        const tv_sec: wasi.timestamp_t = tm / 1_000_000_000;
        const tv_nsec = tm - tv_sec * 1_000_000_000;
        return .{
            .tv_sec = @as(time_t, @intCast(tv_sec)),
            .tv_nsec = @as(isize, @intCast(tv_nsec)),
        };
    }

    pub fn toTimestamp(ts: timespec) wasi.timestamp_t {
        return @as(wasi.timestamp_t, @intCast(ts.tv_sec * 1_000_000_000)) +
            @as(wasi.timestamp_t, @intCast(ts.tv_nsec));
    }
};

pub const STDIN_FILENO = 0;
pub const STDOUT_FILENO = 1;
pub const STDERR_FILENO = 2;

pub const E = wasi.errno_t;

pub const CLOCK = wasi.clockid_t;
pub const IOV_MAX = 1024;
pub const LOCK = struct {
    pub const SH = 0x1;
    pub const EX = 0x2;
    pub const NB = 0x4;
    pub const UN = 0x8;
};
pub const S = struct {
    pub const IEXEC = @compileError("TODO audit this");
    pub const IFBLK = 0x6000;
    pub const IFCHR = 0x2000;
    pub const IFDIR = 0x4000;
    pub const IFIFO = 0xc000;
    pub const IFLNK = 0xa000;
    pub const IFMT = IFBLK | IFCHR | IFDIR | IFIFO | IFLNK | IFREG | IFSOCK;
    pub const IFREG = 0x8000;
    /// There's no concept of UNIX domain socket but we define this value here
    /// in order to line with other OSes.
    pub const IFSOCK = 0x1;
};
pub const fd_t = wasi.fd_t;
pub const pid_t = c_int;
pub const uid_t = u32;
pub const gid_t = u32;
pub const off_t = i64;
pub const ino_t = wasi.inode_t;
pub const dev_t = wasi.device_t;
pub const nlink_t = c_ulonglong;
pub const blksize_t = c_long;
pub const blkcnt_t = c_longlong;

pub const Stat = extern struct {
    dev: dev_t,
    ino: ino_t,
    nlink: nlink_t,
    mode: mode_t,
    uid: uid_t,
    gid: gid_t,
    __pad0: c_uint = 0,
    rdev: dev_t,
    size: off_t,
    blksize: blksize_t,
    blocks: blkcnt_t,
    atim: timespec,
    mtim: timespec,
    ctim: timespec,
    __reserved: [3]c_longlong = [3]c_longlong{ 0, 0, 0 },

    pub fn atime(self: @This()) timespec {
        return self.atim;
    }

    pub fn mtime(self: @This()) timespec {
        return self.mtim;
    }

    pub fn ctime(self: @This()) timespec {
        return self.ctim;
    }

    pub fn fromFilestat(stat: wasi.filestat_t) Stat {
        return .{
            .dev = stat.dev,
            .ino = stat.ino,
            .mode = switch (stat.filetype) {
                .UNKNOWN => 0,
                .BLOCK_DEVICE => S.IFBLK,
                .CHARACTER_DEVICE => S.IFCHR,
                .DIRECTORY => S.IFDIR,
                .REGULAR_FILE => S.IFREG,
                .SOCKET_DGRAM => S.IFSOCK,
                .SOCKET_STREAM => S.IFIFO,
                .SYMBOLIC_LINK => S.IFLNK,
                _ => 0,
            },
            .nlink = stat.nlink,
            .size = @intCast(stat.size),
            .atim = timespec.fromTimestamp(stat.atim),
            .mtim = timespec.fromTimestamp(stat.mtim),
            .ctim = timespec.fromTimestamp(stat.ctim),

            .uid = 0,
            .gid = 0,
            .rdev = 0,
            .blksize = 0,
            .blocks = 0,
        };
    }
};

pub const F = struct {
    pub const GETFD = 1;
    pub const SETFD = 2;
    pub const GETFL = 3;
    pub const SETFL = 4;
};

pub const FD_CLOEXEC = 1;

pub const F_OK = 0;
pub const X_OK = 1;
pub const W_OK = 2;
pub const R_OK = 4;

pub const SEEK = struct {
    pub const SET: wasi.whence_t = .SET;
    pub const CUR: wasi.whence_t = .CUR;
    pub const END: wasi.whence_t = .END;
};

pub const nfds_t = usize;

pub const pollfd = extern struct {
    fd: fd_t,
    events: i16,
    revents: i16,
};

pub const POLL = struct {
    pub const RDNORM = 0x1;
    pub const WRNORM = 0x2;
    pub const IN = RDNORM;
    pub const OUT = WRNORM;
    pub const ERR = 0x1000;
    pub const HUP = 0x2000;
    pub const NVAL = 0x4000;
};