package com.github.aesteve.vertx.nubes.utils.async;

import static com.github.aesteve.vertx.nubes.utils.async.AsyncUtils.completeOrFail;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import com.github.aesteve.vertx.nubes.utils.functional.TriConsumer;

public class MultipleFutures<T> extends SimpleFuture<T> {

	private final Map<Handler<Future<T>>, Future<T>> consumers;
	private static final Logger log = LoggerFactory.getLogger(MultipleFutures.class);

	public MultipleFutures() {
		consumers = new HashMap<>();
	}

	public MultipleFutures(Future<T> after) {
		this();
		join(after);
	}

	public MultipleFutures(Handler<AsyncResult<T>> after) {
		this();
		join(after);
	}

	public <L> MultipleFutures(Collection<L> elements, Function<L, Handler<Future<T>>> transform) {
		this();
		addAll(elements, transform);
	}

	public void add(Handler<Future<T>> handler) {
		if (handler == null) {
			return;
		}
		Future<T> future = Future.future();
		future.setHandler(futureHandler -> {
			checkCallHandler();
		});
		consumers.put(handler, future);
	}

	public void start() {
		if (consumers.isEmpty()) {
			complete();
			return;
		}
		consumers.forEach((consumer, future) -> {
			consumer.handle(future);
		});
	}

	@Override
	public T result() {
		return null;
	}

	@Override
	public Throwable cause() {
		Exception e = new Exception("At least one future failed");
		consumers.forEach((consumer, future) -> {
			if (future.cause() != null) {
				log.error(future.cause());
				if (e.getCause() == null) {
					e.initCause(future.cause());
				} else {
					e.addSuppressed(future.cause());
				}
			}
		});
		return e;
	}

	@Override
	public boolean succeeded() {
		return consumers.values().stream().allMatch(future -> {
			return future.succeeded();
		});
	}

	@Override
	public boolean failed() {
		return consumers.values().stream().anyMatch(future -> {
			return future.failed();
		});
	}

	@Override
	public boolean isComplete() {
		if (super.isComplete()) { // has been marked explicitly
			return true;
		}
		if (consumers.isEmpty()) {
			return false;
		}
		return consumers.values().stream().allMatch(future -> {
			return future.isComplete();
		});
	}

	public void join(Future<T> future) {
		setHandler(completeOrFail(future));
	}

	public void join(Handler<AsyncResult<T>> handler) {
		setHandler(handler);
	}

	public <L> void addAll(Collection<L> list, Function<L, Handler<Future<T>>> transform) {
		list.forEach(element -> {
			add(transform.apply(element));
		});
	}

	public <L> void treatAllNow(Collection<L> list, Function<L, Handler<Future<T>>> transform) {
		addAll(list, transform);
		start();
	}

	public <K, V> void addAll(Map<K, V> map, TriConsumer<K, V, Future<T>> transform) {
		map.forEach((key, value) -> {
			add(res -> {
				transform.accept(key, value, res);
			});
		});
	}

}

@@ -1063,20 +1063,34 @@
 %09public 
-void
+MultipleFutures%3CT%3E
  add(Han
@@ -1142,32 +1142,37 @@
 ull) %7B%0A%09%09%09return
+ this
 ;%0A%09%09%7D%0A%09%09Future%3CT
@@ -1302,31 +1302,60 @@
 ture);%0A%09
-%7D%0A%0A%09public void
+%09return this;%0A%09%7D%0A%0A%09public MultipleFutures%3CT%3E
  start()
@@ -1410,16 +1410,21 @@
 %09%09return
+ this
 ;%0A%09%09%7D%0A%09%09
@@ -1491,32 +1491,47 @@
 (future);%0A%09%09%7D);%0A
+%09%09return this;%0A
 %09%7D%0A%0A%09@Override%0A%09
@@ -2472,28 +2472,42 @@
 %09%7D%0A%0A%09public 
-void
+MultipleFutures%3CT%3E
  join(Future
@@ -2563,23 +2563,52 @@
 );%0A%09
-%7D%0A%0A%09public void
+%09return this;%0A%09%7D%0A%0A%09public MultipleFutures%3CT%3E
  joi
@@ -2668,35 +2668,64 @@
 dler);%0A%09
-%7D%0A%0A%09public %3CL%3E void
+%09return this;%0A%09%7D%0A%0A%09public %3CL%3E MultipleFutures%3CT%3E
  addAll(
@@ -2862,27 +2862,56 @@
 );%0A%09
-%7D%0A%0A%09public %3CL%3E void
+%09return this;%0A%09%7D%0A%0A%09public %3CL%3E MultipleFutures%3CT%3E
  tre
@@ -3018,24 +3018,39 @@
 %0A%09%09start();%0A
+%09%09return this;%0A
 %09%7D%0A%0A%09public 
@@ -3060,12 +3060,26 @@
  V%3E 
-void
+MultipleFutures%3CT%3E
  add
@@ -3235,18 +3235,33 @@
 %09%09%7D);%0A%09%09%7D);%0A
+%09%09return this;%0A
 %09%7D%0A%0A%7D%0A

make API fluent