use std::future::Future;
use std::io;
use std::os::fd::{AsRawFd, RawFd};

use dope_core::Fd;
use dope_core::addr::SockAddr;
use dope_core::submit::{RecvMsgPacket, SendMsgPacket, SubmitOps};

use super::Driver;
use super::request::Request;

impl SubmitOps for &mut Driver {
    fn submit_accept(
        self,
        fd: Fd,
    ) -> impl Future<Output = io::Result<(RawFd, SockAddr)>> + 'static {
        let raw = fd.as_raw_fd();
        let req = self.try_submit_op(SockAddr::empty(), move |addr| {
            io_uring::opcode::Accept::new(
                io_uring::types::Fd(raw),
                addr.addr_mut_ptr(),
                addr.addr_len_ptr(),
            )
            .build()
        });
        async move {
            let (res, addr) = Request::await_submit(req).await;
            res.map(|n| (n as RawFd, addr))
        }
    }

    fn submit_read<B>(self, fd: Fd, buf: B) -> impl Future<Output = (io::Result<u32>, B)> + 'static
    where
        B: AsMut<[u8]> + Unpin + 'static,
    {
        let raw = fd.as_raw_fd();
        let req = self.try_submit_op(buf, move |buf| {
            let slice = buf.as_mut();
            io_uring::opcode::Read::new(
                io_uring::types::Fd(raw),
                slice.as_mut_ptr(),
                slice.len() as u32,
            )
            .build()
        });
        Request::await_submit(req)
    }

    fn submit_send_raw<B>(
        self,
        fd: Fd,
        buf: B,
        len: usize,
    ) -> impl Future<Output = (io::Result<u32>, B)> + 'static
    where
        B: AsRef<[u8]> + Unpin + 'static,
    {
        let raw = fd.as_raw_fd();
        let req = self.try_submit_op(buf, move |buf| {
            let full = buf.as_ref();
            let n = len.min(full.len());
            io_uring::opcode::Send::new(io_uring::types::Fd(raw), full.as_ptr(), n as u32).build()
        });
        Request::await_submit(req)
    }

    fn submit_sendmsg_raw<P>(
        self,
        fd: Fd,
        packet: P,
    ) -> impl Future<Output = (io::Result<u32>, P)> + 'static
    where
        P: SendMsgPacket + Unpin + 'static,
    {
        let raw = fd.as_raw_fd();
        let req = self.try_submit_op(packet, move |packet| {
            io_uring::opcode::SendMsg::new(io_uring::types::Fd(raw), packet.msg_ptr()).build()
        });
        Request::await_submit(req)
    }

    fn submit_recvmsg_raw<P>(
        self,
        fd: Fd,
        packet: P,
    ) -> impl Future<Output = (io::Result<u32>, P)> + 'static
    where
        P: RecvMsgPacket + Unpin + 'static,
    {
        let raw = fd.as_raw_fd();
        let req = self.try_submit_op(packet, move |packet| {
            io_uring::opcode::RecvMsg::new(io_uring::types::Fd(raw), packet.msg_mut_ptr()).build()
        });
        Request::await_submit(req)
    }
}
