use std::convert::TryInto;
use chrono::{DateTime, Utc};
use nom::{
Parser,
bytes::{complete::tag, streaming::take},
combinator::map_res,
};
use super::parsers::*;
use crate::common::MessageParseError;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ScreenData {
screen_data_matrix: Box<[[u8; ScreenData::COLUMNS]; ScreenData::ROWS]>,
timestamp: DateTime<Utc>,
}
impl ScreenData {
pub const WIDTH_PX: u8 = 128;
pub const HEIGHT_PX: u8 = 64;
pub(crate) const PREFIX: &'static [u8] = b"$D";
const ROWS: usize = 8;
const COLUMNS: usize = 128;
const ROW_HEIGHT_PX: usize = 8;
pub fn get_pixel(&self, x: u8, y: u8) -> bool {
let row = usize::from(y) / Self::ROW_HEIGHT_PX;
let column = usize::from(x);
(self.screen_data_matrix[row][column] & (1 << (y % 8))) > 0
}
pub fn get_pixel_checked(&self, x: u8, y: u8) -> Option<bool> {
let row = usize::from(y) / Self::ROW_HEIGHT_PX;
let column = usize::from(x);
Some((self.screen_data_matrix.get(row)?.get(column)? & (1 << (y % 8))) > 0)
}
pub fn timestamp(&self) -> DateTime<Utc> {
self.timestamp
}
}
impl<'a> TryFrom<&'a [u8]> for ScreenData {
type Error = MessageParseError<'a>;
fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
let (bytes, _) = tag(Self::PREFIX)(bytes)?;
let (bytes, screen_data): (&[u8], &[u8; Self::ROWS * Self::COLUMNS]) =
map_res(take(Self::ROWS * Self::COLUMNS), TryInto::try_into).parse(bytes)?;
let _ = parse_opt_line_ending(bytes)?;
let screen_data_matrix = {
let mut matrix = Box::new([[0; ScreenData::COLUMNS]; ScreenData::ROWS]);
for (row_index, row_bytes) in screen_data.chunks_exact(ScreenData::COLUMNS).enumerate()
{
matrix[row_index].clone_from_slice(row_bytes);
}
matrix
};
Ok(ScreenData {
screen_data_matrix,
timestamp: Utc::now(),
})
}
}