/*-
 * 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::{self, Arguments, Write}, marker::PhantomData};


use crate::{formatters::SyslogFormatter, sync::syslog_trait::SyslogApi, Priority, SyslogDestination};

/// A standart trait which provides the access to the instance which implements [std::fmt::Write].
/// 
/// The syslog requres the [Priority] of the message to be specified. The `Write` does not 
/// allow to provide additional arguments. For this reason, a function `stream` borrows the
/// current syslog instance and stores the [Priority] which will be used later.
pub trait SyStreamApi<'stream, F: SyslogFormatter, D: SyslogDestination, S: SyslogApi<F, D>>
{
    /// Creates the temporary structure which holds the [Priority] and implements 
    /// [std::fmt::Write]. A [SyStream] is returned which can be used directly
    /// with [write].
    /// 
    /// ```ignore
    /// write!(SYSLOG.stream(Priority::LOG_DEBUG),
    /// ```
    fn stream(&'stream self, pri: Priority) -> SyStream<'stream, D, F, S>;
}

/// A structure which holds the [Priority] and reference to the syslog instance.
/// 
/// It implements [std::fmt::Write] so, the macro [write] can be used direclty.
pub struct SyStream<'stream, D: SyslogDestination, F: SyslogFormatter, S: SyslogApi<F, D>>
{
    /// Syslog instance
    pub(crate) inner: &'stream S,

    /// Priority
    pub(crate) pri: Priority,

    pub(crate) _p: PhantomData<F>,

    pub(crate) _p1: PhantomData<D>,
}

impl<'stream, D: SyslogDestination, F: SyslogFormatter, S: SyslogApi<F, D>> Write 
for SyStream<'stream, D, F, S>
{
    fn write_str(&mut self, s: &str) -> fmt::Result 
    {
        let s = s.to_string();

        self.inner.syslog(self.pri, s.into());

        return Ok(());
    }

    fn write_fmt(self: &mut Self, args: Arguments<'_>) -> fmt::Result
    {
        if let Some(s) = args.as_str() 
        {
            self.inner.syslog(self.pri, s.into());

            return Ok(());
        } 
        else 
        {
            self.inner.syslog(self.pri, args.to_string().into());

            return Ok(());
        }
    }
}

