use core::{cell::Cell, ptr::NonNull};
use alloc::{boxed::Box, rc::Rc, vec};
use crate::display::tiled::{Tile, TileFormat, TileIndex, VRAM_MANAGER};
#[derive(Clone)]
pub(crate) struct Tiles<T>
where
T: TileInfo,
{
tiles: Rc<TilesInner<T>>,
}
struct TilesInner<T>
where
T: TileInfo,
{
tile_data: Box<[T]>,
in_screenblock: Cell<Option<NonNull<u8>>>,
colours: TileFormat,
}
impl<T> Clone for TilesInner<T>
where
T: TileInfo,
{
fn clone(&self) -> Self {
for tile in &self.tile_data {
if *tile != T::default() {
VRAM_MANAGER.increase_reference(tile.tile_index(self.colours));
}
}
Self {
tile_data: self.tile_data.clone(),
in_screenblock: Cell::new(None),
colours: self.colours,
}
}
}
impl<T> Drop for TilesInner<T>
where
T: TileInfo,
{
fn drop(&mut self) {
for tile in &self.tile_data {
if *tile != T::default() {
VRAM_MANAGER.remove_tile(tile.tile_index(self.colours));
}
}
}
}
pub(crate) trait TileInfo: Default + Eq + Copy {
fn tile_index(self, colours: TileFormat) -> TileIndex;
}
impl TileInfo for Tile {
fn tile_index(self, colours: TileFormat) -> TileIndex {
self.tile_index(colours)
}
}
impl TileInfo for u8 {
fn tile_index(self, _colours: TileFormat) -> TileIndex {
TileIndex::EightBpp(self as u16)
}
}
impl<T> Tiles<T>
where
T: TileInfo,
{
pub(crate) fn new(size: usize, colours: TileFormat) -> Self {
let tiles = vec![T::default(); size].into_boxed_slice();
Self {
tiles: Rc::new(TilesInner {
tile_data: tiles,
in_screenblock: Cell::new(None),
colours,
}),
}
}
pub(crate) fn set_tile(&mut self, pos: usize, tile: T) {
let tile_data = Rc::make_mut(&mut self.tiles);
tile_data.tile_data[pos] = tile;
tile_data.in_screenblock.set(None);
}
pub(crate) fn as_ptr(&self) -> *const T {
self.tiles().as_ptr()
}
pub(crate) fn colours(&self) -> TileFormat {
self.tiles.colours
}
pub(crate) fn get(&self, index: usize) -> T {
self.tiles()[index]
}
pub(crate) fn tiles(&self) -> &[T] {
&self.tiles.tile_data
}
pub(crate) fn is_dirty(&self, screenblock_ptr: NonNull<u8>) -> bool {
self.tiles.in_screenblock.get() != Some(screenblock_ptr)
}
pub(crate) fn clean(&self, screenblock_ptr: NonNull<u8>) {
self.tiles.in_screenblock.set(Some(screenblock_ptr));
}
}