use crate::{ConfigRegionAccess, PciAddress};
use bit_field::BitField;
use core::fmt::Formatter;
mod msi;
mod msix;
pub use msi::{MsiCapability, MultipleMessageSupport, TriggerMode};
pub use msix::MsixCapability;
#[derive(Clone, Copy)]
pub struct PciCapabilityAddress {
    pub address: PciAddress,
    pub offset: u16,
}
impl core::fmt::Debug for PciCapabilityAddress {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        write!(f, "{}, offset: {:02x}", self.address, self.offset)
    }
}
#[derive(Clone, Copy, Debug)]
pub enum PciCapability {
    PowerManagement(PciCapabilityAddress),
    AcceleratedGraphicsPort(PciCapabilityAddress),
    VitalProductData(PciCapabilityAddress),
    SlotIdentification(PciCapabilityAddress),
    Msi(MsiCapability),
    CompactPCIHotswap(PciCapabilityAddress),
    PciX(PciCapabilityAddress),
    HyperTransport(PciCapabilityAddress),
    Vendor(PciCapabilityAddress),
    DebugPort(PciCapabilityAddress),
    CompactPCICentralResourceControl(PciCapabilityAddress),
    PciHotPlugControl(PciCapabilityAddress),
    BridgeSubsystemVendorId(PciCapabilityAddress),
    AGP3(PciCapabilityAddress),
    PciExpress(PciCapabilityAddress),
    MsiX(MsixCapability),
    Unknown { address: PciCapabilityAddress, id: u8 },
}
impl PciCapability {
    fn parse(
        id: u8,
        address: PciCapabilityAddress,
        extension: u16,
        access: impl ConfigRegionAccess,
    ) -> Option<PciCapability> {
        match id {
            0x00 => None, 0x01 => Some(PciCapability::PowerManagement(address)),
            0x02 => Some(PciCapability::AcceleratedGraphicsPort(address)),
            0x03 => Some(PciCapability::VitalProductData(address)),
            0x04 => Some(PciCapability::SlotIdentification(address)),
            0x05 => Some(PciCapability::Msi(MsiCapability::new(address, extension))),
            0x06 => Some(PciCapability::CompactPCIHotswap(address)),
            0x07 => Some(PciCapability::PciX(address)),
            0x08 => Some(PciCapability::HyperTransport(address)),
            0x09 => Some(PciCapability::Vendor(address)),
            0x0A => Some(PciCapability::DebugPort(address)),
            0x0B => Some(PciCapability::CompactPCICentralResourceControl(address)),
            0x0C => Some(PciCapability::PciHotPlugControl(address)),
            0x0D => Some(PciCapability::BridgeSubsystemVendorId(address)),
            0x0E => Some(PciCapability::AGP3(address)),
            0x10 => Some(PciCapability::PciExpress(address)),
            0x11 => Some(PciCapability::MsiX(MsixCapability::new(address, extension, access))),
            _ => Some(PciCapability::Unknown { address, id }),
        }
    }
    pub fn address(&self) -> PciCapabilityAddress {
        match *self {
            PciCapability::PowerManagement(address) => address,
            PciCapability::AcceleratedGraphicsPort(address) => address,
            PciCapability::VitalProductData(address) => address,
            PciCapability::SlotIdentification(address) => address,
            PciCapability::Msi(msi_cap) => msi_cap.address,
            PciCapability::CompactPCIHotswap(address) => address,
            PciCapability::PciX(address) => address,
            PciCapability::HyperTransport(address) => address,
            PciCapability::Vendor(address) => address,
            PciCapability::DebugPort(address) => address,
            PciCapability::CompactPCICentralResourceControl(address) => address,
            PciCapability::PciHotPlugControl(address) => address,
            PciCapability::BridgeSubsystemVendorId(address) => address,
            PciCapability::AGP3(address) => address,
            PciCapability::PciExpress(address) => address,
            PciCapability::MsiX(msix_cap) => msix_cap.address,
            PciCapability::Unknown { address, id: _ } => address,
        }
    }
}
pub struct CapabilityIterator<T: ConfigRegionAccess> {
    address: PciAddress,
    offset: u16,
    access: T,
}
impl<T: ConfigRegionAccess> CapabilityIterator<T> {
    pub(crate) fn new(address: PciAddress, offset: u16, access: T) -> CapabilityIterator<T> {
        CapabilityIterator { address, offset, access }
    }
}
impl<T: ConfigRegionAccess> Iterator for CapabilityIterator<T> {
    type Item = PciCapability;
    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if self.offset == 0 {
                return None;
            }
            let data = unsafe { self.access.read(self.address, self.offset) };
            let next_ptr = data.get_bits(8..16);
            let id = data.get_bits(0..8);
            let extension = data.get_bits(16..32) as u16;
            let cap = PciCapability::parse(
                id as u8,
                PciCapabilityAddress { address: self.address, offset: self.offset },
                extension,
                &self.access,
            );
            self.offset = next_ptr as u16;
            if let Some(cap) = cap {
                return Some(cap);
            }
        }
    }
}