use crate::socks2http::socks5;
use futures_util::{future::BoxFuture, FutureExt};
use hyper::Uri;
use pin_project::pin_project;
use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin;
use std::task::{self, Poll};
use tokio::net::TcpStream;
#[derive(Clone)]
pub struct SocksConnector {
    proxy: SocketAddr,
}
impl SocksConnector {
    pub fn new(addr: SocketAddr) -> SocksConnector {
        SocksConnector { proxy: addr }
    }
}
impl hyper::service::Service<Uri> for SocksConnector {
    type Error = std::io::Error;
    type Future = SocksConnecting;
    type Response = TcpStream;
    fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }
    fn call(&mut self, dst: Uri) -> Self::Future {
        let proxy = self.proxy;
        SocksConnecting {
            fut: async move {
                match crate::socks2http::address::host_addr(&dst) {
                    None => {
                        use std::io::{Error, ErrorKind};
                        let err = Error::new(ErrorKind::Other, "URI must be a valid Address");
                        Err(err)
                    }
                    Some(addr) => socks5::connect(&addr, &proxy).await,
                }
            }
            .boxed(),
        }
    }
}
#[pin_project]
pub struct SocksConnecting {
    #[pin]
    fut: BoxFuture<'static, std::io::Result<TcpStream>>,
}

impl Future for SocksConnecting {
    type Output = std::io::Result<TcpStream>;
    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        self.project().fut.poll(cx)
    }
}
pub type SocksClient = hyper::Client<SocksConnector, hyper::Body>;
