use crate::renderer::context_element::IntoContextElement;
use std::borrow::Borrow;

pub trait BreadcrumbTree {
    fn get_ice(&self) -> &dyn IntoContextElement;

    fn get_parent(&self) -> Option<&dyn BreadcrumbTree>;
}

pub struct BreadcrumbTreeNode<'a, C: IntoContextElement> {
    parent: Option<&'a dyn BreadcrumbTree>,
    element: BreadcrumbTreeNodeElement<'a, C>,
}

enum BreadcrumbTreeNodeElement<'a, C: IntoContextElement> {
    Owned(C),
    Borrowed(&'a C),
}

impl<'a, C: IntoContextElement> BreadcrumbTreeNode<'a, C> {
    pub fn ice_iter(&'a self) -> IceTreeIterator<'a> {
        IceTreeIterator(Some(self))
    }

    pub fn breadcrumb_iter(&'a self) -> BreadcrumbTreeIterator<'a> {
        BreadcrumbTreeIterator(Some(self))
    }
}

impl<'a, C: IntoContextElement> Borrow<dyn IntoContextElement + 'a>
    for BreadcrumbTreeNodeElement<'a, C>
{
    fn borrow(&self) -> &(dyn IntoContextElement + 'a) {
        match self {
            BreadcrumbTreeNodeElement::Owned(ice) => ice,
            BreadcrumbTreeNodeElement::Borrowed(ice) => *ice,
        }
    }
}

impl<'a, C: IntoContextElement> BreadcrumbTree for BreadcrumbTreeNode<'a, C> {
    fn get_ice(&self) -> &dyn IntoContextElement {
        self.element.borrow()
    }

    fn get_parent(&self) -> Option<&dyn BreadcrumbTree> {
        self.parent
    }
}

pub struct IceTreeIterator<'a>(Option<&'a dyn BreadcrumbTree>);

impl<'a> Iterator for IceTreeIterator<'a> {
    type Item = &'a dyn IntoContextElement;

    fn next(&mut self) -> Option<Self::Item> {
        let ret = self.0;
        self.0 = self.0.map(|node| node.get_parent()).flatten();
        ret.map(|node| node.get_ice())
    }
}

impl<'a, C: IntoContextElement> IntoIterator for &'a BreadcrumbTreeNode<'a, C> {
    type Item = &'a dyn BreadcrumbTree;
    type IntoIter = BreadcrumbTreeIterator<'a>;

    fn into_iter(self) -> BreadcrumbTreeIterator<'a> {
        self.breadcrumb_iter()
    }
}

pub struct BreadcrumbTreeIterator<'a>(Option<&'a dyn BreadcrumbTree>);

impl<'a> Iterator for BreadcrumbTreeIterator<'a> {
    type Item = &'a dyn BreadcrumbTree;

    fn next(&mut self) -> Option<Self::Item> {
        let ret = self.0;
        self.0 = self.0.map(|node| node.get_parent()).flatten();
        ret
    }
}