const std = @import("std");

// --- 1. IO PRIMITIVES ---

// --- 1. IO PRIMITIVES ---

export fn write(fd: i32, buf: [*]const u8, count: usize) isize {
    // Forward stdout (1) to Kernel Log
    if (fd == 1) {
        const sys = @as(*const ion.SysTable, @ptrFromInt(0x83000000));
        if (sys.fn_log != 0) {
            const func = @as(*const fn ([*]const u8, u64) void, @ptrFromInt(sys.fn_log));
            func(buf, count);
        }
    }
    return @intCast(count);
}

export fn close(fd: i32) i32 {
    _ = fd;
    return 0; // Success stub
}

export fn fputc(c: i32, stream: ?*anyopaque) i32 {
    _ = stream;
    const char = @as(u8, @intCast(c));
    const buf = [1]u8{char};
    _ = write(1, &buf, 1);
    return c;
}

export fn fputs(s: [*]const u8, stream: ?*anyopaque) i32 {
    _ = stream;
    var len: usize = 0;
    while (s[len] != 0) : (len += 1) {}
    _ = write(1, s, len);
    return 1;
}

// --- 2. THE MISSING SYMBOLS (STUBS) ---

// Nim checks for errors on streams. We say "No error".
export fn ferror(stream: ?*anyopaque) i32 {
    _ = stream;
    return 0;
}

export fn clearerr(stream: ?*anyopaque) void {
    _ = stream;
}

// Nim looks for chars in memory (optimized scans).
export fn memchr(s: ?*const anyopaque, c: i32, n: usize) ?*anyopaque {
    if (s) |src_ptr| {
        const src: [*]const u8 = @ptrCast(src_ptr);
        const target = @as(u8, @intCast(c));
        var i: usize = 0;
        while (i < n) : (i += 1) {
            if (src[i] == target) return @ptrCast(@constCast(src + i));
        }
    }
    return null;
}

// 2. File I/O (VFS Bridge - via SysTable Hypercall Vector)
// We cannot link directly, so we use the SysTable function pointers.

export fn open(path: [*]const u8, flags: i32) i32 {
    _ = flags;
    const sys = @as(*const ion.SysTable, @ptrFromInt(0x83000000));
    if (sys.fn_vfs_open != 0) {
        const func = @as(*const fn ([*]const u8) i32, @ptrFromInt(sys.fn_vfs_open));
        return func(path);
    }
    return -1;
}

export fn list_files(buf: [*]u8, len: u64) i64 {
    const sys = @as(*const ion.SysTable, @ptrFromInt(0x83000000));
    if (sys.fn_vfs_list != 0) {
        const func = @as(*const fn ([*]u8, u64) i64, @ptrFromInt(sys.fn_vfs_list));
        return func(buf, len);
    }
    return 0;
}

export fn read(fd: i32, buf: [*]u8, count: usize) isize {
    if (fd == 0) {
        // Stdin (Console)
        var pkt: ion.IonPacket = undefined;
        while (!ion.sys_input_pop(&pkt)) {
            nexus_yield();
        }
        const to_copy = @min(count, pkt.len);
        const src = @as([*]const u8, @ptrFromInt(pkt.data));
        @memcpy(buf[0..to_copy], src[0..to_copy]);
        ion_user_free(pkt);
        return @intCast(to_copy);
    } else {
        // VFS Read via SysTable
        const sys = @as(*const ion.SysTable, @ptrFromInt(0x83000000));
        if (sys.fn_vfs_read != 0) {
            const func = @as(*const fn (i32, [*]u8, u64) i64, @ptrFromInt(sys.fn_vfs_read));
            return @intCast(func(fd, buf, @intCast(count)));
        }
        return -1;
    }
}

// Nim tries to read lines.
export fn fgets(s: [*]u8, size: i32, stream: ?*anyopaque) ?[*]u8 {
    _ = stream;
    if (size <= 0) return null;

    var pkt: ion.IonPacket = undefined;
    while (!ion.sys_input_pop(&pkt)) {
        nexus_yield();
    }

    const to_copy = @min(@as(usize, @intCast(size - 1)), pkt.len);
    const src = @as([*]const u8, @ptrFromInt(pkt.data));
    @memcpy(s[0..to_copy], src[0..to_copy]);
    s[to_copy] = 0;

    ion_user_free(pkt);
    return s;
}

