use crate::Engine;
use crate::eqtb::{Loc, Val};
use crate::maincontrol::ModeKind;
use crate::nodes::{NodeData, NodeRef, id};
use crate::primitives_gen::assign_dir as ad;
use crate::save::GroupCode;
pub mod dir_sub {
pub const BEGIN: u16 = 0;
pub const END: u16 = 1;
}
impl Engine {
pub fn new_dir_node(&mut self, dir: i8, end: bool) -> NodeRef {
let level = i32::from(self.save.cur_level);
self.new_node(
id::DIR,
if end { dir_sub::END } else { dir_sub::BEGIN },
NodeData::Dir { dir, level },
)
}
pub fn text_dir_assigned(&mut self, d: i32, prev: i32) {
let level = self.save.cur_level;
let dir = d.rem_euclid(4) as i8;
let same_group = self.text_dirs.last().is_some_and(|&(l, _, _)| l == level);
let mut before = prev.rem_euclid(4) as i8;
if self.mode.abs_kind() == ModeKind::Vertical {
if same_group && let Some((_, _, b)) = self.text_dirs.pop() {
before = b;
}
} else {
if same_group && let Some((_, old, b)) = self.text_dirs.pop() {
before = b;
let p = self.new_dir_node(old, true);
self.tail_append(p);
}
let p = self.new_dir_node(dir, false);
self.tail_append(p);
}
self.text_dirs.push((level, dir, before));
let n = self.eqtb.local_count[1].val + 1;
self.define(false, Loc::LocalCount(1), Val::Int(i64::from(n)));
}
pub fn end_group_text_dirs(&mut self) {
if self.mode.abs_kind() == ModeKind::Vertical {
return;
}
let level = self.save.cur_level;
let open: Vec<i8> = self
.text_dirs
.iter()
.rev()
.take_while(|&&(l, _, _)| l == level)
.map(|&(_, d, _)| d)
.collect();
for d in open {
let p = self.new_dir_node(d, true);
self.tail_append(p);
}
}
pub fn drop_text_dirs_above(&mut self, level: u16) {
while self.text_dirs.last().is_some_and(|&(l, _, _)| l > level) {
self.text_dirs.pop();
}
}
pub fn default_box_dir(&self) -> i32 {
match self.mode.abs_kind() {
ModeKind::Vertical => self.dir_param(ad::BODYDIR),
ModeKind::Math => self.dir_param(ad::MATHDIR),
_ => self.dir_param(ad::TEXTDIR),
}
}
pub fn begin_box_dirs(&mut self, group: GroupCode, dir: i32) {
if !matches!(
group,
GroupCode::Hbox | GroupCode::AdjustedHbox | GroupCode::Vbox | GroupCode::Vtop
) {
return;
}
for p in [ad::BODYDIR, ad::PARDIR, ad::TEXTDIR] {
self.define(false, Loc::DirPar(p), Val::Int(i64::from(dir)));
}
self.reset_local_counts(false);
let outer = std::mem::take(&mut self.text_dirs);
self.outer_text_dirs.push(outer);
}
pub fn begin_math_dirs(&mut self) {
let d = self.dir_param(ad::MATHDIR);
let t = self.dir_param(ad::TEXTDIR);
self.outer_math_dirs.push((self.save.cur_level, d, t));
for p in [ad::BODYDIR, ad::PARDIR, ad::TEXTDIR] {
self.define(false, Loc::DirPar(p), Val::Int(i64::from(d)));
}
}
pub fn reset_local_counts(&mut self, whatsits: bool) {
let tracing = self
.eqtb
.int(crate::primitives_gen::assign_int::TRACINGASSIGNS)
> 0;
for i in [0, 1] {
if (i == 1 || whatsits) && (tracing || self.eqtb.local_count[i as usize].val != 0) {
self.define(false, Loc::LocalCount(i), Val::Int(0));
}
}
}
pub fn end_box_dirs(&mut self) {
if let Some(outer) = self.outer_text_dirs.pop() {
self.text_dirs = outer;
}
}
pub fn par_text_dir(&mut self) {
let inner: Vec<(i8, i8)> = self
.text_dirs
.iter()
.filter(|&&(l, _, _)| l > 1)
.map(|&(_, d, b)| (d, b))
.collect();
let outside = match inner.first() {
Some(&(_, b)) => b,
None => self.dir_param(ad::TEXTDIR).rem_euclid(4) as i8,
};
if i32::from(outside) != self.dir_param(ad::PARDIR) {
let p = self.new_dir_node(outside, false);
self.tail_append(p);
}
for (d, _) in inner {
let p = self.new_dir_node(d, false);
self.tail_append(p);
}
}
pub fn track_open_dirs(&self, mut p: NodeRef, stop: NodeRef, open: &mut Vec<i8>) {
while !p.is_null() && p != stop {
if let NodeData::Dir { dir, .. } = self.nodes.data(p) {
if self.nodes.subtype(p) == dir_sub::END {
open.pop();
} else {
open.push(*dir);
}
}
p = self.nodes.next(p);
}
}
pub fn end_dir_nodes(&mut self, open: &[i8]) -> Option<(NodeRef, NodeRef)> {
self.dir_node_chain(open.iter().rev().copied(), true)
}
pub fn begin_dir_nodes(&mut self, open: &[i8]) -> Option<(NodeRef, NodeRef)> {
self.dir_node_chain(open.iter().copied(), false)
}
fn dir_node_chain(
&mut self,
dirs: impl Iterator<Item = i8>,
end: bool,
) -> Option<(NodeRef, NodeRef)> {
let mut head: Option<(NodeRef, NodeRef)> = None;
for d in dirs {
let p = self.new_dir_node(d, end);
head = match head {
None => Some((p, p)),
Some((h, t)) => {
self.nodes.link(t, p);
Some((h, p))
}
};
}
head
}
}