/*-
 * syslog-rs - a syslog client translated from libc to rust
 * 
 * Copyright 2025 Aleksandr Morozov
 * 
 * The syslog-rs crate can be redistributed and/or modified
 * under the terms of either of the following licenses:
 *
 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
 *
 *   2. The MIT License (MIT)
 *                     
 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
 */

use std::fmt;

use crate::
{
    error::SyRes, 
    formatters::SyslogFormatter, 
    map_error_code, 
    sy_sync_queue::
    {
        SyCmd, 
        SyslogQueueChanRcv, 
        SyslogQueueChanSnd, 
        SyslogQueueChannel, 
        SyslogQueueOneChanRcv, 
        SyslogQueueOneChanSnd
    }, 
    SyslogDestination
};

impl<F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>> SyslogQueueChanRcv<F, D, S> 
for smol::channel::Receiver<SyCmd<F, D, S>>
{
    fn q_recv_blocking(&mut self) -> Option<SyCmd<F, D, S>>
    {
        return 
            self.recv_blocking().ok();
    }
}

impl<F: SyslogFormatter, D: SyslogDestination, S: SyslogQueueChannel<F, D>> SyslogQueueChanSnd<F, D, S> 
for smol::channel::Sender<SyCmd<F, D, S>>
{
    fn q_send_blocking(&self, msg: SyCmd<F, D, S>) -> SyRes<()> 
    {
        return 
            self
                .send_blocking(msg)
                .map_err(|e| 
                    map_error_code!(SendError, "send_blocking() error: '{}'", e)
                );
    }

    async 
    fn q_send(&self, msg: SyCmd<F, D, S>) -> SyRes<()>
    {
        return 
            self
                .send(msg)
                .await
                .map_err(|e| 
                    map_error_code!(SendError, "send_blocking() error: '{}'", e)
                );
    }
}
 
impl<C: Send + fmt::Debug> SyslogQueueOneChanSnd<C> for smol::channel::Sender<C>
{
    fn send_once_blocking(self, data: C) -> Result<(), C>
    {
        return 
            self
                .send_blocking(data)
                .map_err(|e| e.into_inner());
                
    }
}

impl<C> SyslogQueueOneChanRcv<C> for smol::channel::Receiver<C>
{
    fn recv_once_blocking(self) -> SyRes<C> 
    {
        return 
            self
                .recv_blocking()
                .map_err(|e|
                    map_error_code!(UnboundedChannelError, "channel error: '{}'", e)
                );
    }

    async 
    fn recv_once(self) -> SyRes<C> where Self: Sized
    {
        return 
            self
                .recv()
                .await
                .map_err(|e|
                    map_error_code!(UnboundedChannelError, "channel error: '{}'", e)
                );

    }
}

#[derive(Debug, Clone)]
pub struct SmolQueueAdapter;

unsafe impl Send for SmolQueueAdapter {}

impl<F: SyslogFormatter, D: SyslogDestination> SyslogQueueChannel<F, D> for SmolQueueAdapter
{
    const ADAPTER_NAME: &'static str = "smol";
    
    type ChannelSnd = smol::channel::Sender<SyCmd<F, D, Self>>;

    type ChannelRcv = smol::channel::Receiver<SyCmd<F, D, Self>>;

    type OneShotChannelSnd<C: Send + fmt::Debug> = smol::channel::Sender<C>;

    type OneShotChannelRcv<C> = smol::channel::Receiver<C>;

    fn create_channel() -> (Self::ChannelSnd, Self::ChannelRcv) 
    {
        return smol::channel::unbounded::<SyCmd<F, D, Self>>();
    }

    fn create_oneshot_channel<C: Send + fmt::Debug>() -> (Self::OneShotChannelSnd<C>, Self::OneShotChannelRcv<C>) 
    {
        return smol::channel::bounded::<C>(1);
    }
}

pub type DefaultQueueAdapter = SmolQueueAdapter;
