use std::collections::HashSet;
use super::row::{ConvRow, row};
use super::{ConvBall, conversations};
use crate::git_tree::{Agent, DescentRow, descent_order};
use crate::monitor::Check;
use crate::ui_state::SeenKind;
pub fn visible_rows(
agents: &[Agent],
ws: &str,
seen: &dyn Fn(SeenKind, &str, &str, &str) -> bool,
now_unix: i64,
ball: &dyn Fn(&str) -> ConvBall,
checks: &[Check],
expanded: &HashSet<String>,
) -> Vec<ConvRow> {
let mut convs: Vec<(i64, Vec<ConvRow>)> = Vec::new();
for subtree in conversations(agents) {
let mut rows = Vec::new();
let mut last_active = 0;
for at in visible_indices(&subtree, agents, expanded) {
let (t, r) = row(
agents,
slice_at(&subtree, at),
ws,
seen,
now_unix,
ball,
checks,
);
if at == 0 {
last_active = t;
}
rows.push(r);
}
convs.push((last_active, rows));
}
convs.sort_by(|(ta, a), (tb, b)| tb.cmp(ta).then_with(|| head_id(a).cmp(&head_id(b))));
convs.into_iter().flat_map(|(_, rows)| rows).collect()
}
fn head_id(rows: &[ConvRow]) -> String {
rows.first().map(|r| r.root_id.clone()).unwrap_or_default()
}
fn visible_indices(
subtree: &[DescentRow],
agents: &[Agent],
expanded: &HashSet<String>,
) -> Vec<usize> {
let mut out = Vec::new();
let mut cut: Option<usize> = None;
for (at, r) in subtree.iter().enumerate() {
match cut {
Some(depth) if r.depth > depth => continue,
_ => cut = None,
}
out.push(at);
if !agents
.get(r.index)
.is_some_and(|a| expanded.contains(&a.agent_id))
{
cut = Some(r.depth);
}
}
out
}
fn slice_at(subtree: &[DescentRow], at: usize) -> &[DescentRow] {
let depth = subtree.get(at).map_or(0, |r| r.depth);
let rest = subtree.get(at + 1..).unwrap_or_default();
let len = rest
.iter()
.position(|r| r.depth <= depth)
.unwrap_or(rest.len());
subtree.get(at..=at + len).unwrap_or_default()
}
pub fn step(rows: &[ConvRow], selected: Option<&str>, delta: isize) -> Option<String> {
let n = rows.len();
if n == 0 {
return None;
}
let here = selected.and_then(|id| rows.iter().position(|r| r.root_id == id));
let next = match here {
Some(i) => (i as isize + delta).rem_euclid(n as isize) as usize,
None if delta >= 0 => 0,
None => n - 1,
};
rows.get(next).map(|r| r.root_id.clone())
}
pub fn parent_of(rows: &[ConvRow], id: &str) -> Option<String> {
let at = rows.iter().position(|r| r.root_id == id)?;
let depth = rows.get(at)?.depth;
rows.get(..at)?
.iter()
.rev()
.find(|r| r.depth < depth)
.map(|r| r.root_id.clone())
}
pub fn ancestors(agents: &[Agent], agent_id: &str) -> Vec<String> {
let mut stack: Vec<String> = Vec::new();
for r in descent_order(agents) {
let id = agents.get(r.index).map_or("", |a| a.agent_id.as_str());
stack.truncate(r.depth);
if id == agent_id {
return stack;
}
stack.push(id.to_owned());
}
Vec::new()
}