/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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 com.android.dialer.common.concurrent;

import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import java.util.concurrent.ExecutorService;

/**
 * Provides a consistent interface for doing background work in either UI or non-UI contexts.
 *
 * <p>You may create an executor from a UI component (activity or fragment) or a non-UI component.
 * Using this class provides a number of benefits:
 *
 * <ul>
 *   <li>Ensures that UI tasks keep running across configuration changes by using a headless
 *       fragment.
 *   <li>Forces exceptions to crash the application, unless the user implements their own onFailure
 *       method.
 *   <li>Checks for dead UI components which can be encountered if a UI task runs longer than its
 *       UI. If a dead UI component is encountered, onSuccess/onFailure are not called (because they
 *       can't be) but a message is logged.
 *   <li>Helps prevent memory leaks in UI tasks by ensuring that callbacks are nulled out when the
 *       headless fragment is detached.
 *   <li>UI and non-UI threads are shared across the application and run at reasonable priorities
 * </ul>
 *
 * <p>Executors accept a single input and output parameter which should be immutable data objects.
 * If you don't require an input or output, use Void and null as needed.
 *
 * <p>You may optionally specify onSuccess and onFailure listeners; the default behavior on success
 * is a no-op and the default behavior on failure is to crash the application.
 *
 * <p>To use an executor from a UI component, you must create it in your onCreate method and then
 * use it from anywhere:
 *
 * <pre><code>
 *
 * public class MyActivity extends Activity {
 *
 *   private final DialerExecutor&lt;MyInputType&gt; myExecutor;
 *
 *   public void onCreate(Bundle state) {
 *     super.onCreate(bundle);
 *
 *     // Must be called in onCreate; don't use non-static or anonymous inner classes for worker!
 *     myExecutor = DialerExecutorComponent.get(context).dialerExecutorFactory()
 *         .createUiTaskBuilder(fragmentManager, taskId, worker)
 *         .onSuccess(this::onSuccess)  // Lambdas, anonymous, or non-static inner classes all fine
 *         .onFailure(this::onFailure)  // Lambdas, anonymous, or non-static inner classes all fine
 *         .build();
 *     );
 *   }
 *
 *   private static class MyWorker implements Worker&lt;MyInputType, MyOutputType&gt; {
 *     MyOutputType doInBackground(MyInputType input) { ... }
 *   }
 *   private void onSuccess(MyOutputType output) { ... }
 *   private void onFailure(Throwable throwable) { ... }
 *
 *   private void userDidSomething() { myExecutor.executeParallel(input); }
 * }
 * </code></pre>
 *
 * <p>Usage for non-UI tasks is the same, except that tasks can be created from anywhere instead of
 * in onCreate. Non-UI tasks use low-priority threads separate from the UI task threads so as not to
 * compete with more critical UI tasks.
 *
 * <pre><code>
 *
 * public class MyManager {
 *
 *   private final DialerExecutor&lt;MyInputType&gt; myExecutor;
 *
 *   public void init() {
 *     // Don't use non-static or anonymous inner classes for worker!
 *     myExecutor = DialerExecutorComponent.get(context).dialerExecutorFactory()
 *         .createNonUiTaskBuilder(worker)
 *         .onSuccess(this::onSuccess)  // Lambdas, anonymous, or non-static inner classes all fine
 *         .onFailure(this::onFailure)  // Lambdas, anonymous, or non-static inner classes all fine
 *         .build();
 *     );
 *   }
 *
 *   private static class MyWorker implements Worker&lt;MyInputType, MyOutputType&gt; {
 *     MyOutputType doInBackground(MyInputType input) { ... }
 *   }
 *   private void onSuccess(MyOutputType output) { ... }
 *   private void onFailure(Throwable throwable) { ... }
 *
 *   private void userDidSomething() { myExecutor.executeParallel(input); }
 * }
 * </code></pre>
 *
 * Note that non-UI tasks are intended to be relatively quick; for example reading/writing shared
 * preferences or doing simple database work. If you submit long running non-UI tasks you may
 * saturate the shared application threads and block other tasks. Also, this class does not create
 * any wakelocks, so a long running task could be killed if the device goes to sleep while your task
 * is still running. If you have to do long running or periodic work, consider using a job
 * scheduler.
 */
public interface DialerExecutor<InputT> {

  /** Functional interface for doing work in the background. */
  interface Worker<InputT, OutputT> {
    @WorkerThread
    @Nullable
    OutputT doInBackground(@Nullable InputT input) throws Throwable;
  }

  /** Functional interface for handling the result of background work. */
  interface SuccessListener<OutputT> {
    @MainThread
    void onSuccess(@Nullable OutputT output);
  }

  /** Functional interface for handling an error produced while performing background work. */
  interface FailureListener {
    @MainThread
    void onFailure(@NonNull Throwable throwable);
  }

  /** Builder for {@link DialerExecutor}. */
  interface Builder<InputT, OutputT> {

    /**
     * Optional. Default is no-op.
     *
     * @param successListener a function executed on the main thread upon task success. There are no
     *     restraints on this as it is executed on the main thread, so lambdas, anonymous, or inner
     *     classes of your activity or fragment are all fine.
     */
    @NonNull
    Builder<InputT, OutputT> onSuccess(@NonNull SuccessListener<OutputT> successListener);

    /**
     * Optional. If this is not set and your worker throws an exception, the application will crash.
     *
     * @param failureListener a function executed on the main thread upon task failure. There are no
     *     restraints on this as it is executed on the main thread, so lambdas, anonymous, or inner
     *     classes of your activity or fragment are all fine.
     */
    @NonNull
    Builder<InputT, OutputT> onFailure(@NonNull FailureListener failureListener);

    /**
     * Builds the {@link DialerExecutor} which can be used to execute your task (repeatedly with
     * differing inputs if desired).
     */
    @NonNull
    DialerExecutor<InputT> build();
  }

  /** Executes the task such that repeated executions for this executor are serialized. */
  @MainThread
  void executeSerial(@Nullable InputT input);

  /**
   * Executes the task after waiting {@code waitMillis}. If called while the previous invocation is
   * still waiting to be started, the original invocation is cancelled.
   *
   * <p>This is useful for tasks which might get scheduled many times in very quick succession, but
   * it is only the last one that actually needs to be executed.
   */
  @MainThread
  void executeSerialWithWait(@Nullable InputT input, long waitMillis);

  /**
   * Executes the task on a thread pool shared across the application. Multiple calls using this
   * method may result in tasks being executed in parallel.
   */
  @MainThread
  void executeParallel(@Nullable InputT input);

  /**
   * Executes the task on a custom executor service. This should rarely be used; instead prefer
   * {@link #executeSerial(Object)} or {@link #executeParallel(Object)}.
   */
  @MainThread
  void executeOnCustomExecutorService(
      @NonNull ExecutorService executorService, @Nullable InputT input);
}
