/*
 * Written by Dawid Kurzyniec and released to the public domain, as explained
 * at http://creativecommons.org/licenses/publicdomain
 */

package edu.emory.mathcs.util.concurrent;

import java.util.*;

/**
 * This class provide some value-added over java.util.Timer: that is,
 * it enables rescheduling of previously scheduled tasks.
 * <p>
 * Note: it seems that cancelling the task and scheduling a new one in such
 * case usually leads to better designs, so this (experimental) class may
 * not be that useful after all.
 *
 * @author Dawid Kurzyniec
 * @version 1.0
 */
public class AlarmClock {
    final Timer engine;
    final Runnable task;
    TimerTask timerTask;

    public AlarmClock(Timer engine, Runnable task) {
        this.engine = engine;
        this.task = task;
    }

    /**
     * Reschedule previously scheduled task unless it would postpone it.
     * In other words, if the requested delay is longer than the time remaining
     * until the previously scheduled execution, the request is ignored.
     *
     * @param delay the new delay before the task is to be executed,
     *              measured from the moment the method is invoked.
     * @return true if successful, false otherwise (that is, if task is already
     *         running or complete)
     */
    public synchronized void setAlarmDelayIfSooner(long delay) {
        if (delay < 0) {
            // infinite; can't be more restrictive than anything
            return;
        }
        if (timerTask != null) {
            long oldDelay = timerTask.scheduledExecutionTime() - System.currentTimeMillis();
            if (delay >= oldDelay) {
                // not more restrictive; ignore request
                return;
            }
        }
        setAlarmDelay(delay);
    }

    /**
     * Reschedule previously scheduled task.
     *
     * @param delay the new delay before the task is to be executed,
     *              measured from the moment the method is invoked.
     * @return true if successful, false otherwise (that is, if task is already
     *         running or complete)
     */
    public synchronized boolean setAlarmDelay(long delay) {
        if (timerTask != null) {
            if (!cancel()) {
                // task is running at the moment
                return false;
            }
        }
        // start timer
        timerTask = new TimerTask() {
            public void run() {
                task.run();
                synchronized (AlarmClock.this) {
                    // job done; suicide
                    AlarmClock.this.timerTask = null;
                }
            }
        };

        engine.schedule(timerTask, delay);
        return true;
    }

    /**
     * Cancel the scheduled task.
     *
     * @return true if successful, false otherwise.
     * @see TimerTask#cancel
     */
    public synchronized boolean cancel() {
        if (timerTask != null) {
            boolean result = timerTask.cancel();
            timerTask = null;
            return result;
        }
        return false;
    }
}
