#![cfg(feature = "bitmap")]
use std::error::Error;
use std::fmt::Display;
use std::os::raw::c_void;
use bytemuck::Pod;
use bytemuck::Zeroable;
use parking_lot::Mutex;
use parking_lot::Condvar;
use std::sync::Arc;
use half::f16;
use crate::prelude::CapturePixelFormat;
use crate::prelude::VideoFrame;
#[cfg(target_os = "macos")]
use crate::platform::macos::frame::MacosVideoFrame;
#[cfg(target_os = "macos")]
use crate::platform::platform_impl::objc_wrap::CVPixelFormat;
#[cfg(target_os = "windows")]
use crate::feature::dx11::{WindowsDx11VideoFrame, WindowsDx11VideoFrameError};
#[cfg(target_os = "windows")]
use windows::Win32::Graphics::Direct3D11::ID3D11Texture2D;
#[cfg(target_os = "windows")]
use windows::Graphics::DirectX::DirectXPixelFormat;
#[cfg(target_os = "windows")]
use windows::core::ComInterface;
#[cfg(target_os = "windows")]
use windows::Win32::Graphics::Direct3D11::{D3D11_CPU_ACCESS_READ, D3D11_MAPPED_SUBRESOURCE, D3D11_MAP_READ, D3D11_TEXTURE2D_DESC, D3D11_USAGE_STAGING};
#[cfg(target_os = "windows")]
use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM};
#[cfg(target_os = "windows")]
use windows::Win32::System::WinRT::Direct3D11::IDirect3DDxgiInterfaceAccess;
#[cfg(target_os = "windows")]
use windows::Win32::Graphics::Direct3D11::D3D11_USAGE_DYNAMIC;
#[derive(Clone)]
struct BitmapPool<T: Sized + Zeroable + Copy> {
    free_bitmaps_and_count: Arc<Mutex<(Vec<Box<[T]>>, usize)>>,
    free_condition: Arc<Condvar>,
    max: usize,
}
impl<T: Sized + Zeroable + Copy> BitmapPool<T> {
    pub fn new(initial_count: usize, max: usize, initial_resolution: (usize, usize)) -> Arc<Self> {
        let mut free_bitmaps = Vec::new();
        for _ in 0..initial_count {
            free_bitmaps.push(
                vec![T::zeroed(); initial_resolution.0 * initial_resolution.1].into_boxed_slice()
            )
        }
        Arc::new(Self {
            free_bitmaps_and_count: Arc::new(Mutex::new((free_bitmaps, initial_count))),
            free_condition: Arc::new(Condvar::new()),
            max,
        })
    }
    fn make_new_bitmap(self: &Arc<Self>, resolution: (usize, usize)) -> Option<PooledBitmap<T>> {
        Some(PooledBitmap {
            data: PooledBitmapData {
                data: Some(vec![T::zeroed(); resolution.0 * resolution.1].into_boxed_slice()),
                pool: self.clone()
            },
            width: resolution.0,
            height: resolution.1
        })
    }
    pub fn try_get_bitmap(self: &Arc<Self>, resolution: (usize, usize)) -> Option<PooledBitmap<T>> {
        let mut free_bitmaps_and_count = self.free_bitmaps_and_count.lock();
        self.try_get_bitmap_internal(resolution, &mut free_bitmaps_and_count)
    }
    pub fn get_bitmap(self: &Arc<Self>, resolution: (usize, usize)) -> PooledBitmap<T> {
        let mut free_bitmaps_and_count = self.free_bitmaps_and_count.lock();
        loop {
            if let Some(pooled_bitmap) = self.try_get_bitmap_internal(resolution, &mut free_bitmaps_and_count) {
                return pooled_bitmap;
            } else {
                self.free_condition.wait(&mut free_bitmaps_and_count);
            }
        }
    }
    fn try_get_bitmap_internal(self: &Arc<Self>, resolution: (usize, usize), free_bitmaps_and_count: &mut (Vec<Box<[T]>>, usize)) -> Option<PooledBitmap<T>> {
        if let Some(bitmap_data) = free_bitmaps_and_count.0.pop() {
            if bitmap_data.len() <= resolution.0 * resolution.1 {
                return Some(
                    PooledBitmap {
                        data: PooledBitmapData {
                            data: Some(bitmap_data),
                            pool: self.clone()
                        },
                        width: resolution.0,
                        height: resolution.1
                    }
                );
            }
            free_bitmaps_and_count.1 -= 1;
        }
        if free_bitmaps_and_count.1 < self.max {
            return self.make_new_bitmap(resolution);
        }
        None
    }
    pub fn free_pooled(&self) {
        let mut free_bitmaps_and_count = self.free_bitmaps_and_count.lock();
        let count = free_bitmaps_and_count.0.len();
        free_bitmaps_and_count.0.clear();
        free_bitmaps_and_count.1 -= count;
    }
}
struct PooledBitmapData<T: Sized + Zeroable + Copy> {
    pub data: Option<Box<[T]>>,
    pub pool: Arc<BitmapPool<T>>,
}
impl<T: Sized + Zeroable + Copy> Drop for PooledBitmapData<T> {
    fn drop(&mut self) {
        if let Some(data) = self.data.take() {
            let mut free_bitmaps_and_count = self.pool.free_bitmaps_and_count.lock();
            free_bitmaps_and_count.0.push(data);
            self.pool.free_condition.notify_all();
        }
    }
}
pub struct PooledBitmap<T: Sized + Copy + Zeroable> {
    data: PooledBitmapData<T>,
    pub width: usize,
    pub height: usize,
}
impl<T: Sized + Zeroable + Copy> AsRef<[T]> for PooledBitmap<T> {
    fn as_ref(&self) -> &[T] {
        &self.data.data.as_ref().unwrap()[..]
    }
}
impl<T: Sized + Zeroable + Copy> AsMut<[T]> for PooledBitmap<T> {
    fn as_mut(&mut self) -> &mut [T] {
        &mut self.data.data.as_mut().unwrap()[..]
    }
}
pub trait BitmapDataBgra8x4: Sized + AsRef<[[u8; 4]]> + AsMut<[[u8; 4]]> {}
impl<T: Sized + AsRef<[[u8; 4]]> + AsMut<[[u8; 4]]>> BitmapDataBgra8x4 for T {}
pub struct FrameBitmapBgraUnorm8x4<Data: BitmapDataBgra8x4> {
    pub data: Data,
    pub width:  usize,
    pub height: usize,
}
pub trait BitmapDataArgbUnormPacked2101010: Sized + AsRef<[u32]> {}
impl<T: Sized + AsRef<[u32]> + AsMut<[u32]>> BitmapDataArgbUnormPacked2101010 for T {}
pub struct FrameBitmapArgbUnormPacked2101010<Data: BitmapDataArgbUnormPacked2101010> {
    pub data: Data,
    pub width:  usize,
    pub height: usize,
}
pub trait BitmapDataRgbaF16x4: Sized + AsRef<[[f16; 4]]> {}
impl<T: Sized + AsRef<[[f16; 4]]> + AsMut<[[f16; 4]]>> BitmapDataRgbaF16x4 for T {}
pub struct FrameBitmapRgbaF16x4<Data: BitmapDataRgbaF16x4> {
    pub data: Data,
    pub width:  usize,
    pub height: usize,
}
pub enum VideoRange {
    Video,
    Full,
}
pub trait BitmapDataLuma: Sized + AsRef<[u8]> {}
impl<T: Sized + AsRef<[u8]> + AsMut<[u8]>> BitmapDataLuma for T {}
pub trait BitmapDataChroma: Sized + AsRef<[[u8; 2]]> {}
impl<T: Sized + AsRef<[[u8; 2]]> + AsMut<[[u8; 2]]>> BitmapDataChroma for T {}
pub struct FrameBitmapYCbCr<LumaData: BitmapDataLuma, ChromaData: BitmapDataChroma> {
    pub luma_data: LumaData,
    pub luma_width: usize,
    pub luma_height: usize,
    pub chroma_data: ChromaData,
    pub chroma_width: usize,
    pub chroma_height: usize,
    pub range: VideoRange,
}
pub enum FrameBitmap<DataBgra: BitmapDataBgra8x4, DataArgbPacked: BitmapDataArgbUnormPacked2101010, DataRgbaF16: BitmapDataRgbaF16x4, DataLuma: BitmapDataLuma, DataChroma: BitmapDataChroma> {
    BgraUnorm8x4(FrameBitmapBgraUnorm8x4<DataBgra>),
    ArgbUnormPacked2101010(FrameBitmapArgbUnormPacked2101010<DataArgbPacked>),
    RgbaF16x4(FrameBitmapRgbaF16x4<DataRgbaF16>),
    YCbCr(FrameBitmapYCbCr<DataLuma, DataChroma>),
}
pub type BoxedSliceFrameBitmap = FrameBitmap<
    Box<[[u8; 4]]>,
    Box<[u32]>,
    Box<[[f16; 4]]>,
    Box<[u8]>,
    Box<[[u8; 2]]>
