use std::collections::HashMap;
use crossterm::{
cursor, queue,
style::{Print, ResetColor, SetStyle},
};
use duat_core::{form::Form, ui::Axis};
use kasuari::Variable;
use super::{
Border, VarPoint,
edges::{Edge, EdgeCoords},
stdout::Stdout,
};
use crate::{BorderStyle, area::Coord};
pub struct Variables {
list: HashMap<Variable, (u32, usize)>,
edges: Vec<(Variable, Edge)>,
}
impl Variables {
pub fn new() -> Self {
Self { list: HashMap::new(), edges: Vec::new() }
}
pub fn new_var(&mut self) -> Variable {
let var = Variable::new();
self.list.insert(var, (0, 0));
var
}
pub fn new_point(&mut self) -> VarPoint {
VarPoint::new(self.new_var(), self.new_var())
}
#[track_caller]
pub fn add_edge(&mut self, [lhs, rhs]: [VarPoint; 2], axis: Axis, fr: Border) -> Variable {
let var = Variable::new();
self.edges.push((var, Edge::new(lhs, rhs, axis.perp(), fr)));
var
}
pub fn remove(&mut self, var: Variable) {
self.list.remove(&var);
}
pub fn insert(&mut self, var: Variable) {
self.list.insert(var, (0, 0));
}
#[track_caller]
pub fn remove_edge(&mut self, var: Variable) {
self.edges.retain(|(v, _)| v != &var);
}
pub fn update_variables(&mut self, changes: Vec<(Variable, f64)>) {
for (var, new) in changes {
let Some((value, changes)) = self.list.get_mut(&var) else {
continue;
};
let new = new.round() as u32;
*changes += (*value != new) as usize;
*value = new;
}
}
pub fn print_edges(&mut self, stdout: &mut Stdout, border_form: Form) {
let edges: Vec<EdgeCoords> = {
let edges = std::mem::take(&mut self.edges);
let coords = edges.iter().filter_map(|(_, e)| e.coords(self)).collect();
self.edges = edges;
coords
};
let mut crossings = Vec::<(Coord, [Option<BorderStyle>; 4])>::new();
for (i, &coords) in edges.iter().enumerate() {
if let Axis::Horizontal = coords.axis {
let char = match coords.line {
Some(line) => super::edges::horizontal(line, line),
None => unreachable!(),
};
let line = char
.to_string()
.repeat((coords.br.x - coords.tl.x) as usize);
queue!(
stdout,
cursor::MoveTo(coords.tl.x as u16, coords.tl.y as u16),
ResetColor,
SetStyle(border_form.style),
Print(line)
)
.unwrap()
} else {
let char = match coords.line {
Some(line) => super::edges::vertical(line, line),
None => unreachable!(),
};
for y in coords.tl.y..coords.br.y {
queue!(
stdout,
cursor::MoveTo(coords.tl.x as u16, y as u16),
ResetColor,
SetStyle(border_form.style),
Print(char)
)
.unwrap()
}
}
for &other_coords in edges[(i + 1)..].iter() {
if let Some((coord, sides)) = coords.crossing(other_coords) {
let prev_crossing = crossings.iter_mut().find(|(c, ..)| *c == coord);
if let Some((_, [right, up, left, down])) = prev_crossing {
*right = right.or(sides[0]);
*up = up.or(sides[1]);
*left = left.or(sides[2]);
*down = down.or(sides[3]);
} else {
crossings.push((coord, sides));
}
}
}
}
for (coord, [right, up, left, down]) in crossings {
queue!(
stdout,
cursor::MoveTo(coord.x as u16, coord.y as u16),
SetStyle(border_form.style),
Print(super::edges::crossing(right, up, left, down, true))
)
.unwrap()
}
}
pub fn value(&mut self, var: Variable, is_printing_now: bool) -> (u32, usize) {
let (value, changes) = self.list.get_mut(&var).unwrap();
if is_printing_now {
(*value, std::mem::take(changes))
} else {
(*value, 0)
}
}
pub fn coord(&mut self, var_point: VarPoint, is_printing: bool) -> (Coord, bool) {
let (x, x_changes) = self.value(var_point.x(), is_printing);
let (y, y_change) = self.value(var_point.y(), is_printing);
(Coord::new(x, y), x_changes + y_change != 0)
}
pub fn has_changed(&self, var: Variable) -> bool {
self.list.get(&var).unwrap().1 > 0
}
}