package jetbrains.mps.samples.ActionWithProgress.plugin;

/*Generated by MPS */

import jetbrains.mps.workbench.action.BaseAction;
import javax.swing.Icon;
import jetbrains.mps.workbench.action.ActionAccess;
import com.intellij.openapi.actionSystem.AnActionEvent;
import java.util.Map;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import jetbrains.mps.project.MPSProject;
import jetbrains.mps.ide.actions.MPSCommonDataKeys;
import org.jetbrains.annotations.NotNull;
import com.intellij.openapi.progress.PerformInBackgroundOption;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.ProgressIndicator;
import jetbrains.mps.progress.ProgressMonitorAdapter;
import org.jetbrains.mps.openapi.module.SRepository;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProgressManager;

public class BackgroundableProgressAction_Action extends BaseAction {
  private static final Icon ICON = null;

  public BackgroundableProgressAction_Action() {
    super("BackgroundableProgressAction", "", ICON);
    this.setIsAlwaysVisible(false);
    this.setActionAccess(ActionAccess.UNDO_PROJECT);
    updateInBackground(true);
  }
  @Override
  public boolean isDumbAware() {
    return true;
  }
  @Override
  protected boolean collectActionData(AnActionEvent event, final Map<String, Object> _params) {
    if (!(super.collectActionData(event, _params))) {
      return false;
    }
    {
      Project p = event.getData(CommonDataKeys.PROJECT);
      if (p == null) {
        return false;
      }
    }
    {
      MPSProject p = event.getData(MPSCommonDataKeys.MPS_PROJECT);
      if (p == null) {
        return false;
      }
    }
    return true;
  }
  @Override
  public void doExecute(@NotNull final AnActionEvent event, final Map<String, Object> _params) {
    // Indicates whether the progress dialog has the'Cancel' option
    boolean canBeCanceled = true;

    // Will be  sent to the background with the flag PerformInBackgroundOption.ALWAYS_BACKGROUND
    PerformInBackgroundOption showProgress = PerformInBackgroundOption.DEAF;

    // This is a backgroundable task. It can be sent to the background and canceled
    // The PerformInBackgroundOption flag specifies if the progress is shown to the user
    // or if should be in the background from the start
    // ALWAYS_BACKGROUND is probably the best solution for quick background tasks -
    // the user will not get a blinking, hardly noticable progress bar visible for only a few fractions of a second
    // Important thing - you need to implement the onCacel() method
    // Your code needs to frequently check if the process has been canceled (between every calculation steps)
    // and handle yourself all steps to revert the action

    final Task.Backgroundable backgroundable = new Task.Backgroundable(event.getData(CommonDataKeys.PROJECT), "Backgroundable cancelable task", canBeCanceled, showProgress) {
      @Override
      public void run(@NotNull final ProgressIndicator indicator) {
        final ProgressMonitorAdapter adapter = new ProgressMonitorAdapter(indicator);
        SRepository repository = event.getData(MPSCommonDataKeys.MPS_PROJECT).getRepository();

        adapter.start("Progress in progress...", 4);
        int stepValue = 1;

        // a normal step
        adapter.step("Do simple work...");
        BackgroundableProgressAction_Action.this.doWork(event);
        adapter.advance(stepValue);
        // Check if the progress is canceled after each step
        if (adapter.isCanceled()) {
          return;
        }

        // ReadAction in step is ok
        repository.getModelAccess().runReadAction(new Runnable() {
          public void run() {
            adapter.step("Do some work with Read Lock...");
            BackgroundableProgressAction_Action.this.doWork(event);
          }
        });
        adapter.advance(stepValue);
        if (adapter.isCanceled()) {
          return;
        }

        // WriteAction in step is ok
        repository.getModelAccess().runWriteAction(new Runnable() {
          public void run() {
            adapter.step("Do some work with Write Lock...");
            BackgroundableProgressAction_Action.this.doWork(event);
          }
        });
        adapter.advance(stepValue);
        if (adapter.isCanceled()) {
          return;
        }

        adapter.step("Finishing...");
        BackgroundableProgressAction_Action.this.doWork(event);
        adapter.advance(stepValue);
        if (adapter.isCanceled()) {
          return;
        }

        adapter.done();
      }

      @Override
      public void onCancel() {
        super.onCancel();
        // Needs to handle reverting changes for all the finished steps
        // This method does not interrupt the steps - steps must be either short or have such interruption capability
      }
    };
    // The execute() method of actions must be very quick
    // so every long calculation must be invoked outside of this method like this:
    ApplicationManager.getApplication().invokeLater(() -> ProgressManager.getInstance().run(backgroundable));
  }
  private void doWork(final AnActionEvent event) {
    // 42 because it is ultimate answer to everything =)
    BackgroundableProgressAction_Action.this.fib(44, event);
  }
  private int fib(int n, final AnActionEvent event) {
    // Very ineffective implementation with exponential time complexity
    if (n < 1) {
      throw new IllegalArgumentException();
    }

    if (n == 1) {
      return 0;
    } else if (n == 2 || n == 3) {
      return 1;
    }

    return BackgroundableProgressAction_Action.this.fib(n - 1, event) + BackgroundableProgressAction_Action.this.fib(n - 2, event);
  }
}
