use std::io::{self, ErrorKind};
use std::os::unix::net::{UnixListener, UnixStream};

use smithay::reexports::calloop::generic::Generic;
use smithay::reexports::calloop::{
    self, EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory,
};

#[derive(Debug)]
pub struct SocketSource {
    inner: Generic<UnixListener>,
}

impl SocketSource {
    pub fn new(listener: UnixListener) -> calloop::Result<Self> {
        listener.set_nonblocking(true)?;
        Ok(Self { inner: Generic::new(listener, Interest::READ, Mode::Level) })
    }
}

impl EventSource for SocketSource {
    type Error = io::Error;
    type Event = UnixStream;
    type Metadata = ();
    type Ret = ();

    fn process_events<F>(
        &mut self,
        readiness: Readiness,
        token: Token,
        mut callback: F,
    ) -> io::Result<PostAction>
    where
        F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
    {
        self.inner.process_events(readiness, token, |_, listener| {
            loop {
                match listener.accept() {
                    Ok((stream, _)) => callback(stream, &mut ()),
                    Err(e) if e.kind() == ErrorKind::WouldBlock => break,
                    Err(e) => return Err(e),
                }
            }
            Ok(PostAction::Continue)
        })
    }

    fn register(&mut self, poll: &mut Poll, factory: &mut TokenFactory) -> calloop::Result<()> {
        self.inner.register(poll, factory)
    }

    fn reregister(&mut self, poll: &mut Poll, factory: &mut TokenFactory) -> calloop::Result<()> {
        self.inner.reregister(poll, factory)
    }

    fn unregister(&mut self, poll: &mut Poll) -> calloop::Result<()> {
        self.inner.unregister(poll)
    }
}
