package jetbrains.mps.progress;

/*Generated by MPS */

import org.jetbrains.mps.openapi.util.ProgressMonitor;
import jetbrains.mps.annotations.GeneratedClass;
import java.util.function.BooleanSupplier;
import jetbrains.mps.internal.collections.runtime.Sequence;
import java.util.List;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import java.util.Collection;

/**
 * A task that is supposed to run with {@link org.jetbrains.mps.openapi.util.ProgressMonitor }.
 * <p>The method {@link jetbrains.mps.progress.ProgressTask#schedule(TaskScheduler) } is called at some point after {@link jetbrains.mps.progress.ProgressTask#initMonitor(ProgressMonitor) }. 
 * The latter method is intended to be overridden by subclasses of this class that wish
 * to allocate a portion (a sub-task) of the parent monitor's total work. 
 */
@GeneratedClass(node = "r:37761ffb-9538-49ac-a331-c8121d2c14b2(jetbrains.mps.progress)/6032839525407361830", model = "r:37761ffb-9538-49ac-a331-c8121d2c14b2(jetbrains.mps.progress)")
public abstract class ProgressTask {

  public static final Runnable NOP = () -> {
  };
  public static final BooleanSupplier ALWAYS = () -> true;
  public static final ProgressMonitor EMPTY = new EmptyProgressMonitor();

  public static ProgressTask nop() {
    return new SimpleTask("nop");
  }

  public static ProgressTask just(Runnable runnable) {
    return new SimpleTask("runnable", runnable);
  }

  public static ProgressTask just(Runnable runnable, BooleanSupplier readyCondition) {
    return new SimpleTask("runnable", runnable, readyCondition);
  }

  public static ProgressTask sequence(ProgressTask... tasks) {
    return new MultiTask(Sequence.fromIterable(Sequence.fromArray(tasks)).toList(), false);
  }

  protected ProgressMonitor myMonitor = EMPTY;

  /**
   * Override to create a "subtask" on the parent's monitor
   * 
   * @param parentMonitor the monitor of the parent task
   */
  public void initMonitor(ProgressMonitor parentMonitor) {
    this.myMonitor = parentMonitor;
  }

  /**
   * Schedule the task with the specified instance of {@link jetbrains.mps.progress.TaskScheduler }.
   */
  public abstract IdleWork schedule(TaskScheduler scheduler);

  public abstract String getTitle();

  public boolean isReady() {
    return true;
  }

  public void onSuccess() {
  }

  public void onCancel() {
  }

  public void onThrowable(Throwable t) {
  }

  public void onFinished() {
  }

  public ProgressTask then(ProgressTask tsk) {
    return ProgressTask.sequence(this, tsk);
  }

  public static class Builder {

    private List<ProgressTask> myTasks = ListSequence.fromList(new ArrayList<ProgressTask>());

    public Builder() {
    }

    public Builder(Runnable runnable) {
      ListSequence.fromList(myTasks).addElement(ProgressTask.just(runnable));
    }

    public Builder addTask(Runnable runnable) {
      ListSequence.fromList(myTasks).addElement(ProgressTask.just(runnable));
      return this;
    }

    public Builder addTask(ProgressTask task) {
      ListSequence.fromList(myTasks).addElement(task);
      return this;
    }

    public Builder addTask(Runnable runnable, BooleanSupplier readyCondition) {
      ListSequence.fromList(myTasks).addElement(ProgressTask.just(runnable, readyCondition));
      return this;
    }

    public ProgressTask asParallel() {
      if (ListSequence.fromList(myTasks).isEmpty()) {
        return ProgressTask.nop();
      }
      return new MultiTask(myTasks, true);
    }

    public ProgressTask asSequential() {
      if (ListSequence.fromList(myTasks).isEmpty()) {
        return ProgressTask.nop();
      }
      return new MultiTask(myTasks, false);
    }

  }

  public static class SimpleTask extends ProgressTask {
    private final Runnable myRunnable;
    private final BooleanSupplier myReadyCondition;
    private final String myTitle;

    public SimpleTask(String title) {
      this(title, NOP);
    }
    public SimpleTask(String title, Runnable runnable) {
      this(title, runnable, ALWAYS);
    }
    public SimpleTask(String title, Runnable runnable, BooleanSupplier readyCondition) {
      this.myTitle = title;
      this.myRunnable = runnable;
      this.myReadyCondition = readyCondition;
    }
    @Override
    public String getTitle() {
      return myTitle;
    }
    @Override
    public IdleWork schedule(TaskScheduler scheduler) {
      return IdleWork.Support.run(this::run);
    }
    protected void run() {
      myRunnable.run();
    }
    @Override
    public boolean isReady() {
      return myReadyCondition.getAsBoolean();
    }
  }

  public static class MultiTask extends ProgressTask {
    private final Collection<ProgressTask> myTasks;
    private final boolean myParallel;

    public MultiTask(Collection<ProgressTask> tasks, boolean parallel) {
      this.myTasks = tasks;
      this.myParallel = parallel;
    }
    public MultiTask(ProgressTask... tasks) {
      this.myTasks = Sequence.fromIterable(Sequence.fromArray(tasks)).toList();
      this.myParallel = false;
    }
    @Override
    public String getTitle() {
      return String.format("multi(size=%d, parallel=%b)", myTasks.size(), myParallel);
    }
    @Override
    public IdleWork schedule(TaskScheduler scheduler) {
      if (myParallel) {
        return scheduler.scheduleParallel(myTasks, myMonitor);

      } else {
        return scheduler.scheduleSequential(myTasks, myMonitor);
      }
    }
  }

  public static class DelegatingTask extends ProgressTask {
    private final ProgressTask myDelegate;

    public DelegatingTask(ProgressTask delegate) {
      this.myDelegate = delegate;
    }
    @Override
    public String getTitle() {
      return String.format("delegating(%s)", myDelegate.getTitle());
    }
    @Override
    public IdleWork schedule(TaskScheduler scheduler) {
      return scheduler.schedule(myDelegate, myMonitor);
    }
    @Override
    public boolean isReady() {
      return myDelegate.isReady();
    }
    protected ProgressTask getDelegate() {
      return this.myDelegate;
    }
  }
}
