// Frame-local scratch arena. Allocates from a single Vec, resets each frame.
// Avoids per-frame heap allocations for temporary render data.

/// Frame-local scratch arena. Allocates from a single Vec, resets each frame.
/// Avoids per-frame heap allocations for temporary render data.
pub struct FrameArena {
    data: Vec<u8>,
    offset: usize,
}

impl FrameArena {
    /// Create an arena with the given fixed capacity in bytes.
    pub fn new(capacity: usize) -> Self {
        Self {
            data: vec![0u8; capacity],
            offset: 0,
        }
    }

    /// Allocate `size` bytes from the arena, 8-byte aligned.
    /// Returns `None` if the arena is full.
    pub fn allocate(&mut self, size: usize) -> Option<&mut [u8]> {
        let aligned = (size + 7) & !7; // 8-byte alignment
        if self.offset + aligned > self.data.len() {
            return None;
        }
        let start = self.offset;
        self.offset += aligned;
        Some(&mut self.data[start..start + size])
    }

    /// Reset the arena for the next frame. Does not deallocate.
    pub fn reset(&mut self) {
        self.offset = 0;
    }

    /// Number of bytes currently allocated.
    pub fn used(&self) -> usize {
        self.offset
    }

    /// Total capacity in bytes.
    pub fn capacity(&self) -> usize {
        self.data.len()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn new_arena() {
        let arena = FrameArena::new(1024);
        assert_eq!(arena.capacity(), 1024);
        assert_eq!(arena.used(), 0);
    }

    #[test]
    fn allocate_returns_writable_slice() {
        let mut arena = FrameArena::new(256);
        let slice = arena.allocate(4).unwrap();
        assert_eq!(slice.len(), 4);
        slice.copy_from_slice(&[1, 2, 3, 4]);
    }

    #[test]
    fn allocate_aligns_to_8() {
        let mut arena = FrameArena::new(256);
        arena.allocate(3).unwrap(); // 3 bytes, padded to 8
        assert_eq!(arena.used(), 8);
        arena.allocate(1).unwrap(); // 1 byte, padded to 8
        assert_eq!(arena.used(), 16);
    }

    #[test]
    fn allocate_over_capacity_returns_none() {
        let mut arena = FrameArena::new(16);
        assert!(arena.allocate(24).is_none());
    }

    #[test]
    fn allocate_fills_exactly() {
        let mut arena = FrameArena::new(16);
        assert!(arena.allocate(16).is_some());
        assert!(arena.allocate(1).is_none());
    }

    #[test]
    fn reset_allows_reuse() {
        let mut arena = FrameArena::new(16);
        arena.allocate(16).unwrap();
        arena.reset();
        assert_eq!(arena.used(), 0);
        assert!(arena.allocate(8).is_some());
    }
}
