use alloc::format;
use core::cell::Cell;
use core::ptr::NonNull;
use super::bitmap::BitmapRef;
use crate::capi_state::CApiState;
use crate::ctypes::*;
use crate::error::Error;
use crate::null_terminated::ToNullTerminatedString;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Context {
None,
Screen,
Bitmap(NonNull<CBitmap>),
}
pub struct Video {
ptr: NonNull<CVideoPlayer>,
context: Cell<Context>,
}
impl Video {
pub fn from_file(path: &str) -> Result<Video, Error> {
let ptr = unsafe { Self::fns().loadVideo.unwrap()(path.to_null_terminated_utf8().as_ptr()) };
if ptr.is_null() {
Err(Error::NotFoundError)
} else {
Ok(Video {
context: Cell::new(Context::None),
ptr: NonNull::new(ptr).unwrap(),
})
}
}
fn get_render_error(&self, fn_name: &str) -> Error {
let msg = unsafe {
crate::null_terminated::parse_null_terminated_utf8(Self::fns().getError.unwrap()(
self.cptr() as *mut _
))
};
match msg {
Ok(err) => format!("{}: {}", fn_name, err).into(),
Err(err) => format!(
"{}: unable to parse UTF-8 error string from Playdate. {}",
fn_name, err
)
.into(),
}
}
pub fn render_frame_to_screen(&self, n: i32) -> Result<(), Error> {
if self.context.get() != Context::Screen {
unsafe { Self::fns().useScreenContext.unwrap()(self.cptr() as *mut _) }
self.context.set(Context::Screen);
}
if unsafe { Self::fns().renderFrame.unwrap()(self.cptr() as *mut _, n) } == 0 {
return Err(self.get_render_error("render_frame_to_screen"));
}
return Ok(());
}
pub fn render_frame_to_bitmap(&self, n: i32, bitmap: &mut BitmapRef) -> Result<(), Error> {
if self.context.get() != Context::Bitmap(NonNull::new(bitmap.cptr_mut()).unwrap()) {
if unsafe { Self::fns().setContext.unwrap()(self.cptr() as *mut _, bitmap.cptr_mut()) } == 0 {
return Err(self.get_render_error("render_frame_to_bitmap"));
}
}
if unsafe { Self::fns().renderFrame.unwrap()(self.cptr() as *mut _, n) } == 0 {
return Err(self.get_render_error("render_frame_to_bitmap"));
}
return Ok(());
}
fn info(&self) -> (i32, i32, f32, i32, i32) {
let mut width = 0;
let mut height = 0;
let mut frame_rate = 0.0;
let mut frame_count = 0;
let mut current_frame = 0;
unsafe {
Self::fns().getInfo.unwrap()(
self.cptr() as *mut _,
&mut width,
&mut height,
&mut frame_rate,
&mut frame_count,
&mut current_frame,
)
};
(width, height, frame_rate, frame_count, current_frame)
}
pub fn out_width(&self) -> i32 {
let (width, _, _, _, _) = self.info();
width
}
pub fn out_height(&self) -> i32 {
let (_, height, _, _, _) = self.info();
height
}
pub fn frame_rate(&self) -> f32 {
let (_, _, frame_rate, _, _) = self.info();
frame_rate
}
pub fn frame_count(&self) -> i32 {
let (_, _, _, frame_count, _) = self.info();
frame_count
}
pub fn current_frame(&self) -> i32 {
let (_, _, _, _, current_frame) = self.info();
current_frame
}
pub(crate) fn cptr(&self) -> *const CVideoPlayer {
self.ptr.as_ptr()
}
pub(crate) fn cptr_mut(&mut self) -> *mut CVideoPlayer {
self.ptr.as_ptr()
}
pub(crate) fn fns() -> &'static craydate_sys::playdate_video {
unsafe { &*CApiState::get().cgraphics.video }
}
}
impl Drop for Video {
fn drop(&mut self) {
unsafe { Self::fns().freePlayer.unwrap()(self.cptr_mut()) }
}
}