use std::mem::size_of;
use std::ptr::{addr_of, addr_of_mut};
use std::slice;
use bitflags::bitflags;
use pipewire_wrapper_proc_macro::RawWrapper;
use crate::enum_wrapper;
use crate::spa::pod::object::format::VideoFormat;
use crate::spa::pod::sequence::PodSequenceRef;
use crate::spa::type_::{PointRef, RectangleRef, RegionRef};
use crate::wrapper::RawWrapper;
#[derive(RawWrapper, Debug)]
#[repr(transparent)]
pub struct MetaRef {
    #[raw]
    raw: spa_sys::spa_meta,
}
impl MetaRef {
    pub fn type_(&self) -> MetaType {
        MetaType::from_raw(self.raw.type_)
    }
    pub fn set_type(&mut self, type_: MetaType) {
        self.raw.type_ = type_.into();
    }
    pub fn size(&self) -> u32 {
        self.raw.size
    }
    pub fn set_size(&mut self, size: u32) {
        self.raw.size = size;
    }
    fn data_ptr(&self) -> *mut ::std::os::raw::c_void {
        self.raw.data
    }
    #[allow(clippy::mut_from_ref)]
    unsafe fn data_slice<T>(&self) -> &mut [T]
    where
        T: RawWrapper,
    {
        let element_size = size_of::<T>();
        let len = self.size() as usize / element_size;
        slice::from_raw_parts_mut(self.data_ptr() as *mut T, len)
    }
    pub fn data(&self) -> MetaData {
        unsafe {
            match self.type_() {
                MetaType::HEADER => MetaData::HEADER(self.data_slice()),
                MetaType::VIDEO_CROP => MetaData::VIDEO_CROP(self.data_slice()),
                MetaType::VIDEO_DAMAGE => MetaData::VIDEO_DAMAGE(self.data_slice()),
                MetaType::BITMAP => MetaData::BITMAP(self.data_slice()),
                MetaType::CURSOR => MetaData::CURSOR(self.data_slice()),
                MetaType::CONTROL => MetaData::CONTROL(self.data_slice()),
                MetaType::BUSY => MetaData::BUSY(self.data_slice()),
                _ => MetaData::INVALID,
            }
        }
    }
}
#[repr(u32)]
#[derive(Debug)]
#[allow(non_camel_case_types)]
pub enum MetaData<'a> {
    INVALID = MetaType::INVALID.raw,
    HEADER(&'a mut [MetaHeaderRef]) = MetaType::HEADER.raw,
    VIDEO_CROP(&'a mut [MetaRegionRef]) = MetaType::VIDEO_CROP.raw,
    VIDEO_DAMAGE(&'a mut [MetaRegionRef]) = MetaType::VIDEO_DAMAGE.raw,
    BITMAP(&'a mut [MetaBitmapRef]) = MetaType::BITMAP.raw,
    CURSOR(&'a mut [MetaCursorRef]) = MetaType::CURSOR.raw,
    CONTROL(&'a mut [MetaControlRef]) = MetaType::CONTROL.raw,
    BUSY(&'a mut [MetaBusyRef]) = MetaType::BUSY.raw,
}
enum_wrapper!(
    MetaType,
    spa_sys::spa_meta_type,
    INVALID: spa_sys::SPA_META_Invalid,
    HEADER: spa_sys::SPA_META_Header,
    VIDEO_CROP: spa_sys::SPA_META_VideoCrop,
    VIDEO_DAMAGE: spa_sys::SPA_META_VideoDamage,
    BITMAP: spa_sys::SPA_META_Bitmap,
    CURSOR: spa_sys::SPA_META_Cursor,
    CONTROL: spa_sys::SPA_META_Control,
    BUSY: spa_sys::SPA_META_Busy,
    _LAST: spa_sys::_SPA_META_LAST,
);
#[derive(RawWrapper, Debug)]
#[repr(transparent)]
pub struct MetaHeaderRef {
    #[raw]
    raw: spa_sys::spa_meta_header,
}
bitflags! {
    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
    #[repr(transparent)]
    pub struct HeaderFlags: u32 {
        const DISCONT = spa_sys::SPA_META_HEADER_FLAG_DISCONT;
        const CORRUPTED = spa_sys::SPA_META_HEADER_FLAG_CORRUPTED;
        const MARKER = spa_sys::SPA_META_HEADER_FLAG_MARKER;
        const HEADER = spa_sys::SPA_META_HEADER_FLAG_HEADER;
        const GAP = spa_sys::SPA_META_HEADER_FLAG_GAP;
        const DELTA_UNIT = spa_sys::SPA_META_HEADER_FLAG_DELTA_UNIT;
    }
}
impl MetaHeaderRef {
    pub fn flags(&self) -> HeaderFlags {
        HeaderFlags::from_bits_retain(self.raw.flags)
    }
    pub fn set_flags(&mut self, flags: HeaderFlags) {
        self.raw.flags = flags.bits()
    }
    pub fn offset(&self) -> u32 {
        self.raw.offset
    }
    pub fn set_offset(&mut self, offset: u32) {
        self.raw.offset = offset
    }
    pub fn pts(&self) -> i64 {
        self.raw.pts
    }
    pub fn set_pts(&mut self, pts: i64) {
        self.raw.pts = pts
    }
    pub fn dts_offset(&self) -> i64 {
        self.raw.dts_offset
    }
    pub fn set_dts_offset(&mut self, dts_offset: i64) {
        self.raw.dts_offset = dts_offset
    }
    pub fn seq(&self) -> u64 {
        self.raw.seq
    }
    pub fn set_seq(&mut self, seq: u64) {
        self.raw.seq = seq
    }
}
#[derive(RawWrapper, Debug)]
#[repr(transparent)]
pub struct MetaRegionRef {
    #[raw]
    raw: spa_sys::spa_meta_region,
}
impl MetaRegionRef {
    pub fn region(&self) -> &RegionRef {
        unsafe { RegionRef::from_raw_ptr(addr_of!(self.raw.region)) }
    }
    pub fn region_mut(&mut self) -> &mut RegionRef {
        unsafe { RegionRef::mut_from_raw_ptr(addr_of_mut!(self.raw.region)) }
    }
    pub fn is_valid(&self) -> bool {
        self.raw.region.size.width != 0 && self.raw.region.size.height != 0
    }
}
#[derive(RawWrapper, Debug)]
#[repr(transparent)]
pub struct MetaBitmapRef {
    #[raw]
    raw: spa_sys::spa_meta_bitmap,
}
impl MetaBitmapRef {
    pub fn format(&self) -> VideoFormat {
        VideoFormat::from_raw(self.raw.format)
    }
    pub fn set_format(&mut self, format: VideoFormat) {
        self.raw.format = format.into()
    }
    pub fn size(&self) -> &RectangleRef {
        unsafe { RectangleRef::from_raw_ptr(addr_of!(self.raw.size)) }
    }
    pub fn size_mut(&mut self) -> &mut RectangleRef {
        unsafe { RectangleRef::mut_from_raw_ptr(addr_of_mut!(self.raw.size)) }
    }
    pub fn stride(&self) -> i32 {
        self.raw.stride
    }
    pub fn set_stride(&mut self, stride: i32) {
        self.raw.stride = stride
    }
    pub fn offset(&self) -> u32 {
        self.raw.offset
    }
    pub fn set_offset(&mut self, offset: u32) {
        self.raw.offset = offset
    }
    pub unsafe fn bitmap<T: Sized>(&self) -> Option<&mut [T]> {
        if self.raw.offset >= size_of::<MetaBitmapRef>() as u32 {
            let bitmap_ptr = (self.as_raw_ptr() as *mut u8).offset(self.raw.offset as isize);
            let len = self.raw.stride as u32 * self.raw.size.height / size_of::<T>() as u32;
            Some(slice::from_raw_parts_mut(bitmap_ptr.cast(), len as usize))
        } else {
            None
        }
    }
    pub fn is_valid(&self) -> bool {
        self.format() != VideoFormat::UNKNOWN
    }
}
#[derive(RawWrapper, Debug)]
#[repr(transparent)]
pub struct MetaCursorRef {
    #[raw]
    raw: spa_sys::spa_meta_cursor,
}
impl MetaCursorRef {
    pub fn id(&self) -> u32 {
        self.raw.id
    }
    pub fn set_id(&mut self, id: u32) {
        self.raw.id = id
    }
    pub fn flags(&self) -> u32 {
        self.raw.flags
    }
    pub fn set_flags(&mut self, flags: u32) {
        self.raw.flags = flags
    }
    pub fn position(&self) -> &PointRef {
        unsafe { PointRef::from_raw_ptr(addr_of!(self.raw.position)) }
    }
    pub fn position_mut(&mut self) -> &mut PointRef {
        unsafe { PointRef::mut_from_raw_ptr(addr_of_mut!(self.raw.position)) }
    }
    pub fn hotspot(&self) -> &PointRef {
        unsafe { PointRef::from_raw_ptr(addr_of!(self.raw.hotspot)) }
    }
    pub fn hotspot_mut(&mut self) -> &mut PointRef {
        unsafe { PointRef::mut_from_raw_ptr(addr_of_mut!(self.raw.hotspot)) }
    }
    pub fn bitmap_offset(&self) -> u32 {
        self.raw.bitmap_offset
    }
    pub fn set_bitmap_offset(&mut self, bitmap_offset: u32) {
        self.raw.bitmap_offset = bitmap_offset
    }
    pub unsafe fn bitmap(&self) -> Option<&mut MetaBitmapRef> {
        unsafe {
            if self.raw.bitmap_offset >= size_of::<MetaCursorRef>() as u32 {
                let bitmap_ptr =
                    (self.as_raw_ptr() as *mut u8).offset(self.bitmap_offset() as isize);
                Some(MetaBitmapRef::mut_from_raw_ptr(bitmap_ptr.cast()))
            } else {
                None
            }
        }
    }
    pub fn is_valid(&self) -> bool {
        self.raw.id != 0
    }
}
#[derive(RawWrapper, Debug)]
#[repr(transparent)]
pub struct MetaControlRef {
    #[raw]
    raw: spa_sys::spa_meta_control,
}
impl MetaControlRef {
    pub fn sequence(&self) -> &PodSequenceRef {
        unsafe { PodSequenceRef::from_raw_ptr(&self.raw.sequence) }
    }
}
#[derive(RawWrapper, Debug)]
#[repr(transparent)]
pub struct MetaBusyRef {
    #[raw]
    raw: spa_sys::spa_meta_busy,
}
impl MetaBusyRef {
    pub fn flags(&self) -> u32 {
        self.raw.flags
    }
    pub fn count(&self) -> u32 {
        self.raw.count
    }
}