// MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
// Phase 3.5c: The Canvas - Static Framebuffer in BSS
// This is our "Video RAM" living in kernel memory.

const std = @import("std");

// Resolution: 800x600 @ 32bpp (ARGB)
pub const WIDTH: usize = 800;
pub const HEIGHT: usize = 600;
pub const STRIDE: usize = WIDTH;

// The Physical Backing Store (1.9MB in BSS)
// Zero-initialized at boot.
var fb_memory: [WIDTH * HEIGHT]u32 = [_]u32{0} ** (WIDTH * HEIGHT);

// Clip rectangle (for MU_COMMAND_CLIP)
var clip_x: i32 = 0;
var clip_y: i32 = 0;
var clip_w: i32 = WIDTH;
var clip_h: i32 = HEIGHT;

pub const Rect = struct {
    x: i32,
    y: i32,
    w: i32,
    h: i32,
};

pub fn get_buffer() [*]u32 {
    return &fb_memory;
}

pub fn get_buffer_phys() usize {
    // Identity mapping for now
    return @intFromPtr(&fb_memory);
}

pub fn get_size() usize {
    return WIDTH * HEIGHT * @sizeOf(u32);
}

pub fn init() void {
    // Clear to dark blue (Nexus brand)
    clear(0xFF101030);
}

pub fn clear(color: u32) void {
    @memset(&fb_memory, color);
}

pub fn set_clip(r: Rect) void {
    clip_x = r.x;
    clip_y = r.y;
    clip_w = r.w;
    clip_h = r.h;
}

pub fn put_pixel(x: i32, y: i32, color: u32) void {
    if (x < 0 or y < 0) return;
    if (x >= @as(i32, @intCast(WIDTH)) or y >= @as(i32, @intCast(HEIGHT))) return;

    // Clip check
    if (x < clip_x or x >= clip_x + clip_w) return;
    if (y < clip_y or y >= clip_y + clip_h) return;

    const ux: usize = @intCast(x);
    const uy: usize = @intCast(y);
    fb_memory[uy * WIDTH + ux] = color;
}

// Optimized Rect Fill (The Workhorse for microui)
pub fn fill_rect(x: i32, y: i32, w: i32, h: i32, color: u32) void {
    // Clipping to screen
    var rx = x;
    var ry = y;
    var rw = w;
    var rh = h;

    if (rx < 0) {
        rw += rx;
        rx = 0;
    }
    if (ry < 0) {
        rh += ry;
        ry = 0;
    }
    if (rx + rw > @as(i32, @intCast(WIDTH))) {
        rw = @as(i32, @intCast(WIDTH)) - rx;
    }
    if (ry + rh > @as(i32, @intCast(HEIGHT))) {
        rh = @as(i32, @intCast(HEIGHT)) - ry;
    }
    if (rw <= 0 or rh <= 0) return;

    // Clip to clip rect
    if (rx < clip_x) {
        rw -= (clip_x - rx);
        rx = clip_x;
    }
    if (ry < clip_y) {
        rh -= (clip_y - ry);
        ry = clip_y;
    }
    if (rx + rw > clip_x + clip_w) {
        rw = clip_x + clip_w - rx;
    }
    if (ry + rh > clip_y + clip_h) {
        rh = clip_y + clip_h - ry;
    }
    if (rw <= 0 or rh <= 0) return;

    const start_row: usize = @intCast(ry);
    const end_row: usize = start_row + @as(usize, @intCast(rh));
    const start_col: usize = @intCast(rx);
    const cols: usize = @intCast(rw);

    var row = start_row;
    while (row < end_row) : (row += 1) {
        const offset = row * WIDTH + start_col;
        @memset(fb_memory[offset .. offset + cols], color);
    }
}

// THE FADE (Optimization: Dim pixels instead of clearing)
// This creates the "Ghost Trail" effect cheaply.
pub fn fade_screen(factor: u8) void {
    for (&fb_memory) |*pixel| {
        if (pixel.* == 0) continue;

        // Extract Green Channel (Assuming 0xAARRGGBB)
        // We only care about green for the matrix effect
        var g = (pixel.* >> 8) & 0xFF;

        if (g > factor) {
            g -= factor;
        } else {
            g = 0;
        }

        // Reconstruct: Keep it mostly green but allow it to fade to black
        // We zero out Red and Blue for the matrix look during fade.
        pixel.* = (@as(u32, g) << 8) | 0xFF000000;
    }
}
