//! implementation detail for scaling. look into if you want to add a algorithm
use std::num::NonZeroU32;

#[doc(hidden)]
mod seal {
    #[doc(hidden)]
    pub trait Sealed {}
}

use seal::Sealed;

use crate::Image;
impl Sealed for ChannelCount<1> {}
impl Sealed for ChannelCount<2> {}
impl Sealed for ChannelCount<3> {}
impl Sealed for ChannelCount<4> {}

/// How to scale a image
pub trait ScalingAlgorithm {
    /// Y/Rgb scale
    fn scale_opaque<const N: usize>(
        i: Image<&[u8], N>,
        w: NonZeroU32,
        h: NonZeroU32,
    ) -> Image<Box<[u8]>, N>
    where
        ChannelCount<N>: ToImageView<N>;
    /// Ya/Rgba scale
    fn scale_transparent<const N: usize>(
        i: Image<&mut [u8], N>,
        w: NonZeroU32,
        h: NonZeroU32,
    ) -> Image<Box<[u8]>, N>
    where
        ChannelCount<N>: AlphaDiv<N>;
}

/// helper
pub trait ToImageView<const N: usize>: Sealed {
    #[doc(hidden)]
    type P: fr::PixelExt + fr::Convolution;
    #[doc(hidden)]
    fn wrap(i: Image<&[u8], N>) -> fr::ImageView<'_, Self::P>;
}

/// helper
pub trait AlphaDiv<const N: usize>: Sealed + ToImageView<N> {
    #[doc(hidden)]
    type P: fr::PixelExt + fr::Convolution + fr::AlphaMulDiv;
    #[doc(hidden)]
    fn handle(i: Image<&mut [u8], N>) -> fr::Image<'_, <Self as AlphaDiv<N>>::P>;
    #[doc(hidden)]
    fn unhandle(i: &mut fr::Image<<Self as AlphaDiv<N>>::P>);
}

/// Generic helper for [`Image`] and [`fr::Image`] transfers.
pub struct ChannelCount<const N: usize> {}

macro_rules! tiv {
    ($n:literal, $which:ident) => {
        impl ToImageView<$n> for ChannelCount<$n> {
            type P = fr::$which;
            fn wrap(i: Image<&[u8], $n>) -> fr::ImageView<'_, Self::P> {
                // SAFETY: same conds
                unsafe { fr::ImageView::new(i.width, i.height, i.buffer()) }
            }
        }
    };
}

tiv!(1, U8);
tiv!(2, U8x2);
tiv!(3, U8x3);
tiv!(4, U8x4);

macro_rules! adiv {
    ($n:literal, $which:ident) => {
        impl AlphaDiv<$n> for ChannelCount<$n> {
            type P = fr::$which;
            fn handle(i: Image<&mut [u8], $n>) -> fr::Image<'_, <Self as AlphaDiv<$n>>::P> {
                // SAFETY: we kinda have the same conditions
                let mut i = unsafe { fr::Image::from_slice_u8(i.width, i.height, i.take_buffer()) };
                // SAFETY: mhm
                unsafe { fr::MulDiv::default().multiply_alpha_inplace(&mut i.view_mut()) };

                i
            }

            fn unhandle(i: &mut fr::Image<<Self as AlphaDiv<$n>>::P>) {
                // SAFETY: mhm
                unsafe { fr::MulDiv::default().divide_alpha_inplace(&mut i.view_mut()) }
            }
        }
    };
}

adiv!(2, U8x2);
adiv!(4, U8x4);
