use core::iter;
use crate::{
    clocks::{ClockRef, ClocksIter},
    error::{FdtError, FdtResult},
    interrupt::InterruptController,
    meta::MetaData,
    pci::Pci,
    property::Property,
    read::{FdtReader, U32Array2D},
    Fdt, FdtRangeSilce, FdtReg, Phandle, Token,
};
#[derive(Clone)]
pub struct Node<'a> {
    pub level: usize,
    pub name: &'a str,
    pub(crate) fdt: &'a Fdt<'a>,
    pub(crate) meta_parents: MetaData<'a>,
    pub(crate) meta: MetaData<'a>,
    body: FdtReader<'a>,
}
impl<'a> Node<'a> {
    pub(crate) fn new(
        fdt: &'a Fdt<'a>,
        level: usize,
        name: &'a str,
        reader: FdtReader<'a>,
        meta_parents: MetaData<'a>,
        meta: MetaData<'a>,
    ) -> Self {
        Self {
            fdt,
            level,
            body: reader,
            name,
            meta,
            meta_parents,
        }
    }
    pub fn name(&self) -> &'a str {
        self.name
    }
    pub fn propertys(&self) -> impl Iterator<Item = Property<'a>> + '_ {
        let reader = self.body.clone();
        PropIter {
            reader,
            fdt: self.fdt,
        }
    }
    pub fn find_property(&self, name: &str) -> Option<Property<'a>> {
        self.propertys().find(|x| x.name.eq(name))
    }
    pub fn reg(&self) -> Option<impl Iterator<Item = FdtReg> + 'a> {
        let mut iter = self.propertys();
        let reg = iter.find(|x| x.name.eq("reg"))?;
        Some(RegIter {
            size_cell: self.meta_parents.size_cells.unwrap(),
            address_cell: self.meta_parents.address_cells.unwrap(),
            prop: reg,
            ranges: self.meta_parents.range.clone(),
        })
    }
    pub(crate) fn node_ranges(&self) -> Option<FdtRangeSilce<'a>> {
        let prop = self.find_property("ranges")?;
        Some(FdtRangeSilce::new(
            self.meta.address_cells.unwrap(),
            self.meta_parents.address_cells.unwrap(),
            self.meta.size_cells.unwrap(),
            prop.data.clone(),
        ))
    }
    pub(crate) fn node_interrupt_parent(&self) -> Option<Phandle> {
        let prop = self.find_property("interrupt-parent")?;
        Some(prop.u32().into())
    }
    pub fn interrupt_parent(&self) -> Option<InterruptController<'a>> {
        let phandle = if let Some(p) = self.meta.interrupt_parent {
            Some(p)
        } else {
            self.meta_parents.interrupt_parent
        }?;
        self.fdt
            .get_node_by_phandle(phandle)
            .map(|node| InterruptController { node })
    }
    pub fn compatible(&self) -> Option<impl Iterator<Item = FdtResult<'a, &'a str>> + 'a> {
        let prop = self.find_property("compatible")?;
        let mut value = prop.data.clone();
        Some(iter::from_fn(move || {
            let s = value.take_str();
            match s {
                Ok(s) => {
                    if s.is_empty() {
                        None
                    } else {
                        Some(Ok(s))
                    }
                }
                Err(e) => match e {
                    FdtError::Eof => None,
                    _ => Some(Err(e)),
                },
            }
        }))
    }
    pub fn compatibles(&self) -> impl Iterator<Item = &'a str> + 'a {
        let mut cap_raw = self.compatible();
        iter::from_fn(move || {
            if let Some(caps) = &mut cap_raw {
                let cap = caps.next()?.ok()?;
                Some(cap)
            } else {
                None
            }
        })
    }
    pub fn phandle(&self) -> Option<Phandle> {
        let prop = self.find_property("phandle")?;
        Some(prop.u32().into())
    }
    pub fn interrupts(&self) -> Option<impl Iterator<Item = impl Iterator<Item = u32> + 'a> + 'a> {
        let prop = self.find_property("interrupts")?;
        let cell_size = self.interrupt_parent()?.interrupt_cells();
        Some(U32Array2D::new(prop.raw_value(), cell_size))
    }
    pub fn clocks(&'a self) -> impl Iterator<Item = ClockRef<'a>> + 'a {
        ClocksIter::new(self)
    }
    pub fn clock_frequency(&self) -> Option<u32> {
        let prop = self.find_property("clock-frequency")?;
        Some(prop.u32())
    }
    pub fn into_pci(self) -> Option<Pci<'a>> {
        if self.name.contains("pci") {
            Some(Pci { node: self })
        } else {
            None
        }
    }
}
struct RegIter<'a> {
    size_cell: u8,
    address_cell: u8,
    prop: Property<'a>,
    ranges: Option<FdtRangeSilce<'a>>,
}
impl<'a> Iterator for RegIter<'a> {
    type Item = FdtReg;
    fn next(&mut self) -> Option<Self::Item> {
        let child_bus_address = self.prop.data.take_by_cell_size(self.address_cell)?;
        let mut address = child_bus_address;
        if let Some(ranges) = &self.ranges {
            for one in ranges.iter() {
                let range_child_bus_address = one.child_bus_address().as_u64();
                let range_parent_bus_address = one.parent_bus_address().as_u64();
                if child_bus_address >= range_child_bus_address
                    && child_bus_address < range_child_bus_address + one.size as u64
                {
                    address =
                        child_bus_address - range_child_bus_address + range_parent_bus_address;
                }
            }
        }
        let size = if self.size_cell > 0 {
            Some(self.prop.data.take_by_cell_size(self.size_cell)? as usize)
        } else {
            None
        };
        Some(FdtReg {
            address,
            child_bus_address,
            size,
        })
    }
}
struct PropIter<'a> {
    fdt: &'a Fdt<'a>,
    reader: FdtReader<'a>,
}
impl<'a> Iterator for PropIter<'a> {
    type Item = Property<'a>;
    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.reader.take_token() {
                Some(token) => match token {
                    Token::Prop => break,
                    Token::Nop => {}
                    _ => return None,
                },
                None => return None,
            }
        }
        self.reader.take_prop(self.fdt)
    }
}