use kernel_arch_x86::intrinsics::{IoPort, IoPortAccess};
use libk_util::sync::IrqSafeSpinlock;
use ygg_driver_pci::LegacyPciAccess;

struct LegacyPciInner {
    address: IoPort<u32>,
    data: IoPort<u32>,
}

pub(super) struct LegacyPci {
    inner: IrqSafeSpinlock<LegacyPciInner>,
}

pub(super) static PCI: LegacyPci = LegacyPci {
    inner: IrqSafeSpinlock::new(LegacyPciInner {
        address: IoPort::new(0xCF8),
        data: IoPort::new(0xCFC),
    }),
};

impl LegacyPciAccess for LegacyPci {
    fn write_u32(&self, bus: u8, slot: u8, func: u8, offset: u8, value: u32) {
        assert_eq!(offset & 0x3, 0);
        let inner = self.inner.lock();
        inner.address.write(Self::addr(bus, slot, func, offset));
        inner.data.write(value);
    }

    fn read_u32(&self, bus: u8, slot: u8, func: u8, offset: u8) -> u32 {
        assert_eq!(offset & 0x3, 0);
        let inner = self.inner.lock();
        inner.address.write(Self::addr(bus, slot, func, offset));
        inner.data.read()
    }
}

impl LegacyPci {
    #[inline]
    const fn addr(bus: u8, slot: u8, func: u8, offset: u8) -> u32 {
        ((bus as u32) << 16)
            | ((slot as u32) << 11)
            | ((func as u32) << 8)
            | (offset as u32 & 0xFC)
            | (1 << 31)
    }
}
