use core::slice;
use std::ffi::{CStr, CString};
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::mem::size_of;
use crate::spa::pod::pod_buf::{AllocPod, PodBuf};
use crate::spa::pod::restricted::{CloneTo, PodRawValue};
use crate::spa::pod::string::PodStringRef;
use crate::spa::pod::{
    BasicTypePod, FromValue, PodIntRef, PodResult, PodValue, SizedPod, WritePod, POD_ALIGN,
};
pub struct PodIterator<'a, E: SizedPod> {
    size: usize,
    first_element_ptr: *const E,
    current_element_ptr: *const E,
    phantom: PhantomData<&'a E>,
}
impl<'a, E: SizedPod> PodIterator<'a, E> {
    pub fn from_container<C: SizedPod>(container: &'a C) -> Self {
        unsafe {
            let first_element_ptr = (container as *const C).offset(1).cast();
            Self::new(first_element_ptr, container.pod_size() - size_of::<C>())
        }
    }
    pub fn new(first_element_ptr: *const E, size: usize) -> Self {
        Self {
            size,
            first_element_ptr,
            current_element_ptr: first_element_ptr,
            phantom: PhantomData,
        }
    }
    pub fn build() -> PodIteratorBuilder<E> {
        PodIteratorBuilder::new()
    }
    unsafe fn inside(&self, ptr: *const E) -> bool {
        let max_offset_bytes = self.max_offset_bytes();
        let offset_bytes =
            (ptr as *const u8).offset_from(self.first_element_ptr as *const u8) as usize;
        offset_bytes < max_offset_bytes && (offset_bytes + (*ptr).pod_size()) <= max_offset_bytes
    }
    unsafe fn next_element_ptr(&self) -> *const E {
        let ptr = self.current_element_ptr;
        let size = (*ptr).pod_size();
        let next_ptr = (ptr as *const u8).add(size);
        next_ptr.add(next_ptr.align_offset(POD_ALIGN)).cast()
    }
    fn max_offset_bytes(&self) -> usize {
        self.size
    }
    pub(crate) unsafe fn as_bytes(&self) -> &[u8] {
        slice::from_raw_parts(self.first_element_ptr as *const u8, self.max_offset_bytes())
    }
}
impl<'a, E: SizedPod + 'a> Iterator for PodIterator<'a, E> {
    type Item = &'a E;
    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            let current_element_ptr = self.current_element_ptr;
            if self.inside(current_element_ptr) {
                self.current_element_ptr = self.next_element_ptr();
                Some(&*current_element_ptr)
            } else {
                None
            }
        }
    }
}
impl<'a, E: SizedPod + 'a> Debug for PodIterator<'_, E> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("PodIterator").finish()
    }
}
pub struct PodValueIterator<'a, E: PodRawValue> {
    size: usize,
    element_size: usize,
    first_element_ptr: *const E::RawValue,
    current_element_ptr: *const E::RawValue,
    phantom: PhantomData<&'a ()>,
}
impl<'a, E: PodRawValue> PodValueIterator<'a, E> {
    pub fn new(first_element_ptr: *const E::RawValue, size: usize, element_size: usize) -> Self {
        Self {
            size,
            element_size,
            first_element_ptr,
            current_element_ptr: first_element_ptr,
            phantom: PhantomData,
        }
    }
    unsafe fn inside(&self, ptr: *const E::RawValue) -> bool {
        let max_offset_bytes = self.size;
        let offset_bytes =
            (ptr as *const u8).offset_from(self.first_element_ptr as *const u8) as usize;
        offset_bytes < max_offset_bytes && (offset_bytes + self.element_size) <= max_offset_bytes
    }
    unsafe fn next_element_ptr(&self) -> *const E::RawValue {
        let ptr = self.current_element_ptr;
        let size = self.element_size;
        (ptr as *const u8).add(size).cast()
    }
    pub fn element_size(&self) -> usize {
        self.element_size
    }
    pub(crate) unsafe fn as_bytes(&self) -> &[u8] {
        slice::from_raw_parts(self.first_element_ptr as *const u8, self.size)
    }
}
impl<'a, E: PodRawValue + 'a> Iterator for PodValueIterator<'a, E> {
    type Item = E::Value;
    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            let current_element_ptr = self.current_element_ptr;
            if self.inside(current_element_ptr) {
                self.current_element_ptr = self.next_element_ptr();
                E::parse_raw_value(current_element_ptr, self.element_size).ok()
            } else {
                None
            }
        }
    }
}
impl<'a, E: PodRawValue + 'a> Debug for PodValueIterator<'a, E> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("PodValueIterator").finish()
    }
}
#[repr(transparent)]
pub struct AllocatedPodIterator<E: SizedPod> {
    data: AllocPod<E>,
}
impl<E: SizedPod> AllocatedPodIterator<E> {
    pub fn iter(&self) -> PodIterator<E> {
        PodIterator::new(self.data.as_ptr(), self.data.size())
    }
}
impl<'a, E> AllocatedPodIterator<E>
where
    E: 'a,
    &'a E: WritePod,
    E: SizedPod,
{
    pub fn from_values(
        values: impl IntoIterator<Item = &'a <&'a E as PodValue>::Value>,
    ) -> PodResult<Self> {
        Ok(PodIteratorBuilder::from_values(values)?.into_pod_iter())
    }
}
pub struct AllocatedPodValueIterator<E: PodRawValue> {
    data: Vec<E::RawValue>,
}
impl<E: PodRawValue> AllocatedPodValueIterator<E> {
    pub fn new(data: Vec<E::RawValue>) -> Self {
        Self { data }
    }
    pub fn iter(&self) -> PodValueIterator<E> {
        let data_slice = self.data.as_slice();
        let element_size = size_of::<E::RawValue>();
        PodValueIterator::new(
            data_slice.as_ptr(),
            std::mem::size_of_val(data_slice),
            element_size,
        )
    }
}
pub struct PodIteratorBuilder<E: SizedPod> {
    buf: PodBuf<E>,
}
impl<E: SizedPod> PodIteratorBuilder<E> {
    pub fn new() -> Self {
        Self { buf: PodBuf::new() }
    }
    pub fn into_pod_iter(self) -> AllocatedPodIterator<E> {
        AllocatedPodIterator {
            data: self.buf.into_pod(),
        }
    }
}
impl<E: SizedPod> Default for PodIteratorBuilder<E> {
    fn default() -> Self {
        Self::new()
    }
}
impl<E: SizedPod> PodIteratorBuilder<E> {
    pub fn push_pod(mut self, pod_value: &E) -> PodResult<Self> {
        pod_value.clone_to(&mut self.buf)?;
        Ok(self)
    }
    pub fn push_alloc_pod(mut self, alloc_pod: AllocPod<E>) -> PodResult<Self> {
        unsafe {
            self.buf.append_alloc_pod(alloc_pod);
        }
        Ok(self)
    }
}
impl<'a, E> PodIteratorBuilder<E>
where
    &'a E: WritePod,
    E: 'a,
    E: SizedPod,
{
    pub fn push_value(mut self, pod_value: &<&'a E as PodValue>::Value) -> PodResult<Self> {
        <&'a E as WritePod>::write_pod(&mut self.buf, pod_value)?;
        Ok(self)
    }
    pub fn from_values(
        values: impl IntoIterator<Item = &'a <&'a E as PodValue>::Value>,
    ) -> PodResult<Self> {
        let mut builder = Self::new();
        for v in values {
            builder = builder.push_value(v)?;
        }
        Ok(builder)
    }
}
#[test]
fn test_from_values() {
    let mut builder: PodIteratorBuilder<PodIntRef> = PodIteratorBuilder::new()
        .push_value(&123)
        .unwrap()
        .push_value(&1)
        .unwrap()
        .push_value(&2)
        .unwrap()
        .push_value(&3)
        .unwrap()
        .push_value(&4)
        .unwrap();
    let allocated_iter = builder.into_pod_iter();
    let v: Vec<i32> = allocated_iter.iter().map(|e| e.value().unwrap()).collect();
    assert_eq!(v, vec![123, 1, 2, 3, 4]);
    let mut builder: PodIteratorBuilder<PodIntRef> =
        PodIteratorBuilder::from_values([&123, &1, &2, &3, &4]).unwrap();
    let allocated_iter = builder.into_pod_iter();
    let v: Vec<i32> = allocated_iter.iter().map(|e| e.value().unwrap()).collect();
    assert_eq!(v, vec![123, 1, 2, 3, 4]);
}
#[test]
fn test_from_pods() {
    let mut builder: PodIteratorBuilder<PodStringRef> = PodIteratorBuilder::new();
    let string = CString::new("asd").unwrap();
    let allocated_pod = PodBuf::<PodStringRef>::from_value(&string.as_ref())
        .unwrap()
        .into_pod();
    builder = builder.push_pod(allocated_pod.as_pod()).unwrap();
    let string = CString::new("def").unwrap();
    let allocated_pod = PodBuf::<PodStringRef>::from_value(&string.as_ref())
        .unwrap()
        .into_pod();
    builder = builder.push_pod(allocated_pod.as_pod()).unwrap();
    let allocated_iter = builder.into_pod_iter();
    let v: Vec<&CStr> = allocated_iter.iter().map(|e| e.value().unwrap()).collect();
    assert_eq!(v.get(0).unwrap(), &CString::new("asd").unwrap().as_ref());
    assert_eq!(v.get(1).unwrap(), &CString::new("def").unwrap().as_ref());
}