use std::fmt::{Debug, Display, Formatter};
use std::io::{Seek, Write};
use std::mem::size_of;
use std::ops::{Deref, Rem};
use std::os::fd::RawFd;
use std::slice;
use spa_sys::spa_pod;
use array::PodArrayRef;
use bitmap::PodBitmapRef;
use bytes::PodBytesRef;
use choice::PodChoiceRef;
use id::PodIdRef;
use pipewire_wrapper_proc_macro::RawWrapper;
use restricted::PodRawValue;
use sequence::PodSequenceRef;
use string::PodStringRef;
use struct_::PodStructRef;
use crate::spa::pod::choice::{ChoiceType, ChoiceValueType};
use crate::spa::pod::object::PodObjectRef;
use crate::spa::pod::pod_buf::{AllocPod, PodBuf};
use crate::spa::pod::pointer::PodPointerRef;
use crate::spa::pod::restricted::{
    BasicTypePod, CloneTo, PodHeader, PrimitiveValue, SizedPod, WritePod, WriteValue,
};
use crate::spa::type_::{FractionRef, RectangleRef, Type};
use crate::wrapper::RawWrapper;
use self::restricted::write_header;
pub mod array;
pub mod bitmap;
pub mod bytes;
pub mod choice;
pub mod control;
pub mod id;
pub mod iterator;
pub mod object;
pub mod pointer;
pub mod sequence;
pub mod string;
pub mod struct_;
pub mod pod_buf;
mod restricted;
macro_rules! primitive_type_pod_impl {
    ($pod_ref_type:ty, $pod_type:expr, $value_raw_type:ty) => {
        primitive_type_pod_impl!(
            $pod_ref_type,
            $pod_type,
            $value_raw_type,
            $value_raw_type,
            v,
            v
        );
    };
    ($pod_ref_type:ty, $pod_type:expr, $value_raw_type:ty, $value_type:ty) => {
        primitive_type_pod_impl!($pod_ref_type, $pod_type, $value_raw_type, $value_type, v, v);
    };
    ($pod_ref_type:ty, $pod_type:expr, $value_raw_type:ty, $value_type:ty, $value_ident:ident, $convert_value_expr:expr) => {
        impl restricted::PrimitiveValue for $pod_ref_type {}
        impl PodRawValue for $pod_ref_type {
            type RawValue = $value_raw_type;
            fn parse_raw_value(ptr: *const Self::RawValue, size: usize) -> PodResult<Self::Value> {
                if size < size_of::<$value_raw_type>() {
                    Err(PodError::DataIsTooShort(size_of::<$value_raw_type>(), size))
                } else {
                    let $value_ident = unsafe { *ptr };
                    Ok($convert_value_expr)
                }
            }
            fn raw_value_ptr(&self) -> *const Self::RawValue {
                &self.raw.value
            }
        }
        impl PodValue for $pod_ref_type {
            type Value = $value_type;
            fn value(&self) -> PodResult<Self::Value> {
                Self::parse_raw_value(&self.raw_value(), self.raw.pod.size as usize)
            }
        }
        impl WritePod for $pod_ref_type {
            fn write_pod<W>(buffer: &mut W, value: &<Self as PodValue>::Value) -> PodResult<()>
            where
                W: Write + Seek,
            {
                restricted::write_header(
                    buffer,
                    size_of::<$value_raw_type>() as u32,
                    <$pod_ref_type>::static_type(),
                )?;
                Self::write_raw_value(buffer, value)?;
                restricted::write_align_padding(buffer)?;
                Ok(())
            }
        }
        impl WriteValue for $pod_ref_type {
            fn write_raw_value<W>(
                buffer: &mut W,
                value: &<Self as PodValue>::Value,
            ) -> PodResult<()>
            where
                W: Write + Seek,
            {
                let value: $value_raw_type = (*value).into();
                restricted::write_value(buffer, &value)?;
                Ok(())
            }
        }
        impl PodHeader for $pod_ref_type {
            fn pod_header(&self) -> &spa_sys::spa_pod {
                &self.raw.pod
            }
            fn static_type() -> Type {
                $pod_type
            }
        }
        impl $pod_ref_type {
            pub fn raw_value(&self) -> $value_raw_type {
                self.raw.value
            }
        }
        impl Debug for $pod_ref_type {
            fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
                unsafe {
                    f.debug_struct(stringify!($pod_ref_type))
                        .field("pod.type", &self.pod_type())
                        .field("pod.size", &self.pod_size())
                        .field("value", &self.value())
                        .finish()
                }
            }
        }
    };
}
pub enum PodError {
    DataIsTooShort(usize, usize),
    UnknownPodTypeToDowncast,
    WrongPodTypeToCast(Type, Type),
    StringIsNotNullTerminated,
    IndexIsOutOfRange,
    ChoiceElementMissing,
    UnexpectedChoiceElement,
    UnexpectedChoiceElementSize(usize, usize),
    UnsupportedChoiceElementType,
    UnexpectedControlType(u32),
    UnexpectedObjectType(u32),
    UnexpectedChoiceType(ChoiceType, ChoiceType),
    IOError(std::io::Error),
    PodIsNotAligned,
}
impl From<PodError> for crate::Error {
    fn from(value: PodError) -> Self {
        Self::PodParseError(value)
    }
}
impl From<std::io::Error> for PodError {
    fn from(value: std::io::Error) -> Self {
        Self::IOError(value)
    }
}
impl Display for PodError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            PodError::DataIsTooShort(expected, actual) => write!(
                f,
                "POD data size is too small for that type, expected {} > actual {}",
                expected, actual
            ),
            PodError::UnknownPodTypeToDowncast => {
                write!(f, "Cannot downcast Pod, child type is unknown")
            }
            PodError::WrongPodTypeToCast(cast_type, actual_type) => write!(
                f,
                "Cannot cast Pod with type {:?} to type {:?}",
                actual_type, cast_type
            ),
            PodError::StringIsNotNullTerminated => write!(f, "String is not null terminated"),
            PodError::IndexIsOutOfRange => write!(f, "Index is out of range"),
            PodError::ChoiceElementMissing => {
                write!(f, "Cannot find required element for choice type")
            }
            PodError::UnexpectedChoiceElement => write!(f, "Unexpected element in the choice type"),
            PodError::UnexpectedChoiceElementSize(expected, actual) => write!(
                f,
                "Unexpected choice element size, expected {} != actual {}",
                expected, actual
            ),
            PodError::UnsupportedChoiceElementType => write!(f, "Unsupported choice element type"),
            PodError::UnexpectedControlType(type_) => {
                write!(f, "Unexpected control type {}", type_)
            }
            PodError::UnexpectedObjectType(type_) => {
                write!(f, "Unexpected object type {}", type_)
            }
            PodError::UnexpectedChoiceType(expected, actual) => {
                write!(
                    f,
                    "Unexpected choice type, expected {:?} actual {:?}",
                    expected, actual
                )
            }
            PodError::IOError(err) => {
                write!(f, "IOError {:?}", err)
            }
            PodError::PodIsNotAligned => {
                write!(f, "Pod is not aligned!")
            }
        }
    }
}
impl Debug for PodError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self)
    }
}
impl std::error::Error for PodError {}
type PodResult<T> = Result<T, PodError>;
const POD_ALIGN: usize = 8;
pub trait PodValue {
    type Value: Debug;
    fn value(&self) -> PodResult<Self::Value>;
}
pub trait FromValue<'a>
where
    Self: 'a,
    Self: Sized,
    &'a Self: WritePod,
{
    fn from_value(value: &<&'a Self as PodValue>::Value) -> PodResult<AllocPod<Self>>;
}
pub trait FromPrimitiveValue
where
    Self: Sized,
    Self: WritePod,
{
    fn from_primitive(value: <Self as PodValue>::Value) -> PodResult<AllocPod<Self>>;
}
pub trait ToOwnedPod {
    type PodType: Sized;
    fn to_owned_pod(&self) -> PodResult<AllocPod<Self::PodType>>;
}
impl<'a, T> ToOwnedPod for &'a T
where
    &'a T: CloneTo,
{
    type PodType = T;
    fn to_owned_pod(&self) -> PodResult<AllocPod<Self::PodType>> {
        AllocPod::from_pod(self)
    }
}
impl<'a, T> FromValue<'a> for T
where
    T: Sized,
    T: 'a,
    &'a T: WritePod,
{
    fn from_value(value: &<&'a Self as PodValue>::Value) -> PodResult<AllocPod<Self>> {
        Ok(PodBuf::<Self>::from_value(value)?.into_pod())
    }
}
impl<T> FromPrimitiveValue for T
where
    T: Sized,
    T: WritePod,
{
    fn from_primitive(value: <Self as PodValue>::Value) -> PodResult<AllocPod<Self>> {
        Ok(PodBuf::<Self>::from_primitive_value(value)?.into_pod())
    }
}
impl<'a, T> PodValue for &'a T
where
    T: PodRawValue,
    T: PrimitiveValue,
{
    type Value = T::Value;
    fn value(&self) -> PodResult<Self::Value> {
        (*self).value()
    }
}
impl<'a, T> WritePod for &'a T
where
    T: WritePod,
    T: PrimitiveValue,
{
    fn write_pod<W>(buffer: &mut W, value: &<Self as PodValue>::Value) -> PodResult<()>
    where
        W: Write + Seek,
    {
        T::write_pod(buffer, value)
    }
}
impl<'a, T> WriteValue for &'a T
where
    T: WriteValue,
    T: PrimitiveValue,
{
    fn write_raw_value<W>(buffer: &mut W, value: &<Self as PodValue>::Value) -> PodResult<()>
    where
        W: Write + Seek,
    {
        T::write_raw_value(buffer, value)
    }
}
impl<T: PodHeader> SizedPod for T {
    fn pod_size(&self) -> usize {
        self.pod_header().size as usize + size_of::<spa_sys::spa_pod>()
    }
}
impl<T: SizedPod> CloneTo for &T {
    fn clone_to(&self, buffer: &mut impl Write) -> PodResult<()> {
        let size = self.pod_size();
        let slice = unsafe { slice::from_raw_parts(*self as *const T as *const u8, size) };
        buffer.write_all(slice)?;
        let rem = size.rem(POD_ALIGN);
        if rem > 0 {
            let padding_len = POD_ALIGN - rem;
            let padding = vec![0u8; padding_len];
            buffer.write_all(padding.as_slice())?;
        }
        Ok(())
    }
}
impl<T> BasicTypePod for T
where
    T: PodHeader,
    T: RawWrapper,
    T: Debug,
{
}
pub trait Upcast {
    fn upcast(&self) -> &PodRef;
}
impl<'a, T: BasicTypePod> Upcast for &'a T {
    fn upcast(&self) -> &PodRef {
        unsafe { PodRef::from_raw_ptr(self.pod_header()) }
    }
}
#[derive(RawWrapper)]
#[repr(transparent)]
pub struct PodRef {
    #[raw]
    raw: spa_sys::spa_pod,
}
impl PodRef {
    pub fn size(&self) -> u32 {
        self.raw.size
    }
    pub fn type_(&self) -> Type {
        Type::from_raw(self.raw.type_)
    }
    pub fn downcast(&self) -> PodResult<BasicType> {
        unsafe {
            match self.pod_type() {
                Type::NONE => Ok(BasicType::NONE),
                Type::BOOL => self.cast().map(BasicType::BOOL),
                Type::ID => self.cast().map(BasicType::ID),
                Type::INT => self.cast().map(BasicType::INT),
                Type::LONG => self.cast().map(BasicType::LONG),
                Type::FLOAT => self.cast().map(BasicType::FLOAT),
                Type::DOUBLE => self.cast().map(BasicType::DOUBLE),
                Type::STRING => self.cast().map(BasicType::STRING),
                Type::BYTES => self.cast().map(BasicType::BYTES),
                Type::RECTANGLE => self.cast().map(BasicType::RECTANGLE),
                Type::FRACTION => self.cast().map(BasicType::FRACTION),
                Type::BITMAP => self.cast().map(BasicType::BITMAP),
                Type::ARRAY => self.cast().map(BasicType::ARRAY),
                Type::STRUCT => self.cast().map(BasicType::STRUCT),
                Type::OBJECT => self.cast().map(BasicType::OBJECT),
                Type::SEQUENCE => self.cast().map(BasicType::SEQUENCE),
                Type::POINTER => self.cast().map(BasicType::POINTER),
                Type::FD => self.cast().map(BasicType::FD),
                Type::CHOICE => self.cast().map(BasicType::CHOICE),
                Type::POD => self.cast().map(BasicType::POD),
                _ => Err(PodError::UnknownPodTypeToDowncast),
            }
        }
    }
}
impl PodHeader for PodRef {
    fn pod_header(&self) -> &spa_pod {
        &self.raw
    }
    fn static_type() -> Type {
        Type::POD
    }
}
impl PodValue for PodRef {
    type Value = ();
    fn value(&self) -> PodResult<Self::Value> {
        Ok(())
    }
}
impl<'a> PodValue for &'a PodRef {
    type Value = BasicTypeValue<'a>;
    fn value(&self) -> PodResult<Self::Value> {
        Self::parse_raw_value(self.as_raw_ptr(), self.size() as usize)
    }
}
impl<'a> WritePod for &'a PodRef {
    fn write_pod<W>(buffer: &mut W, value: &<Self as PodValue>::Value) -> PodResult<()>
    where
        W: Write + Seek,
    {
        match value {
            BasicTypeValue::NONE => write_header(buffer, 0, Type::NONE),
            BasicTypeValue::BOOL(v) => PodBoolRef::write_pod(buffer, v),
            BasicTypeValue::ID(v) => PodIdRef::write_pod(buffer, v),
            BasicTypeValue::INT(v) => PodIntRef::write_pod(buffer, v),
            BasicTypeValue::LONG(v) => PodLongRef::write_pod(buffer, v),
            BasicTypeValue::FLOAT(v) => PodFloatRef::write_pod(buffer, v),
            BasicTypeValue::DOUBLE(v) => PodDoubleRef::write_pod(buffer, v),
            BasicTypeValue::STRING(v) => <&PodStringRef>::write_pod(buffer, v),
            BasicTypeValue::BYTES(v) => <&PodBytesRef>::write_pod(buffer, v),
            BasicTypeValue::RECTANGLE(v) => PodRectangleRef::write_pod(buffer, v),
            BasicTypeValue::FRACTION(v) => PodFractionRef::write_pod(buffer, v),
            BasicTypeValue::BITMAP(v) => <&PodBitmapRef>::write_pod(buffer, v),
            BasicTypeValue::ARRAY(v) => <&PodArrayRef>::write_pod(buffer, v),
            BasicTypeValue::STRUCT(v) => <&PodStructRef>::write_pod(buffer, v),
            BasicTypeValue::OBJECT(v) => <&PodObjectRef>::write_pod(buffer, v),
            BasicTypeValue::SEQUENCE(v) => <&PodSequenceRef>::write_pod(buffer, v),
            BasicTypeValue::POINTER(v) => <&PodPointerRef>::write_pod(buffer, v),
            BasicTypeValue::FD(v) => PodFdRef::write_pod(buffer, v),
            BasicTypeValue::CHOICE(v) => PodChoiceRef::<PodRef>::write_choice_value(buffer, v),
            BasicTypeValue::POD(v) => Err(PodError::UnknownPodTypeToDowncast),
        }
    }
}
impl<'a, T> PodRawValue for &'a T
where
    T: PodRawValue,
    T: PrimitiveValue,
{
    type RawValue = T::RawValue;
    fn raw_value_ptr(&self) -> *const Self::RawValue {
        (*self).raw_value_ptr()
    }
    fn parse_raw_value(ptr: *const Self::RawValue, size: usize) -> PodResult<Self::Value> {
        T::parse_raw_value(ptr, size)
    }
}
impl PodRawValue for PodRef {
    type RawValue = spa_sys::spa_pod;
    fn raw_value_ptr(&self) -> *const Self::RawValue {
        &self.raw
    }
    fn parse_raw_value(ptr: *const Self::RawValue, size: usize) -> PodResult<Self::Value> {
        Ok(())
    }
}
impl<'a> PodRawValue for &'a PodRef {
    type RawValue = spa_sys::spa_pod;
    fn raw_value_ptr(&self) -> *const Self::RawValue {
        &self.raw
    }
    fn parse_raw_value(ptr: *const Self::RawValue, _size: usize) -> PodResult<Self::Value> {
        Ok(match unsafe { PodRef::from_raw_ptr(ptr).downcast()? } {
            BasicType::NONE => BasicTypeValue::NONE,
            BasicType::BOOL(pod) => BasicTypeValue::BOOL(pod.value()?),
            BasicType::ID(pod) => BasicTypeValue::ID(pod.value()?),
            BasicType::INT(pod) => BasicTypeValue::INT(pod.value()?),
            BasicType::LONG(pod) => BasicTypeValue::LONG(pod.value()?),
            BasicType::FLOAT(pod) => BasicTypeValue::FLOAT(pod.value()?),
            BasicType::DOUBLE(pod) => BasicTypeValue::DOUBLE(pod.value()?),
            BasicType::STRING(pod) => BasicTypeValue::STRING(pod.value()?),
            BasicType::BYTES(pod) => BasicTypeValue::BYTES(pod.value()?),
            BasicType::RECTANGLE(pod) => BasicTypeValue::RECTANGLE(pod.value()?),
            BasicType::FRACTION(pod) => BasicTypeValue::FRACTION(pod.value()?),
            BasicType::BITMAP(pod) => BasicTypeValue::BITMAP(pod.value()?),
            BasicType::ARRAY(pod) => BasicTypeValue::ARRAY(pod.value()?),
            BasicType::STRUCT(pod) => BasicTypeValue::STRUCT(pod.value()?),
            BasicType::OBJECT(pod) => BasicTypeValue::OBJECT(pod.value()?),
            BasicType::SEQUENCE(pod) => BasicTypeValue::SEQUENCE(pod.value()?),
            BasicType::POINTER(pod) => BasicTypeValue::POINTER(pod.value()?),
            BasicType::FD(pod) => BasicTypeValue::FD(pod.value()?),
            BasicType::CHOICE(pod) => BasicTypeValue::CHOICE(pod.choice_value()?),
            _ => return Err(PodError::UnknownPodTypeToDowncast),
        })
    }
}
impl Debug for PodRef {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("PodRef")
            .field("size", &self.size())
            .field("type", &self.type_())
            .field("value", &self.value())
            .finish()
    }
}
#[repr(u32)]
#[derive(Debug)]
#[allow(non_camel_case_types)]
pub enum BasicType<'a> {
    NONE = Type::NONE.raw,
    BOOL(&'a PodBoolRef) = Type::BOOL.raw,
    ID(&'a PodIdRef) = Type::ID.raw,
    INT(&'a PodIntRef) = Type::INT.raw,
    LONG(&'a PodLongRef) = Type::LONG.raw,
    FLOAT(&'a PodFloatRef) = Type::FLOAT.raw,
    DOUBLE(&'a PodDoubleRef) = Type::DOUBLE.raw,
    STRING(&'a PodStringRef) = Type::STRING.raw,
    BYTES(&'a PodBytesRef) = Type::BYTES.raw,
    RECTANGLE(&'a PodRectangleRef) = Type::RECTANGLE.raw,
    FRACTION(&'a PodFractionRef) = Type::FRACTION.raw,
    BITMAP(&'a PodBitmapRef) = Type::BITMAP.raw,
    ARRAY(&'a PodArrayRef) = Type::ARRAY.raw,
    STRUCT(&'a PodStructRef) = Type::STRUCT.raw,
    OBJECT(&'a PodObjectRef) = Type::OBJECT.raw,
    SEQUENCE(&'a PodSequenceRef) = Type::SEQUENCE.raw,
    POINTER(&'a PodPointerRef) = Type::POINTER.raw,
    FD(&'a PodFdRef) = Type::FD.raw,
    CHOICE(&'a PodChoiceRef) = Type::CHOICE.raw,
    POD(&'a PodRef) = Type::POD.raw,
}
#[repr(u32)]
#[derive(Debug)]
#[allow(non_camel_case_types)]
pub enum BasicTypeValue<'a> {
    NONE = Type::NONE.raw,
    BOOL(<PodBoolRef as PodValue>::Value) = Type::BOOL.raw,
    ID(<PodIdRef as PodValue>::Value) = Type::ID.raw,
    INT(<PodIntRef as PodValue>::Value) = Type::INT.raw,
    LONG(<PodLongRef as PodValue>::Value) = Type::LONG.raw,
    FLOAT(<PodFloatRef as PodValue>::Value) = Type::FLOAT.raw,
    DOUBLE(<PodDoubleRef as PodValue>::Value) = Type::DOUBLE.raw,
    STRING(<&'a PodStringRef as PodValue>::Value) = Type::STRING.raw,
    BYTES(<&'a PodBytesRef as PodValue>::Value) = Type::BYTES.raw,
    RECTANGLE(<PodRectangleRef as PodValue>::Value) = Type::RECTANGLE.raw,
    FRACTION(<PodFractionRef as PodValue>::Value) = Type::FRACTION.raw,
    BITMAP(<&'a PodBitmapRef as PodValue>::Value) = Type::BITMAP.raw,
    ARRAY(<&'a PodArrayRef as PodValue>::Value) = Type::ARRAY.raw,
    STRUCT(<&'a PodStructRef as PodValue>::Value) = Type::STRUCT.raw,
    OBJECT(<&'a PodObjectRef as PodValue>::Value) = Type::OBJECT.raw,
    SEQUENCE(<&'a PodSequenceRef as PodValue>::Value) = Type::SEQUENCE.raw,
    POINTER(<&'a PodPointerRef as PodValue>::Value) = Type::POINTER.raw,
    FD(<PodFdRef as PodValue>::Value) = Type::FD.raw,
    CHOICE(ChoiceValueType) = Type::CHOICE.raw,
    POD(&'a PodRef) = Type::POD.raw,
}
#[derive(RawWrapper)]
#[repr(transparent)]
pub struct PodBoolRef {
    #[raw]
    raw: spa_sys::spa_pod_bool,
}
primitive_type_pod_impl!(PodBoolRef, Type::BOOL, i32, bool, v, v != 0);
#[derive(RawWrapper)]
#[repr(transparent)]
pub struct PodIntRef {
    #[raw]
    raw: spa_sys::spa_pod_int,
}
primitive_type_pod_impl!(PodIntRef, Type::INT, i32);
#[derive(RawWrapper)]
#[repr(transparent)]
pub struct PodLongRef {
    #[raw]
    raw: spa_sys::spa_pod_long,
}
primitive_type_pod_impl!(PodLongRef, Type::LONG, i64);
#[derive(RawWrapper)]
#[repr(transparent)]
pub struct PodFloatRef {
    #[raw]
    raw: spa_sys::spa_pod_float,
}
primitive_type_pod_impl!(PodFloatRef, Type::FLOAT, f32);
#[derive(RawWrapper)]
#[repr(transparent)]
pub struct PodDoubleRef {
    #[raw]
    raw: spa_sys::spa_pod_double,
}
primitive_type_pod_impl!(PodDoubleRef, Type::DOUBLE, f64);
#[derive(RawWrapper, Copy, Clone)]
#[repr(transparent)]
pub struct PodRectangleRef {
    #[raw]
    raw: spa_sys::spa_pod_rectangle,
}
primitive_type_pod_impl!(
    PodRectangleRef,
    Type::RECTANGLE,
    spa_sys::spa_rectangle,
    RectangleRef,
    v,
    RectangleRef::from_raw(v)
);
#[derive(RawWrapper, Copy, Clone)]
#[repr(transparent)]
pub struct PodFractionRef {
    #[raw]
    raw: spa_sys::spa_pod_fraction,
}
primitive_type_pod_impl!(
    PodFractionRef,
    Type::FRACTION,
    spa_sys::spa_fraction,
    FractionRef,
    v,
    FractionRef::from_raw(v)
);
#[derive(RawWrapper)]
#[repr(transparent)]
pub struct PodFdRef {
    #[raw]
    raw: spa_sys::spa_pod_fd,
}
primitive_type_pod_impl!(PodFdRef, Type::FD, i64, RawFd, v, v as RawFd);