const std = @import("std");

extern fn console_write(ptr: [*]const u8, len: usize) void;

// Embed the Subject Zero binary
export var subject_bin = @embedFile("subject.bin");

export fn ion_loader_load(path: [*:0]const u8) u64 {
    _ = path;

    console_write("[Loader] Parsing ELF\n", 21);

    // Verify ELF Magic
    const magic = subject_bin[0..4];
    if (magic[0] != 0x7F or magic[1] != 'E' or magic[2] != 'L' or magic[3] != 'F') {
        console_write("[Loader] ERROR: Invalid ELF magic\n", 35);
        return 0;
    }

    // Parse ELF64 Header
    const e_entry = read_u64_le(subject_bin[0x18..0x20]);
    const e_phoff = read_u64_le(subject_bin[0x20..0x28]);
    const e_phentsize = read_u16_le(subject_bin[0x36..0x38]);
    const e_phnum = read_u16_le(subject_bin[0x38..0x3a]);

    console_write("[Loader] Entry: 0x", 18);
    print_hex(e_entry);
    console_write("\n[Loader] Loading ", 17);
    print_hex(e_phnum);
    console_write(" segments\n", 10);

    // Load each PT_LOAD segment
    var i: usize = 0;
    while (i < e_phnum) : (i += 1) {
        const ph_offset = e_phoff + (i * e_phentsize);
        const p_type = read_u32_le(subject_bin[ph_offset .. ph_offset + 4]);

        if (p_type == 1) { // PT_LOAD
            const p_offset = read_u64_le(subject_bin[ph_offset + 8 .. ph_offset + 16]);
            const p_vaddr = read_u64_le(subject_bin[ph_offset + 16 .. ph_offset + 24]);
            const p_filesz = read_u64_le(subject_bin[ph_offset + 32 .. ph_offset + 40]);
            const p_memsz = read_u64_le(subject_bin[ph_offset + 40 .. ph_offset + 48]);

            const dest = @as([*]u8, @ptrFromInt(p_vaddr));

            // Copy file content
            if (p_filesz > 0) {
                const src = subject_bin[p_offset .. p_offset + p_filesz];
                @memcpy(dest[0..p_filesz], src);
            }

            // Zero BSS (memsz > filesz)
            if (p_memsz > p_filesz) {
                @memset(dest[p_filesz..p_memsz], 0);
            }
        }
    }

    console_write("[Loader] ELF loaded successfully\n", 33);
    return e_entry;
}

fn read_u16_le(bytes: []const u8) u16 {
    return @as(u16, bytes[0]) | (@as(u16, bytes[1]) << 8);
}

fn read_u32_le(bytes: []const u8) u32 {
    return @as(u32, bytes[0]) |
        (@as(u32, bytes[1]) << 8) |
        (@as(u32, bytes[2]) << 16) |
        (@as(u32, bytes[3]) << 24);
}

fn read_u64_le(bytes: []const u8) u64 {
    var result: u64 = 0;
    var j: usize = 0;
    while (j < 8) : (j += 1) {
        result |= @as(u64, bytes[j]) << @intCast(j * 8);
    }
    return result;
}

fn print_hex(value: u64) void {
    const hex_chars = "0123456789ABCDEF";
    var buf: [16]u8 = undefined;
    var i: usize = 0;
    while (i < 16) : (i += 1) {
        const shift: u6 = @intCast((15 - i) * 4);
        const nibble = (value >> shift) & 0xF;
        buf[i] = hex_chars[nibble];
    }
    console_write(&buf, 16);
}

export fn launch_subject() void {
    const target_addr = ion_loader_load("/sysro/bin/subject");
    console_write("[Loader] Jumping...\n", 20);

    const entry = @as(*const fn () void, @ptrFromInt(target_addr));
    entry();
}

pub fn panic(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {
    console_write("[Loader] PANIC: ", 16);
    console_write(msg.ptr, msg.len);
    console_write("\n", 1);
    while (true) {}
}
