use super::*;

/// Implementation detail for [`IntoChannel::map_input`].
pub struct Map<T, U, F, OverloadId, Handler> {
    pub(crate) convert: F,
    pub(crate) handler: Handler,
    pub(crate) marker: core::marker::PhantomData<(T, U, OverloadId)>,
}

impl<T, U, F, OverloadId, Handler: IntoChannel<OverloadId, U>> IntoChannel<ChannelPair, T>
    for Map<T, U, F, OverloadId, Handler>
where
    MapSender<T, U, F, Handler::Sender, OverloadId>: OneShotChannel<ChannelPair, T>,
{
    type Receiver = Handler::Receiver;
    type Sender = MapSender<T, U, F, Handler::Sender, OverloadId>;
    fn into_channel(
        self,
    ) -> (
        MapSender<T, U, F, Handler::Sender, OverloadId>,
        Self::Receiver,
    ) {
        let Map {
            convert, handler, ..
        } = self;
        let (tx, rx) = handler.into_channel();
        (
            MapSender {
                convert,
                tx,
                marker: Default::default(),
            },
            rx,
        )
    }
}

/// Implementation detail for [`IntoChannel::map_input`].
pub struct MapSender<T, U, F, Sender, OverloadId> {
    convert: F,
    tx: Sender,
    #[allow(clippy::type_complexity)]
    marker: core::marker::PhantomData<fn() -> (T, U, OverloadId)>,
}
impl<T, U, F, Sender, OverloadId> OneShotChannel<ChannelPair, T>
    for MapSender<T, U, F, Sender, OverloadId>
where
    OverloadId: 'static,
    U: 'static,
    T: 'static,
    F: FnOnce(T) -> U + Send + 'static + Sync,
    Sender: OneShotChannel<OverloadId, U>,
{
    fn send_infallibly_once(self, value: T) {
        self.tx.send_infallibly_once((self.convert)(value))
    }
}
impl<T, U, F, Sender, OverloadId> ConcurrentChannel<ChannelPair, T>
    for MapSender<T, U, F, Sender, OverloadId>
where
    OverloadId: 'static,
    U: 'static,
    T: 'static,
    F: Fn(T) -> U + Send + 'static + Sync,
    Sender: ConcurrentChannel<OverloadId, U>,
{
    fn send_infallibly(&self, value: T) {
        self.tx.send_infallibly((self.convert)(value))
    }
}

impl<T, U, F, Sender, OverloadId> Channel<ChannelPair, T> for MapSender<T, U, F, Sender, OverloadId>
where
    OverloadId: 'static,
    U: 'static,
    T: 'static,
    F: FnMut(T) -> U + Send + 'static + Sync,
    Sender: Channel<OverloadId, U>,
{
    fn send_infallibly_mut(&mut self, value: T) {
        self.tx.send_infallibly_mut((self.convert)(value))
    }
}
