use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::sync::Arc;
use std::vec;
use cairo_lang_filesystem::span::TextOffset;
use crate::node::db::SyntaxGroup;
use crate::node::ids::GreenId;
use crate::node::key_fields::get_key_fields;
use crate::node::kind::SyntaxKind;
use crate::node::stable_ptr::SyntaxStablePtr;
use crate::node::{SyntaxNode, SyntaxNodeInner};
pub struct SyntaxNodeChildIterator<'db> {
    db: &'db dyn SyntaxGroup,
    node: SyntaxNode,
    green_iterator: vec::IntoIter<GreenId>,
    offset: TextOffset,
    key_map: HashMap<(SyntaxKind, Vec<GreenId>), usize>,
}
impl<'db> Iterator for SyntaxNodeChildIterator<'db> {
    type Item = SyntaxNode;
    fn next(&mut self) -> Option<Self::Item> {
        let green_id = self.green_iterator.next()?;
        self.next_inner(green_id)
    }
}
impl<'db> DoubleEndedIterator for SyntaxNodeChildIterator<'db> {
    fn next_back(&mut self) -> Option<<SyntaxNodeChildIterator<'db> as Iterator>::Item> {
        let green_id = self.green_iterator.next_back()?;
        self.next_inner(green_id)
    }
}
impl<'db> ExactSizeIterator for SyntaxNodeChildIterator<'db> {
    fn len(&self) -> usize {
        self.green_iterator.len()
    }
}
impl<'db> SyntaxNodeChildIterator<'db> {
    pub(super) fn new(node: &SyntaxNode, db: &'db dyn SyntaxGroup) -> Self {
        SyntaxNodeChildIterator {
            db,
            node: node.clone(),
            green_iterator: node.green_node(db).children().into_iter(),
            offset: node.offset(),
            key_map: HashMap::new(),
        }
    }
    fn next_inner(
        &mut self,
        green_id: GreenId,
    ) -> Option<<SyntaxNodeChildIterator<'db> as Iterator>::Item> {
        let green = self.db.lookup_intern_green(green_id);
        let width = green.width();
        let kind = green.kind;
        let key_fields: Vec<GreenId> = get_key_fields(kind, green.children());
        let index = match self.key_map.entry((kind, key_fields.clone())) {
            Entry::Occupied(mut entry) => entry.insert(entry.get() + 1),
            Entry::Vacant(entry) => {
                entry.insert(1);
                0
            }
        };
        let stable_ptr = self.db.intern_stable_ptr(SyntaxStablePtr::Child {
            parent: self.node.0.stable_ptr,
            kind,
            key_fields,
            index,
        });
        let res = SyntaxNode(Arc::new(SyntaxNodeInner {
            green: green_id,
            offset: self.offset,
            parent: Some(self.node.clone()),
            stable_ptr,
        }));
        self.offset = self.offset.add_width(width);
        Some(res)
    }
}
#[derive(Debug, Copy, Clone)]
pub enum WalkEvent<T> {
    Enter(T),
    Leave(T),
}
impl<T> WalkEvent<T> {
    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> WalkEvent<U> {
        match self {
            WalkEvent::Enter(it) => WalkEvent::Enter(f(it)),
            WalkEvent::Leave(it) => WalkEvent::Leave(f(it)),
        }
    }
}
pub struct Preorder<'a> {
    db: &'a dyn SyntaxGroup,
    layers: Vec<PreorderLayer<'a>>,
}
struct PreorderLayer<'a> {
    start: SyntaxNode,
    children: Option<SyntaxNodeChildIterator<'a>>,
}
impl<'a> Preorder<'a> {
    pub(super) fn new(start: SyntaxNode, db: &'a dyn SyntaxGroup) -> Self {
        let initial_layer = PreorderLayer::<'a> { start, children: None };
        let mut layers = Vec::with_capacity(32);
        layers.push(initial_layer);
        Self { db, layers }
    }
}
impl<'a> Iterator for Preorder<'a> {
    type Item = WalkEvent<SyntaxNode>;
    fn next(&mut self) -> Option<Self::Item> {
        let mut layer = self.layers.pop()?;
        match layer.children.take() {
            None => {
                let event = WalkEvent::Enter(layer.start.clone());
                layer.children = Some(layer.start.children(self.db));
                self.layers.push(layer);
                Some(event)
            }
            Some(mut iter) => match iter.next() {
                None => {
                    Some(WalkEvent::Leave(layer.start.clone()))
                }
                Some(start) => {
                    let event = WalkEvent::Enter(start.clone());
                    let new_layer =
                        PreorderLayer { children: Some(start.children(self.db)), start };
                    layer.children = Some(iter);
                    self.layers.extend([layer, new_layer]);
                    Some(event)
                }
            },
        }
    }
}