//! Equivalent of [`crate::func`] for [`Copy`]/[`Fn`] Category.
//! Not well maintained due to not being used in the main RADN code base.
//!
//! See also: [`super::clone_func`]

pub trait CopyWeakFunctor {
    type CF<'a, A: Copy>: Copy;
}

pub trait CopyFunctor: CopyWeakFunctor {
    fn copy_fmap<'a, A: 'a + Copy, B: 'a + Copy, F: 'a + Fn(A) -> B>(
        f: F,
        fa: Self::CF<'a, A>,
    ) -> Self::CF<'a, B>;

    fn copy_replace<'a, A: 'a + Copy, B: 'a + Copy>(fa: Self::CF<'a, A>, b: B) -> Self::CF<'a, B> {
        Self::copy_fmap(move |_| b, fa)
    }

    fn copy_void<'a, A: 'a + Copy>(fa: Self::CF<'a, A>) -> Self::CF<'a, ()> {
        Self::copy_replace(fa, ())
    }
}

pub trait CopyApplicativeSeq: CopyFunctor {
    fn copy_seq<'a, A: 'a + Copy, B: 'a + Copy, F: 'a + Copy + Fn(A) -> B>(
        ff: Self::CF<'a, F>,
        fa: Self::CF<'a, A>,
    ) -> Self::CF<'a, B>;

    fn _copy_la2<'a, A: 'a + Copy, B: 'a + Copy, C: 'a + Copy, F: 'a + Copy + Fn(A, B) -> C>(
        f: F,
        fa: Self::CF<'a, A>,
        fb: Self::CF<'a, B>,
    ) -> Self::CF<'a, C> {
        Self::copy_seq(Self::copy_fmap(move |a| move |b| f(a, b), fa), fb)
    }
}

pub trait CopyApplicativeLA2: CopyFunctor {
    fn copy_la2<'a, A: 'a + Copy, B: 'a + Copy, C: 'a + Copy, F: 'a + Copy + Fn(A, B) -> C>(
        f: F,
        fa: Self::CF<'a, A>,
        fb: Self::CF<'a, B>,
    ) -> Self::CF<'a, C>;

    fn _copy_seq<'a, A: 'a + Copy, B: 'a + Copy, F: 'a + Copy + Fn(A) -> B>(
        ff: Self::CF<'a, F>,
        fa: Self::CF<'a, A>,
    ) -> Self::CF<'a, B> {
        Self::copy_la2(|f, a| f(a), ff, fa)
    }
}

pub trait CopyApplicative: CopyFunctor + CopyApplicativeSeq + CopyApplicativeLA2 {
    fn copy_pure<'a, A: 'a + Copy>(a: A) -> Self::CF<'a, A>;

    fn copy_discard_first<'a, A: 'a + Copy, B: 'a + Copy>(
        fa: Self::CF<'a, A>,
        fb: Self::CF<'a, B>,
    ) -> Self::CF<'a, B> {
        Self::copy_seq(Self::copy_replace(fa, |b| b), fb)
    }

    fn copy_discard_second<'a, A: 'a + Copy, B: 'a + Copy>(
        fa: Self::CF<'a, A>,
        fb: Self::CF<'a, B>,
    ) -> Self::CF<'a, A> {
        Self::copy_la2(|a, _| a, fa, fb)
    }
}

pub trait CopyMonad: CopyApplicative {
    fn copy_bind<'a, A: 'a + Copy, B: 'a + Copy, F: 'a + Copy + Fn(A) -> Self::CF<'a, B>>(
        fa: Self::CF<'a, A>,
        f: F,
    ) -> Self::CF<'a, B>;

    fn copy_join<'a, A: 'a + Copy>(ffa: Self::CF<'a, Self::CF<'a, A>>) -> Self::CF<'a, A>
    where
        Self::CF<'a, A>: 'a,
    {
        // ugly
        Self::copy_bind(ffa, |fa| fa)
    }
}