use crate::clean::{self, DocFragment, DocFragmentKind, Item};
use crate::core::DocContext;
use crate::fold;
use crate::fold::DocFolder;
use crate::passes::Pass;

use std::mem::take;

pub const COLLAPSE_DOCS: Pass = Pass {
    name: "collapse-docs",
    run: collapse_docs,
    description: "concatenates all document attributes into one document attribute",
};

pub fn collapse_docs(krate: clean::Crate, _: &DocContext<'_>) -> clean::Crate {
    let mut krate = Collapser.fold_crate(krate);
    krate.collapsed = true;
    krate
}

struct Collapser;

impl fold::DocFolder for Collapser {
    fn fold_item(&mut self, mut i: Item) -> Option<Item> {
        i.attrs.collapse_doc_comments();
        self.fold_item_recur(i)
    }
}

fn collapse(doc_strings: &mut Vec<DocFragment>) {
    let mut docs = vec![];
    let mut last_frag: Option<DocFragment> = None;

    for frag in take(doc_strings) {
        if let Some(mut curr_frag) = last_frag.take() {
            let curr_kind = &curr_frag.kind;
            let new_kind = &frag.kind;

            if matches!(*curr_kind, DocFragmentKind::Include { .. })
                || curr_kind != new_kind
                || curr_frag.parent_module != frag.parent_module
            {
                if *curr_kind == DocFragmentKind::SugaredDoc
                    || *curr_kind == DocFragmentKind::RawDoc
                {
                    // add a newline for extra padding between segments
                    curr_frag.doc.push('\n');
                }
                docs.push(curr_frag);
                last_frag = Some(frag);
            } else {
                curr_frag.doc.push('\n');
                curr_frag.doc.push_str(&frag.doc);
                curr_frag.span = curr_frag.span.to(frag.span);
                last_frag = Some(curr_frag);
            }
        } else {
            last_frag = Some(frag);
        }
    }

    if let Some(frag) = last_frag.take() {
        docs.push(frag);
    }
    *doc_strings = docs;
}

impl clean::Attributes {
    pub fn collapse_doc_comments(&mut self) {
        collapse(&mut self.doc_strings);
    }
}
