use super::{
    super::{Position, PrimInt},
    ExploreStructuralPositions, Scout, SpHandle, StructuralPosition, StructuralPositionStore,
};
use crate::{
    position::TreePath,
    store::defaults::LabelIdentifier,
    types::{
        self, AnyType, HyperAST, NodeId, NodeStore, Tree, Typed, WithChildren, WithSerialization,
    },
};
use num::{one, zero};
use std::fmt::Debug;
impl<IdN, Idx: PrimInt> StructuralPositionStore<IdN, Idx> {
    pub fn with_position(x: StructuralPosition<IdN, Idx>) -> Self {
        let l = x.parents.len();
        assert!(!x.offsets[1..].contains(&zero()));
        let nodes = x.parents;
        Self {
            nodes,
            parents: (0..l).into_iter().collect(),
            offsets: x.offsets,
            }
    }
    pub fn new(root: IdN) -> Self {
        Self::with_position(StructuralPosition::new(root))
    }
}
impl<IdN, Idx> Default for StructuralPositionStore<IdN, Idx> {
    fn default() -> Self {
        Self {
            nodes: Default::default(),
            parents: Default::default(),
            offsets: Default::default(),
            }
    }
}
impl<IdN: NodeId, Idx: PrimInt> StructuralPositionStore<IdN, Idx> {
    pub fn get(&self, s: SpHandle) -> ExploreStructuralPositions<IdN, Idx> {
        ExploreStructuralPositions { sps: self, i: s.0, _phantom: Default::default() }
    }
    pub fn push_up_scout(&self, s: &mut Scout<IdN, Idx>) -> Option<IdN>
    where
        IdN: Copy + Eq + Debug,
    {
        s.up(self)
    }
    pub fn ends_positions<'store, HAST>(
        &'store self,
        stores: &'store HAST,
        ends: &[SpHandle],
    ) -> Vec<Position>
    where
        HAST: HyperAST<'store, IdN = IdN::IdN, Label = LabelIdentifier, Idx = Idx>,
        HAST::T: Typed<Type = AnyType> + WithSerialization + WithChildren,
        <<HAST as HyperAST<'store>>::T as types::WithChildren>::ChildIdx: Debug,
        IdN: Copy + Eq + Debug + NodeId,
        IdN::IdN: NodeId<IdN = IdN::IdN> + Debug,
    {
        let mut r = vec![];
        for x in ends.iter() {
            let it = self.get(*x);
            let position_converter = &crate::position::PositionConverter::new(&it)
            .with_stores(stores);
            r.push(position_converter.compute_pos_post_order::<_,Position,_>())
            }
        r
    }
    pub fn to_relaxed_positions<'store, HAST: HyperAST<'store>>(
        &self,
        _stores: &HAST,
    ) -> Vec<Position> {
        todo!()
    }
    pub fn check_with<'store, HAST>(
        &self,
        stores: &'store HAST,
        scout: &Scout<IdN, Idx>,
    ) -> Result<(), String>
    where
        HAST: HyperAST<
            'store,
            IdN = IdN,
            Label = LabelIdentifier,
        >,
        HAST::T: WithChildren<ChildIdx = Idx>,
        <<HAST as HyperAST<'store>>::T as types::WithChildren>::ChildIdx: Debug,
        IdN: Copy + Eq + Debug + NodeId<IdN = IdN>,
    {
        scout.path.check(stores).map_err(|_| "bad path")?;
        if self.nodes.is_empty() {
            return Ok(());
        }
        let mut i = scout.ancestors;
        if !scout.path.parents.is_empty() {
            let e = scout.path.parents[0];
            let p = self.nodes[i];
            let o = scout.path.offsets[0];
            if o.is_zero() {
                if i != 0 {
                    return Err(format!("bad offset"));
                }
                return Ok(());
            }
            let o = o - one();
            let b = stores.node_store().resolve(&p);
            if !b.has_children() || Some(e) != b.child(&num::cast(o).unwrap()) {
                return Err(if b.has_children() {
                    format!("error on link: {:?} {:?} {:?}", b.child_count(), o, p,)
                } else {
                    format!("error no children on link: {:?} {:?}", o, p,)
                });
            }
        }
        while i > 0 {
            let e = self.nodes[i];
            let o = self.offsets[i] - one();
            let p = self.nodes[self.parents[i]];
            let b = stores.node_store().resolve(&p);
            if !b.has_children() || Some(e) != b.child(&num::cast(o).unwrap()) {
                return Err(if b.has_children() {
                    format!("error: {:?} {:?} {:?}", b.child_count(), o, p,)
                } else {
                    format!("error no children: {:?} {:?}", o, p,)
                });
            }
            i -= 1;
        }
        Ok(())
    }
    pub fn check<'store, HAST>(&self, stores: &'store HAST) -> Result<(), String>
    where
        HAST: HyperAST<'store, IdN = IdN::IdN>,
        HAST::T: WithChildren,
        <<HAST as HyperAST<'store>>::T as types::WithChildren>::ChildIdx: Debug,
        IdN: Copy + Eq + Debug + NodeId,
        IdN::IdN: NodeId<IdN = IdN::IdN>,
    {
        assert_eq!(self.offsets.len(), self.parents.len());
        assert_eq!(self.nodes.len(), self.parents.len());
        if self.nodes.is_empty() {
            return Ok(());
        }
        let mut i = self.nodes.len() - 1;
        while i > 0 {
            let e = self.nodes[i];
            let o = self.offsets[i] - one();
            let o: <HAST::T as WithChildren>::ChildIdx = num::cast(o).unwrap();
            let p = self.nodes[self.parents[i]];
            let b = stores.node_store().resolve(p.as_id());
            if !b.has_children() || Some(e.as_id()) != b.child(&o).as_ref() {
                return Err(if b.has_children() {
                    format!("error: {:?} {:?} {:?}", b.child_count(), o, p,)
                } else {
                    format!("error no children: {:?} {:?}", o, p,)
                });
            }
            i -= 1;
        }
        Ok(())
    }
}
impl<IdN: Copy, Idx: PrimInt> StructuralPositionStore<IdN, Idx> {
    pub fn push(&mut self, x: &mut Scout<IdN, Idx>) -> SpHandle {
        assert_eq!(x.path.parents.len(), x.path.offsets.len());
        if x.path.offsets.is_empty() {
            return SpHandle(x.ancestors);
        }
        assert!(
            !x.path.offsets[1..].contains(&zero()),
            "{:?}",
            &x.path.offsets
        );
        if x.path.offsets[0].is_zero() {
            assert!(x.ancestors == 0, "{:?} {}", &x.path.offsets, &x.ancestors);
            if x.path.offsets.len() == 1 {
                return SpHandle(0);
            }
            let l = x.path.parents.len() - 2;
            let o = self.parents.len();
            self.nodes.extend(&x.path.parents[1..]);
            self.parents.push(x.ancestors);
            self.parents
                .extend((o..o + l).into_iter().collect::<Vec<_>>());
            self.offsets.extend(&x.path.offsets[1..]);
            x.ancestors = self.nodes.len() - 1;
            x.path = StructuralPosition::empty()
        } else {
            let l = x.path.parents.len() - 1;
            let o = self.parents.len();
            self.nodes.extend(x.path.parents.clone());
            self.parents.push(x.ancestors);
            self.parents
                .extend((o..o + l).into_iter().collect::<Vec<_>>());
            self.offsets.extend(&x.path.offsets);
            x.ancestors = self.nodes.len() - 1;
            x.path = StructuralPosition::empty()
            }
        assert!(
            self.offsets.is_empty() || !self.offsets[1..].contains(&zero()),
            "{:?}",
            &self.offsets
        );
        assert_eq!(self.offsets.len(), self.parents.len());
        assert_eq!(self.nodes.len(), self.parents.len());
        SpHandle(self.nodes.len() - 1)
    }
}