/*
 * Copyright (c) 2014-2016 Spotify AB
 *
 * 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.spotify.futures.jmh;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

import static java.lang.Integer.parseInt;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static java.util.stream.Collectors.toList;


@BenchmarkMode(Mode.SampleTime)
@Warmup(iterations = 2)
@Measurement(iterations = 3)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@SuppressWarnings("unused")
public class AllAsListBenchmark {

  @State(Scope.Benchmark)
  public static class Input {

    @Param({
        "4",
        "16",
        "64",
        "256",
        "1024"
    })
    String inputSize;

    public List<CompletionStage<String>> stages() {
      return Collections.nCopies(parseInt(inputSize), completedFuture("hello"));
    }
  }

  @Benchmark
  @Fork(1)
  public void stream(final Input input) throws Exception {
    final List<CompletionStage<String>> stages = input.stages();

    @SuppressWarnings("unchecked") // generic array creation
    final CompletableFuture<String>[] all = stages.stream()
        .map(CompletionStage::toCompletableFuture)
        .toArray(CompletableFuture[]::new);
    final CompletableFuture<List<String>> future = CompletableFuture.allOf(all)
        .thenApply(i -> Stream.of(all)
            .map(CompletableFuture::join)
            .collect(toList()));

    future.get();
  }

  @Benchmark
  @Fork(1)
  public void instantiateAndFor(final Input input) throws Exception {
    final List<CompletionStage<String>> stages = input.stages();

    @SuppressWarnings("unchecked") // generic array creation
    final CompletableFuture<String>[] all = new CompletableFuture[stages.size()];
    for (int i = 0; i < stages.size(); i++) {
      all[i] = stages.get(i).toCompletableFuture();
    }
    final CompletableFuture<List<String>> future = CompletableFuture.allOf(all)
        .thenApply(ignored -> {
          final List<String> result = new ArrayList<>(all.length);
          for (int i = 0; i < all.length; i++) {
            result.add(all[i].join());
          }
          return result;
        });

    future.get();
  }


  @Benchmark
  @Fork(1)
  public void instantiateAndForeach(final Input input) throws Exception {
    final List<CompletionStage<String>> stages = input.stages();

    @SuppressWarnings("unchecked") // generic array creation
    final CompletableFuture<String>[] all = new CompletableFuture[stages.size()];
    for (int i = 0; i < stages.size(); i++) {
      all[i] = stages.get(i).toCompletableFuture();
    }

    final CompletableFuture<List<String>> future = CompletableFuture.allOf(all)
        .thenApply(ignored -> {
          final List<String> result = new ArrayList<>(all.length);
          for (CompletableFuture<String> entry : all) {
            result.add(entry.join());
          }
          return result;
        });

    future.get();
  }


  public static void main(String[] args) throws Exception {
    Options opt = new OptionsBuilder()
        .include(".*" + AllAsListBenchmark.class.getSimpleName() + ".*")
        .build();
    new Runner(opt).run();
  }

}

@@ -626,16 +626,64 @@
 s.jmh;%0A%0A
+import com.spotify.futures.CompletableFutures;%0A%0A
 import o
@@ -774,97 +774,8 @@
 de;%0A
-import org.openjdk.jmh.annotations.Fork;%0Aimport org.openjdk.jmh.annotations.Measurement;%0A
 impo
@@ -992,51 +992,8 @@
 te;%0A
-import org.openjdk.jmh.annotations.Warmup;%0A
 impo
@@ -1376,50 +1376,8 @@
 m;%0A%0A
-import static java.lang.Integer.parseInt;%0A
 impo
@@ -1497,92 +1497,67 @@
 t;%0A%0A
-%0A@BenchmarkMode(Mode.SampleTime)%0A@Warmup(iterations = 2)%0A@Measurement(iterations = 3
+@SuppressWarnings(%22unused%22)%0A@BenchmarkMode(Mode.AverageTime
 )%0A@O
@@ -1596,36 +1596,8 @@
 DS)%0A
-@SuppressWarnings(%22unused%22)%0A
 publ
@@ -1683,17 +1683,16 @@
 Input %7B%0A
-%0A
     @Par
@@ -1777,19 +1777,12 @@
   %7D)
-%0A    String
+ int
  inp
@@ -1879,17 +1879,8 @@
 ies(
-parseInt(
 inpu
@@ -1884,17 +1884,16 @@
 nputSize
-)
 , comple
@@ -1935,32 +1935,270 @@
 Benchmark%0A  
-@Fork(1)
+public void actual(final Input input) throws Exception %7B%0A    final List%3CCompletionStage%3CString%3E%3E stages = input.stages();%0A    final CompletableFuture%3CList%3CString%3E%3E future = CompletableFutures.allAsList(stages);%0A    future.get();%0A  %7D%0A%0A  @Benchmark
 %0A  public vo
@@ -2754,35 +2754,24 @@
  @Benchmark%0A
-  @Fork(1)%0A
   public voi
@@ -3481,17 +3481,16 @@
 );%0A  %7D%0A%0A
-%0A
   @Bench
@@ -3498,19 +3498,8 @@
 ark%0A
-  @Fork(1)%0A
   pu
@@ -4228,17 +4228,16 @@
 );%0A  %7D%0A%0A
-%0A
   public
@@ -4292,16 +4292,22 @@
 n %7B%0A    
+final 
 Options 
@@ -4354,15 +4354,8 @@
 ude(
-%22.*%22 + 
 AllA
@@ -4394,15 +4394,8 @@
 me()
- + %22.*%22
 )%0A  

Tweak allAsList benchmark setup.