use libk_util::OneTimeInit;
use yggdrasil_abi::error::Error;

use crate::{PciAddress, PciConfigurationSpace};

/// Provides access to the legacy (port I/O-driven) PCI configuration space
#[derive(Clone, Debug)]
#[repr(transparent)]
pub struct PciLegacyConfigurationSpace {
    address: PciAddress,
}

pub trait LegacyPciAccess {
    fn read_u32(&self, bus: u8, slot: u8, func: u8, offset: u8) -> u32;
    fn write_u32(&self, bus: u8, slot: u8, func: u8, offset: u8, value: u32);
}

impl PciConfigurationSpace for PciLegacyConfigurationSpace {
    fn read_u32(&self, offset: usize) -> u32 {
        PCI.get().read_u32(
            self.address.bus,
            self.address.device,
            self.address.function,
            offset as _,
        )
    }

    fn write_u32(&self, offset: usize, value: u32) {
        PCI.get().write_u32(
            self.address.bus,
            self.address.device,
            self.address.function,
            offset as _,
            value,
        )
    }
}

impl PciLegacyConfigurationSpace {
    pub fn probe(address: PciAddress) -> Result<Option<Self>, Error> {
        let this = PciLegacyConfigurationSpace { address };
        Ok(if this.is_valid() { Some(this) } else { None })
    }
}

pub(crate) static PCI: OneTimeInit<&'static dyn LegacyPciAccess> = OneTimeInit::new();
