//! Display device structures and interfaces
use core::ops::{Deref, DerefMut};

use alloc::sync::Arc;
use yggdrasil_abi::error::Error;

use crate::device::display::{Color, DisplayDevice, DisplayOwner, PixelFormat};

/// Convenience wrapper for accessing display framebuffers from the kernel side
pub struct KernelFramebufferAccess {
    display: Arc<dyn DisplayDevice>,
    base: usize,
    stride: usize,
    format: PixelFormat,
    size: usize,
}

impl KernelFramebufferAccess {
    /// Attempts to acquire kernel access to a framebuffer of a display device
    pub fn acquire(display: Arc<dyn DisplayDevice>) -> Result<Self, Error> {
        // Lock the display by the kernel
        display.lock(DisplayOwner::Kernel)?;

        let Ok(framebuffer) = display.active_framebuffer() else {
            display.unlock(DisplayOwner::Kernel).ok();
            return Err(Error::InvalidOperation);
        };
        let Some(kernel_base) = framebuffer.kernel_base else {
            display.unlock(DisplayOwner::Kernel).ok();
            return Err(Error::InvalidOperation);
        };
        let Some(mode) = display.active_display_mode() else {
            display.unlock(DisplayOwner::Kernel).ok();
            return Err(Error::InvalidOperation);
        };

        Ok(Self {
            display,
            base: kernel_base,
            size: framebuffer.size / size_of::<u32>(),
            stride: framebuffer.stride / size_of::<u32>(),
            format: mode.pixel_format,
        })
    }

    /// Returns the framebuffer pixel format
    pub fn format(&self) -> PixelFormat {
        self.format
    }

    /// Updates a pixel in the framebuffer
    #[inline]
    #[optimize(speed)]
    pub fn set_pixel(&mut self, x: u32, y: u32, v: u32) {
        let p = y as usize * self.stride + x as usize;
        if p >= self.size {
            return;
        }
        self[p] = v;
    }

    /// Fills a rectangle with pixels
    #[optimize(speed)]
    pub fn fill_rect(&mut self, sx: u32, sy: u32, w: u32, h: u32, c: Color) {
        let v = c.to_pixel_u32(self.format);
        for y in sy..sy + h {
            let ry = y as usize * self.stride..;
            let row = &mut self[ry];
            row[sx as usize..sx as usize + w as usize].fill(v);
        }
    }
}

impl Deref for KernelFramebufferAccess {
    type Target = [u32];

    #[inline]
    fn deref(&self) -> &Self::Target {
        unsafe {
            core::slice::from_raw_parts(core::ptr::with_exposed_provenance(self.base), self.size)
        }
    }
}

impl DerefMut for KernelFramebufferAccess {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe {
            core::slice::from_raw_parts_mut(
                core::ptr::with_exposed_provenance_mut(self.base),
                self.size,
            )
        }
    }
}

impl Drop for KernelFramebufferAccess {
    fn drop(&mut self) {
        self.display.unlock(DisplayOwner::Kernel).ok();
    }
}
