use std::collections::HashSet;
use crate::foundations::StyleChain;
use crate::introspection::{Locator, SplitLocator, Tag, TagElem};
use crate::layout::{PagebreakElem, Parity};
use crate::realize::Pair;
pub enum Item<'a> {
Run(&'a [Pair<'a>], StyleChain<'a>, Locator<'a>),
Tags(&'a [Pair<'a>]),
Parity(Parity, StyleChain<'a>, Locator<'a>),
}
pub fn collect<'a>(
mut children: &'a mut [Pair<'a>],
mut locator: SplitLocator<'a>,
mut initial: StyleChain<'a>,
) -> Vec<Item<'a>> {
let mut items: Vec<Item<'a>> = vec![];
let mut staged_empty_page = true;
while let Some(&(elem, styles)) = children.first() {
if let Some(pagebreak) = elem.to_packed::<PagebreakElem>() {
let strong = !pagebreak.weak(styles);
if strong && staged_empty_page {
let locator = locator.next(&elem.span());
items.push(Item::Run(&[], initial, locator));
}
if let Some(parity) = pagebreak.to(styles) {
let locator = locator.next(&elem.span());
items.push(Item::Parity(parity, styles, locator));
}
if !pagebreak.boundary(styles) {
initial = styles;
}
staged_empty_page |= strong;
children = &mut children[1..];
} else {
let end =
children.iter().take_while(|(c, _)| !c.is::<PagebreakElem>()).count();
let end = migrate_unterminated_tags(children, end);
if end == 0 {
continue;
}
let (group, rest) = children.split_at_mut(end);
children = rest;
if group.iter().all(|(c, _)| c.is::<TagElem>())
&& !(staged_empty_page
&& children.iter().all(|&(c, s)| {
c.to_packed::<PagebreakElem>().is_some_and(|c| c.boundary(s))
}))
{
items.push(Item::Tags(group));
continue;
}
let locator = locator.next(&elem.span());
items.push(Item::Run(group, initial, locator));
staged_empty_page = false;
}
}
if staged_empty_page {
items.push(Item::Run(&[], initial, locator.next(&())));
}
items
}
fn migrate_unterminated_tags(children: &mut [Pair], mid: usize) -> usize {
let (before, after) = children.split_at(mid);
let start = mid - before.iter().rev().take_while(|&(c, _)| c.is::<TagElem>()).count();
let end = mid + after.iter().take_while(|&(c, _)| c.is::<PagebreakElem>()).count();
let excluded: HashSet<_> = children[start..mid]
.iter()
.filter_map(|(c, _)| match c.to_packed::<TagElem>()?.tag {
Tag::Start(_) => None,
Tag::End(loc, _) => Some(loc),
})
.collect();
let key = |(c, _): &Pair| match c.to_packed::<TagElem>() {
Some(elem) => {
if excluded.contains(&elem.tag.location()) {
-1
} else {
1
}
}
None => 0,
};
children[start..end].sort_by_key(key);
start + children[start..end].iter().take_while(|pair| key(pair) == -1).count()
}