mod kind;
mod node_match;
mod pattern;
#[cfg(feature = "regex")]
mod text;
use crate::meta_var::MetaVarEnv;
use crate::traversal::Pre;
use crate::{Doc, Language, Node};
use bit_set::BitSet;
use std::borrow::Cow;
pub use kind::{KindMatcher, KindMatcherError};
pub use node_match::NodeMatch;
pub use pattern::{Pattern, PatternError, PatternNode};
#[cfg(feature = "regex")]
pub use text::{RegexMatcher, RegexMatcherError};
pub trait Matcher<L: Language> {
  fn match_node_with_env<'tree, D: Doc<Lang = L>>(
    &self,
    _node: Node<'tree, D>,
    _env: &mut Cow<MetaVarEnv<'tree, D>>,
  ) -> Option<Node<'tree, D>>;
  fn potential_kinds(&self) -> Option<BitSet> {
    None
  }
  fn get_match_len<D: Doc<Lang = L>>(&self, _node: Node<D>) -> Option<usize> {
    None
  }
  fn match_node<'tree, D: Doc<Lang = L>>(
    &self,
    node: Node<'tree, D>,
  ) -> Option<NodeMatch<'tree, D>> {
    let mut env = Cow::Owned(MetaVarEnv::new());
    let node = self.match_node_with_env(node, &mut env)?;
    Some(NodeMatch::new(node, env.into_owned()))
  }
  fn find_node<'tree, D: Doc<Lang = L>>(
    &self,
    node: Node<'tree, D>,
  ) -> Option<NodeMatch<'tree, D>> {
    for n in node.dfs() {
      if let Some(ret) = self.match_node(n.clone()) {
        return Some(ret);
      }
    }
    None
  }
}
impl<L: Language> Matcher<L> for str {
  fn match_node_with_env<'tree, D: Doc<Lang = L>>(
    &self,
    node: Node<'tree, D>,
    env: &mut Cow<MetaVarEnv<'tree, D>>,
  ) -> Option<Node<'tree, D>> {
    let pattern = Pattern::str(self, node.lang().clone());
    pattern.match_node_with_env(node, env)
  }
  fn get_match_len<D: Doc<Lang = L>>(&self, node: Node<D>) -> Option<usize> {
    let pattern = Pattern::str(self, node.lang().clone());
    pattern.get_match_len(node)
  }
}
impl<L, T> Matcher<L> for &T
where
  L: Language,
  T: Matcher<L> + ?Sized,
{
  fn match_node_with_env<'tree, D: Doc<Lang = L>>(
    &self,
    node: Node<'tree, D>,
    env: &mut Cow<MetaVarEnv<'tree, D>>,
  ) -> Option<Node<'tree, D>> {
    (**self).match_node_with_env(node, env)
  }
  fn potential_kinds(&self) -> Option<BitSet> {
    (**self).potential_kinds()
  }
  fn match_node<'tree, D: Doc<Lang = L>>(
    &self,
    node: Node<'tree, D>,
  ) -> Option<NodeMatch<'tree, D>> {
    (**self).match_node(node)
  }
  fn find_node<'tree, D: Doc<Lang = L>>(
    &self,
    node: Node<'tree, D>,
  ) -> Option<NodeMatch<'tree, D>> {
    (**self).find_node(node)
  }
  fn get_match_len<D: Doc<Lang = L>>(&self, node: Node<D>) -> Option<usize> {
    (**self).get_match_len(node)
  }
}
pub struct FindAllNodes<'tree, D: Doc, M: Matcher<D::Lang>> {
  dfs: Pre<'tree, D>,
  matcher: M,
}
impl<'tree, D: Doc, M: Matcher<D::Lang>> FindAllNodes<'tree, D, M> {
  pub fn new(matcher: M, node: Node<'tree, D>) -> Self {
    Self {
      dfs: node.dfs(),
      matcher,
    }
  }
}
impl<'tree, D: Doc, M: Matcher<D::Lang>> Iterator for FindAllNodes<'tree, D, M> {
  type Item = NodeMatch<'tree, D>;
  fn next(&mut self) -> Option<Self::Item> {
    let kinds = self.matcher.potential_kinds();
    for cand in self.dfs.by_ref() {
      if let Some(k) = &kinds {
        if !k.contains(cand.kind_id().into()) {
          continue;
        }
      }
      if let Some(matched) = self.matcher.match_node(cand) {
        return Some(matched);
      }
    }
    None
  }
}
pub struct MatchAll;
impl<L: Language> Matcher<L> for MatchAll {
  fn match_node_with_env<'tree, D: Doc<Lang = L>>(
    &self,
    node: Node<'tree, D>,
    _env: &mut Cow<MetaVarEnv<'tree, D>>,
  ) -> Option<Node<'tree, D>> {
    Some(node)
  }
  fn potential_kinds(&self) -> Option<BitSet> {
    None
  }
}
pub struct MatchNone;
impl<L: Language> Matcher<L> for MatchNone {
  fn match_node_with_env<'tree, D: Doc<Lang = L>>(
    &self,
    _node: Node<'tree, D>,
    _env: &mut Cow<MetaVarEnv<'tree, D>>,
  ) -> Option<Node<'tree, D>> {
    None
  }
  fn potential_kinds(&self) -> Option<BitSet> {
    Some(BitSet::new())
  }
}