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

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

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

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

use {
    std::{
        io::{ErrorKind, Write},
        os::{
            fd::FromRawFd,
            unix::net::{SocketAddr, UnixListener, UnixStream},
        },
    },
    crate::{
        Result,
        UdsxUnixStream,
        linux::Cmd,
    },
    super::{Namaste, RwNamaste},
};

#[cfg(not(target_os="android"))]
use std::os::linux::net::SocketAddrExt;

#[cfg(target_os="android")]
use std::os::android::net::SocketAddrExt;

#[cfg(test)]
mod tests;

const CLONABLE: Option<bool> = Some(true);
const UNCLONABLE: Option<bool> = Some(false);

macro_rules! read { ($self: ident) => {{
    loop {
        match Namaste::make(&$self.read_id, CLONABLE) {
            Ok(read) => return match Namaste::make(&$self.write_id, UNCLONABLE) {
                Ok(_) => Ok(read),
                Err(_) => Err(err!("Another write is working")),
            },
            Err(err) => match err.kind() {
                ErrorKind::AddrInUse => match connect_to(&$self.read_id) {
                    Ok(mut stream) => {
                        Cmd::DuplicateServerFd.encode(&mut stream)?;
                        stream.flush()?;

                        let read = Namaste::make_from_unix_listener(
                            unsafe {
                                UnixListener::from_raw_fd(stream.recv_streams::<_, _, 1>(crate::linux::STREAM_ID)?[usize::MIN])
                            },
                            CLONABLE,
                        )?;
                        return Ok(read);
                    },
                    Err(_) => continue,
                },
                _ => return Err(err),
            },
        };
    }
}}}

macro_rules! write { ($self: ident) => {{
    let err = || Err(err!("Another read is working"));
    let write = Namaste::make(&$self.write_id, UNCLONABLE)?;
    match connect_to(&$self.read_id) {
        Ok(_) => err(),
        Err(_) => match Namaste::make(&$self.read_id, UNCLONABLE) {
            Ok(_) => Ok(write),
            Err(_) => err(),
        },
    }
}}}

impl<R, W> RwNamaste<R, W> where R: AsRef<[u8]>, W: AsRef<[u8]> {

    /// # Takes a read
    #[cfg(not(feature="tokio"))]
    #[doc(cfg(not(feature="tokio")))]
    pub (crate) fn read(&self) -> Result<Namaste> {
        read!(self)
    }

    /// # Takes a read
    #[cfg(feature="tokio")]
    #[doc(cfg(feature="tokio"))]
    pub (crate) async fn read(&self) -> Result<Namaste> {
        read!(self)
    }

    /// # Takes a write
    #[cfg(not(feature="tokio"))]
    #[doc(cfg(not(feature="tokio")))]
    pub (crate) fn write(&self) -> Result<Namaste> {
        write!(self)
    }

    /// # Takes a write
    #[cfg(feature="tokio")]
    #[doc(cfg(feature="tokio"))]
    pub (crate) async fn write(&self) -> Result<Namaste> {
        write!(self)
    }

}

/// # Connects to a server
fn connect_to<B>(address: B) -> Result<UnixStream> where B: AsRef<[u8]> {
    UnixStream::connect_addr(&SocketAddr::from_abstract_name(address)?)
}
