/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software License
 * version 1.1, a copy of which has been included with this distribution in
 * the LICENSE file.
 */
package org.apache.cornerstone.blocks.scheduler;

import java.util.NoSuchElementException;
import org.apache.phoenix.Service;
import java.util.Timer;
import java.util.TimerTask;
import java.util.HashMap;
import java.util.Map;
import java.util.Collections;
import java.util.Date;
import org.apache.avalon.Disposable;
import org.apache.avalon.Initializable;
import org.apache.phoenix.Block;
import org.apache.cornerstone.services.scheduler.TimeScheduler;
import org.apache.cornerstone.services.scheduler.TimeTrigger;
import org.apache.cornerstone.services.scheduler.Target;
import org.apache.cornerstone.services.scheduler.PeriodicTimeTrigger;

/**
 * This service provides a way to regularly schedule jobs.
 *
 * @author <a href="mailto:harmeet@apache.org">Harmeet Bedi</a>
 */
public class TimeSchedulerImpl 
    implements Block, TimeScheduler, Initializable, Disposable
{
    // name to TimerTask map.
    private Map taskMap;
    private Timer timer;

    public void init()
    {
        taskMap = Collections.synchronizedMap(new HashMap());
        timer = new Timer();
    }

    public void dispose()
    {
        taskMap = null;
        timer.cancel();
    }

    /** this method converts Avalon TimeTrigger mechanism to JDK Timer based 
     * mechanism. 
     * The Trigger is expected to be <PeriodicTimeTrigger>. 
     * Warning: This method may have cause a very small and in most cases 
     * inconsequential skew in the alarm mechanism. 
     */
    public void addTrigger(String name, TimeTrigger trigger, Target target ) {
        if ( ( trigger instanceof PeriodicTimeTrigger ) == false ) 
        {
            throw new RuntimeException
                ("Currently only the periodic timer is supported");
        }
        Task task = new Task(name,trigger,target);
        taskMap.put(name,task);
        PeriodicTimeTrigger ptm = (PeriodicTimeTrigger)trigger;
        long current = ptm.getStartTime();
        long period = ptm.getPeriod();
        if ( period <= 0 )
            timer.schedule(task,new Date(current));
        else 
        {
            long delay = current-System.currentTimeMillis();
            while ( delay < 0 )
                delay += period;
            timer.schedule(task,delay,period);
        }
    }
    public void removeTrigger( String name )
        throws NoSuchElementException 
    {
        Task task = (Task)taskMap.remove(name);
        task.cancel();
    }

    public void resetTrigger( String name ) throws NoSuchElementException 
    {
        Task task = (Task)taskMap.remove(name);
        if ( task == null )
            throw new NoSuchElementException(name);
        task.cancel();
        addTrigger(name,task.trigger,task.target);
    }

    private static class Task extends TimerTask 
    {
        final String name;
        final Target target;
        final TimeTrigger trigger;
        Task(String name,TimeTrigger trigger,Target target) {
            this.name = name;
            this.trigger = trigger;
            this.target = target;
        }
        public void run() {
            target.targetTriggered(name);
        }
    }  // class Task
}

