pub struct Words<S: AsRef<str>> {
    source: S,
    position: usize,
    previous: usize,
    preserve_whitespace: bool,
}

impl<S: AsRef<str>> Words<S> {
    pub fn new(source: S) -> Self {
        Self {
            source,
            previous: 0,
            position: 0,
            preserve_whitespace: false,
        }
    }

    pub fn preserving_whitespace(source: S) -> Self {
        Self {
            source,
            previous: 0,
            position: 0,
            preserve_whitespace: true,
        }
    }
}

impl<S: AsRef<str>> Words<S> {
    pub fn undo(&mut self) {
        self.position = self.previous;
    }
}

impl<S: AsRef<str>> Iterator for Words<S> {
    type Item = String;

    fn next(&mut self) -> Option<String> {
        self.previous = self.position;
        let chars: Vec<char> = self.source.as_ref().chars().skip(self.position).collect();
        let mut start = 0;
        while start < chars.len() && chars[start].is_whitespace() {
            start += 1;
        }
        self.position += start;
        if start == chars.len() {
            if chars.len() == 0 {
                return None
            } else if self.preserve_whitespace {
                return Some(chars[..].into_iter().collect())
            } else {
                return Some(" ".to_string())
            }
        }
        let mut len = 0;
        while start+len < chars.len() {
            if chars[start+len] == '-' {
                len += 1;
                break;
            }
            if chars[start+len].is_whitespace() {
                break;
            }
            len += 1;
        }
        self.position += len;
        if chars[0].is_whitespace() {
            if self.preserve_whitespace {
                return Some(chars[0..start+len].into_iter().collect::<String>())
            } else {
                return Some(String::from(" ") + &chars[start..start+len].into_iter().collect::<String>())
            }
        } else {
            return Some(chars[start..start+len].into_iter().collect::<String>())
        }
    }
}
