//! Implementations of DekuRead and DekuWrite for [T; N] where 0 < N <= 32

use crate::reader::Reader;
use crate::writer::Writer;
use crate::{DekuError, DekuReader, DekuWriter};
use core::mem::MaybeUninit;
use no_std_io::io::{Read, Seek, Write};

impl<'a, Ctx: Copy, T, const N: usize> DekuReader<'a, Ctx> for [T; N]
where
    T: DekuReader<'a, Ctx>,
{
    fn from_reader_with_ctx<R: Read + Seek>(
        reader: &mut Reader<R>,
        ctx: Ctx,
    ) -> Result<Self, DekuError>
    where
        Self: Sized,
    {
        let mut array: [MaybeUninit<T>; N] = [const { MaybeUninit::uninit() }; N];
        for (n, item) in array.iter_mut().enumerate() {
            match T::from_reader_with_ctx(reader, ctx) {
                Ok(value) => {
                    item.write(value);
                }
                Err(err) => {
                    // Drop initialized items
                    for item in &mut array[0..n] {
                        // SAFETY: `item` is certain to be initialized
                        unsafe {
                            item.assume_init_drop();
                        }
                    }

                    return Err(err);
                }
            };
        }

        // SAFETY: `array` is certain to be initialized
        let array = unsafe {
            // TODO: array_assume_init: https://github.com/rust-lang/rust/issues/96097
            (core::ptr::addr_of!(array).cast::<[T; N]>()).read()
        };
        Ok(array)
    }
}

impl<Ctx: Copy, T, const N: usize> DekuWriter<Ctx> for [T; N]
where
    T: DekuWriter<Ctx>,
{
    fn to_writer<W: Write + Seek>(
        &self,
        writer: &mut Writer<W>,
        ctx: Ctx,
    ) -> Result<(), DekuError> {
        for v in self {
            v.to_writer(writer, ctx)?;
        }
        Ok(())
    }
}

impl<Ctx: Copy, T> DekuWriter<Ctx> for &[T]
where
    T: DekuWriter<Ctx>,
{
    fn to_writer<W: Write + Seek>(
        &self,
        writer: &mut Writer<W>,
        ctx: Ctx,
    ) -> Result<(), DekuError> {
        for v in *self {
            v.to_writer(writer, ctx)?;
        }
        Ok(())
    }
}

impl<Ctx: Copy, T> DekuWriter<Ctx> for [T]
where
    T: DekuWriter<Ctx>,
{
    fn to_writer<W: Write + Seek>(
        &self,
        writer: &mut Writer<W>,
        ctx: Ctx,
    ) -> Result<(), DekuError> {
        for v in self {
            v.to_writer(writer, ctx)?;
        }
        Ok(())
    }
}

impl<T: crate::DekuSize, const N: usize> crate::DekuSize for [T; N] {
    const SIZE_BITS: usize = T::SIZE_BITS * N;
}

#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
    use super::*;
    use rstest::rstest;
    use std::io::Cursor;

    use crate::{ctx::Endian, writer::Writer};
    #[cfg(feature = "bits")]
    use crate::{reader::Reader, DekuReader};

    #[cfg(feature = "bits")]
    #[rstest(input,endian,expected,
        case::normal_le([0xDD, 0xCC, 0xBB, 0xAA].as_ref(), Endian::Little, [0xCCDD, 0xAABB]),
        case::normal_be([0xDD, 0xCC, 0xBB, 0xAA].as_ref(), Endian::Big, [0xDDCC, 0xBBAA]),
        #[should_panic(expected = "Incomplete(NeedSize { bits: 16 })")]
        case::normal_be([0xDD, 0xCC].as_ref(), Endian::Big, [0xDDCC, 0xBBAA]),
    )]
    fn test_bit_read(input: &[u8], endian: Endian, expected: [u16; 2]) {
        let mut cursor = std::io::Cursor::new(input);
        let mut reader = Reader::new(&mut cursor);
        let res_read = <[u16; 2]>::from_reader_with_ctx(&mut reader, endian).unwrap();
        assert_eq!(expected, res_read);
    }

    #[rstest(input,endian,expected,
        case::normal_le([0xDDCC, 0xBBAA], Endian::Little, vec![0xCC, 0xDD, 0xAA, 0xBB]),
        case::normal_be([0xDDCC, 0xBBAA], Endian::Big, vec![0xDD, 0xCC, 0xBB, 0xAA]),
    )]
    fn test_bit_write(input: [u16; 2], endian: Endian, expected: Vec<u8>) {
        // test writer

        use std::io::Cursor;
        let mut writer = Writer::new(Cursor::new(vec![]));
        input.to_writer(&mut writer, endian).unwrap();
        assert_eq!(expected, writer.inner.into_inner());
    }

    #[rstest(input,endian,expected,
        case::normal_le(
            [[0xCCDD, 0xAABB], [0x8899, 0x6677]],
            Endian::Little,
            vec![0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66],
        ),
        case::normal_be(
            [[0xDDCC, 0xBBAA], [0x9988, 0x7766]],
            Endian::Big,
            vec![0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66],
        ),
    )]
    fn test_nested_array_bit_write(input: [[u16; 2]; 2], endian: Endian, expected: Vec<u8>) {
        // test writer

        let mut writer = Writer::new(Cursor::new(vec![]));
        input.to_writer(&mut writer, endian).unwrap();
        assert_eq!(expected, writer.inner.into_inner());

        // test &slice
        let input = input.as_ref();
        let mut writer = Writer::new(Cursor::new(vec![]));
        input.to_writer(&mut writer, endian).unwrap();
        assert_eq!(expected, writer.inner.into_inner());
    }
}
