#![allow(clippy::type_complexity)]
#![allow(unused)]
use hazardflow_macro::magic;
use super::*;
use crate::prelude::*;
#[magic(module::split)]
pub fn module_split<I1: Interface, I2: Interface, O1: Interface, O2: Interface>(
    _m: impl FnOnce(I1, I2) -> (O1, O2),
) -> (fn(I1) -> O1, fn(I2) -> O2) {
    compiler_magic!()
}
pub fn module_split3<I1: Interface, I2: Interface, I3: Interface, O1: Interface, O2: Interface, O3: Interface>(
    m: impl FnOnce(I1, I2, I3) -> (O1, O2, O3),
) -> (impl FnOnce(I1) -> O1, impl FnOnce(I2) -> O2, impl FnOnce(I3) -> O3) {
    let (m1, m23) = module_split(move |i1, (i2, i3)| {
        let (e1, e2, e3) = m(i1, i2, i3);
        (e1, (e2, e3))
    });
    let (m2, m3) = module_split(move |i2, i3| m23((i2, i3)));
    (m1, m2, m3)
}
#[magic(module::from_fn)]
pub fn from_fn<I: Interface, O: Interface, J: Interface, T, const N: usize>(f: T) -> [fn(I, J) -> (O, J); N]
where T: FnOnce(I, J) -> (O, J) {
    compiler_magic!()
}
#[magic(module::seq)]
pub fn seq<I: Interface, O: Interface, J: Interface, const N: usize>(
    ms: [fn(I, J) -> (O, J); N],
) -> impl FnOnce([I; N], J) -> ([O; N], J) {
    |is, j| compiler_magic!()
}
pub fn flip<I1: Interface, I2: Interface, O1: Interface, O2: Interface>(
    f: impl FnOnce(I1, I2) -> (O1, O2),
) -> impl FnOnce(I2, I1) -> (O2, O1) {
    move |i2, i1| {
        let (o1, o2) = f(i1, i2);
        (o2, o1)
    }
}
pub fn exclusive<H: Hazard, EH: Hazard, const D: Dep, const ED: Dep>(
    m: impl FnOnce(I<AndH<H>, D>) -> I<EH, ED>,
) -> impl FnOnce(I<AndH<H>, D>) -> I<EH, ED> {
    move |i| {
        let (m_resp_tx, m_resp_rx) = channel::<I<EH, ED>>();
        let m_resp = ().comb(m_resp_rx);
        let (e, m_req) = unsafe {
            (i, m_resp).fsm::<(I<EH, ED>, I<AndH<H>, D>), bool>(false, |(ip1, ip2), (er1, er2), s| {
                let ep1 = ip2;
                let et1 = ep1.is_some_and(|p| EH::ready(p, er1));
                let ep2 = if s && !et1 { None } else { ip1 };
                let et2 = ep2.is_some_and(|p| AndH::<H>::ready(p, er2));
                let ir1 = if !s || et1 { er2 } else { Ready::new(false, er2.inner) };
                let it1 = ip1.is_some_and(|p| AndH::<H>::ready(p, ir1));
                let ir2 = er1;
                let s_next = if !s && it1 && et1 {
                    false
                } else if it1 {
                    true
                } else if et1 {
                    false
                } else {
                    s
                };
                ((ep1, ep2), (ir1, ir2), s_next)
            })
        };
        m_req.comb(m).comb(m_resp_tx);
        e
    }
}
pub fn channel<I: Interface>() -> (impl FnOnce(I), impl FnOnce(()) -> I) {
    let m = move |i, ()| ((), i);
    module_split(m)
}