package org.cstamas.vertx.sisu;

import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.inject.Named;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.AsyncResult;
import io.vertx.core.Context;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Verticle;
import io.vertx.core.Vertx;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;

import static java.util.Objects.requireNonNull;

/**
 * Bootstrap {@link Verticle} that gets {@link Map} of {@link Verticle}s injected by Sisu, and it simply relays the
 * lifecycle calls to injected instances.
 */
@Named(BootstrapVerticle.NAME)
public class BootstrapVerticle
    extends AbstractVerticle
{
  private static final Logger log = LoggerFactory.getLogger(BootstrapVerticle.class);

  public static final String NAME = "bootstrap";

  private final Predicate<String> filter;

  private Map<String, Verticle> verticles;

  public BootstrapVerticle(final Predicate<String> filter) {
    this.filter = requireNonNull(filter);
  }

  @Inject
  public void populateVerticles(final Map<String, Verticle> verticleMap) {
    verticles = verticleMap.entrySet().stream()
        .filter(e -> filter.test(e.getKey()))
        .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
    if (verticles.isEmpty()) {
      throw new IllegalArgumentException(
          "No verticle participates in bootstrap? (are they discoverable, or filter filtered out all of them?)");
    }
    log.debug("Bootstrapping following verticles: " + verticles);
  }

  @Override
  public void init(final Vertx vertx, final Context context) {
    super.init(vertx, context);
    verticles.values().stream().forEach(e -> e.init(vertx, context));
  }

  @Override
  public void start(final Future<Void> startFuture) throws Exception {
    log.debug("Starting bootstrap verticle");
    AtomicInteger atomicInteger = new AtomicInteger(verticles.size());
    for (Verticle verticle : verticles.values()) {
      Future<Void> f = Future.future();
      f.setHandler(delegatingHandler(atomicInteger, startFuture));
      verticle.start(f);
    }
  }

  @Override
  public void stop(final Future<Void> stopFuture) throws Exception {
    log.debug("Stopping bootstrap verticle");
    AtomicInteger atomicInteger = new AtomicInteger(verticles.size());
    for (Verticle verticle : verticles.values()) {
      Future<Void> f = Future.future();
      f.setHandler(delegatingHandler(atomicInteger, stopFuture));
      verticle.stop(f);
    }
  }

  @Nonnull
  private Handler<AsyncResult<Void>> delegatingHandler(final AtomicInteger counter,
                                                       final Future<Void> lifecycleFuture)
  {
    return (AsyncResult<Void> event) -> {
      int val = counter.decrementAndGet();
      if (event.failed()) {
        lifecycleFuture.fail(event.cause());
      }
      else if (val == 0) {
        lifecycleFuture.complete();
      }
    };
  }
}

@@ -43,19 +43,25 @@
 va.util.
-Map
+ArrayList
 ;%0Aimport
@@ -75,17 +75,12 @@
 til.
-Map.Entry
+List
 ;%0Aim
@@ -98,39 +98,39 @@
 til.
-concurrent.atomic.AtomicInteger
+Map;%0Aimport java.util.Map.Entry
 ;%0Aim
@@ -205,41 +205,8 @@
 s;%0A%0A
-import javax.annotation.Nonnull;%0A
 impo
@@ -317,27 +317,31 @@
 tx.core.
-AsyncResult
+CompositeFuture
 ;%0Aimport
@@ -397,38 +397,8 @@
 re;%0A
-import io.vertx.core.Handler;%0A
 impo
@@ -2004,55 +2004,46 @@
     
-AtomicInteger atomicInteger = new AtomicInteger
+List%3CFuture%3E futures = new ArrayList%3C%3E
 (ver
@@ -2159,99 +2159,241 @@
     
-f.setHandler(delegatingHandler(atomicInteger, startFuture));%0A      verticle.start(f);%0A    %7D
+verticle.start(f);%0A      futures.add(f);%0A    %7D%0A    CompositeFuture.all(futures).setHandler(ar -%3E %7B%0A      if (ar.succeeded()) %7B%0A        startFuture.complete();%0A      %7D%0A      else %7B%0A        startFuture.fail(ar.cause());%0A      %7D%0A    %7D);
 %0A  %7D
@@ -2529,55 +2529,46 @@
     
-AtomicInteger atomicInteger = new AtomicInteger
+List%3CFuture%3E futures = new ArrayList%3C%3E
 (ver
@@ -2684,449 +2684,163 @@
     
-f.setHandler(delegatingHandler(atomicInteger, stopFuture));%0A      verticle.stop(f);%0A    %7D%0A  %7D%0A%0A  @Nonnull%0A  private Handler%3CAsyncResult%3CVoid%3E%3E delegatingHandler(final AtomicInteger counter,%0A                                                       final Future%3CVoid%3E lifecycleFuture)%0A  %7B%0A    return (AsyncResult%3CVoid%3E event) -%3E %7B%0A      int val = counter.decrementAndGet();%0A      if (event.failed()) %7B%0A        lifecycleFuture.fail(event.caus
+verticle.stop(f);%0A      futures.add(f);%0A    %7D%0A    CompositeFuture.all(futures).setHandler(ar -%3E %7B%0A      if (ar.succeeded()) %7B%0A        stopFuture.complet
 e()
-)
 ;%0A  
@@ -2860,58 +2860,45 @@
 lse 
-if (val == 0) %7B%0A        lifecycleFuture.complet
+%7B%0A        stopFuture.fail(ar.caus
 e()
+)
 ;%0A  
@@ -2908,16 +2908,17 @@
  %7D%0A    %7D
+)
 ;%0A  %7D%0A%7D%0A

Use CompositeFuture instead of hooky-pooky