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

Namaste

Copyright (C) 2019, 2021-2024  Anonymous

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

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/>.

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

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

use {
    alloc::sync::Arc,
    core::sync::atomic::{self, AtomicBool},
    std::{
        io::{self, ErrorKind, Write},
        os::unix::net::UnixListener,
        sync::{
            OnceLock,
            mpsc::{self, Sender, TryRecvError},
        },
        thread,
    },
    crate::Result,
    self::{
        message::Message,
        server::Server,
    },
};

mod message;
mod server;

macro_rules! print_err { ($err: expr) => {{
    {
        let mut stderr = io::stderr().lock();
        if stderr.write_all(__!("{err}\n", err=$err).as_bytes()).is_ok() {
            if stderr.flush().is_err() {
                // Ignore it
            }
        }
    }
}}}

pub (super) const ATOMIC_ORDERING: atomic::Ordering = atomic::Ordering::Relaxed;

static SENDER: OnceLock<Result<Sender<Message>>> = OnceLock::new();

/// # Adds new listener, which will be managed by one single thread
pub (super) fn add(listener: UnixListener, clonable: bool, stop_flag: Arc<AtomicBool>) -> Result<()> {
    if stop_flag.load(ATOMIC_ORDERING) {
        return Err(err!(ErrorKind::InvalidData, "Invalid initial value of stop-flag"));
    }

    match get_or_init_sender() {
        Ok(sender) => sender.send(Message::Add { listener, clonable, stop_flag }).map_err(|_| err!()),
        Err(err) => Err(err!("{err}")),
    }
}

fn get_or_init_sender<'a>() -> &'a Result<Sender<Message>> {
    SENDER.get_or_init(|| {
        match Server::make() {
            Ok(mut server) => {
                let (sender, receiver) = mpsc::channel();
                thread::spawn(move || loop {
                    if let Err(err) = server.check() {
                        print_err!(err);
                    }
                    match receiver.try_recv() {
                        Ok(Message::Add { listener, clonable, stop_flag }) => if let Err(err) = server.push(listener, clonable, stop_flag) {
                            print_err!(err);
                        },
                        Err(TryRecvError::Empty) => {},
                        Err(TryRecvError::Disconnected) => {
                            print_err!("Channel is down!");
                            break;
                        },
                    };
                });
                Ok(sender)
            },
            Err(err) => Err(err),
        }
    })
}