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

Namaste

Copyright (C) 2019, 2021-2023  Anonymous

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

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

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

//! # Namaste on Linux

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

use {
    core::time::Duration,
    std::{
        os::{
            fd::AsRawFd,
            linux::net::SocketAddrExt,
            unix::net::{SocketAddr, UnixListener},
        },
        sync::mpsc::{self, RecvTimeoutError},
        thread,
    },
    crate::{Result, UdsxUnixStream},
};

pub use self::rw_namaste::*;

mod rw_namaste;

type Message = ();
type Sender = mpsc::Sender<Message>;

const STREAM_ID: &[u8] = &[
    0x1e, 0x64, 0x83, 0x1b, 0xfb, 0x9a, 0x2d, 0x3d, 0x58, 0x80, 0x8c, 0xc2, 0xfd, 0xd3, 0x92, 0x23, 0x48, 0x5f, 0xe1, 0x8d, 0xdb, 0x80, 0x62,
    0x16, 0xe7, 0xd7, 0xa7, 0x3b, 0x9a, 0x27, 0x0c, 0xdd, 0xfe, 0x4b, 0x51, 0x92, 0xd0, 0xb2, 0x0c, 0xb5, 0xb8, 0x58, 0x95, 0x24, 0x3b, 0xec,
    0x75, 0xb5, 0x08, 0x1e, 0x27, 0x85, 0x0b, 0xd7, 0xcb, 0xb4, 0x0c, 0xbd, 0x30, 0x6d, 0x62, 0x5e, 0x0c, 0xcc,
];

/// # Namaste on Linux
#[derive(Debug)]
pub struct Namaste {
    _sender: Sender,
}

impl Namaste {

    /// # Makes new instance using abstract Linux socket
    pub (crate) fn make<B>(id: B) -> Result<Self> where B: AsRef<[u8]> {
        Ok(Self {
            _sender: spawn_thread(UnixListener::bind_addr(&SocketAddr::from_abstract_name(&id)?)?)?,
        })
    }

}

/// # Spawns thread to process the `UnixListener`
fn spawn_thread(unix_listener: UnixListener) -> Result<Sender> {
    unix_listener.set_nonblocking(true)?;

    let (sender, receiver) = mpsc::channel();

    thread::spawn(move || for stream in unix_listener.incoming() {
        match stream {
            Ok(stream) => if stream.send_streams(STREAM_ID, [unix_listener.as_raw_fd()]).is_err() {
                // Ignore it
            },
            Err(_) => match receiver.recv_timeout(Duration::from_millis(10)) {
                Ok(()) => return,
                Err(RecvTimeoutError::Timeout) => continue,
                Err(RecvTimeoutError::Disconnected) => return,
            },
        };
    });

    Ok(sender)
}
