#[cfg(any(target_os = "yggdrasil", rust_analyzer))]
mod yggdrasil;
#[cfg(any(target_os = "yggdrasil", rust_analyzer))]
pub use yggdrasil::*;

#[cfg(any(unix, rust_analyzer))]
mod unix;
#[cfg(any(unix, rust_analyzer))]
pub use unix::*;

use std::{
    fmt,
    io::{self, Read, Write},
    ops::DerefMut,
    os::fd::{AsRawFd, IntoRawFd, OwnedFd, RawFd},
    path::Path,
    process::Stdio,
    time::Duration,
};

use crate::io::{SerialPortSettings, TerminalSize};

// I/O

pub(crate) trait Poll: Sized + AsRawFd {
    fn new() -> io::Result<Self>;
    fn add<F: AsRawFd>(&mut self, interest: &F) -> io::Result<()>;
    fn remove<F: AsRawFd>(&mut self, interest: &F) -> io::Result<()>;
    fn wait(&mut self, timeout: Option<Duration>) -> io::Result<Option<RawFd>>;
}

pub(crate) trait TimerFd: Sized + AsRawFd {
    fn new() -> io::Result<Self>;
    fn start(&mut self, timeout: Duration) -> io::Result<()>;
    fn is_expired(&mut self) -> io::Result<bool>;
}

pub(crate) trait PidFd: Sized + AsRawFd {
    fn new(pid: u32) -> io::Result<Self>;
    fn exit_status(&self) -> io::Result<i32>;
}

pub(crate) trait Pipe: Read + Write + AsRawFd + Sized {
    fn new(read_nonblocking: bool, write_nonblocking: bool) -> io::Result<(Self, Self)>;
    fn to_child_stdio(&self) -> Stdio;
}

pub(crate) trait RawStdin: Sized + Read + AsRawFd {
    fn open() -> io::Result<Self>;
    fn set_options(&mut self, options: TerminalOptionsImpl) -> io::Result<TerminalOptionsImpl>;
    // fn new(stdin: &'a mut Stdin) -> io::Result<Self>;
}

pub(crate) trait PtyMaster: Read + Write + AsRawFd + IntoRawFd {
    fn resize_terminal(&mut self, size: TerminalSize) -> io::Result<()>;
}

#[allow(unused)]
pub(crate) trait PtySlave: Read + Write + AsRawFd + IntoRawFd {}

pub(crate) trait SerialPort: Read + Write + AsRawFd + IntoRawFd + Sized {
    fn open<P: AsRef<Path>>(device: P, options: &SerialPortSettings) -> io::Result<Self>;
}

pub trait TerminalOptions: Copy {
    fn normal() -> Self;
    fn raw() -> Self;

    fn erase_char(&self) -> u8;
    fn word_erase_char(&self) -> u8;
    fn eof_char(&self) -> u8;
    fn interrupt_char(&self) -> u8;
}

// Networking

#[allow(unused)]
pub(crate) trait BorrowedAddress<'a>: fmt::Debug + From<&'a Path> {
    type Owned: OwnedAddress;
}

#[allow(unused)]
pub(crate) trait OwnedAddress: fmt::Debug + Clone {
    type Borrowed<'a>: BorrowedAddress<'a>;

    fn from_borrowed(borrowed: &Self::Borrowed<'_>) -> Self;
    fn as_borrowed(&self) -> Self::Borrowed<'_>;
}

pub(crate) trait LocalPacketSocket: AsRawFd + Sized {
    type Address<'a>: BorrowedAddress<'a>;
    type OwnedAddress: OwnedAddress;

    fn new() -> io::Result<Self>;
    fn bind<P: AsRef<Path>>(path: P) -> io::Result<Self>;
    fn connect<P: AsRef<Path>>(path: P) -> io::Result<Self>;

    fn set_nonblocking(&self, nb: bool) -> io::Result<()>;

    fn receive(&self, data: &mut [u8]) -> io::Result<usize>;
    fn receive_from(&self, data: &mut [u8]) -> io::Result<(usize, Self::OwnedAddress)>;
    fn receive_with_ancillary(&self, data: &mut [u8]) -> io::Result<(usize, Option<OwnedFd>)>;
    fn receive_with_ancillary_from(
        &self,
        data: &mut [u8],
    ) -> io::Result<(usize, Option<OwnedFd>, Self::OwnedAddress)>;

    fn send(&self, data: &[u8]) -> io::Result<usize>;
    fn send_to(&self, data: &[u8], address: &Self::Address<'_>) -> io::Result<usize>;
    fn send_with_ancillary(&self, data: &[u8], ancillary: RawFd) -> io::Result<usize>;
    fn send_with_ancillary_to(
        &self,
        data: &[u8],
        ancillary: RawFd,
        address: &Self::Address<'_>,
    ) -> io::Result<usize>;
}

// Memory

pub(crate) trait FileMapping: AsRawFd + DerefMut<Target = [u8]> + Sized {
    fn map<F: Into<OwnedFd>>(file: F, size: usize, write: bool) -> io::Result<Self>;
}

pub(crate) trait SharedMemory: AsRawFd + Sized {
    type Mapping: FileMapping;

    fn new(size: usize) -> io::Result<Self>;
    fn map(self) -> io::Result<Self::Mapping>;
}
