use rustc_hash::FxHashSet;
use crate::{
    node::{
        ElementNode,
        FromAnyValue,
        NodeType,
        OwnedAttributeView,
    },
    prelude::AttributeName,
    tags::TagName,
    NodeId,
};
#[derive(Debug)]
pub struct NodeView<'a, V: FromAnyValue = ()> {
    id: NodeId,
    inner: &'a NodeType<V>,
    mask: &'a NodeMask,
    height: u16,
}
impl<'a, V: FromAnyValue> NodeView<'a, V> {
    pub fn new(id: NodeId, node: &'a NodeType<V>, view: &'a NodeMask, height: u16) -> Self {
        Self {
            inner: node,
            mask: view,
            id,
            height,
        }
    }
    pub fn node_type(&self) -> &'a NodeType<V> {
        self.inner
    }
    pub fn node_id(&self) -> NodeId {
        self.id
    }
    pub fn height(&self) -> u16 {
        self.height
    }
    pub fn tag(&self) -> Option<&'a TagName> {
        self.mask
            .tag
            .then_some(match &self.inner {
                NodeType::Element(ElementNode { tag, .. }) => Some(tag),
                _ => None,
            })
            .flatten()
    }
    pub fn attributes<'b>(
        &'b self,
    ) -> Option<impl Iterator<Item = OwnedAttributeView<'a, V>> + 'b> {
        match &self.inner {
            NodeType::Element(ElementNode { attributes, .. }) => Some(
                attributes
                    .iter()
                    .filter(move |(attr, _)| self.mask.attritutes.contains(attr))
                    .map(|(attr, val)| OwnedAttributeView {
                        attribute: attr,
                        value: val,
                    }),
            ),
            _ => None,
        }
    }
    pub fn text(&self) -> Option<&str> {
        self.mask.text.then_some(self.inner.text()).flatten()
    }
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum AttributeMask {
    All,
    Some(FxHashSet<AttributeName>),
}
impl AttributeMask {
    pub fn contains(&self, attr: &AttributeName) -> bool {
        match self {
            AttributeMask::All => true,
            AttributeMask::Some(attrs) => attrs.contains(attr),
        }
    }
    pub fn union(&self, other: &Self) -> Self {
        match (self, other) {
            (AttributeMask::Some(s), AttributeMask::Some(o)) => {
                AttributeMask::Some(s.union(o).cloned().collect())
            }
            _ => AttributeMask::All,
        }
    }
    fn overlaps(&self, other: &Self) -> bool {
        match (self, other) {
            (AttributeMask::All, AttributeMask::Some(attrs)) => !attrs.is_empty(),
            (AttributeMask::Some(attrs), AttributeMask::All) => !attrs.is_empty(),
            (AttributeMask::Some(attrs1), AttributeMask::Some(attrs2)) => {
                !attrs1.is_disjoint(attrs2)
            }
            _ => true,
        }
    }
}
impl Default for AttributeMask {
    fn default() -> Self {
        AttributeMask::Some(FxHashSet::default())
    }
}
#[derive(Default, PartialEq, Eq, Clone, Debug)]
pub struct NodeMask {
    attritutes: AttributeMask,
    tag: bool,
    text: bool,
    listeners: bool,
}
impl NodeMask {
    pub fn overlaps(&self, other: &Self) -> bool {
        (self.tag && other.tag)
            || self.attritutes.overlaps(&other.attritutes)
            || (self.text && other.text)
            || (self.listeners && other.listeners)
    }
    pub fn union(&self, other: &Self) -> Self {
        Self {
            attritutes: self.attritutes.union(&other.attritutes),
            tag: self.tag | other.tag,
            text: self.text | other.text,
            listeners: self.listeners | other.listeners,
        }
    }
    pub fn add_attributes(&mut self, attributes: AttributeMask) {
        self.attritutes = self.attritutes.union(&attributes);
    }
    pub fn attributes(&self) -> &AttributeMask {
        &self.attritutes
    }
    pub fn set_tag(&mut self) {
        self.tag = true;
    }
    pub fn tag(&self) -> bool {
        self.tag
    }
    pub fn set_text(&mut self) {
        self.text = true;
    }
    pub fn text(&self) -> bool {
        self.text
    }
    pub fn set_listeners(&mut self) {
        self.listeners = true;
    }
    pub fn listeners(&self) -> bool {
        self.listeners
    }
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum AttributeMaskBuilder<'a> {
    All,
    Some(&'a [AttributeName]),
}
impl Default for AttributeMaskBuilder<'_> {
    fn default() -> Self {
        AttributeMaskBuilder::Some(&[])
    }
}
#[derive(Default, PartialEq, Eq, Clone, Debug)]
pub struct NodeMaskBuilder<'a> {
    attritutes: AttributeMaskBuilder<'a>,
    tag: bool,
    text: bool,
    listeners: bool,
}
impl<'a> NodeMaskBuilder<'a> {
    pub const NONE: Self = Self::new();
    pub const ALL: Self = Self::new()
        .with_attrs(AttributeMaskBuilder::All)
        .with_text()
        .with_tag()
        .with_listeners();
    pub const fn new() -> Self {
        Self {
            attritutes: AttributeMaskBuilder::Some(&[]),
            tag: false,
            text: false,
            listeners: false,
        }
    }
    pub const fn with_attrs(mut self, attritutes: AttributeMaskBuilder<'a>) -> Self {
        self.attritutes = attritutes;
        self
    }
    pub const fn with_tag(mut self) -> Self {
        self.tag = true;
        self
    }
    pub const fn with_text(mut self) -> Self {
        self.text = true;
        self
    }
    pub const fn with_listeners(mut self) -> Self {
        self.listeners = true;
        self
    }
    pub fn build(self) -> NodeMask {
        NodeMask {
            attritutes: match self.attritutes {
                AttributeMaskBuilder::All => AttributeMask::All,
                AttributeMaskBuilder::Some(attrs) => {
                    AttributeMask::Some(FxHashSet::from_iter(attrs.iter().copied()))
                }
            },
            tag: self.tag,
            text: self.text,
            listeners: self.listeners,
        }
    }
}