use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq)]
pub enum ExpandableItemState<I, V> {
    Open(Vec<TreeItem<I, V>>),
    Closed,
}
pub trait ItemPath: PartialEq {
    fn item_starts_with(&self, other: &Self) -> bool;
}
impl ItemPath for PathBuf {
    fn item_starts_with(&self, other: &Self) -> bool {
        self.starts_with(other)
    }
}
#[derive(Debug, Clone, PartialEq)]
pub enum TreeItem<I, V> {
    Expandable {
        id: I,
        value: V,
        state: ExpandableItemState<I, V>,
    },
    Standalone {
        id: I,
        value: V,
    },
}
impl<I, V> TreeItem<I, V>
where
    I: ItemPath + Clone,
    V: Clone + PartialEq,
{
    pub fn id(&self) -> &I {
        match self {
            Self::Expandable { id, .. } => id,
            Self::Standalone { id, .. } => id,
        }
    }
    pub fn set_state(&mut self, item_id: &I, item_state: &ExpandableItemState<I, V>) {
        if let TreeItem::Expandable { id, state, .. } = self {
            if id == item_id {
                *state = item_state.clone();
            } else if item_id.item_starts_with(id) {
                if let ExpandableItemState::Open(items) = state {
                    for item in items {
                        item.set_state(item_id, item_state);
                    }
                }
            }
        }
    }
    pub fn flat(&self, depth: usize, root_id: &I) -> Vec<FlatItem<I>> {
        let mut flat_items = vec![self.clone().into_flat(depth, root_id.clone())];
        if let TreeItem::Expandable {
            state: ExpandableItemState::Open(items),
            ..
        } = self
        {
            for item in items {
                let inner_items = item.flat(depth + 1, root_id);
                flat_items.extend(inner_items);
            }
        }
        flat_items
    }
    fn into_flat(self, depth: usize, root_id: I) -> FlatItem<I> {
        match self {
            TreeItem::Standalone { id, .. } => FlatItem {
                id,
                is_standalone: true,
                is_open: false,
                depth,
                root_id,
            },
            TreeItem::Expandable { id, state, .. } => FlatItem {
                id,
                is_standalone: false,
                is_open: state != ExpandableItemState::Closed,
                depth,
                root_id,
            },
        }
    }
}
#[derive(Clone, Debug, PartialEq)]
pub struct FlatItem<I> {
    pub id: I,
    pub is_open: bool,
    pub is_standalone: bool,
    pub depth: usize,
    pub root_id: I,
}
#[cfg(test)]
mod test {
    use crate::FlatItem;
    #[test]
    fn tree() {
        use std::{
            path::PathBuf,
            str::FromStr,
        };
        use crate::{
            ExpandableItemState,
            TreeItem,
        };
        let mut tree = TreeItem::Expandable {
            id: PathBuf::from_str("/").unwrap(),
            value: (),
            state: ExpandableItemState::Open(vec![
                TreeItem::Expandable {
                    id: PathBuf::from_str("/1").unwrap(),
                    value: (),
                    state: ExpandableItemState::Open(vec![
                        TreeItem::Standalone {
                            id: PathBuf::from_str("/1/1").unwrap(),
                            value: (),
                        },
                        TreeItem::Standalone {
                            id: PathBuf::from_str("/1/2").unwrap(),
                            value: (),
                        },
                    ]),
                },
                TreeItem::Expandable {
                    id: PathBuf::from_str("/2").unwrap(),
                    value: (),
                    state: ExpandableItemState::Closed,
                },
                TreeItem::Standalone {
                    id: PathBuf::from_str("/3").unwrap(),
                    value: (),
                },
            ]),
        };
        tree.set_state(
            &PathBuf::from_str("/1").unwrap(),
            &ExpandableItemState::Closed,
        );
        tree.set_state(
            &PathBuf::from_str("/2").unwrap(),
            &ExpandableItemState::Open(vec![TreeItem::Expandable {
                id: PathBuf::from_str("/2/1").unwrap(),
                value: (),
                state: ExpandableItemState::Open(vec![]),
            }]),
        );
        tree.set_state(
            &PathBuf::from_str("/2/1").unwrap(),
            &ExpandableItemState::Closed,
        );
        tree.set_state(
            &PathBuf::from_str("/3").unwrap(),
            &ExpandableItemState::Closed,
        );
        let flat_items = tree.flat(0, &PathBuf::from_str("/").unwrap());
        assert_eq!(
            flat_items,
            vec![
                FlatItem {
                    id: PathBuf::from_str("/").unwrap(),
                    is_open: true,
                    is_standalone: false,
                    depth: 0,
                    root_id: PathBuf::from_str("/").unwrap(),
                },
                FlatItem {
                    id: PathBuf::from_str("/1").unwrap(),
                    is_open: false,
                    is_standalone: false,
                    depth: 1,
                    root_id: PathBuf::from_str("/").unwrap(),
                },
                FlatItem {
                    id: PathBuf::from_str("/2").unwrap(),
                    is_open: true,
                    is_standalone: false,
                    depth: 1,
                    root_id: PathBuf::from_str("/").unwrap(),
                },
                FlatItem {
                    id: PathBuf::from_str("/2/1").unwrap(),
                    is_open: false,
                    is_standalone: false,
                    depth: 2,
                    root_id: PathBuf::from_str("/").unwrap(),
                },
                FlatItem {
                    id: PathBuf::from_str("/3").unwrap(),
                    is_open: false,
                    is_standalone: true,
                    depth: 1,
                    root_id: PathBuf::from_str("/").unwrap(),
                },
            ]
        )
    }
}