/*-
 * 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::time::Duration;
use std::io::{IoSlice, Write};


use smol::io::AsyncWrite;
use smol::Timer;

use crate::smol::io::AsyncWriteExt;

use crate::error::SyRes;
use crate::{map_error_os};
use crate::{a_sync::syslog_async_internal::AsyncSyslogInternalIO, LogStat};

pub type DefaultIOs = SmolIOs;

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

impl SmolIOs
{
    /// Sends to the FD i.e file of stderr, stdout or any which 
    /// implements [Write] `write_vectored` in async manner
    ///
    /// # Arguments
    /// 
    /// * `file_fd` - mutable consume of the container FD.
    ///
    /// * `msg` - a reference on array of data
    ///
    /// * `newline` - a new line string ref i.e "\n" or "\r\n"
    pub(crate) 
    fn send_to_fd<W>(mut file_fd: W, msg: &str, newline: &str) -> SyRes<usize>
    where W: Write
    {
        return 
            file_fd
                .write_vectored(
                    &[IoSlice::new(msg.as_bytes()), IoSlice::new(newline.as_bytes())]
                )
                .map_err(|e|
                    map_error_os!(e, "async_send_to_fd() writev() failed")
                );
    }

    pub(crate) async 
    fn async_send_to_fd<W>(mut file_fd: W, msg: &str, newline: &str) -> SyRes<usize>
    where W: AsyncWrite + Unpin
    {
        return 
            file_fd
                .write_vectored(
                    &[IoSlice::new(msg.as_bytes()), IoSlice::new(newline.as_bytes())]
                )
                .await
                .map_err(|e|
                    map_error_os!(e, "async_send_to_fd() writev() failed")
                );
    }
}

#[cfg(target_family = "unix")]
impl AsyncSyslogInternalIO for SmolIOs
{
    #[inline]
    async 
    fn send_to_stderr(logstat: LogStat, msg: &str)
    {
        
        if logstat.intersects(LogStat::LOG_PERROR) == true
        {
            let stderr_lock = std::io::stderr();

            let newline = "\n";
            let _ = Self::send_to_fd(stderr_lock, msg, newline);
        }
    }

    #[inline]
    async 
    fn send_to_syscons(logstat: LogStat, msg_payload: &str)
    {
        use nix::libc;
        use smol::fs::OpenOptions;
        use smol::fs::unix::OpenOptionsExt;
        use crate::{PATH_CONSOLE};

        if logstat.intersects(LogStat::LOG_CONS)
        {
            let syscons = 
               OpenOptions
                    ::new()
                        .create(false)
                        .read(false)
                        .write(true)
                        .custom_flags(libc::O_NONBLOCK | libc::O_CLOEXEC)
                        .open(*PATH_CONSOLE)
                        .await;

            if let Ok(file) = syscons
            {
                let newline = "\n";
                let _ = Self::async_send_to_fd(file, msg_payload, newline);
            }
        }
    }

    #[inline]
    async 
    fn sleep_micro(us: u64)
    {
        Timer::after(Duration::from_micros(us));
    }
}

#[cfg(target_family = "windows")]
impl AsyncSyslogInternalIO for SmolIOs
{
    #[inline]
    async 
    fn send_to_stderr(logstat: LogStat, msg: &str)
    {
        if logstat.intersects(LogStat::LOG_PERROR) == true
        {
           eprintln!("{}", msg);
        }
    }

    #[inline]
    async 
    fn send_to_syscons(logstat: LogStat, msg_payload: &str)
    {
        if logstat.intersects(LogStat::LOG_CONS)
        {
            eprintln!("{}", msg_payload);
        }
    }
    
    #[inline]
    async 
    fn sleep_micro(us: u64) 
    {
        Timer::after(Duration::from_micros(us));
    }
}
