// License: see LICENSE file at root directory of `master` branch

//! # `SubStrIter`

use alloc::string::String;

use core::ops::Deref;

/// # A sub string
///
/// This struct is used by [`SubStrIter`][::SubStrIter], which can be made via [`sub_strs()`][::sub_strs()].
///
/// You can dereference this struct to `&str`. It can also give you start and end indexes of the original string.
///
/// [::SubStrIter]: struct.SubStrIter.html
/// [::sub_strs()]: fn.sub_strs.html
#[derive(Debug)]
pub struct SubStr<'a> {
    target: &'a str,
    start: usize,
    end: usize,
    inner: &'a str,
}

impl<'a> SubStr<'a> {

    /// # Start index
    pub fn start(&self) -> usize {
        self.start
    }

    /// # End index
    pub fn end(&self) -> usize {
        self.end
    }

    /// # Inner string between start and end phrases
    ///
    /// # Examples
    ///
    /// ```
    /// use core::str::FromStr;
    ///
    /// let sample = "some_id: 99,";
    /// let some_id = sub_strs::sub_strs(sample, "some_id:", ",").next().unwrap();
    /// assert_eq!(u8::from_str(some_id.inner().trim()).unwrap(), 99);
    /// ```
    pub fn inner(&self) -> &'a str {
        self.inner
    }

}

impl Deref for SubStr<'_> {

    type Target = str;

    fn deref(&self) -> &Self::Target {
        self.target
    }

}

impl PartialEq<str> for SubStr<'_> {

    fn eq(&self, other: &str) -> bool {
        self.target == other
    }

}

impl PartialEq<SubStr<'_>> for &str {

    fn eq(&self, other: &SubStr) -> bool {
        self == &other.target
    }

}

impl PartialEq<String> for SubStr<'_> {

    fn eq(&self, other: &String) -> bool {
        self.target == other
    }

}

impl PartialEq<SubStr<'_>> for String {

    fn eq(&self, other: &SubStr) -> bool {
        self == other.target
    }

}

/// # An iterator of [`SubStr`][::SubStr]
///
/// You can make an instance of this struct by [`sub_strs()`][::sub_strs()].
///
/// [::SubStr]: struct.SubStr.html
/// [::sub_strs()]: fn.sub_strs.html
#[derive(Debug)]
pub struct SubStrIter<'a, S, T> where S: AsRef<str>, T: AsRef<str> {
    src: &'a str,
    src_index: usize,
    start: S,
    end: T,
}

impl<'a, S, T> SubStrIter<'a, S, T> where S: AsRef<str>, T: AsRef<str> {

    /// # Makes new instance
    fn new(source: &'a str, start: S, end: T) -> Self {
        Self {
            src: source,
            src_index: 0,
            start,
            end,
        }
    }

}

impl<'a, S, T> Iterator for SubStrIter<'a, S, T> where S: AsRef<str>, T: AsRef<str> {

    type Item = SubStr<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        let start = self.start.as_ref();
        let start_len = start.len();

        let src = &self.src[self.src_index..];
        if let Some(mut start_idx) = src.find(start) {
            let end = self.end.as_ref();
            let delta = start_idx + start_len;
            if let Some(end_idx) = src[delta..].find(end) {
                if start_len > 0 && end_idx > 0 {
                    if let Some(next_char_bytes) = src[start_idx..].chars().next().map(|c| c.len_utf8()) {
                        if let Some(next_start_idx) = src[start_idx + next_char_bytes .. delta + end_idx].rfind(start) {
                            start_idx += next_char_bytes + next_start_idx;
                        }
                    }
                }

                let end_len = end.len();
                let end_idx = delta + end_idx + end_len;
                let sub_start = self.src_index + start_idx;
                self.src_index += end_idx;

                let target = &src[start_idx..end_idx];
                let inner = &target[start_len .. target.len() - end_len];

                return Some(SubStr {
                    target,
                    start: sub_start,
                    end: self.src_index,
                    inner,
                });
            }
        }

        None
    }

}

/// # Finds sub strings
///
/// ## Examples
///
/// ```
/// use core::str::FromStr;
///
/// let sample = "test(some(test(0)     test(1)---test(2) some test(3)";
/// let start = "test(";
/// let end = ")";
/// for (i, s) in sub_strs::sub_strs(sample, start, end).enumerate() {
///     let expected = format!("{}{}{}", start, i, end);
///     assert_eq!(s, expected);
///     assert_eq!(&sample[s.start()..s.end()], expected);
///     assert_eq!(usize::from_str(s.inner()).unwrap(), i);
/// }
/// ```
pub fn sub_strs<'a, S, T>(source: &'a str, start: S, end: T) -> SubStrIter<'a, S, T> where S: AsRef<str>, T: AsRef<str> {
    SubStrIter::new(source, start, end)
}
