use pulldown_cmark::{CowStr, Event, Tag};
use std::collections::VecDeque;

pub fn new<'a, I: Iterator<Item = Event<'a>>>(iter: I) -> FootnoteDefs<'a, I> {
    FootnoteDefs {
        iter,
        footnote_events: VecDeque::new(),
        has_started_emitting_defs: false,
    }
}

pub struct FootnoteDefs<'a, I: Iterator<Item = Event<'a>>> {
    iter: I,
    footnote_events: VecDeque<Event<'a>>,
    has_started_emitting_defs: bool,
}

impl<'a, I: Iterator<Item = Event<'a>>> Iterator for FootnoteDefs<'a, I> {
    type Item = Event<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        match self.iter.next() {
            Some(Event::Start(Tag::FootnoteDefinition(ref id))) => {
                self.footnote_events.push_back(Event::Html(
                    format!(
                        r#"<div id="{}" class="footnote-item"><span class="footnote-marker">{}.</span>"#,
                        id, id
                    )
                    .into(),
                ));

                loop {
                    match self.iter.next() {
                        Some(Event::End(Tag::FootnoteDefinition(_))) => {
                            self.footnote_events.push_back(Event::Html("</div>".into()));
                            break;
                        }
                        Some(e) => {
                            self.footnote_events.push_back(e);
                        }
                        None => {
                            break;
                        }
                    }
                }
                self.next()
            }
            Some(e) => Some(e),
            None => {
                if !self.has_started_emitting_defs && !self.footnote_events.is_empty() {
                    self.has_started_emitting_defs = true;
                    let before: CowStr<'a> =
                        r#"<hr class="footnotes-sep"><section class="footnotes">"#.into();
                    self.footnote_events.push_front(Event::Html(before));
                    let after: CowStr<'a> = "</section>".into();
                    self.footnote_events.push_back(Event::Html(after));
                }
                self.footnote_events.pop_front()
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use pulldown_cmark::{html, Options, Parser};

    fn render(s: &str) -> String {
        let mut out = String::new();
        let parser = Parser::new_ext(s, Options::ENABLE_FOOTNOTES);
        let footnote_backrefs = super::new(parser);
        html::push_html(&mut out, footnote_backrefs);
        out
    }

    #[test]
    fn test_group_footnotes() {
        assert_eq!(
            render("foo[^1]\n\n[^1]: bar\n\nbaz"),
            r##"<p>foo<sup class="footnote-reference"><a href="#1">1</a></sup></p>
<p>baz</p>
<hr class="footnotes-sep"><section class="footnotes"><div id="1" class="footnote-item"><span class="footnote-marker">1.</span>
<p>bar</p>
</div></section>"##
        );
    }

    #[test]
    fn test_multiple() {
        assert_eq!(
            render("foo[^1] bar[^2]\n\n[^1]: foo\n\n[^2]: bar"),
            r##"<p>foo<sup class="footnote-reference"><a href="#1">1</a></sup> bar<sup class="footnote-reference"><a href="#2">2</a></sup></p>
<hr class="footnotes-sep"><section class="footnotes"><div id="1" class="footnote-item"><span class="footnote-marker">1.</span>
<p>foo</p>
</div><div id="2" class="footnote-item"><span class="footnote-marker">2.</span>
<p>bar</p>
</div></section>"##
        );
    }
}
