Documentation
use super::prelude::*;

#[derive(Clone, DataSize, Debug)]
pub struct NumberTree<T> {
    pub limits: Option<(i32, i32)>,
    pub node: NumberTreeNode<T>,
}

#[derive(Clone, DataSize, Debug)]
pub enum NumberTreeNode<T> {
    Leaf(Vec<(i32, T)>),
    Intermediate(Vec<Ref<NumberTree<T>>>),
}
impl<T: Object> Object for NumberTree<T> {
    fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
        use itertools::Itertools;

        let mut dict = p.resolve(resolve)?.into_dictionary()?;

        let limits = match dict.remove("Limits") {
            Some(limits) => {
                let limits = t!(limits.resolve(resolve)?.into_array());
                if limits.len() != 2 {
                    bail!("Error reading NameTree: 'Limits' is not of length 2");
                }
                let min = t!(limits[0].as_integer());
                let max = t!(limits[1].as_integer());

                Some((min, max))
            }
            None => None,
        };

        let kids = dict.remove("Kids");
        let nums = dict.remove("Nums");
        match (kids, nums) {
            (Some(kids), _) => {
                let kids = t!(kids
                    .resolve(resolve)?
                    .into_array()?
                    .iter()
                    .map(|kid| Ref::<NumberTree<T>>::from_primitive(kid.clone(), resolve))
                    .collect::<Result<Vec<_>>>());
                Ok(NumberTree {
                    limits,
                    node: NumberTreeNode::Intermediate(kids),
                })
            }
            (None, Some(nums)) => {
                let list = nums.into_array()?;
                let mut items = Vec::with_capacity(list.len() / 2);
                for (key, item) in list.into_iter().tuples() {
                    let idx = t!(key.as_integer());
                    let val = t!(T::from_primitive(item, resolve));
                    items.push((idx, val));
                }
                Ok(NumberTree {
                    limits,
                    node: NumberTreeNode::Leaf(items),
                })
            }
            (None, None) => {
                warn!("Neither Kids nor Names present in NumberTree node.");
                Ok(NumberTree {
                    limits,
                    node: NumberTreeNode::Intermediate(vec![]),
                })
            }
        }
    }
}
impl<T: ObjectWrite> ObjectWrite for NumberTree<T> {
    fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
        let mut dict = Dictionary::new();
        if let Some(limits) = self.limits {
            dict.insert("Limits", vec![limits.0.into(), limits.1.into()]);
        }
        match self.node {
            NumberTreeNode::Leaf(ref items) => {
                let mut nums = Vec::with_capacity(items.len() * 2);
                for &(idx, ref label) in items {
                    nums.push(idx.into());
                    nums.push(label.to_primitive(update)?);
                }
                dict.insert("Nums", nums);
            }
            NumberTreeNode::Intermediate(ref kids) => {
                dict.insert(
                    "Kids",
                    kids.iter().map(|r| r.get_inner().into()).collect::<Vec<_>>(),
                );
            }
        }
        Ok(dict.into())
    }
}
impl<T: Object + DataSize> NumberTree<T> {
    pub fn walk(&self, r: &impl Resolve, callback: &mut dyn FnMut(i32, &T)) -> Result<()> {
        match self.node {
            NumberTreeNode::Leaf(ref items) => {
                for &(idx, ref val) in items {
                    callback(idx, val);
                }
            }
            NumberTreeNode::Intermediate(ref items) => {
                for &tree_ref in items {
                    let tree = r.get(tree_ref)?;
                    tree.walk(r, callback)?;
                }
            }
        }
        Ok(())
    }
}