use std::future;
use std::io;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
pub trait ToSocketAddrs: sealed::ToSocketAddrsPriv {}
type ReadyFuture<T> = future::Ready<io::Result<T>>;
cfg_net! {
    pub(crate) fn to_socket_addrs<T>(arg: T) -> T::Future
    where
        T: ToSocketAddrs,
    {
        arg.to_socket_addrs(sealed::Internal)
    }
}
impl<T: ToSocketAddrs + ?Sized> ToSocketAddrs for &T {}
impl<T> sealed::ToSocketAddrsPriv for &T
where
    T: sealed::ToSocketAddrsPriv + ?Sized,
{
    type Iter = T::Iter;
    type Future = T::Future;
    fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
        (**self).to_socket_addrs(sealed::Internal)
    }
}
impl ToSocketAddrs for SocketAddr {}
impl sealed::ToSocketAddrsPriv for SocketAddr {
    type Iter = std::option::IntoIter<SocketAddr>;
    type Future = ReadyFuture<Self::Iter>;
    fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
        let iter = Some(*self).into_iter();
        future::ready(Ok(iter))
    }
}
impl ToSocketAddrs for SocketAddrV4 {}
impl sealed::ToSocketAddrsPriv for SocketAddrV4 {
    type Iter = std::option::IntoIter<SocketAddr>;
    type Future = ReadyFuture<Self::Iter>;
    fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
        SocketAddr::V4(*self).to_socket_addrs(sealed::Internal)
    }
}
impl ToSocketAddrs for SocketAddrV6 {}
impl sealed::ToSocketAddrsPriv for SocketAddrV6 {
    type Iter = std::option::IntoIter<SocketAddr>;
    type Future = ReadyFuture<Self::Iter>;
    fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
        SocketAddr::V6(*self).to_socket_addrs(sealed::Internal)
    }
}
impl ToSocketAddrs for (IpAddr, u16) {}
impl sealed::ToSocketAddrsPriv for (IpAddr, u16) {
    type Iter = std::option::IntoIter<SocketAddr>;
    type Future = ReadyFuture<Self::Iter>;
    fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
        let iter = Some(SocketAddr::from(*self)).into_iter();
        future::ready(Ok(iter))
    }
}
impl ToSocketAddrs for (Ipv4Addr, u16) {}
impl sealed::ToSocketAddrsPriv for (Ipv4Addr, u16) {
    type Iter = std::option::IntoIter<SocketAddr>;
    type Future = ReadyFuture<Self::Iter>;
    fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
        let (ip, port) = *self;
        SocketAddrV4::new(ip, port).to_socket_addrs(sealed::Internal)
    }
}
impl ToSocketAddrs for (Ipv6Addr, u16) {}
impl sealed::ToSocketAddrsPriv for (Ipv6Addr, u16) {
    type Iter = std::option::IntoIter<SocketAddr>;
    type Future = ReadyFuture<Self::Iter>;
    fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
        let (ip, port) = *self;
        SocketAddrV6::new(ip, port, 0, 0).to_socket_addrs(sealed::Internal)
    }
}
impl ToSocketAddrs for &[SocketAddr] {}
impl sealed::ToSocketAddrsPriv for &[SocketAddr] {
    type Iter = std::vec::IntoIter<SocketAddr>;
    type Future = ReadyFuture<Self::Iter>;
    fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
        #[inline]
        fn slice_to_vec(addrs: &[SocketAddr]) -> Vec<SocketAddr> {
            addrs.to_vec()
        }
        let iter = slice_to_vec(self).into_iter();
        future::ready(Ok(iter))
    }
}
cfg_net! {
    impl ToSocketAddrs for str {}
    impl sealed::ToSocketAddrsPriv for str {
        type Iter = sealed::OneOrMore;
        type Future = sealed::MaybeReady;
        fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
            use crate::blocking::spawn_blocking;
            use sealed::MaybeReady;
            let res: Result<SocketAddr, _> = self.parse();
            if let Ok(addr) = res {
                return MaybeReady(sealed::State::Ready(Some(addr)));
            }
            let s = self.to_owned();
            MaybeReady(sealed::State::Blocking(spawn_blocking(move || {
                std::net::ToSocketAddrs::to_socket_addrs(&s)
            })))
        }
    }
    impl ToSocketAddrs for (&str, u16) {}
    impl sealed::ToSocketAddrsPriv for (&str, u16) {
        type Iter = sealed::OneOrMore;
        type Future = sealed::MaybeReady;
        fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
            use crate::blocking::spawn_blocking;
            use sealed::MaybeReady;
            let (host, port) = *self;
            if let Ok(addr) = host.parse::<Ipv4Addr>() {
                let addr = SocketAddrV4::new(addr, port);
                let addr = SocketAddr::V4(addr);
                return MaybeReady(sealed::State::Ready(Some(addr)));
            }
            if let Ok(addr) = host.parse::<Ipv6Addr>() {
                let addr = SocketAddrV6::new(addr, port, 0, 0);
                let addr = SocketAddr::V6(addr);
                return MaybeReady(sealed::State::Ready(Some(addr)));
            }
            let host = host.to_owned();
            MaybeReady(sealed::State::Blocking(spawn_blocking(move || {
                std::net::ToSocketAddrs::to_socket_addrs(&(&host[..], port))
            })))
        }
    }
    impl ToSocketAddrs for (String, u16) {}
    impl sealed::ToSocketAddrsPriv for (String, u16) {
        type Iter = sealed::OneOrMore;
        type Future = sealed::MaybeReady;
        fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
            (self.0.as_str(), self.1).to_socket_addrs(sealed::Internal)
        }
    }
    impl ToSocketAddrs for String {}
    impl sealed::ToSocketAddrsPriv for String {
        type Iter = <str as sealed::ToSocketAddrsPriv>::Iter;
        type Future = <str as sealed::ToSocketAddrsPriv>::Future;
        fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
            (&self[..]).to_socket_addrs(sealed::Internal)
        }
    }
}
pub(crate) mod sealed {
    use std::future::Future;
    use std::io;
    use std::net::SocketAddr;
    #[doc(hidden)]
    pub trait ToSocketAddrsPriv {
        type Iter: Iterator<Item = SocketAddr> + Send + 'static;
        type Future: Future<Output = io::Result<Self::Iter>> + Send + 'static;
        fn to_socket_addrs(&self, internal: Internal) -> Self::Future;
    }
    #[allow(missing_debug_implementations)]
    pub struct Internal;
    cfg_net! {
        use crate::blocking::JoinHandle;
        use std::option;
        use std::pin::Pin;
        use std::task::{Context, Poll};
        use std::vec;
        #[doc(hidden)]
        #[derive(Debug)]
        pub struct MaybeReady(pub(super) State);
        #[derive(Debug)]
        pub(super) enum State {
            Ready(Option<SocketAddr>),
            Blocking(JoinHandle<io::Result<vec::IntoIter<SocketAddr>>>),
        }
        #[doc(hidden)]
        #[derive(Debug)]
        pub enum OneOrMore {
            One(option::IntoIter<SocketAddr>),
            More(vec::IntoIter<SocketAddr>),
        }
        impl Future for MaybeReady {
            type Output = io::Result<OneOrMore>;
            fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
                match self.0 {
                    State::Ready(ref mut i) => {
                        let iter = OneOrMore::One(i.take().into_iter());
                        Poll::Ready(Ok(iter))
                    }
                    State::Blocking(ref mut rx) => {
                        let res = ready!(Pin::new(rx).poll(cx))?.map(OneOrMore::More);
                        Poll::Ready(res)
                    }
                }
            }
        }
        impl Iterator for OneOrMore {
            type Item = SocketAddr;
            fn next(&mut self) -> Option<Self::Item> {
                match self {
                    OneOrMore::One(i) => i.next(),
                    OneOrMore::More(i) => i.next(),
                }
            }
            fn size_hint(&self) -> (usize, Option<usize>) {
                match self {
                    OneOrMore::One(i) => i.size_hint(),
                    OneOrMore::More(i) => i.size_hint(),
                }
            }
        }
    }
}