package io.trane.future;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.function.Supplier;

import org.openjdk.jmh.annotations.Benchmark;

public class JavaSyncFutureBenchmark {

  private static final String string = "s";
  private static final RuntimeException exception = new RuntimeException();
  private static final Supplier<String> exceptionSupplier = () -> {
    throw exception;
  };
  private static final CompletableFuture<String> constFuture = CompletableFuture.completedFuture(string);
  private static final CompletableFuture<Void> constVoidFuture = CompletableFuture.completedFuture(null);
  private static final Function<String, String> mapF = i -> string;
  private static final Function<String, CompletableFuture<String>> flatMapF = i -> constFuture;
  private static final Runnable ensureF = () -> {
  };

  @Benchmark
  public CompletableFuture<String> newPromise() {
    return new CompletableFuture<String>();
  }

  @Benchmark
  public CompletableFuture<String> value() {
    return CompletableFuture.completedFuture(string);
  }

  @Benchmark
  public CompletableFuture<String> exception() {
    return CompletableFuture.supplyAsync(exceptionSupplier);
  }

  @Benchmark
  public String mapConst() throws InterruptedException, ExecutionException {
    return constFuture.thenApply(mapF).get();
  }

  @Benchmark
  public String mapConstN() throws InterruptedException, ExecutionException {
    CompletableFuture<String> f = constFuture;
    for (int i = 0; i < N.n; i++)
      f = f.thenApply(mapF);
    return f.get();
  }

  @Benchmark
  public String mapPromise() throws InterruptedException, ExecutionException {
    CompletableFuture<String> p = new CompletableFuture<String>();
    CompletableFuture<String> f = p.thenApply(mapF);
    p.complete(string);
    return f.get();
  }

  @Benchmark
  public String mapPromiseN() throws InterruptedException, ExecutionException {
    CompletableFuture<String> p = new CompletableFuture<String>();
    CompletableFuture<String> f = p;
    for (int i = 0; i < N.n; i++)
      f = f.thenApply(mapF);
    p.complete(string);
    return f.get();
  }

  @Benchmark
  public String flatMapConst() throws InterruptedException, ExecutionException {
    return constFuture.thenCompose(flatMapF).get();
  }

  @Benchmark
  public String flatMapConstN() throws InterruptedException, ExecutionException {
    CompletableFuture<String> f = constFuture;
    for (int i = 0; i < N.n; i++)
      f = f.thenCompose(flatMapF);
    return f.get();
  }

  @Benchmark
  public String flatMapPromise() throws InterruptedException, ExecutionException {
    CompletableFuture<String> p = new CompletableFuture<String>();
    p.thenCompose(flatMapF);
    p.complete(string);
    return p.get();
  }

  @Benchmark
  public String flatMapPromiseN() throws InterruptedException, ExecutionException {
    CompletableFuture<String> p = new CompletableFuture<>();
    CompletableFuture<String> f = p;
    for (int i = 0; i < N.n; i++)
      f = f.thenCompose(flatMapF);
    p.complete(string);
    return f.get();
  }

  @Benchmark
  public Void ensureConst() throws InterruptedException, ExecutionException {
    return constVoidFuture.thenRun(ensureF).get();
  }

  @Benchmark
  public Void ensureConstN() throws InterruptedException, ExecutionException {
    CompletableFuture<Void> f = constVoidFuture;
    for (int i = 0; i < N.n; i++)
      f = f.thenRun(ensureF);
    return f.get();
  }

  @Benchmark
  public Void ensurePromise() throws InterruptedException, ExecutionException {
    CompletableFuture<Void> p = new CompletableFuture<Void>();
    CompletableFuture<Void> f = p.thenRun(ensureF);
    p.complete(null);
    return f.get();
  }

  @Benchmark
  public Void ensurePromiseN() throws InterruptedException, ExecutionException {
    CompletableFuture<Void> p = new CompletableFuture<>();
    CompletableFuture<Void> f = p;
    for (int i = 0; i < N.n; i++)
      f = f.thenRun(ensureF);
    p.complete(null);
    return f.get();
  }

  @Benchmark
  public String setValue() throws InterruptedException, ExecutionException {
    CompletableFuture<String> p = new CompletableFuture<>();
    p.complete(string);
    return p.get();
  }

  @Benchmark
  public String setValueN() throws InterruptedException, ExecutionException {
    CompletableFuture<String> p = new CompletableFuture<>();
    CompletableFuture<String> f = p;
    for (int i = 0; i < N.n; i++)
      f = f.thenApply(mapF);
    p.complete(string);
    return f.get();
  }

  private CompletableFuture<Integer> loop(int i) {
    if (i > 0)
      if (i % 512 == 0)
        return CompletableFuture.completedFuture(i - 1).thenComposeAsync(this::loop);
      else
        return CompletableFuture.completedFuture(i - 1).thenCompose(this::loop);
    else
      return CompletableFuture.completedFuture(0);
  }

  @Benchmark
  public Integer recursiveConst() throws InterruptedException, ExecutionException {
    return loop(N.n).get();
  }
}