>;
pub type PooledFrameBitmap = FrameBitmap<
    PooledBitmap<[u8; 4]>,
    PooledBitmap<u32>,
    PooledBitmap<[f16; 4]>,
    PooledBitmap<u8>,
    PooledBitmap<[u8; 2]>,
>;
pub struct FrameBitmapPool {
    bgra_u8x4: Arc<BitmapPool<[u8; 4]>>,
    argb_packed_2101010: Arc<BitmapPool<u32>>,
    rgba_f16x4: Arc<BitmapPool<[f16; 4]>>,
    luma: Arc<BitmapPool<u8>>,
    chroma: Arc<BitmapPool<[u8; 2]>>,
}
impl FrameBitmapPool {
    pub fn new_with_initial_capacity(capacity: usize, initial_resolution: (usize, usize), max: usize, format: CapturePixelFormat) -> Self {
        Self {
            bgra_u8x4: BitmapPool::new(
                if format == CapturePixelFormat::Bgra8888 { capacity } else { 0 },
                max,
                initial_resolution
            ),
            argb_packed_2101010: BitmapPool::new(
                if format == CapturePixelFormat::Argb2101010 { capacity } else { 0 },
                max,
                initial_resolution
            ),
            rgba_f16x4: BitmapPool::new(
                0,
                max,
                initial_resolution
            ),
            luma: BitmapPool::new(
                if format == CapturePixelFormat::F420 || format == CapturePixelFormat::V420 { capacity } else { 0 },
                max,
                initial_resolution
            ),
            chroma: BitmapPool::new(
                if format == CapturePixelFormat::F420 || format == CapturePixelFormat::V420 { capacity } else { 0 },
                max,
                initial_resolution
            )
        }
    }
    pub fn new(max: usize) -> Self {
        Self {
            bgra_u8x4: BitmapPool::new(0, max, (0, 0)),
            argb_packed_2101010: BitmapPool::new(0, max, (0, 0)),
            rgba_f16x4: BitmapPool::new(0, max, (0, 0)),
            luma: BitmapPool::new(0, max, (0, 0)),
            chroma: BitmapPool::new(0, max, (0, 0)),
        }
    }
    pub fn free_pooled(&self) {
        self.bgra_u8x4.free_pooled();
        self.argb_packed_2101010.free_pooled();
        self.rgba_f16x4.free_pooled();
        self.luma.free_pooled();
        self.chroma.free_pooled();
    }
}
pub trait VideoFrameBitmap {
    fn get_bitmap(&self) -> Result<BoxedSliceFrameBitmap, VideoFrameBitmapError>;
    fn try_get_pooled_bitmap(&self, bitmap_pool: &FrameBitmapPool) -> Result<Option<PooledFrameBitmap>, VideoFrameBitmapError>;
    fn get_pooled_bitmap(&self, bitmap_pool: &FrameBitmapPool) -> Result<PooledFrameBitmap, VideoFrameBitmapError>;
}
#[derive(Clone, Debug)]
pub enum VideoFrameBitmapError {
    Other(String),
}
impl Display for VideoFrameBitmapError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Other(error) => f.write_fmt(format_args!("VideoFrameBitmapError::Other(\"{}\")", error)),
        }
    }
}
impl Error for VideoFrameBitmapError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        None
    }
    fn description(&self) -> &str {
        "description() is deprecated; use Display"
    }
    fn cause(&self) -> Option<&dyn Error> {
        self.source()
    }
}
#[derive(Copy, Clone)]
struct VideoFramePlanePtr {
    ptr: *const c_void,
    width: usize,
    height: usize,
    bytes_per_row: usize,
}
enum VideoFrameDataCopyPtrs {
    Bgra8888(VideoFramePlanePtr),
    ArgbPacked2101010(VideoFramePlanePtr),
    RgbaF16x4(VideoFramePlanePtr),
    F420{luma: VideoFramePlanePtr, chroma: VideoFramePlanePtr},
    V420{luma: VideoFramePlanePtr, chroma: VideoFramePlanePtr},
}
trait VideoFrameBitmapInternal {
    fn get_bitmap_internal<T>(&self, output_mapping: &impl Fn(VideoFrameDataCopyPtrs) -> Result<T, VideoFrameBitmapError>) -> Result<T, VideoFrameBitmapError>; 
}
impl VideoFrameBitmapInternal for VideoFrame {
    fn get_bitmap_internal<T>(&self, output_mapping: &impl Fn(VideoFrameDataCopyPtrs) -> Result<T, VideoFrameBitmapError>) -> Result<T, VideoFrameBitmapError> {
        #[cfg(target_os = "windows")]
        {
            let (width, height) = self.impl_video_frame.frame_size;
            match self.get_dx11_surface() {
                Err(WindowsDx11VideoFrameError::Other(x)) => Err(VideoFrameBitmapError::Other(x)),
                Ok((surface, pixel_format)) => {
                    let dxgi_format = match pixel_format {
                        DirectXPixelFormat::B8G8R8A8UIntNormalized => DXGI_FORMAT_B8G8R8A8_UNORM,
                        DirectXPixelFormat::R10G10B10A2UIntNormalized => DXGI_FORMAT_R10G10B10A2_UNORM,
                        _ => return Err(VideoFrameBitmapError::Other("Unknown or unsupported pixel format on DXGISurface".to_string())),
                    };
                    
                    unsafe {
                        let surface_desc = surface.Description()
                            .map_err(|_| VideoFrameBitmapError::Other("Couldn't get description of frame surface".to_string()))?;
                        let mut new_texture_desc = D3D11_TEXTURE2D_DESC::default();
                        new_texture_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ.0 as u32;
                        new_texture_desc.ArraySize = 1;
                        new_texture_desc.BindFlags = 0;
                        new_texture_desc.Width = surface_desc.Width as u32;
                        new_texture_desc.Height = surface_desc.Height as u32;
                        new_texture_desc.MipLevels = 1;
                        new_texture_desc.SampleDesc.Count = 1;
                        new_texture_desc.SampleDesc.Quality = 0;
                        new_texture_desc.Usage.0 = D3D11_USAGE_STAGING.0 | D3D11_USAGE_DYNAMIC.0;
                        new_texture_desc.Format = dxgi_format;
                        let mut staging_texture = Option::<ID3D11Texture2D>::None;
                        let staging_tex_result = self.impl_video_frame.device.CreateTexture2D(&new_texture_desc as *const _, None, Some(&mut staging_texture as *mut _));
                        staging_tex_result.map_err(|error| VideoFrameBitmapError::Other(format!("Failed to create texture: {}", error.to_string())))?;
                        let dxgi_interfce_access: IDirect3DDxgiInterfaceAccess = surface.cast()
                            .map_err(|_| VideoFrameBitmapError::Other("Couldn't create surface interface access".to_string()))?;
                        let surface_texture: ID3D11Texture2D = dxgi_interfce_access.GetInterface()
                            .map_err(|_| VideoFrameBitmapError::Other("Couldn't create surface texture from surface IDirect3DDxgiInterfaceAccess".to_string()))?;
                        let device = self.impl_video_frame.device.GetImmediateContext()
                            .map_err(|_| VideoFrameBitmapError::Other("Couldn't get immediate d3d11 context".to_string()))?;
                        let staging_texture = staging_texture.unwrap();
                        device.CopyResource(&staging_texture, &surface_texture);
                        let mut mapped_resource = D3D11_MAPPED_SUBRESOURCE::default();
                        let map_result = device.Map(&staging_texture, 0, D3D11_MAP_READ, 0, Some(&mut mapped_resource as *mut _));
                        map_result.map_err(|_| VideoFrameBitmapError::Other("Couldn't map staging texture".to_string()))?;
                        match pixel_format {
                            DirectXPixelFormat::B8G8R8A8UIntNormalized => {
                                let bpr = mapped_resource.RowPitch as usize;
                                let plane_ptr = VideoFramePlanePtr {
                                    ptr: mapped_resource.pData as *const c_void,
                                    width,
                                    height,
                                    bytes_per_row: bpr
                                };
        
                                let mapping_result = output_mapping(VideoFrameDataCopyPtrs::Bgra8888(plane_ptr));
                                
                                let _ = device.Unmap(&staging_texture, 0);
                                mapping_result
                            },
                            DirectXPixelFormat::R10G10B10A2UIntNormalized => {
                                let bpr = mapped_resource.RowPitch as usize;
                                let plane_ptr = VideoFramePlanePtr {
                                    ptr: mapped_resource.pData as *const c_void,
                                    width,
                                    height,
                                    bytes_per_row: bpr
                                };
        
                                let mapping_result = output_mapping(VideoFrameDataCopyPtrs::ArgbPacked2101010(plane_ptr));
                                
                                let _ = device.Unmap(&staging_texture, 0);
                                
                                mapping_result
                            },
                            _ => {
                                Err(VideoFrameBitmapError::Other("Unknown or unsupported pixel format on DXGISurface".to_string()))
                            }
                        }
                    }
                }
            }
        }
        #[cfg(target_os = "macos")]
        {
            let iosurface = match &self.impl_video_frame {
                MacosVideoFrame::SCStream(sc_frame) => {
                    match sc_frame.sample_buffer.get_image_buffer().map(|image_buffer| image_buffer.get_iosurface()).flatten() {
                        Some(iosurface) => iosurface,
                        None => return Err(VideoFrameBitmapError::Other("Failed to get iosurface".to_string())),
                    }
                },
                MacosVideoFrame::CGDisplayStream(cg_display_frame) => {
                    cg_display_frame.io_surface.clone()
                }
            };
            if let Ok(lock_gaurd) = iosurface.lock(true, false) {
                let pixel_format = iosurface.get_pixel_format();
                match pixel_format {
                    Some(CVPixelFormat::BGRA8888) => {
                        let bpr = iosurface.get_bytes_per_row();
                        let height = iosurface.get_height();
                        let width = iosurface.get_width();
                        let base_address = lock_gaurd.get_base_address().ok_or(VideoFrameBitmapError::Other("Failed to get base address of iosurface".into()))?;
                        
                        let plane_ptr = VideoFramePlanePtr {
                            ptr: base_address,
                            width,
                            height,
                            bytes_per_row: bpr
                        };
                        output_mapping(VideoFrameDataCopyPtrs::Bgra8888(plane_ptr))
                    },
                    Some(CVPixelFormat::V420) |
                    Some(CVPixelFormat::F420) => {
                        let luma_bpr = iosurface.get_bytes_per_row_of_plane(0);
                        let luma_height = iosurface.get_height_of_plane(0);
                        let luma_width = iosurface.get_width_of_plane(0);
                        let luma_base_address = lock_gaurd.get_base_address_of_plane(0).ok_or(VideoFrameBitmapError::Other("Failed to get base address of iosurface".into()))?;
                        let luma_plane_ptr = VideoFramePlanePtr {
                            ptr: luma_base_address,
                            width: luma_width,
                            height: luma_height,
                            bytes_per_row: luma_bpr,
                        };
                        let chroma_bpr = iosurface.get_bytes_per_row_of_plane(1);
                        let chroma_height = iosurface.get_height_of_plane(1);
                        let chroma_width = iosurface.get_width_of_plane(1);
                        let chroma_base_address = lock_gaurd.get_base_address_of_plane(1).ok_or(VideoFrameBitmapError::Other("Failed to get base address of iosurface".into()))?;
                        let chroma_plane_ptr = VideoFramePlanePtr {
                            ptr: chroma_base_address,
                            width: chroma_width,
                            height: chroma_height,
                            bytes_per_row: chroma_bpr,
                        };
                        if pixel_format == Some(CVPixelFormat::V420) {
                            output_mapping(VideoFrameDataCopyPtrs::V420 { luma: luma_plane_ptr, chroma: chroma_plane_ptr })
                        } else {
                            output_mapping(VideoFrameDataCopyPtrs::F420 { luma: luma_plane_ptr, chroma: chroma_plane_ptr })
                        }
                    },
                    _ => Err(VideoFrameBitmapError::Other("Unknown pixel format on iosurface".to_string()))
                }
            } else {
                Err(VideoFrameBitmapError::Other("Failed to lock iosurface".to_string()))
            }
        }
    }
}
fn copy_boxed_slice_plane<T: Sized + Copy + Pod + Zeroable>(plane_ptr: VideoFramePlanePtr) -> Box<[T]> {
    let mut image_data = vec![T::zeroed(); plane_ptr.width * plane_ptr.height];
    let src_slice = unsafe { std::slice::from_raw_parts(plane_ptr.ptr as *const u8, plane_ptr.bytes_per_row * plane_ptr.height) };
    for y in 0..plane_ptr.height {
        let source_slice = bytemuck::cast_slice::<_, T>(&src_slice[(plane_ptr.bytes_per_row * y)..(plane_ptr.bytes_per_row * y + std::mem::size_of::<T>() * plane_ptr.width)]);
        image_data[(plane_ptr.width * y)..(plane_ptr.width * y + plane_ptr.width)].copy_from_slice(source_slice);
    }
    image_data.into_boxed_slice()
}
fn copy_pooled_plane<T: Sized + Copy + Pod + Zeroable>(plane_ptr: VideoFramePlanePtr, pool: &Arc<BitmapPool<T>>) -> PooledBitmap<T> {
    let mut bitmap = pool.get_bitmap((plane_ptr.width, plane_ptr.height));
    let src_slice = unsafe { std::slice::from_raw_parts(plane_ptr.ptr as *const u8, plane_ptr.bytes_per_row * plane_ptr.height) };
    for y in 0..plane_ptr.height {
        let source_slice = bytemuck::cast_slice::<_, T>(&src_slice[(plane_ptr.bytes_per_row * y)..(plane_ptr.bytes_per_row * y + std::mem::size_of::<T>() * plane_ptr.width)]);
        AsMut::as_mut(&mut bitmap)[(plane_ptr.width * y)..(plane_ptr.width * y + plane_ptr.width)].copy_from_slice(source_slice);
    }
    bitmap
}
fn try_copy_pooled_plane<T: Sized + Copy + Pod + Zeroable>(plane_ptr: VideoFramePlanePtr, pool: &Arc<BitmapPool<T>>) -> Option<PooledBitmap<T>> {
    let mut bitmap = pool.try_get_bitmap((plane_ptr.width, plane_ptr.height))?;
    let src_slice = unsafe { std::slice::from_raw_parts(plane_ptr.ptr as *const u8, plane_ptr.bytes_per_row * plane_ptr.height) };
    for y in 0..plane_ptr.height {
        let source_slice = bytemuck::cast_slice::<_, T>(&src_slice[(plane_ptr.bytes_per_row * y)..(plane_ptr.bytes_per_row * y + std::mem::size_of::<T>() * plane_ptr.width)]);
        AsMut::as_mut(&mut bitmap)[(plane_ptr.width * y)..(plane_ptr.width * y + plane_ptr.width)].copy_from_slice(source_slice);
    }
    Some(bitmap)
}
impl VideoFrameBitmap for VideoFrame {
    fn get_bitmap(&self) -> Result<BoxedSliceFrameBitmap, VideoFrameBitmapError> {
        self.get_bitmap_internal::<BoxedSliceFrameBitmap>(&|copy_ptrs| {
            match copy_ptrs {
                VideoFrameDataCopyPtrs::Bgra8888(bgra_plane_ptr) => {
                    Ok(BoxedSliceFrameBitmap::BgraUnorm8x4(FrameBitmapBgraUnorm8x4 {
                        data: copy_boxed_slice_plane(bgra_plane_ptr),
                        width: bgra_plane_ptr.width,
                        height: bgra_plane_ptr.height,
                    }))
                },
                VideoFrameDataCopyPtrs::ArgbPacked2101010(argb_plane_ptr) => {
                    Ok(BoxedSliceFrameBitmap::ArgbUnormPacked2101010(FrameBitmapArgbUnormPacked2101010 {
                        data: copy_boxed_slice_plane(argb_plane_ptr),
                        width: argb_plane_ptr.width,
                        height: argb_plane_ptr.height,
                    }))
                },
                VideoFrameDataCopyPtrs::F420 { luma: luma_plane_ptr, chroma: chroma_plane_ptr } => {
                    Ok(BoxedSliceFrameBitmap::YCbCr(FrameBitmapYCbCr {
                        luma_data: copy_boxed_slice_plane(luma_plane_ptr),
                        luma_width: luma_plane_ptr.width,
                        luma_height: luma_plane_ptr.height,
                        chroma_data: copy_boxed_slice_plane(chroma_plane_ptr),
                        chroma_width: chroma_plane_ptr.width,
                        chroma_height: chroma_plane_ptr.height,
                        range: VideoRange::Full
                    }))
                },
                VideoFrameDataCopyPtrs::V420 { luma: luma_plane_ptr, chroma: chroma_plane_ptr } => {
                    Ok(BoxedSliceFrameBitmap::YCbCr(FrameBitmapYCbCr {
                        luma_data: copy_boxed_slice_plane(luma_plane_ptr),
                        luma_width: luma_plane_ptr.width,
                        luma_height: luma_plane_ptr.height,
                        chroma_data: copy_boxed_slice_plane(chroma_plane_ptr),
                        chroma_width: chroma_plane_ptr.width,
                        chroma_height: chroma_plane_ptr.height,
                        range: VideoRange::Video
                    }))
                },
                VideoFrameDataCopyPtrs::RgbaF16x4(rgba_plane_ptr) => {
                    Ok(BoxedSliceFrameBitmap::RgbaF16x4(FrameBitmapRgbaF16x4 {
                        data: copy_boxed_slice_plane(rgba_plane_ptr),
                        width: rgba_plane_ptr.width,
                        height: rgba_plane_ptr.height,
                    }))
                }
            }
        })
    }
    fn get_pooled_bitmap(&self, bitmap_pool: &FrameBitmapPool) -> Result<PooledFrameBitmap, VideoFrameBitmapError> {
        self.get_bitmap_internal::<PooledFrameBitmap>(&|copy_ptrs| {
            match copy_ptrs {
                VideoFrameDataCopyPtrs::Bgra8888(bgra_plane_ptr) => {
                    Ok(PooledFrameBitmap::BgraUnorm8x4(FrameBitmapBgraUnorm8x4 {
                        data: copy_pooled_plane(bgra_plane_ptr, &bitmap_pool.bgra_u8x4),
                        width: bgra_plane_ptr.width,
                        height: bgra_plane_ptr.height,
                    }))
                },
                VideoFrameDataCopyPtrs::ArgbPacked2101010(argb_plane_ptr) => {
                    Ok(PooledFrameBitmap::ArgbUnormPacked2101010(FrameBitmapArgbUnormPacked2101010 {
                        data: copy_pooled_plane(argb_plane_ptr, &bitmap_pool.argb_packed_2101010),
                        width: argb_plane_ptr.width,
                        height: argb_plane_ptr.height,
                    }))
                },
                VideoFrameDataCopyPtrs::F420 { luma: luma_plane_ptr, chroma: chroma_plane_ptr } => {
                    Ok(PooledFrameBitmap::YCbCr(FrameBitmapYCbCr {
                        luma_data: copy_pooled_plane(luma_plane_ptr, &bitmap_pool.luma),
                        luma_width: luma_plane_ptr.width,
                        luma_height: luma_plane_ptr.height,
                        chroma_data: copy_pooled_plane(chroma_plane_ptr, &bitmap_pool.chroma),
                        chroma_width: chroma_plane_ptr.width,
                        chroma_height: chroma_plane_ptr.height,
                        range: VideoRange::Full
                    }))
                },
                VideoFrameDataCopyPtrs::V420 { luma: luma_plane_ptr, chroma: chroma_plane_ptr } => {
                    Ok(PooledFrameBitmap::YCbCr(FrameBitmapYCbCr {
                        luma_data: copy_pooled_plane(luma_plane_ptr, &bitmap_pool.luma),
                        luma_width: luma_plane_ptr.width,
                        luma_height: luma_plane_ptr.height,
                        chroma_data: copy_pooled_plane(chroma_plane_ptr, &bitmap_pool.chroma),
                        chroma_width: chroma_plane_ptr.width,
                        chroma_height: chroma_plane_ptr.height,
                        range: VideoRange::Video
                    }))
                },
                VideoFrameDataCopyPtrs::RgbaF16x4(rgba_plane_ptr) => {
                    Ok(PooledFrameBitmap::RgbaF16x4(FrameBitmapRgbaF16x4 {
                        data: copy_pooled_plane(rgba_plane_ptr, &bitmap_pool.rgba_f16x4),
                        width: rgba_plane_ptr.width,
                        height: rgba_plane_ptr.height,
                    }))
                }
            }
        })
    }
    fn try_get_pooled_bitmap(&self, bitmap_pool: &FrameBitmapPool) -> Result<Option<PooledFrameBitmap>, VideoFrameBitmapError> {
        self.get_bitmap_internal::<Option<PooledFrameBitmap>>(&|copy_ptrs| {
            match copy_ptrs {
                VideoFrameDataCopyPtrs::Bgra8888(bgra_plane_ptr) => {
                    if let Some(data) = try_copy_pooled_plane(bgra_plane_ptr, &bitmap_pool.bgra_u8x4) {
                        Ok(Some(PooledFrameBitmap::BgraUnorm8x4(FrameBitmapBgraUnorm8x4 {
                            data,
                            width: bgra_plane_ptr.width,
                            height: bgra_plane_ptr.height,
                        })))
                    } else {
                        Ok(None)
                    }
                },
                VideoFrameDataCopyPtrs::ArgbPacked2101010(argb_plane_ptr) => {
                    if let Some(data) = try_copy_pooled_plane(argb_plane_ptr, &bitmap_pool.argb_packed_2101010) {
                        Ok(Some(PooledFrameBitmap::ArgbUnormPacked2101010(FrameBitmapArgbUnormPacked2101010 {
                            data,
                            width: argb_plane_ptr.width,
                            height: argb_plane_ptr.height,
                        })))
                    } else {
                        Ok(None)
                    }
                },
                VideoFrameDataCopyPtrs::F420 { luma: luma_plane_ptr, chroma: chroma_plane_ptr } => {
                    if let (Some(luma_data), Some(chroma_data)) = (try_copy_pooled_plane(luma_plane_ptr, &bitmap_pool.luma), try_copy_pooled_plane(chroma_plane_ptr, &bitmap_pool.chroma)) {
                        Ok(Some(PooledFrameBitmap::YCbCr(FrameBitmapYCbCr {
                            luma_data,
                            luma_width: luma_plane_ptr.width,
                            luma_height: luma_plane_ptr.height,
                            chroma_data,
                            chroma_width: chroma_plane_ptr.width,
                            chroma_height: chroma_plane_ptr.height,
                            range: VideoRange::Full
                        })))
                    } else {
                        Ok(None)
                    }
                    
                },
                VideoFrameDataCopyPtrs::V420 { luma: luma_plane_ptr, chroma: chroma_plane_ptr } => {
                    if let (Some(luma_data), Some(chroma_data)) = (try_copy_pooled_plane(luma_plane_ptr, &bitmap_pool.luma), try_copy_pooled_plane(chroma_plane_ptr, &bitmap_pool.chroma)) {
                        Ok(Some(PooledFrameBitmap::YCbCr(FrameBitmapYCbCr {
                            luma_data,
                            luma_width: luma_plane_ptr.width,
                            luma_height: luma_plane_ptr.height,
                            chroma_data,
                            chroma_width: chroma_plane_ptr.width,
                            chroma_height: chroma_plane_ptr.height,
                            range: VideoRange::Video
                        })))
                    } else {
                        Ok(None)
                    }
                },
                VideoFrameDataCopyPtrs::RgbaF16x4(rgba_plane_ptr) => {
                    if let Some(data) = try_copy_pooled_plane(rgba_plane_ptr, &bitmap_pool.rgba_f16x4) {
                        Ok(Some(PooledFrameBitmap::RgbaF16x4(FrameBitmapRgbaF16x4 {
                            data,
                            width: rgba_plane_ptr.width,
                            height: rgba_plane_ptr.height,
                        })))
                    } else {
                        Ok(None)
                    }
                }
            }
        })
    }
}