use crate::drawable::Pixel;
use crate::geometry::Point;
use crate::pixelcolor::{BinaryColor, PixelColor};
use crate::Drawing;
use core::{
cmp::PartialEq,
fmt::{self, Write},
iter,
};
const SIZE: usize = 64;
#[derive(Clone)]
pub struct MockDisplay<C>([Option<C>; SIZE * SIZE])
where
C: PixelColor;
impl<C> MockDisplay<C>
where
C: PixelColor,
{
pub fn new() -> Self {
Self::default()
}
pub fn width(&self) -> usize {
SIZE
}
pub fn height(&self) -> usize {
SIZE
}
pub fn get_pixel(&self, p: Point) -> Option<C> {
let Point { x, y } = p;
self.0[x as usize + y as usize * SIZE]
}
pub fn set_pixel(&mut self, p: Point, color: Option<C>) {
let Point { x, y } = p;
self.0[x as usize + y as usize * SIZE] = color;
}
}
impl<C> MockDisplay<C>
where
C: PixelColor + ColorMapping<C>,
{
pub fn from_pattern(pattern: &[&str]) -> MockDisplay<C> {
let pattern_width = pattern.first().map_or(0, |row| row.len());
let pattern_height = pattern.len();
assert!(pattern_width <= SIZE);
assert!(pattern_height <= SIZE);
for row in pattern {
assert_eq!(row.len(), pattern_width);
}
let pattern_colors = pattern
.into_iter()
.flat_map(|row| {
row.chars()
.map(|c| match c {
' ' => None,
_ => Some(C::char_to_color(c)),
})
.chain(iter::repeat(None))
.take(SIZE)
})
.chain(iter::repeat(None))
.take(SIZE * SIZE);
let mut display = MockDisplay::new();
for (i, color) in pattern_colors.enumerate() {
display.0[i] = color;
}
display
}
}
impl<C> Default for MockDisplay<C>
where
C: PixelColor,
{
fn default() -> Self {
Self([None; SIZE * SIZE])
}
}
impl<C> fmt::Debug for MockDisplay<C>
where
C: PixelColor + ColorMapping<C>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let empty_rows = self
.0
.rchunks(SIZE)
.take_while(|row| row.into_iter().all(Option::is_none))
.count();
writeln!(f, "MockDisplay[")?;
for row in self.0.chunks(SIZE).take(SIZE - empty_rows) {
for color in row {
f.write_char(color.map_or(' ', C::color_to_char))?;
}
writeln!(f)?;
}
if empty_rows > 0 {
writeln!(f, "({} empty rows skipped)", empty_rows)?;
}
writeln!(f, "]")?;
Ok(())
}
}
impl<C> PartialEq for MockDisplay<C>
where
C: PixelColor,
{
fn eq(&self, other: &MockDisplay<C>) -> bool {
self.0.into_iter().eq(other.0.into_iter())
}
}
impl<C> Drawing<C> for MockDisplay<C>
where
C: PixelColor,
{
fn draw<T>(&mut self, item_pixels: T)
where
T: IntoIterator<Item = Pixel<C>>,
{
for Pixel(Point { x, y }, color) in item_pixels {
if x < 0 || y < 0 || x >= SIZE as i32 || y >= SIZE as i32 {
continue;
}
let i = x + y * SIZE as i32;
self.0[i as usize] = Some(color);
}
}
}
pub trait ColorMapping<C> {
fn char_to_color(c: char) -> C;
fn color_to_char(color: C) -> char;
}
impl ColorMapping<BinaryColor> for BinaryColor {
fn char_to_color(c: char) -> Self {
match c {
'.' => BinaryColor::Off,
'#' => BinaryColor::On,
_ => panic!("Invalid char in pattern: '{}'", c),
}
}
fn color_to_char(color: BinaryColor) -> char {
match color {
BinaryColor::Off => '.',
BinaryColor::On => '#',
}
}
}