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

use crate::{bus::Bus, clock::ClockController, dma::DmaAllocator};

pub struct DeviceInitContext {
    pub dma_allocator: Arc<dyn DmaAllocator>,
}

pub trait Device: Sync + Send {
    fn display_name(&self) -> &str;

    /// Initializes the device, making it ready for operation.
    /// The method is also responsible for registering the device with appropriate OS subsystems
    /// (e.g. registering a terminal ttySn for a serial port)
    ///
    /// # Safety
    ///
    /// The caller must make sure the function is only called once.
    unsafe fn init(self: Arc<Self>, cx: DeviceInitContext) -> Result<(), Error> {
        let _ = cx;
        Ok(())
    }

    /// Initializes the IRQ handling options on this device: binds its IRQ(s) to their handlers and
    /// enables their reception.
    ///
    /// # Safety
    ///
    /// The caller must make sure the function is only called once. The caller must also make sure
    /// the function is not called before the device's [Device::init] is called.
    unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
        Ok(())
    }

    /// Deinitializes device into an "unusable"/"destroyed" state when it's removed. The device
    /// should return an error for all following requests after calling this method.
    ///
    /// # Safety
    ///
    /// This function directly affects the device state, the caller must take care of proper
    /// synchronization.
    unsafe fn deinit(&self) -> Result<(), Error> {
        Ok(())
    }

    fn as_bus(self: Arc<Self>) -> Option<Arc<dyn Bus>> {
        None
    }

    fn as_clock_controller(self: Arc<Self>) -> Option<Arc<dyn ClockController>> {
        None
    }
}
