use crate::node::{Cells, Fdt};

#[derive(Debug)]
#[repr(align(4))]
struct DevTreeBytes<const N: usize> {
    data: [u8; N],
}

static SIMPLE_TREE: DevTreeBytes<2157> = DevTreeBytes {
    data: *include_bytes!("../assets/simple.dtb"),
};

fn simple_tree() -> Fdt<'static> {
    unsafe { Fdt::from_raw(SIMPLE_TREE.data.as_ptr()).expect("Failed to construct devicetree") }
}

#[test]
fn root_node_test() {
    let fdt = simple_tree();

    let root = fdt.root_node();
    assert_eq!(root.name(), "");
    assert_eq!(
        root.compatible().map(|mut s| s.next()).flatten(),
        Some("dummy,simple64-board")
    );
}

#[test]
fn nested_nodes_test() {
    let fdt = simple_tree();

    let uart = fdt.find_compatible(&["ns16550a"]).expect("No node found");
    assert_eq!(uart.name(), "serial@10000000");

    let led = fdt.find_compatible(&["gpio-leds"]).expect("No node found");
    assert_eq!(led.name(), "led");

    let led0 = led.subnodes().next().expect("No sub-sub nodes");
    assert_eq!(led0.name(), "led0");

    let led0_parent = led0.parent();
    assert_eq!(led0_parent, Some(led));
    assert_eq!(led0_parent.as_ref().unwrap().name(), "led");
    assert_eq!(
        led0_parent.as_ref().unwrap().parent().unwrap().name(),
        "soc"
    );

    assert_eq!(
        led0_parent
            .as_ref()
            .unwrap()
            .parent()
            .unwrap()
            .parent()
            .unwrap()
            .name(),
        ""
    );

    assert_eq!(
        led0_parent
            .unwrap()
            .parent()
            .unwrap()
            .parent()
            .unwrap()
            .parent(),
        None
    );
}

#[test]
fn reg_property_test() {
    let fdt = simple_tree();

    let uart = fdt.find_compatible(&["ns16550a"]).expect("No node found");
    let mut reg = uart
        .reg_addresses()
        .expect("Failed to parse reg as a list of addresses");

    assert_eq!(reg.next(), Some((0x10000000, 0x100)));
    assert_eq!(reg.next(), None);
}

#[test]
fn ranges_property_test() {
    let fdt = simple_tree();

    let soc = fdt
        .find_node(|node| node.name() == "soc")
        .expect("No node found");
    let mut ranges = soc
        .ranges_addresses()
        .expect("Failed to parse ranges as a list of addresses");

    assert_eq!(ranges.next(), Some((Cells::new(&[0, 0]), 0x0, 0x80000000)));
    assert_eq!(
        ranges.next(),
        Some((
            Cells::new(&[0, 0x80000000u32.to_be()]),
            0x80000000,
            0x80000000
        ))
    );
    assert_eq!(ranges.next(), None);
}

#[test]
fn find_node_test() {
    let fdt = simple_tree();

    _ = fdt
        .find_node(|node| node.name() == "soc")
        .expect("Expected to find node 'soc'");

    assert_eq!(
        fdt.find_node(|node| node.name() == "Doesn't exist"),
        None,
        "Expected to not find any node"
    );
}

#[test]
fn interrupts_nexus() {
    let fdt = simple_tree();
    let pci = fdt
        .find_compatible(&["dummy,simple-pci"])
        .expect("No pci node found");
    assert!(pci.name().starts_with("pci"));

    let mut interrupt_map_mask = pci
        .interrupt_map_mask()
        .expect("Failed to retrieve interrupt map mask");
    assert_eq!(
        interrupt_map_mask.next_array::<4>(),
        Some([0xf800, 0x00, 0x00, 0x07])
    );
    assert_eq!(interrupt_map_mask.next_cell(), None);

    let interrupt_map = pci
        .parse_interrupt_map()
        .expect("Failed to retrieve interrupt map");

    let map = interrupt_map
        .map(|(mut a_c, mut i_c, no, no_a_c, mut no_i_c)| {
            assert_eq!(a_c.len(), 3);
            assert_eq!(i_c.len(), 1);
            assert_eq!(no_a_c.len(), 0);
            assert_eq!(no_i_c.len(), 2);

            (
                a_c.next_array::<3>().unwrap(),
                i_c.next_cell().unwrap(),
                no,
                no_i_c.next_array::<2>().unwrap(),
            )
        })
        .collect::<Vec<_>>();
    let interrupt_controller = fdt
        .find_node(|n| n.is_interrupt_controller())
        .expect("Failed to find interrupt controller");

    assert_eq!(
        map,
        &[
            ([0x8800, 0, 0], 1, interrupt_controller.clone(), [2, 1]),
            ([0x8800, 0, 0], 2, interrupt_controller.clone(), [3, 1]),
            ([0x8800, 0, 0], 3, interrupt_controller.clone(), [4, 1]),
            ([0x8800, 0, 0], 4, interrupt_controller.clone(), [1, 1]),
            ([0x9000, 0, 0], 1, interrupt_controller.clone(), [3, 1]),
            ([0x9000, 0, 0], 2, interrupt_controller.clone(), [4, 1]),
            ([0x9000, 0, 0], 3, interrupt_controller.clone(), [1, 1]),
            ([0x9000, 0, 0], 4, interrupt_controller.clone(), [2, 1]),
        ]
    );
}
