/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.cloud.pubsub.testing;

import com.google.api.gax.testing.DownloadableEmulatorRunner;
import com.google.api.gax.testing.GCloudEmulatorRunner;
import com.google.api.gax.testing.LocalServiceHelper;

import io.grpc.ManagedChannel;
import io.grpc.netty.NegotiationType;
import io.grpc.netty.NettyChannelBuilder;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * A class that runs a Pubsub emulator instance for use in tests.
 */
public class LocalPubsubHelper {

  private final int port;
  private final LocalServiceHelper serviceHelper;

  // Local server settings
  private static final int DEFAULT_PORT = 8080;
  private static final String DEFAULT_HOST = "localhost";
  private static final URL EMULATOR_URL;

  // GCloud emulator settings
  private static final String GCLOUD_CMD_TEXT = "gcloud beta emulators pubsub start";
  private static final String GCLOUD_CMD_PORT_FLAG = "--host-port=";
  private static final String VERSION_PREFIX = "pubsub-emulator";
  private static final String MIN_VERSION = "2016.01.13";
  private static final String BIN_CMD_PORT_FLAG = "--port=";

  // Downloadable emulator settings
  private static final String FILENAME = "pubsub-emulator-20160113-2.zip";
  private static final String BIN_NAME = "pubsub-emulator/bin/cloud-pubsub-fake";
  private static final String MD5_CHECKSUM = "20943e9defa300f2de101568459c133d";

  static {
    try {
      EMULATOR_URL = new URL("http://storage.googleapis.com/pubsub/tools/" + FILENAME);
    } catch (MalformedURLException ex) {
      throw new IllegalStateException(ex);
    }
  }

  /**
   * Constructs a new LocalPubsubHelper. The method start() must
   * be called before it is used.
   */
  public LocalPubsubHelper() {
    port = LocalServiceHelper.findAvailablePort(DEFAULT_PORT);
    List<String> gcloudCommand = new ArrayList<>(Arrays.asList(GCLOUD_CMD_TEXT.split(" ")));
    gcloudCommand.add(GCLOUD_CMD_PORT_FLAG + "localhost:" + port);
    GCloudEmulatorRunner gcloudRunner =
        new GCloudEmulatorRunner(gcloudCommand, VERSION_PREFIX, MIN_VERSION);
    DownloadableEmulatorRunner downloadRunner =
        new DownloadableEmulatorRunner(Arrays.asList(BIN_NAME, BIN_CMD_PORT_FLAG + port),
            EMULATOR_URL, MD5_CHECKSUM);
    serviceHelper =
        new LocalServiceHelper(Arrays.asList(gcloudRunner, downloadRunner), port);
  }

  /**
   * Start the local pubsub emulator through gcloud, download the zip file if user does not have
   * gcloud installed.
   *
   * @throws InterruptedException
   * @throws IOException
   */
  public void start() throws IOException, InterruptedException {
    String blockUntilOutput = "Server started, listening on " + port;
    serviceHelper.start(blockUntilOutput);
  }

  /**
   * Reset the internal state of the emulator.
   *
   * @throws IOException
   */
  public void reset() throws IOException {
    this.serviceHelper.sendPostRequest("/reset");
  }

  /**
   * Quit the local emulator and related local service.
   *
   * @throws InterruptedException
   * @throws IOException
   */
  public void stop() throws IOException, InterruptedException {
    this.serviceHelper.sendPostRequest("/shutdown");
    this.serviceHelper.stop();
  }

  /**
   * Creates a channel for making requests to the in-memory service.
   */
  public ManagedChannel createChannel() {
    return NettyChannelBuilder.forAddress(DEFAULT_HOST, port)
        .negotiationType(NegotiationType.PLAINTEXT)
        .build();
  }
}

@@ -830,118 +830,130 @@
 er;%0A
-%0A
 import 
-io.grpc.ManagedChannel;%0Aimport io.grpc.netty.NegotiationType;%0Aimport io.grpc.netty.NettyChannelBuilder
+com.google.cloud.AuthCredentials;%0Aimport com.google.cloud.RetryParams;%0Aimport com.google.cloud.pubsub.PubSubOptions
 ;%0A%0Ai
@@ -1114,16 +1114,151 @@
 il.List;
+%0Aimport java.util.UUID;%0A%0Aimport io.grpc.ManagedChannel;%0Aimport io.grpc.netty.NegotiationType;%0Aimport io.grpc.netty.NettyChannelBuilder;
 %0A%0A/**%0A *
@@ -1434,16 +1434,50 @@
 eHelper;
+%0A  private final String projectId;
 %0A%0A  // L
@@ -1643,16 +1643,83 @@
 TOR_URL;
+%0A  private static final String PROJECT_ID_PREFIX = %22test-project-%22;
 %0A%0A  // G
@@ -3239,32 +3239,24 @@
 viceHelper =
-%0A       
  new LocalSe
@@ -3315,24 +3315,90 @@
 er), port);%0A
+    projectId = PROJECT_ID_PREFIX + UUID.randomUUID().toString();%0A
   %7D%0A%0A  /**%0A 
@@ -4426,16 +4426,16 @@
 , port)%0A
-
         
@@ -4499,11 +4499,389 @@
 d();%0A  %7D
+%0A%0A  /**%0A   * Returns a %7B@link PubSubOptions%7D instance that sets the host to use the PubSub emulator on%0A   * localhost.%0A   */%0A  public PubSubOptions options() %7B%0A    return PubSubOptions.builder()%0A        .projectId(projectId)%0A        .host(%22localhost:%22 + port)%0A        .authCredentials(AuthCredentials.noAuth())%0A        .retryParams(RetryParams.noRetries())%0A        .build();%0A  %7D
 %0A%7D%0A

Add options() method and project name to LocalPubsubHelper (#999)