/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package schedule;

import java.util.ArrayList;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.log4j.Logger;

/**
 * Scheduler class is responsible for executing scheduled tasks at right time
 * with correct parameters. Scheduler uses Timer class to perform one time
 * actions repeatedly. After each action Timer is used to schedule execution
 * of another action. Time when Timer is invoked is time of next earliest
 * action to run.
 * 
 * @author Marcel Duris
 */
public class Scheduler implements IScheduler{

    /**
     * This private inner class is invoked by timer to execute all actions
     * that are to be run at this time.
     *
     * Only method of this class searches for tasks, which are to be executed
     * at time of invocation of that method and starts timer using method of
     * outer class.
     */
    private class Executor extends TimerTask {

        /**
         * Searches all tasks and starts separate thread for them, if they are
         * to be run.
         */
        @Override
        public void run() {
            Date now = new Date();
            for (ITask task : tasks) {
                if (task.accepts(now)) {
                    new Thread(task).start();
                }
            }

            reschedule();
        }
    }
    private ArrayList<ITask> tasks;     /* List of tasks */
    private Timer timer;                /* Timer used for scheduling */
    private static Scheduler scheduler; /* Singleton */

    private Scheduler() {
        timer = new Timer();
        tasks = new ArrayList<ITask>();
    }

    /**
     * Returns instance of Scheduler. If no scheduler exists, new is created.
     * Only one scheduler is permitted to exists at one time.
     *
     * @return instance of Scheduler
     */
    public static Scheduler getScheduler() {
        if (scheduler == null) {
            scheduler = new Scheduler();
        }
        return scheduler;
    }

    /**
     * This method can be used to reset timer after loading new tasks. Under
     * normal circumstances timer would not take new tasks into account. If
     * timer is scheduled to run at certain time and new task, which should
     * run before timer is invoked, is added, task will be skipped and will
     * run at next opportunity.
     */
    public void reschedule() {
        if (tasks.size() == 0) {
            return;
        }
        Date date = tasks.get(0).nextDue();
        Date now = new Date();
        for (int i = 1; i < tasks.size(); i++) {
            Date other = tasks.get(i).nextDue();
            if (date.after(other) && other.after(now)) {
                date = other;
            }
        }

        timer.cancel();
        timer = new Timer();
        timer.schedule(new Executor(), date);
        Logger.getLogger("qbackup.scheduler").info("Next action scheduled at " + date.toString());
    }

    /**
     * Cancels timer and clears list of tasks.
     */
    public void clear() {
        timer.cancel();
        tasks = new ArrayList<ITask>();
    }

    /**
     * This method can be used to add new tasks into list of tasks scheduler
     * will run.
     *
     * @param task class implementing ITask interface that is to be run as certain time
     */
    public void addTask(ITask task) {
        tasks.add(task);
    }
}
