/**
 * TimerManager.java
 * Created Jan 18, 2007
 * Copyright (C) Tandberg Television 2007
 */
package com.tandbergtv.workflow.driver.timer;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;
import org.jbpm.graph.exe.Token;

import com.tandbergtv.workflow.core.service.thread.ISchedulerService;
import com.tandbergtv.workflow.core.service.thread.Scheduler;

/**
 * Default timer manager
 * 
 * @author Sahil Verma
 */
public class TimerManager implements ITimerService {

	private ISchedulerService<Void> scheduler;
	
	private Map<Token, Future<Void>> futures;
	
	private static final String SERVICE_NAME = "token-timers";
	
	private static final Logger logger = Logger.getLogger(TimerManager.class);
	
	/**
	 * This lock must be taken around all operations performed on the timer list 
	 */
	private static final Lock timerlock = new ReentrantLock();
	
	/**
	 * Creates a TimerManager
	 */
	public TimerManager() {
		super();
		this.scheduler = new Scheduler<Void>(SERVICE_NAME, 1, 1);
		this.futures = new HashMap<Token, Future<Void>>();
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.ITimerService#addTimer(com.tandbergtv.workflow.driver.TokenTimer)
	 */
	public void addTimer(TokenTimer timer) {
		long delay = timer.getTimer().getDueDate().getTime() - System.currentTimeMillis();
		
		timerlock.lock();
		
		try {
			Future<Void> future = this.scheduler.schedule(timer, delay);
			this.futures.put(timer.getToken(), future);
		} finally {
			timerlock.unlock();
		}
		
		logger.info(timer.getToken() + " added timer, total " + this.scheduler.count());
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.ITimerService#deleteTimer(org.jbpm.graph.exe.Token)
	 */
	public void deleteTimer(Token token) {
		timerlock.lock();
		
		try {
			Future<Void> future = this.futures.get(token);
			
			if (future != null) {
				future.cancel(true);
				this.futures.remove(token);
				/* 
				 * Remember that cancel() only deactivates the future, we have to make sure that the 
				 * future is removed from the executor
				 */
				this.scheduler.purge();
			}
		} finally {
			timerlock.unlock();
		}
		
		logger.info(token + " deleted timer, total " + this.scheduler.count());
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.IDriverService#start()
	 */
	public void start() {
		this.scheduler.start();
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.driver.service.IDriverService#stop()
	 */
	public void stop() {
		this.scheduler.stop();
	}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.core.service.Service#getServiceName()
	 */
	public String getServiceName() {
		return SERVICE_NAME;
	}
}
