
import com.google.gwt.activity.shared.Activity;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.user.client.ui.AcceptsOneWidget;

public abstract class ActivityAsyncProxy implements Activity {

  private boolean hasAsyncBeenIssued;
  private boolean hasAsyncBeenCancelled;
  private boolean hasAsyncFailed;
  private AcceptsOneWidget display;
  private EventBus eventBus;
  private Activity instance;

  @Override
  public String mayStop() {
    checkHasAsyncFailed();
    assert this.instance != null;
    return this.instance.mayStop();
  }

  @Override
  public void onCancel() {
    if (this.instance != null) {
      this.instance.onCancel();
    }
    checkHasAsyncFailed();
    this.hasAsyncBeenCancelled = true;
  }

  @Override
  public void onStop() {
    checkHasAsyncFailed();
    assert this.instance != null;
    this.instance.onStop();
  }

  @Override
  public void start(AcceptsOneWidget panel, EventBus eventBus) {
    if (this.instance != null) {
      this.instance.start(panel, eventBus);
    }
    checkHasAsyncFailed();
    assert !this.hasAsyncBeenIssued || this.hasAsyncBeenCancelled;
    this.display = panel;
    this.eventBus = eventBus;
    this.hasAsyncBeenCancelled = false;
    if (!this.hasAsyncBeenIssued) {
      this.hasAsyncBeenIssued = true;
      doAsync(new RunAsyncCallback() {

        @Override
        public void onSuccess() {
          if (!ActivityAsyncProxy.this.hasAsyncBeenCancelled) {
            assert ActivityAsyncProxy.this.instance == null;
            ActivityAsyncProxy.this.instance = createInstance();
            ActivityAsyncProxy.this.instance.start(ActivityAsyncProxy.this.display, ActivityAsyncProxy.this.eventBus);
          }
        }

        @Override
        public void onFailure(Throwable reason) {
          ActivityAsyncProxy.this.hasAsyncFailed = true;
          if (GWT.getUncaughtExceptionHandler() != null) {
            GWT.getUncaughtExceptionHandler().onUncaughtException(reason);
          }
        }
      });
    }
  }

  protected abstract void doAsync(RunAsyncCallback callback);

  protected abstract Activity createInstance();

  private void checkHasAsyncFailed() {
    if (this.hasAsyncFailed) {
      throw new IllegalStateException("runAsync load previously failed");
    }
  }
}
