use std::collections::VecDeque;
use hydroflow::hydroflow_syntax;
use hydroflow::scheduled::graph::Hydroflow;
use tokio::sync::mpsc::UnboundedSender;
use tokio_stream::wrappers::UnboundedReceiverStream;
use crate::protocol::{Timestamp, Token};
pub(crate) fn rga_adjacency(
input_recv: UnboundedReceiverStream<(Token, Timestamp)>,
rga_send: UnboundedSender<(Token, Timestamp)>,
list_send: UnboundedSender<(Timestamp, Timestamp)>,
) -> Hydroflow<'static> {
hydroflow_syntax! {
insertAfter = source_stream(input_recv) -> tee();
adjacency_sibs = insertAfter[adjacency]
-> map(|(child, parent): (Token, Timestamp)| (parent, child.ts))
-> fold_keyed::<'tick, Timestamp, (VecDeque<Timestamp>, Vec<(Timestamp, Timestamp)>)>(
|| (VecDeque::<Timestamp>::new(), Vec::<(Timestamp, Timestamp)>::new()),
|(kids, sibs): &mut (VecDeque<Timestamp>, Vec::<(Timestamp, Timestamp)>), val: Timestamp| {
// siblings (older, younger)
if !kids.is_empty() {
sibs.push((val, *kids.back().unwrap()));
}
kids.push_back(val);
})
-> map(|(parent, (kids, sibs))| ((parent, kids), sibs)) -> unzip();
adjacency = adjacency_sibs[0] -> tee();
siblings = adjacency_sibs[1] -> flatten();
leaves = difference();
split = insertAfter[diff] -> unzip();
split[0] -> map(|child| child.ts) -> [pos]leaves;
split[1] -> [neg]leaves;
nextSiblingAnc = union() -> tee();
siblings -> nextSiblingAnc;
lastChild = adjacency[lastChild] -> map(|(parent, kids): (Timestamp, VecDeque<Timestamp>)| (parent, *kids.front().unwrap()));
upEdge = join() -> map(|(_parent, (last_child, next_sib)): (Timestamp, (Timestamp, Timestamp))| (last_child, next_sib));
lastChild -> [0]upEdge;
nextSiblingAnc -> [1]upEdge;
upEdge -> nextSiblingAnc;
nextElem = union();
adjacency[nextElem] -> map(|(parent, kids): (Timestamp, VecDeque<Timestamp>)| (parent, *kids.back().unwrap())) -> nextElem;
ne_join = join() -> map(|(prev, (_, next))| (prev, next));
leaves -> map(|prev| (prev, ())) -> [0]ne_join;
nextSiblingAnc -> [1]ne_join;
ne_join -> nextElem;
insertAfter[print] -> for_each(|(c, p): (Token, Timestamp)| rga_send.send((c, p)).unwrap());
nextElem -> for_each(|(first, second)| list_send.send((first, second)).unwrap());
}
}