#[cfg(target_family = "unix")]
use std::path::Path;
use std::sync::OnceLock;
use std::thread;
use std::time::Duration;

use chrono::{Local, SecondsFormat};

use syslog_rs::{NILVALUE, SyncSyslog, SyslogApi, SyslogMsgPriFac};
use syslog_rs::{SyslogFile, NEXTLINE, WSPACE};
use syslog_rs::{common, formatters::{SyslogFormatted, SyslogFormatter}};

use syslog_rs::{LogStat, LogFacility, Priority};

pub static SYNC_SYSLOG: OnceLock<SyncSyslog<MyFormatter, SyslogFile>> = OnceLock::new();

macro_rules! logdebug 
{
    ($($arg:tt)*) => (
        SYNC_SYSLOG.get().unwrap().syslog(Priority::LOG_DEBUG, format!($($arg)*).into());
    )
}

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

unsafe impl Send for MyFormatter {}

impl From<String> for MyFormatter
{
    fn from(value: String) -> MyFormatter 
    {
        return Self(value);
    }
}

impl From<&'static str> for MyFormatter
{
    fn from(value: &'static str) -> MyFormatter
    {
        return Self(value.to_string());
    }
}

impl SyslogFormatter for MyFormatter
{
    fn vsyslog1_format(&self, _max_msg_size: usize, prifac: SyslogMsgPriFac, progname: &str, pid: Option<&str>) -> SyslogFormatted
    {
        let timedate = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false);

        let msg_payload_final = 
            if self.0.ends_with("\n") == true
            {
                common::truncate(&self.0)
            }
            else
            {
                &self.0
            };

        let msg_header = 
            prifac.to_string();
           
        let msg_pkt = 
            [
                WSPACE, "MYFORMATTER",
                WSPACE, timedate.as_str(), 
                WSPACE, progname,
                WSPACE, pid.map_or(NILVALUE, |f| f),
                WSPACE, msg_payload_final, NEXTLINE
            ]
            .concat();


        return 
            SyslogFormatted::new( Some(msg_header),  msg_pkt );
    }
}


pub fn main()
{
    #[cfg(target_family = "unix")]
    let dest_path = Path::new("/tmp/myformatter.log");

    #[cfg(target_family = "windows")]
    let dest_path = 
        {
            let mut p = std::env::temp_dir();
            p.push("myformatter.log");

            p
        };

        println!("path: {}", dest_path.display());
        
    let syslog = 
        SyncSyslog
            ::<MyFormatter, SyslogFile>
            ::openlog_with(
            Some("example"), 
            LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
            LogFacility::LOG_DAEMON,
            SyslogFile::new(dest_path)
        ).unwrap();

    SYNC_SYSLOG.get_or_init(|| syslog);

    logdebug!("test message!");

    SYNC_SYSLOG.get().unwrap().change_identity(Some("another")).unwrap();

    logdebug!("test message new!");

    thread::sleep(Duration::from_micros(10));

    return;
}
