/*
 * Copyright 2011 JBoss, by Red Hat, Inc
 *
 * 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 org.jboss.errai.bus.server.async;

import static java.lang.System.currentTimeMillis;

import org.jboss.errai.common.client.api.tasks.AsyncTask;

/**
 * A <tt>TimedTask</tt> is used for scheduling tasks, and making sure they are run at appropriate times and intervals
 */
public abstract class TimedTask implements Runnable, Comparable<TimedTask>, AsyncTask {
  protected volatile long nextRuntime;
  protected volatile long period;
  protected volatile boolean cancelled = false;

  protected volatile InterruptHandle interruptHook;
  protected volatile Runnable exitHandler;

  /**
   * Gets the period of the task, and when it should be run next
   *
   * @return the interval length
   */
  public long getPeriod() {
    return period;
  }

  /**
   * Sets the period in which the task should be run next
   *
   * @param period
   */
  public void setPeriod(long period) {
    this.period = period;
  }


  public void cancel() {
    period = -1;
  }

  /**
   * Gets the time in which the task will be run.  If -1 is returned, the task is permanently de-scheduled.
   *
   * @return the time the task will be run in milliseconds
   */
  public long nextRuntime() {
    return nextRuntime;
  }

  @Override
  public void cancel(boolean interrupt) {
    if (interrupt && interruptHook != null)
      interruptHook.sendInterrupt();

    cancelled = true;
  }

  /**
   * Returns true if the task has been cancelled or is expired.
   *
   * @return
   */
  @Override
  public boolean isCancelled() {
    return cancelled;
  }

  public boolean calculateNextRuntime() {
    synchronized (this) {
      if (!cancelled && period != -1) {
        nextRuntime = currentTimeMillis() + period;
        return true;
      }
      else {
        nextRuntime = -1;
        return false;
      }
    }
  }


  public boolean isDue(long time) {
    synchronized (this) {
      return !cancelled && nextRuntime <= time && nextRuntime != -1;
    }
  }

  @Override
  public void setExitHandler(Runnable runnable) {
    if (exitHandler != null) {
      throw new IllegalStateException("Exit handler already set to " + exitHandler);
    }
    this.exitHandler = runnable;
    if (!isDue(currentTimeMillis())) {
      runnable.run();
    }
  }

  @Override
  public int compareTo(TimedTask o) {
    if (o == this) {
      return 0;
    }
    if (nextRuntime > o.nextRuntime)
      return 1;
    else if (nextRuntime < o.nextRuntime)
      return -1;
    else
      return 0;
  }
}
