use crate::errors::AleaticoResult;

/// GPU rendering context
pub struct Gpu {
    pub(crate) instance: wgpu::Instance,
    pub(crate) adapter: wgpu::Adapter,
    pub(crate) device: wgpu::Device,
    pub(crate) queue: wgpu::Queue,
}

impl Gpu {
    /// Create a new instance of [`Gpu`]
    pub async fn new(instance: wgpu::Instance, adapter: wgpu::Adapter) -> AleaticoResult<Self> {
        let (device, queue) = adapter
            .request_device(&wgpu::DeviceDescriptor {
                label: None,
                required_features: wgpu::Features::empty(),
                experimental_features: wgpu::ExperimentalFeatures::disabled(),
                required_limits: if cfg!(target_arch = "wasm32") {
                    wgpu::Limits::downlevel_webgl2_defaults()
                } else {
                    wgpu::Limits::default()
                },
                memory_hints: Default::default(),
                trace: wgpu::Trace::Off,
            })
            .await?;

        Ok(Self {
            instance,
            device,
            adapter,
            queue,
        })
    }

    /// Get the [`wgpu`] instance
    pub fn instance(&self) -> &wgpu::Instance {
        &self.instance
    }

    /// Get the selected [`wgpu`] adapter
    pub fn adapter(&self) -> &wgpu::Adapter {
        &self.adapter
    }

    /// Get the [`wgpu`] device
    pub fn device(&self) -> &wgpu::Device {
        &self.device
    }

    /// Get the [`wgpu`] queue
    pub fn queue(&self) -> &wgpu::Queue {
        &self.queue
    }

    /// Create a command encoder
    pub fn create_encoder(&self, label: Option<&str>) -> wgpu::CommandEncoder {
        self.device
            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label })
    }

    /// Submit command buffers to the GPU queue
    pub fn submit<I>(&self, command_buffers: I)
    where
        I: IntoIterator<Item = wgpu::CommandBuffer>,
    {
        self.queue.submit(command_buffers);
    }
}
