use fdt::{node::FdtNode, Fdt};
use log::debug;
use system_error::SystemError;

use crate::{
    driver::{
        open_firmware::fdt::open_firmware_fdt_driver,
        pci::ecam::{pci_ecam_root_info_manager, EcamRootInfo},
    },
    mm::PhysAddr,
};

pub(super) fn pci_host_ecam_driver_init(fdt: &Fdt<'_>) -> Result<(), SystemError> {
    let do_check = |node: FdtNode| -> Result<(), SystemError> {
        let reg = node
            .reg()
            .ok_or(SystemError::EINVAL)?
            .next()
            .ok_or(SystemError::EINVAL)?;
        let paddr = reg.starting_address as usize;
        let size = reg.size.unwrap_or(0);
        let bus_range: &[u8] = node.property("bus-range").ok_or(SystemError::EINVAL)?.value;

        let (bus_begin, bus_end) = match bus_range.len() {
            8 => (
                u32::from_be_bytes(bus_range[0..4].try_into().unwrap()),
                u32::from_be_bytes(bus_range[4..8].try_into().unwrap()),
            ),
            _ => panic!("Unexpected bus-range length"),
        };

        let segement_group_number: &[u8] = node
            .property("linux,pci-domain")
            .ok_or(SystemError::EINVAL)?
            .value;

        let segement_group_number = match segement_group_number.len() {
            4 => u32::from_be_bytes(segement_group_number[0..4].try_into().unwrap()),
            _ => panic!("Unexpected linux,pci-domain length"),
        };

        debug!(
            "pci_host_ecam_driver_init(): {} paddr: {:#x} size: {:#x} bus-range: {}-{} segement_group_number: {}",
            node.name, paddr, size, bus_begin, bus_end, segement_group_number
        );

        pci_ecam_root_info_manager().add_ecam_root_info(EcamRootInfo::new(
            segement_group_number.try_into().unwrap(),
            bus_begin as u8,
            bus_end as u8,
            PhysAddr::new(paddr),
        ));

        Ok(())
    };

    for node in open_firmware_fdt_driver().find_node_by_compatible(&fdt, "pci-host-ecam-generic") {
        if let Err(err) = do_check(node) {
            debug!(
                "pci_host_ecam_driver_init(): check {} error: {:?}",
                node.name, err
            );
        }
    }

    return Ok(());
}
