/*
 * Copyright (c) 2019 the Eclipse Milo Authors
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 */

package org.eclipse.milo.opcua.sdk.server.util;

import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CompletableFuture;

public interface Pending<I, O> {

    /**
     * @return The {@link CompletableFuture} to complete when the result is ready.
     */
    CompletableFuture<O> getFuture();

    /**
     * @return The input parameter to the pending operation.
     */
    I getInput();

    /**
     * Builds a {@link CompletableFuture} suitable for use as a completion callback. When completed, each of the
     * provided {@link Pending}'s {@link CompletableFuture} will be completed.
     * <p>
     * It is assumed that the size of the list this future is completed with matches the number of provided {@link
     * Pending}s. A {@link RuntimeException} will be thrown otherwise.
     *
     * @param pending A list of {@link Pending} operations.
     * @param <I>     Input parameter of {@link Pending} operations.
     * @param <O>     Output parameter of {@link Pending} operations.
     * @return A {@link CompletableFuture} that, when completed, then completes each of the given {@link Pending}'s
     * {@link CompletableFuture}.
     */
    static <I, O> CompletableFuture<List<O>> callback(List<? extends Pending<I, O>> pending) {
        CompletableFuture<List<O>> future = new CompletableFuture<>();

        future.thenAccept(results -> {
            if (results.size() != pending.size()) {
                String message = String.format(
                    "result size (%s) does not match pending size (%s)",
                    results.size(), pending.size());
                throw new RuntimeException(message);
            }

            Iterator<? extends Pending<I, O>> pi = pending.iterator();
            Iterator<O> ri = results.iterator();

            while (pi.hasNext() && ri.hasNext()) {
                pi.next().getFuture().complete(ri.next());
            }
        });

        return future;
    }

}
