use stable_pattern::Searcher;

/// A pattern that can be searched for within a string.
///
/// This trait is implemented for common pattern types including `&str`, `char`,
/// `&[char]`, `&Regex`, and closures of type `FnMut(char) -> bool`.
///
/// This is defined here only because core::str::pattern::Pattern is unstable.
pub trait Pattern: Sized {
    /// The searcher type used to find matches of this pattern.
    type Searcher<'a>: Searcher<'a>;

    /// Constructs a searcher for this pattern in the given `haystack`.
    fn into_searcher(self, haystack: &str) -> Self::Searcher<'_>;
}

impl<'b> Pattern for &'b str {
    type Searcher<'a> = stable_pattern::StrSearcher<'a, 'b>;

    fn into_searcher(self, haystack: &str) -> Self::Searcher<'_> {
        stable_pattern::Pattern::into_searcher(self, haystack)
    }
}

impl<'b> Pattern for &'b [char] {
    type Searcher<'a> = stable_pattern::CharSliceSearcher<'a, 'b>;

    fn into_searcher(self, haystack: &str) -> Self::Searcher<'_> {
        stable_pattern::Pattern::into_searcher(self, haystack)
    }
}

impl Pattern for char {
    type Searcher<'a> = stable_pattern::CharSearcher<'a>;

    fn into_searcher(self, haystack: &str) -> Self::Searcher<'_> {
        stable_pattern::Pattern::into_searcher(self, haystack)
    }
}

impl<'b> Pattern for &&'b str {
    type Searcher<'a> = stable_pattern::StrSearcher<'a, 'b>;

    fn into_searcher(self, haystack: &str) -> Self::Searcher<'_> {
        stable_pattern::Pattern::into_searcher(self, haystack)
    }
}

impl<F> Pattern for F
where
    F: FnMut(char) -> bool,
{
    type Searcher<'a> = stable_pattern::CharPredicateSearcher<'a, F>;

    fn into_searcher(self, haystack: &str) -> Self::Searcher<'_> {
        stable_pattern::Pattern::into_searcher(self, haystack)
    }
}

/// A [`Searcher`] for regex patterns, wrapping the regex match iterator.
#[cfg(feature = "regex")]
#[derive(Debug)]
pub struct RegexSearcher<'r, 't> {
    haystack: &'t str,
    it: regex::Matches<'r, 't>,
    last_step_end: usize,
    next_match: Option<(usize, usize)>,
}

#[cfg(feature = "regex")]
unsafe impl<'r, 't> Searcher<'t> for RegexSearcher<'r, 't> {
    #[inline]
    fn haystack(&self) -> &'t str {
        self.haystack
    }

    #[inline]
    fn next(&mut self) -> stable_pattern::SearchStep {
        use stable_pattern::SearchStep;
        if let Some((s, e)) = self.next_match {
            self.next_match = None;
            self.last_step_end = e;
            return SearchStep::Match(s, e);
        }
        match self.it.next() {
            None => {
                if self.last_step_end < self.haystack().len() {
                    let last = self.last_step_end;
                    self.last_step_end = self.haystack().len();
                    SearchStep::Reject(last, self.haystack().len())
                } else {
                    SearchStep::Done
                }
            }
            Some(m) => {
                let (s, e) = (m.start(), m.end());
                if s == self.last_step_end {
                    self.last_step_end = e;
                    SearchStep::Match(s, e)
                } else {
                    self.next_match = Some((s, e));
                    let last = self.last_step_end;
                    self.last_step_end = s;
                    SearchStep::Reject(last, s)
                }
            }
        }
    }
}

#[cfg(feature = "regex")]
impl<'r> Pattern for &'r regex::Regex {
    type Searcher<'a> = RegexSearcher<'r, 'a>;

    fn into_searcher(self, haystack: &str) -> Self::Searcher<'_> {
        RegexSearcher {
            haystack,
            it: self.find_iter(haystack),
            last_step_end: 0,
            next_match: None,
        }
    }
}
