//! Device tree driver registration
use alloc::{sync::Arc, vec::Vec};
use fdt_rs::spec::Phandle;
use libk_util::{hash_table::DefaultHashTable, sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};

use crate::DeviceTree;

use super::{Driver, Node};

pub(crate) struct DriverRegistration {
    pub(crate) compatible: &'static [&'static str],
    pub(crate) imp: &'static dyn Driver,
}

pub(crate) static DRIVERS: IrqSafeRwLock<Vec<DriverRegistration>> = IrqSafeRwLock::new(Vec::new());
pub(crate) static ROOT: OneTimeInit<Arc<Node>> = OneTimeInit::new();
pub(crate) static DEVICE_TREE: OneTimeInit<&'static DeviceTree> = OneTimeInit::new();
pub(crate) static PHANDLE_MAP: IrqSafeRwLock<DefaultHashTable<Phandle, Arc<Node>>> =
    IrqSafeRwLock::new(DefaultHashTable::new());

impl DriverRegistration {
    pub(crate) fn matches(&self, compatible: &str) -> bool {
        self.compatible.contains(&compatible)
    }
}

#[doc(hidden)]
pub fn register_driver(compatible: &'static [&'static str], driver: &'static dyn Driver) {
    DRIVERS.write().push(DriverRegistration {
        compatible,
        imp: driver,
    });
}

pub(crate) fn register_phandle(phandle: Phandle, node: Arc<Node>) {
    PHANDLE_MAP.write().insert(phandle, node);
}

/// Looks up a node by its `phandle` property. If `ensure_probed` is `true`, will also ensure
/// the found node has a driver/device probed, otherwise it will return a [None].
pub fn lookup_phandle(phandle: Phandle, ensure_probed: bool) -> Option<Arc<Node>> {
    let node = PHANDLE_MAP.read().get(&phandle).cloned();
    if let Some(node) = node.as_ref()
        && ensure_probed
    {
        node.probe()?;
    }
    node
}
