/*
 * Copyright 2017 Google LLC
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google LLC nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.google.api.gax.grpc.testing;

import static io.grpc.MethodDescriptor.generateFullMethodName;
import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall;
import static io.grpc.stub.ServerCalls.asyncClientStreamingCall;
import static io.grpc.stub.ServerCalls.asyncServerStreamingCall;
import static io.grpc.stub.ServerCalls.asyncUnaryCall;
import static io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall;
import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall;

import com.google.api.core.BetaApi;
import com.google.longrunning.Operation;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.type.Color;
import com.google.type.Money;
import io.grpc.BindableService;
import io.grpc.MethodDescriptor;
import io.grpc.ServerServiceDefinition;
import io.grpc.ServiceDescriptor;
import io.grpc.protobuf.ProtoFileDescriptorSupplier;
import io.grpc.protobuf.ProtoUtils;
import io.grpc.stub.ServerCalls.BidiStreamingMethod;
import io.grpc.stub.ServerCalls.ClientStreamingMethod;
import io.grpc.stub.ServerCalls.ServerStreamingMethod;
import io.grpc.stub.ServerCalls.UnaryMethod;
import io.grpc.stub.StreamObserver;

/**
 * This is a manually-written grpc stub class that mirrors the same structure generated by grpc from
 * proto declarations.
 */
@BetaApi
public final class FakeServiceGrpc {

  private FakeServiceGrpc() {}

  public static final String SERVICE_NAME = "google.gax.FakeService";

  public static final MethodDescriptor<Color, Money> METHOD_RECOGNIZE =
      MethodDescriptor.<Color, Money>newBuilder()
          .setType(MethodDescriptor.MethodType.UNARY)
          .setFullMethodName(generateFullMethodName("google.gax.FakeService", "Recognize"))
          .setRequestMarshaller(ProtoUtils.marshaller(Color.getDefaultInstance()))
          .setResponseMarshaller(ProtoUtils.marshaller(Money.getDefaultInstance()))
          .build();

  public static final MethodDescriptor<Color, Operation> METHOD_LONG_RUNNING_RECOGNIZE =
      MethodDescriptor.<Color, Operation>newBuilder()
          .setType(MethodDescriptor.MethodType.UNARY)
          .setFullMethodName(
              generateFullMethodName("google.gax.FakeService", "LongRunningRecognize"))
          .setRequestMarshaller(ProtoUtils.marshaller(Color.getDefaultInstance()))
          .setResponseMarshaller(ProtoUtils.marshaller(Operation.getDefaultInstance()))
          .build();

  public static final MethodDescriptor<Color, Money> METHOD_STREAMING_RECOGNIZE =
      MethodDescriptor.<Color, Money>newBuilder()
          .setType(MethodDescriptor.MethodType.BIDI_STREAMING)
          .setFullMethodName(generateFullMethodName("google.gax.FakeService", "StreamingRecognize"))
          .setRequestMarshaller(ProtoUtils.marshaller(Color.getDefaultInstance()))
          .setResponseMarshaller(ProtoUtils.marshaller(Money.getDefaultInstance()))
          .build();

  public static final MethodDescriptor<Color, Money> METHOD_STREAMING_RECOGNIZE_ERROR =
      MethodDescriptor.<Color, Money>newBuilder()
          .setType(MethodDescriptor.MethodType.BIDI_STREAMING)
          .setFullMethodName(
              generateFullMethodName("google.gax.FakeService", "StreamingRecognizeError"))
          .setRequestMarshaller(ProtoUtils.marshaller(Color.getDefaultInstance()))
          .setResponseMarshaller(ProtoUtils.marshaller(Money.getDefaultInstance()))
          .build();

  public static final MethodDescriptor<Color, Money> METHOD_SERVER_STREAMING_RECOGNIZE =
      MethodDescriptor.<Color, Money>newBuilder()
          .setType(MethodDescriptor.MethodType.SERVER_STREAMING)
          .setFullMethodName(
              generateFullMethodName("google.gax.FakeService", "ServerStreamingRecognize"))
          .setRequestMarshaller(ProtoUtils.marshaller(Color.getDefaultInstance()))
          .setResponseMarshaller(ProtoUtils.marshaller(Money.getDefaultInstance()))
          .build();

  public static final MethodDescriptor<Color, Money> METHOD_CLIENT_STREAMING_RECOGNIZE =
      MethodDescriptor.<Color, Money>newBuilder()
          .setType(MethodDescriptor.MethodType.CLIENT_STREAMING)
          .setFullMethodName(
              generateFullMethodName("google.gax.FakeService", "ClientStreamingRecognize"))
          .setRequestMarshaller(ProtoUtils.marshaller(Color.getDefaultInstance()))
          .setResponseMarshaller(ProtoUtils.marshaller(Money.getDefaultInstance()))
          .build();

  public abstract static class FakeServiceImplBase implements BindableService {

    public void recognize(Color request, StreamObserver<Money> responseObserver) {
      asyncUnimplementedUnaryCall(METHOD_RECOGNIZE, responseObserver);
    }

    public void longRunningRecognize(Color request, StreamObserver<Operation> responseObserver) {
      asyncUnimplementedUnaryCall(METHOD_LONG_RUNNING_RECOGNIZE, responseObserver);
    }

    public StreamObserver<Color> streamingRecognize(StreamObserver<Money> responseObserver) {
      return asyncUnimplementedStreamingCall(METHOD_STREAMING_RECOGNIZE, responseObserver);
    }

    public StreamObserver<Color> streamingRecognizeError(StreamObserver<Money> responseObserver) {
      return asyncUnimplementedStreamingCall(METHOD_STREAMING_RECOGNIZE_ERROR, responseObserver);
    }

