// SPDX-License-Identifier: LCL-1.0
// Copyright (c) 2026 Markus Maiwald
// Stewardship: Self Sovereign Society Foundation
//
// This file is part of the Nexus Commonwealth.
// See legal/LICENSE_COMMONWEALTH.md for license terms.

//! Rumpk Layer 0: Surface Allocator
//!
//! Manages contiguous memory chunks for window buffers (compositor).
//! Provides a simple pool-based allocation for up to 16 surfaces.
//!
//! DECISION(Graphics): Fixed pool size of 32MB for surfaces.
//! Bump-style allocation; reclamation requires system reset.

const std = @import("std");
const uart = @import("uart.zig");

pub const MAX_SURFACES = 16;
pub const SURFACE_POOL_SIZE = 1 * 1024 * 1024; // 1MB for surfaces (temporary reduction)

// Surface Descriptor
pub const Surface = struct {
    id: i32,
    ptr: [*]u32,
    width: u32,
    height: u32,
    active: bool,
};

// Global Surface Pool
// SAFETY(Surfaces): Array is initialized by hal_surface_init before use.
var surfaces: [MAX_SURFACES]Surface = undefined;
var next_surface_id: i32 = 1;

// Backing memory for surfaces (in BSS)
// SAFETY(SurfaceHeap): Memory written by alloc before any read.
// Initialized to `undefined` to avoid zeroing 32MB at boot.
var surface_heap: [SURFACE_POOL_SIZE]u8 align(4096) = undefined;
var heap_offset: usize = 0;

export fn hal_surface_init() void {
    for (&surfaces) |*s| {
        s.id = -1;
        s.active = false;
        // SAFETY(Surfaces): Pointer reset to `undefined`. Populated during allocation.
        s.ptr = undefined;
        s.width = 0;
        s.height = 0;
    }
    heap_offset = 0;
    uart.print("[Surface] Allocator Initialized. Pool: 32MB\n");
}

pub fn alloc(width: u32, height: u32) ?i32 {
    const size = width * height * 4;

    // Alignment to 4096
    const aligned_size = (size + 4095) & ~@as(u32, 4095);

    if (heap_offset + aligned_size > SURFACE_POOL_SIZE) {
        uart.print("[Surface] ERROR: Out of Memory in Surface Pool!\n");
        return null;
    }

    // Find free slot
    var slot: ?*Surface = null;
    for (&surfaces) |*s| {
        if (!s.active) {
            slot = s;
            break;
        }
    }

    if (slot) |s| {
        s.id = next_surface_id;
        next_surface_id += 1;
        s.width = width;
        s.height = height;
        s.ptr = @ptrCast(@alignCast(&surface_heap[heap_offset]));
        s.active = true;

        heap_offset += aligned_size;

        uart.print("[Surface] Allocated ID=");
        uart.print_hex(@intCast(s.id));
        uart.print(" Size=");
        uart.print_hex(size);
        uart.print("\n");

        return s.id;
    }

    uart.print("[Surface] ERROR: Max Surfaces Reached!\n");
    return null;
}

pub fn get_surface(id: i32) ?*Surface {
    for (&surfaces) |*s| {
        if (s.active and s.id == id) return s;
    }
    return null;
}

pub fn free(id: i32) bool {
    for (&surfaces) |*s| {
        if (s.active and s.id == id) {
            s.active = false;
            // Note: In our simple bump-style allocator, we don't reclaim heap space
            // unless we implement a real allocator. For now, we assume surfaces are
            // mostly permanent or the system reboots.
            return true;
        }
    }
    return false;
}

// Exported for Nim
export fn hal_surface_alloc(w: u32, h: u32) i32 {
    return alloc(w, h) orelse -1;
}

export fn hal_surface_get_ptr(id: i32) ?[*]u32 {
    if (get_surface(id)) |s| return s.ptr;
    return null;
}
