use core::{fmt::Debug, ops::Range};
use crate::{error::FdtResult, node::Node, read::FdtReader, FdtError, FdtRangeIter};
pub struct Pci<'a> {
    pub node: Node<'a>,
}
impl<'a> Pci<'a> {
    pub fn bus_range(&self) -> Option<Range<usize>> {
        let prop = self.node.find_property("bus-range")?;
        let mut reader = FdtReader::new(prop.raw_value());
        let start = reader.take_u32()?;
        let end = reader.take_u32()?;
        Some(start as usize..end as usize)
    }
    pub fn ranges(&'a self) -> FdtResult<impl Iterator<Item = PciRange> + 'a> {
        let ranges = self
            .node
            .node_ranges()
            .ok_or(FdtError::NotFound("ranges"))?;
        let iter = ranges.iter();
        Ok(PciRangeIter { iter })
    }
}
pub struct PciRangeIter<'a> {
    iter: FdtRangeIter<'a>,
}
impl Iterator for PciRangeIter<'_> {
    type Item = PciRange;
    fn next(&mut self) -> Option<Self::Item> {
        let one = self.iter.next()?;
        let mut child = one.child_bus_address();
        let cpu_address = one.parent_bus_address().as_u64();
        let size = one.size;
        let hi = child.next().unwrap();
        let mid = child.next().unwrap();
        let low = child.next().unwrap();
        let ss = (hi >> 24) & 0b11;
        let space = match ss {
            0b00 => PciSpace::Configuration,
            0b01 => PciSpace::IO,
            0b10 => PciSpace::Memory32,
            0b11 => PciSpace::Memory64,
            _ => panic!(),
        };
        let child_bus_address = (mid as u64) << 32 | low as u64;
        Some(PciRange {
            space,
            bus_address: child_bus_address,
            cpu_address,
            size,
        })
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PciSpace {
    Configuration,
    IO,
    Memory32,
    Memory64,
}
#[derive(Clone, PartialEq, Eq)]
pub struct PciRange {
    pub space: PciSpace,
    pub bus_address: u64,
    pub cpu_address: u64,
    pub size: u64,
}
impl Debug for PciRange {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "PciRange {{ space: {:?}, child_bus_address: {:#x}, parent_bus_address: {:#x}, size: {:#x} }}", 
        self.space, self.bus_address, self.cpu_address, self.size)
    }
}