use alloc::{collections::BTreeMap, vec::Vec};
use super::bitmap::Bitmap;
use crate::capi_state::CApiState;
#[derive(Debug)]
struct StackBitmap {
id: usize,
bitmap: Bitmap,
}
#[derive(Debug)]
struct HeldBitmap {
refs: usize,
bitmap: Option<Bitmap>,
}
#[derive(Debug)]
pub(crate) struct ContextStack {
stack: Vec<Option<StackBitmap>>,
holding: BTreeMap<usize, HeldBitmap>,
}
impl ContextStack {
pub fn new() -> Self {
ContextStack {
stack: Vec::new(),
holding: BTreeMap::new(),
}
}
pub fn push_framebuffer(&mut self) {
unsafe { Self::fns().pushContext.unwrap()(core::ptr::null_mut()) };
self.stack.push(None)
}
pub fn push_bitmap(&mut self, bitmap: Bitmap) -> ContextStackId {
unsafe { Self::fns().pushContext.unwrap()(bitmap.cptr() as *mut _) };
static mut NEXT_ID: usize = 1;
let id = unsafe {
let id = NEXT_ID;
NEXT_ID += 1;
id
};
self.stack.push(Some(StackBitmap { id, bitmap }));
self.holding.insert(
id,
HeldBitmap {
refs: 1,
bitmap: None,
},
);
ContextStackId { id }
}
pub fn pop(&mut self, state: &'static CApiState) -> Option<ContextStackId> {
unsafe { state.cgraphics.popContext.unwrap()() };
self.stack.pop().and_then(|last| last).and_then(|stack_b| {
match self.holding.get_mut(&stack_b.id) {
None => None,
Some(held) => {
assert!(held.bitmap.is_none());
held.bitmap = Some(stack_b.bitmap);
assert!(held.refs >= 1);
held.refs += 1;
Some(ContextStackId { id: stack_b.id })
}
}
})
}
pub fn take_bitmap(&mut self, id: ContextStackId) -> Option<Bitmap> {
let r = self.holding.remove(&id.id).and_then(|held| held.bitmap);
core::mem::forget(id);
r
}
pub fn fns() -> &'static craydate_sys::playdate_graphics {
CApiState::get().cgraphics
}
}
#[derive(Debug)]
pub struct ContextStackId {
id: usize,
}
impl Clone for ContextStackId {
fn clone(&self) -> Self {
let mut stack = CApiState::get().stack.borrow_mut();
let held = stack.holding.get_mut(&self.id);
let held = unsafe { held.unwrap_unchecked() };
held.refs += 1;
ContextStackId { id: self.id }
}
}
impl Drop for ContextStackId {
fn drop(&mut self) {
let mut stack = CApiState::get().stack.borrow_mut();
match stack.holding.get_mut(&self.id) {
Some(held) => {
held.refs -= 1;
if held.refs == 0 {
stack.holding.remove(&self.id);
}
}
None => (),
}
}
}
impl PartialEq for ContextStackId {
fn eq(&self, other: &Self) -> bool {
self.id.eq(&other.id)
}
}
impl Eq for ContextStackId {}
impl PartialOrd for ContextStackId {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.id.partial_cmp(&other.id)
}
}
impl Ord for ContextStackId {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.id.cmp(&other.id)
}
}
impl core::hash::Hash for ContextStackId {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}