use tonic::Request;
use roto_tonic::RotoCodec;
use hello::{HelloWorldService, OwnedHelloRequest, OwnedHelloResponse};
use roto_runtime::RotoOwned;
use std::task::{Context, Poll};
use tower::Service;

pub use roto_tonic::{BufferPool, StatusBody};

pub mod hello {
    include!(concat!(env!("OUT_DIR"), "/hello.rs"));
}

struct ReadyService<S>(S);

impl<S, Req> Service<Req> for ReadyService<S>
where
    S: Service<Req>,
{
    type Response = S::Response;
    type Error = S::Error;
    type Future = S::Future;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.0.poll_ready(cx)
    }

    fn call(&mut self, req: Req) -> S::Future {
        let waker = futures_util::task::noop_waker();
        let mut cx = std::task::Context::from_waker(&waker);
        let _ = self.poll_ready(&mut cx);
        self.0.call(req)
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let channel = tonic::transport::Channel::from_static("http://[::1]:50051")
        .connect()
        .await?;

    let ready_channel = ReadyService(channel);
    let mut client = tonic::client::Grpc::new(ready_channel);

    // We need to specify the method path. For HelloWorldService/HelloWorld, it is "/hello.HelloWorldService/HelloWorld"
    let mut buf = vec![0u8; 1024];
    let slice = hello::HelloRequestBuilder::builder(&mut buf)
        .name("Roto").unwrap()
        .finish().unwrap();

    let request = OwnedHelloRequest {
        data: bytes::Bytes::copy_from_slice(slice),
    };

    // In tonic's Grpc client, we specify the codec separately.
    let response = client
        .unary(
            Request::new(request),
            http::uri::PathAndQuery::from_static("/hello.HelloWorldService/HelloWorld"),
            RotoCodec::<OwnedHelloResponse, OwnedHelloRequest>::default(),
        )
        .await?;

    let response_msg: OwnedHelloResponse = response.into_inner();
    let reader = response_msg.reader();
    println!("Server responded: {}", reader.message().unwrap_or("No message"));

    Ok(())
}
