/**
 * Copyright (c) 2015-2016 Peti Koch
 *
 * 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 ch.petikoch.examples.mvvm_rxjava.utils;

import ch.petikoch.examples.mvvm_rxjava.rxjava_mvvm.FinishedIndicator;
import rx.Observable;
import rx.Single;
import rx.Subscription;
import rx.schedulers.Schedulers;
import rx.subjects.AsyncSubject;

import java.util.concurrent.Callable;

import static ch.petikoch.examples.mvvm_rxjava.utils.PreserveFullStackTraceOperator.preserveFullStackTrace;

/**
 * Something like <a href="https://github.com/ReactiveX/RxJavaAsyncUtil/blob/0.x/src/main/java/rx/util/async/Async.java">RxJavaAsyncUtil</a>
 * but with {@link Single} as return types.
 */
@Deprecated // This is kind of "imperative" and not lazy like typical Rx
public class AsyncUtils {

    /**
     * @see #executeAsync(Callable)
     */
    // experimental
    public static Single<FinishedIndicator> executeAsync(Runnable runnable) {
        return executeAsync(() -> {
            runnable.run();
            return FinishedIndicator.INSTANCE;
        });
    }

    /**
     * Starts immediately the execution of the given Callable with a thread from
     * {@link Schedulers#io()}.
     *
     * The caller can await the execution termination through subscribing to the {@link Single} return value.
     * It's safe to "share" the {@link Single} return value reference and subscribe to it as many times as you want.
     * All subscribers get the result value (or the error) individually.
     *
     * Cancellation? The execution is automatically cancelled when all subscribers do unsubscribe while
     * the execution is still running. The given {@link Callable} will be interrupted.
     *
     * If there is no subscriber ever to the {@link Single} return value, the callable will be executed unobserved.
     * Make sure to have some kind of "exception handling" also for that case (like try-catch-logging blocks or
     * {@link Thread.UncaughtExceptionHandler}) to not "miss" issues.
     *
     * @param callable the code to execute
     * @param <T>      type of result
     * @return Single instance delivering asynchronously the result of the callable
     */
    // experimental
    public static <T> Single<T> executeAsync(Callable<T> callable) {
        AsyncSubject<T> resultSubject = AsyncSubject.create();

	    final Subscription asyncOp = Observable.fromCallable(callable)
			    .subscribeOn(Schedulers.io())
			    .lift(preserveFullStackTrace())
                .subscribe(
                        t -> {
                            resultSubject.onNext(t);
                            resultSubject.onCompleted();
                        },
                        throwable -> {
                            resultSubject.onError(throwable);
                            resultSubject.onCompleted();
                        }
                );

        return resultSubject.doOnUnsubscribe(asyncOp::unsubscribe)
                .share()
                .toSingle();
    }
}