/**
 * Copyright 2015 Netflix, Inc.
 *
 * 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 io.reactivesocket;

import io.reactivesocket.internal.rx.EmptySubscription;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

@FunctionalInterface
public interface ReactiveSocketFactory<T, R extends ReactiveSocket> {

    Publisher<R> call(T t);

    /**
     * Gets a socket in a blocking manner
     * @param t configuration to create the reactive socket
     * @return blocks on create the socket
     */
    default R callAndWait(T t) {
        AtomicReference<R> reference = new AtomicReference<>();
        AtomicReference<Throwable> error = new AtomicReference<>();
        CountDownLatch latch = new CountDownLatch(1);

        call(t)
            .subscribe(new Subscriber<R>() {
                @Override
                public void onSubscribe(Subscription s) {
                    s.request(1);
                }

                @Override
                public void onNext(R reactiveSocket) {
                    reference.set(reactiveSocket);
                }

                @Override
                public void onError(Throwable t) {
                    error.set(t);
                    latch.countDown();
                }

                @Override
                public void onComplete() {
                    latch.countDown();
                }
            });

        if (error.get() != null) {
            throw new RuntimeException(error.get());
        } else {
            return reference.get();
        }
    }

    /**
     *
     * @param t the configuration used to create the reactive socket
     * @param timeout timeout
     * @param timeUnit timeout units
     * @param executorService ScheduledExecutorService to schedule the timeout on
     * @return
     */
    default Publisher<R> call(T t, long timeout, TimeUnit timeUnit, ScheduledExecutorService executorService) {
        Publisher<R> reactiveSocketPublisher = subscriber -> {
            AtomicBoolean complete = new AtomicBoolean();
            subscriber.onSubscribe(EmptySubscription.INSTANCE);
            call(t)
                .subscribe(new Subscriber<R>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        s.request(1);
                    }

                    @Override
                    public void onNext(R reactiveSocket) {
                        subscriber.onNext(reactiveSocket);
                    }

                    @Override
                    public void onError(Throwable t) {
                        subscriber.onError(t);
                    }

                    @Override
                    public void onComplete() {
                        if (complete.compareAndSet(false, true)) {
                            subscriber.onComplete();
                        }
                    }
                });

            executorService.schedule(() -> {
                if (complete.compareAndSet(false, true)) {
                    subscriber.onError(new TimeoutException());
                }
            }, timeout, timeUnit);
        };

        return reactiveSocketPublisher;
    }

}

@@ -811,33 +811,77 @@
 til.
-concurrent.CountDownLatch
+NoSuchElementException;%0Aimport java.util.concurrent.CompletableFuture
 ;%0Aim
@@ -1069,60 +1069,8 @@
 ean;
-%0Aimport java.util.concurrent.atomic.AtomicReference;
 %0A%0A@F
@@ -1394,183 +1394,62 @@
     
-AtomicReference%3CR%3E reference = new AtomicReference%3C%3E();%0A        AtomicReference%3CThrowable%3E error = new AtomicReference%3C%3E();%0A        CountDownLatch latch = new CountDownLatch(1
+CompletableFuture%3CR%3E future = new CompletableFuture%3C%3E(
 );%0A%0A
@@ -1751,21 +1751,23 @@
     
-reference.s
+future.compl
 et
+e
 (rea
@@ -1900,58 +1900,38 @@
     
-error.set(t);%0A                    latch.countDown(
+future.completeExceptionally(t
 );%0A 
@@ -2041,212 +2041,163 @@
     
-latch.countDown();%0A                %7D%0A            %7D);%0A%0A        if (error.get() != null) %7B%0A            throw new RuntimeException(error.get());%0A        %7D else %7B%0A            return reference.get();%0A        %7D
+future.completeExceptionally(new NoSuchElementException(%22Sequence contains no elements%22));%0A                %7D%0A            %7D);%0A%0A        return future.join();
 %0A   

Fix for ReactiveSocketFactory.callAndWait