/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--

Namaste

Copyright (C) 2019, 2021-2025  Anonymous

There are several releases over multiple years,
they are listed as ranges, such as: "2021-2025".

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/

//! # Root

#![cfg(any(target_os="android", target_os="l4re", target_os="linux", windows))]
#![doc(cfg(any(target_os="android", target_os="l4re", target_os="linux", windows)))]

use {
    core::{
        fmt::Display,
        time::Duration,
    },
    std::{
        io::{self, Error, ErrorKind, Write},
        time::Instant,
    },
    crate::{Namaste, Result},
};

pub use self::rw_namaste::*;

macro_rules! sleep { () => {{
    #[cfg(not(feature="tokio"))]
    std::thread::sleep(SLEEP_DURATION);
    #[cfg(feature="tokio")]
    tokio::time::sleep(SLEEP_DURATION).await;
}}}

mod rw_namaste;

/// # Sleep duration
pub (crate) const SLEEP_DURATION: Duration = Duration::from_millis(sleep_duration!());

/// # Makes new `Namaste`
pub fn make<B>(id: B) -> Result<Namaste> where B: AsRef<[u8]> {
    let result;

    #[cfg(windows)] {
        result = Namaste::make(id, crate::windows::Instance::One);
    }

    #[cfg(any(target_os="linux", target_os="l4re", target_os="android"))] {
        result = Namaste::make(id, None);
    }

    result
}

macro_rules! make_wait { ($id: ident, $timeout: ident) => {{
    let start = Instant::now();
    loop {
        match make(&$id) {
            Ok(namaste) => return Ok(namaste),
            Err(err) => match err.kind() {
                ErrorKind::AddrInUse => {
                    match Instant::now().checked_duration_since(start) {
                        Some(duration) => if duration > $timeout {
                            return Err(Error::new(ErrorKind::TimedOut, __!("Timed out")));
                        },
                        None => return Err(Error::new(ErrorKind::Other, __!("Invalid system time"))),
                    };
                    sleep!();
                },
                _ => return Err(err),
            },
        };
    }
}}}

/// # Tries to make a `Namaste`...
///
/// If *the same ID is in use*, waits and tries again until timed out.
#[cfg(not(feature="tokio"))]
#[doc(cfg(not(feature="tokio")))]
pub fn make_wait<B>(id: B, timeout: Duration) -> Result<Namaste> where B: AsRef<[u8]> {
    make_wait!(id, timeout)
}

/// # Tries to make a `Namaste`...
///
/// If *the same ID is in use*, waits and tries again until timed out.
#[cfg(feature="tokio")]
#[doc(cfg(feature="tokio"))]
pub async fn make_wait<B>(id: B, timeout: Duration) -> Result<Namaste> where B: AsRef<[u8]> {
    make_wait!(id, timeout)
}

/// # Prints error
pub (crate) fn print_err<S>(msg: S) where S: Display {
    let msg = format!("{}\n", msg.to_string().trim());
    if io::stderr().lock().write_all(msg.as_bytes()).is_err() {
        eprintln!("{}", msg.trim());
    }
}
