
use crate::data::error::EcState;
use crate::slave::core::Slave;

pub trait SlaveIterExt: Iterator<Item = Slave> + Sized {

    fn by_state(self, state: EcState) -> ByState<Self> {
        ByState { inner: self, target: state }
    }

    fn by_group(self, group: u8) -> ByGroup<Self> {
        ByGroup { inner: self, group }
    }

    fn by_vendor(self, vendor_id: u32) -> ByVendor<Self> {
        ByVendor { inner: self, vendor_id }
    }

    fn filter_slave<F: FnMut(&Slave) -> bool>(self, pred: F) -> FilterSlave<Self, F> {
        FilterSlave { inner: self, pred }
    }

    fn with_dc(self) -> WithDc<Self> {
        WithDc { inner: self }
    }
}

impl<I: Iterator<Item = Slave>> SlaveIterExt for I {}

pub struct ByState<I> {
    inner: I,
    target: EcState,
}

impl<I: Iterator<Item = Slave>> Iterator for ByState<I> {
    type Item = Slave;
    fn next(&mut self) -> Option<Self::Item> {
        let target = self.target;
        self.inner.find(|s| s.state().map(|st| st == target).unwrap_or(false))
    }
}

pub struct ByGroup<I> {
    inner: I,
    group: u8,
}

impl<I: Iterator<Item = Slave>> Iterator for ByGroup<I> {
    type Item = Slave;
    fn next(&mut self) -> Option<Self::Item> {
        let g = self.group;
        self.inner.find(|s| s.group() == g)
    }
}

pub struct ByVendor<I> {
    inner: I,
    vendor_id: u32,
}

impl<I: Iterator<Item = Slave>> Iterator for ByVendor<I> {
    type Item = Slave;
    fn next(&mut self) -> Option<Self::Item> {
        let v = self.vendor_id;
        self.inner.find(|s| s.vendor_id() == v)
    }
}

pub struct FilterSlave<I, F> {
    inner: I,
    pred: F,
}

impl<I, F> Iterator for FilterSlave<I, F>
where
    I: Iterator<Item = Slave>,
    F: FnMut(&Slave) -> bool,
{
    type Item = Slave;
    fn next(&mut self) -> Option<Self::Item> {
        let pred = &mut self.pred;
        self.inner.find(|s| pred(s))
    }
}

pub struct WithDc<I> {
    inner: I,
}

impl<I: Iterator<Item = Slave>> Iterator for WithDc<I> {
    type Item = Slave;
    fn next(&mut self) -> Option<Self::Item> {
        self.inner.find(|s| {

            s.detailed_info().map(|info| info.hasdc != 0).unwrap_or(false)
        })
    }
}
