fireworks 1.0.4

A fun display of fireworks in the terminal
use firework::{drawing::Drawable, types::Drawables};
use std::{
  io::{Error, Stdout, Write},
  sync::mpsc::Receiver,
  thread,
  time::Duration,
};
use termion::{color::Rgb, cursor::Goto};

use crate::firework;

pub fn start(stdout: &mut Stdout, regen_buffer: Receiver<Drawables>, end_signal: Receiver<bool>) {
  write!(stdout, "{}{}", termion::clear::All, termion::cursor::Hide)
    .expect("Unable to clear terminal for writing");

  let (mut prev_length, mut prev_width) = terminal_length_width();

  loop {
    let to_draw = regen_buffer.recv().expect("Renderer unexpectedly died!");

    output_grid(&to_draw, stdout, prev_length, prev_width)
      .expect("Unable to write to terminal. Exiting.");

    let (length, width) = terminal_length_width();
    prev_length = length;
    prev_width = width;

    if let Ok(v) = end_signal.try_recv() {
      if v {
        for _ in regen_buffer.iter() {
          // no-op
          // clear out drawable queue so we can quit cleanly
        }
        write!(stdout, "{}{}", termion::clear::All, termion::cursor::Show)
          .expect("Unable to restore terminal");
        return;
      }
    }

    thread::sleep(Duration::from_millis(65));
  }
}

fn output_grid(
  drawables: &[Box<dyn Drawable>],
  stdout: &mut Stdout,
  prev_length: u16,
  prev_width: u16,
) -> Result<(), Error> {
  let (length, width) = terminal_length_width();

  if length != prev_length || width != prev_width {
    // Even though it's slower, clear the whole screen.
    // We won't be able to manually clear everything since the coords moved.
    write!(stdout, "{}", termion::clear::All)?;
  } else {
    // otherwise clear every point manually (faster)
    for drawable in drawables.iter() {
      let points = drawable.clear();
      for point in points.iter() {
        if point.y < width {
          write!(
            stdout,
            "{}{} ",
            Goto(point.x % length, width - point.y),
            termion::cursor::Hide,
          )?;
        }
      }
      let points = drawable.draw();
      for point in points.iter() {
        if point.y < width {
          write!(
            stdout,
            "{}{}{}*",
            Goto(point.x % length, width - point.y),
            termion::cursor::Hide,
            Rgb(
              point.pixel_color.r,
              point.pixel_color.g,
              point.pixel_color.b
            )
            .fg_string()
          )?;
        }
      }
    }
  }

  stdout.flush()?;

  Ok(())
}

fn terminal_length_width() -> (u16, u16) {
  termion::terminal_size().expect("Unable to get terminal size")
}