use crate::detail::container::{find_from, find_prev_token, mark};
use crate::detail::element::build_element_value;
use crate::detail::token::{
is_close_bracket_token, is_enclosed_token, is_free_token, is_identified_token,
is_not_delimiter_token, is_open_bracket_token, Token,
};
use crate::element::{Element, ElementKind};
fn find_title_fallback_end(tokens: &[Token], first: usize) -> usize {
let len = tokens.len();
let mut paren_depth = 0usize;
for (offset, token) in tokens.iter().enumerate().skip(first) {
if is_close_bracket_token(token) {
if paren_depth > 0 {
paren_depth -= 1;
continue;
}
return offset;
}
if is_open_bracket_token(token) {
if token.value == "(" {
paren_depth += 1;
continue;
}
if paren_depth == 0 {
return offset;
}
}
}
len
}
fn find_title(tokens: &[Token]) -> (usize, usize) {
let len = tokens.len();
if tokens
.first()
.is_some_and(|t| t.element_kind == Some(ElementKind::Episode))
{
return (len, len);
}
let mut first = find_from(tokens, 0, |t| is_free_token(t) && !is_enclosed_token(t));
let mut last = find_from(tokens, first, is_identified_token);
if first == len {
first = find_from(tokens, 0, is_close_bracket_token);
first = find_from(tokens, first, is_free_token);
last = find_title_fallback_end(tokens, first);
}
if first == len {
return (len, len);
}
if let Some(region) = tokens.get(first..last) {
let open_brackets: Vec<usize> = region
.iter()
.enumerate()
.filter(|(_, t)| is_open_bracket_token(t))
.map(|(i, _)| first + i)
.collect();
if !open_brackets.is_empty() {
let close_count = region.iter().filter(|t| is_close_bracket_token(t)).count();
if close_count != open_brackets.len() {
last = open_brackets.last().copied().unwrap_or(last);
}
}
}
if let Some(prev) = find_prev_token(tokens, last, is_not_delimiter_token) {
let ends_in_non_paren_bracket = tokens
.get(prev)
.is_some_and(|t| is_close_bracket_token(t) && t.value != ")");
if ends_in_non_paren_bracket {
if let Some(open) = find_prev_token(tokens, prev, is_open_bracket_token) {
last = open;
}
}
}
(first, last)
}
pub(super) fn parse_title(tokens: &mut [Token]) -> Option<Element> {
let (first, last) = find_title(tokens);
if first >= last {
return None;
}
let value = build_element_value(tokens.get(first..last)?, false);
if value.is_empty() {
return None;
}
let position = tokens.get(first)?.position;
for i in first..last {
mark(tokens, i, ElementKind::Title);
}
Some(Element {
kind: ElementKind::Title,
value,
position,
})
}