    public void serverStreamingRecognize(Color request, StreamObserver<Money> responseObserver) {
      asyncUnimplementedUnaryCall(METHOD_SERVER_STREAMING_RECOGNIZE, responseObserver);
    }

    public StreamObserver<Color> clientStreamingRecognize(StreamObserver<Money> responseObserver) {
      return asyncUnimplementedStreamingCall(METHOD_CLIENT_STREAMING_RECOGNIZE, responseObserver);
    }

    @Override
    public final ServerServiceDefinition bindService() {
      return ServerServiceDefinition.builder(getServiceDescriptor())
          .addMethod(
              METHOD_RECOGNIZE,
              asyncUnaryCall(new MethodHandlers<Color, Money>(this, METHODID_RECOGNIZE)))
          .addMethod(
              METHOD_LONG_RUNNING_RECOGNIZE,
              asyncUnaryCall(
                  new MethodHandlers<Color, Operation>(this, METHODID_LONG_RUNNING_RECOGNIZE)))
          .addMethod(
              METHOD_STREAMING_RECOGNIZE,
              asyncBidiStreamingCall(
                  new MethodHandlers<Color, Money>(this, METHODID_STREAMING_RECOGNIZE)))
          .addMethod(
              METHOD_STREAMING_RECOGNIZE_ERROR,
              asyncBidiStreamingCall(
                  new MethodHandlers<Color, Money>(this, METHODID_STREAMING_RECOGNIZE_ERROR)))
          .addMethod(
              METHOD_SERVER_STREAMING_RECOGNIZE,
              asyncServerStreamingCall(
                  new MethodHandlers<Color, Money>(this, METHODID_SERVER_STREAMING_RECOGNIZE)))
          .addMethod(
              METHOD_CLIENT_STREAMING_RECOGNIZE,
              asyncClientStreamingCall(
                  new MethodHandlers<Color, Money>(this, METHODID_CLIENT_STREAMING_RECOGNIZE)))
          .build();
    }
  }

  private static final int METHODID_RECOGNIZE = 0;
  private static final int METHODID_LONG_RUNNING_RECOGNIZE = 1;
  private static final int METHODID_STREAMING_RECOGNIZE = 2;
  private static final int METHODID_STREAMING_RECOGNIZE_ERROR = 3;
  private static final int METHODID_SERVER_STREAMING_RECOGNIZE = 4;
  private static final int METHODID_CLIENT_STREAMING_RECOGNIZE = 5;

  private static final class MethodHandlers<Req, Resp>
      implements UnaryMethod<Req, Resp>,
          ServerStreamingMethod<Req, Resp>,
          ClientStreamingMethod<Req, Resp>,
          BidiStreamingMethod<Req, Resp> {

    private final FakeServiceImplBase serviceImpl;
    private final int methodId;

    MethodHandlers(FakeServiceImplBase serviceImpl, int methodId) {
      this.serviceImpl = serviceImpl;
      this.methodId = methodId;
    }

    @Override
    @SuppressWarnings("unchecked")
    public void invoke(Req request, StreamObserver<Resp> responseObserver) {
      switch (methodId) {
        case METHODID_RECOGNIZE:
          serviceImpl.recognize((Color) request, (StreamObserver<Money>) responseObserver);
          break;
        case METHODID_LONG_RUNNING_RECOGNIZE:
          serviceImpl.longRunningRecognize(
              (Color) request, (StreamObserver<Operation>) responseObserver);
          break;
        case METHODID_SERVER_STREAMING_RECOGNIZE:
          serviceImpl.serverStreamingRecognize(
              (Color) request, (StreamObserver<Money>) responseObserver);
          break;
        default:
          throw new AssertionError();
      }
    }

    @Override
    @SuppressWarnings("unchecked")
    public StreamObserver<Req> invoke(StreamObserver<Resp> responseObserver) {
      switch (methodId) {
        case METHODID_STREAMING_RECOGNIZE:
          return (StreamObserver<Req>)
              serviceImpl.streamingRecognize((StreamObserver<Money>) responseObserver);
        case METHODID_STREAMING_RECOGNIZE_ERROR:
          return (StreamObserver<Req>)
              serviceImpl.streamingRecognizeError((StreamObserver<Money>) responseObserver);
        case METHODID_CLIENT_STREAMING_RECOGNIZE:
          return (StreamObserver<Req>)
              serviceImpl.clientStreamingRecognize((StreamObserver<Money>) responseObserver);
        default:
          throw new AssertionError();
      }
    }
  }

  private static final class FakeServiceDescriptorSupplier implements ProtoFileDescriptorSupplier {

    @Override
    public FileDescriptor getFileDescriptor() {
      return null;
    }
  }

  private static volatile ServiceDescriptor serviceDescriptor;

  public static ServiceDescriptor getServiceDescriptor() {
    ServiceDescriptor result = serviceDescriptor;
    if (result == null) {
      synchronized (FakeServiceGrpc.class) {
        result = serviceDescriptor;
        if (result == null) {
          serviceDescriptor =
              result =
                  ServiceDescriptor.newBuilder(SERVICE_NAME)
                      .setSchemaDescriptor(new FakeServiceDescriptorSupplier())
                      .addMethod(METHOD_RECOGNIZE)
                      .addMethod(METHOD_LONG_RUNNING_RECOGNIZE)
                      .addMethod(METHOD_STREAMING_RECOGNIZE)
                      .addMethod(METHOD_STREAMING_RECOGNIZE_ERROR)
                      .addMethod(METHOD_SERVER_STREAMING_RECOGNIZE)
                      .addMethod(METHOD_CLIENT_STREAMING_RECOGNIZE)
                      .build();
        }
      }
    }
    return result;
  }
}