export fn fgetc(stream: ?*anyopaque) i32 {
    _ = stream;
    var c: u8 = undefined;
    const n = read(0, @ptrCast(&c), 1);
    if (n <= 0) return -1;
    return @intCast(c);
}

const CMD_ION_FREE = 0x300;

export fn ion_user_free(pkt: ion.IonPacket) void {
    _ = nexus_syscall(CMD_ION_FREE, pkt.id);
}

// Math stubs (sometimes needed)
export fn dlopen() void {}
export fn dlsym() void {}

export fn strerror(errnum: i32) [*]const u8 {
    _ = errnum;
    return "Unknown Error";
}

extern fn main(argc: i32, argv: [*]const [*]const u8) i32;

// _start relocated to subject_entry.S

const ion = @import("ion.zig");

// Sovereign Syscall: Push to CMD Ring
export fn nexus_syscall(cmd_id: u32, arg: u64) c_int {
    // Construct Packet
    var pkt = ion.CmdPacket{ .kind = cmd_id, ._pad = 0, .arg = arg, .id = 0 };

    // Compute Provenance (SipHash)
    const key = "\xde\xad\xbe\xef\xca\xfe\xba\xbe\x00\x01\x02\x03\x04\x05\x06\x07";

    var hasher = std.crypto.auth.siphash.SipHash128(1, 3).init(key);
    hasher.update(std.mem.asBytes(&pkt.kind));
    hasher.update(std.mem.asBytes(&pkt.arg));
    const hash_int = hasher.finalInt();

    pkt.id = hash_int;

    // Push to High-Priority CMD Ring
    if (!ion.sys_cmd_push(pkt)) {
        return -1; // Error: Ring full/backpressure
    }
    return 0; // Success
}

const NetArgs = ion.NetArgs;

// Network TX: Send Raw Frame
export fn nexus_net_tx(buf: [*]const u8, len: u64) void {
    var args = NetArgs{ .buf = @intFromPtr(buf), .len = len };
    _ = nexus_syscall(ion.CMD_NET_TX, @intFromPtr(&args));
}

// Network RX: Poll Raw Frame
export fn nexus_net_rx(buf: [*]u8, max_len: u64) u64 {
    var args = NetArgs{ .buf = @intFromPtr(buf), .len = max_len };
    _ = nexus_syscall(ion.CMD_NET_RX, @intFromPtr(&args));
    // The kernel updates args.len with the actual received length
    // Wait... args is local stack variable. Kernel writes to it?
    // Userland and Kernel share address space in this unikernel model.
    // So yes, kernel writes to &args.
    return args.len;
}

const BlkArgs = ion.BlkArgs;

export fn nexus_blk_read(sector: u64, buf: [*]u8, len: u64) void {
    var args = BlkArgs{ .sector = sector, .buf = @intFromPtr(buf), .len = len };
    _ = nexus_syscall(ion.CMD_BLK_READ, @intFromPtr(&args));
    nexus_yield(); // Block until Kernel processes it
}

export fn nexus_blk_write(sector: u64, buf: [*]const u8, len: u64) void {
    var args = BlkArgs{ .sector = sector, .buf = @intFromPtr(buf), .len = len };
    _ = nexus_syscall(ion.CMD_BLK_WRITE, @intFromPtr(&args));
    nexus_yield(); // Block until Kernel processes it
}

// Sovereign Yield: Return control to Kernel Scheduler
export fn nexus_yield() void {
    const yield_ptr = @as(*const *const fn () void, @ptrFromInt(0x83000FF0));
    yield_ptr.*();
}

// The Dignified Exit: Subject Termination
export fn exit(status: c_int) noreturn {
    const dbg_msg = "[Shim] exit() called. Signal termination.\n";
    _ = write(1, dbg_msg, dbg_msg.len);

    const CMD_SYS_EXIT: u32 = 1;
    _ = nexus_syscall(CMD_SYS_EXIT, @as(u64, @intCast(status)));

    // The Void: Termination signaled.
    const msg = "[Termination] Signaled. Waiting for System.\n";
    _ = write(1, msg, msg.len);

    while (true) {
        nexus_yield();
    }
}
