use super::*;
pub trait FlatScanFn {
    type InputItem;
    type InputResult;
    type OutputList: ListFn<End = Self>;
    type EndList: ListFn<Item = <Self::OutputList as ListFn>::Item>;
    fn map_item(self, item: Self::InputItem) -> Self::OutputList;
    fn map_result(self, result: Self::InputResult) -> Self::EndList;
}
pub enum FlatScanState<I, F>
where
    I: ListFn,
    F: FlatScanFn<InputItem = I::Item>,
    I::End: ResultFn<Result = F::InputResult>,
{
    Begin {
        flat_scan: F,
        input_list: I,
    },
    OutputList {
        output_list: F::OutputList,
        input_list: I,
    },
    EndList(F::EndList),
}
impl<I, F> ListFn for FlatScanState<I, F>
where
    I: ListFn,
    F: FlatScanFn<InputItem = I::Item>,
    I::End: ResultFn<Result = F::InputResult>,
{
    type Item = <F::OutputList as ListFn>::Item;
    type End = <F::EndList as ListFn>::End;
    fn next(mut self) -> ListState<Self> {
        loop {
            self = match self {
                FlatScanState::Begin {
                    input_list,
                    flat_scan,
                } => match input_list.next() {
                    ListState::Some(some) => FlatScanState::OutputList {
                        output_list: flat_scan.map_item(some.first),
                        input_list: some.next,
                    },
                    ListState::End(end) => {
                        FlatScanState::EndList(flat_scan.map_result(end.result()))
                    }
                },
                FlatScanState::OutputList {
                    output_list,
                    input_list,
                } => match output_list.next() {
                    ListState::Some(some) => {
                        return ListState::some(
                            some.first,
                            FlatScanState::OutputList {
                                output_list: some.next,
                                input_list,
                            },
                        )
                    }
                    ListState::End(flat_scan) => FlatScanState::Begin {
                        input_list,
                        flat_scan,
                    },
                },
                FlatScanState::EndList(end_list) => {
                    return match end_list.next() {
                        ListState::Some(some) => {
                            ListState::some(some.first, FlatScanState::EndList(some.next))
                        }
                        ListState::End(end) => ListState::End(end),
                    }
                }
            }
        }
    }
}
pub trait FlatScan: ListFn {
    fn flat_scan<F>(self, flat_scan: F) -> FlatScanState<Self, F>
    where
        F: FlatScanFn<InputItem = Self::Item>,
        Self::End: ResultFn<Result = F::InputResult>,
    {
        FlatScanState::Begin {
            input_list: self,
            flat_scan,
        }
    }
}
impl<S: ListFn> FlatScan for S {}
#[cfg(test)]
mod tests {
    use super::*;
    struct TestFlatScan();
    impl ResultFn for TestFlatScan {
        type Result = ();
        fn result(self) {}
    }
    impl FlatScanFn for TestFlatScan {
        type InputItem = ();
        type InputResult = ();
        type OutputList = Empty<(), Self>;
        type EndList = Empty<(), Self>;
        fn map_item(self, _: ()) -> Self::OutputList {
            Empty::new(self)
        }
        fn map_result(self, _: ()) -> Self::EndList {
            Empty::new(self)
        }
    }
    #[test]
    fn flat_scan_end() {
        let x = Empty::<(), ()>::new(());
        let f = TestFlatScan();
        let list = x.flat_scan(f);
        let _list1 = list.next();
    }
}