use std::fmt;

use crate::jpeg::data::dct;

#[repr(align(16))]
#[derive(Clone, Copy)]
struct BlockAlign([u8; 0]);

#[derive(Clone, Copy)]
pub struct Block8x8<T> {
    pub data: [T; 64],
    _align: BlockAlign,
}

impl<T> Block8x8<T> {
    pub const fn new(data: [T; 64]) -> Self {
        Self {
            data,
            _align: BlockAlign([]),
        }
    }

    pub fn map<U, F: Fn(T) -> U>(self, f: F) -> Block8x8<U> {
        Block8x8::new(self.data.map(f))
    }
}

impl<T: Copy> Block8x8<T> {
    pub fn splat(v: T) -> Self {
        Self {
            data: [v; 64],
            _align: BlockAlign([]),
        }
    }

    #[inline]
    pub fn get(&self, x: usize, y: usize) -> T {
        self.data[y * 8 + x]
    }

    pub fn unzigzag(from: &[T; 64]) -> Self {
        let mut output = [from[0]; 64];
        super::unzigzag64(from, &mut output);
        Self::new(output)
    }
}

impl<T> From<Block8x8<T>> for Vec<T> {
    fn from(value: Block8x8<T>) -> Self {
        value.data.into()
    }
}

impl Block8x8<i16> {
    #[inline]
    pub fn idct(&self) -> Block8x8<f64> {
        dct::idct_8x8(self)
    }
}

impl<T: fmt::Debug> fmt::Debug for Block8x8<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut outer = f.debug_list();
        for y in 0..8 {
            outer.entry(&&self.data[y * 8..y * 8 + 8]);
        }
        outer.finish()
    }
}

impl<T: fmt::Display> fmt::Display for Block8x8<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for y in 0..8 {
            for x in 0..8 {
                if x != 0 {
                    write!(f, " ")?;
                }
                fmt::Display::fmt(&self.data[y * 8 + x], f)?;
            }
            writeln!(f)?;
        }
        Ok(())
    }
}
