use std::{
    io::{self, Read, Write},
    os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd},
    process::Stdio,
};

use crate::sys::Pipe;

pub struct PipeImpl {
    fd: OwnedFd,
}

impl Pipe for PipeImpl {
    fn new(read_nonblocking: bool, write_nonblocking: bool) -> io::Result<(Self, Self)> {
        if read_nonblocking || write_nonblocking {
            todo!()
        }
        let mut fds = [0; 2];
        let res = unsafe { libc::pipe(fds.as_mut_ptr()) };
        if res < 0 {
            return Err(io::Error::last_os_error());
        }
        let read = unsafe { OwnedFd::from_raw_fd(fds[0]) };
        let write = unsafe { OwnedFd::from_raw_fd(fds[1]) };

        Ok((Self { fd: read }, Self { fd: write }))
    }

    fn to_child_stdio(&self) -> Stdio {
        unsafe { Stdio::from_raw_fd(self.as_raw_fd()) }
    }
}

impl Read for PipeImpl {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let len = unsafe { libc::read(self.fd.as_raw_fd(), buf.as_mut_ptr().cast(), buf.len()) };
        if len < 0 {
            return Err(io::Error::last_os_error());
        }
        Ok(len as usize)
    }
}

impl Write for PipeImpl {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let len = unsafe { libc::write(self.fd.as_raw_fd(), buf.as_ptr().cast(), buf.len()) };
        if len < 0 {
            return Err(io::Error::last_os_error());
        }
        Ok(len as usize)
    }

    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

impl AsRawFd for PipeImpl {
    fn as_raw_fd(&self) -> RawFd {
        self.fd.as_raw_fd()
    }
}
