package com.tandbergtv.workflow.driver.timer;

import java.util.Date;
import java.util.ResourceBundle;

import org.apache.log4j.Logger;
import org.jbpm.graph.def.GraphElement;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.Token;
import org.jbpm.scheduler.exe.Timer;

import com.tandbergtv.workflow.core.DurationUtility;
import com.tandbergtv.workflow.core.WFSInterpreter;
import com.tandbergtv.workflow.core.service.ServiceRegistry;

/**
 * An extension of Jbpm's CreateTimerAction which uses a certain format
 * (hh:mm:ss:SS) to specify duration and the WFSInterpreter to evaluate
 * expressions to that format.
 * 
 * @author Imran Naqvi
 */
public class CreateTimerAction extends org.jbpm.scheduler.def.CreateTimerAction {

	private Long MIN_TIMEOUT;
	
	/**
	 * 
	 */
	private static final long serialVersionUID = -5891512481051475508L;
	
	private static final String BUNDLE_NAME = CreateTimerAction.class.getPackage().getName() + ".timer";
	
	private static final Logger logger = Logger.getLogger(CreateTimerAction.class);

	/**
	 * Creates a {@link CreateTimerAction}
	 */
	public CreateTimerAction() {
		super();
		
		try {
			String value = ResourceBundle.getBundle(BUNDLE_NAME).getString("process.timeout");
			MIN_TIMEOUT = Long.parseLong(value) * 1000L;
		} catch (Exception e) {
			MIN_TIMEOUT = 120 * 1000L;
		}
	}

	/* (non-Javadoc)
	 * @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext)
	 */
	public void execute(ExecutionContext context) throws Exception {
		/* Create and save the new timer */
		Timer timer = createTimer(context);
		ServiceRegistry.getDefault().lookup(ITimerService.class).addTimer(new TokenTimer(timer));
	}

	/* (non-Javadoc)
	 * @see org.jbpm.scheduler.def.CreateTimerAction#createTimer(org.jbpm.graph.exe.ExecutionContext)
	 * @see org.jbpm.calendar.Duration
	 */
	protected Timer createTimer(ExecutionContext context) {
		Token token = context.getToken();
		Timer timer = new Timer(token);
		
		timer.setName(getTimerName());
		timer.setRepeat(getRepeat());
		
		long dueDateinMillisecs = getDurationInMillis(getDueDate(), context);
		Date timerDueDate = new Date(new Date().getTime() + dueDateinMillisecs);

		logger.info(token + ", timer due date: " + timerDueDate + ", duration: " + dueDateinMillisecs / 1000L + " sec");

		timer.setDueDate(timerDueDate);

		timer.setAction(getTimerAction());
		timer.setTransitionName(getTransitionName());
		timer.setGraphElement(context.getEventSource());
		timer.setTaskInstance(context.getTaskInstance());

		if ((getEvent() != null) && (getEvent().getGraphElement() != null)) {
			GraphElement graphElement = getEvent().getGraphElement();
			try {
				context.setTimer(timer);
				graphElement.fireEvent("timer-create", context);
			} finally {
				context.setTimer(null);
			}
		}

		return timer;
	}

	private long getDurationInMillis(String duration, ExecutionContext context) {
		long time = 0L;

		try {
			time = new DurationUtility().getDurationInMillis(duration, new WFSInterpreter(context));
		} catch (Exception e) {
			/* Input was a bogus duration - don't abort, just return zero msec */
			logger.warn(context.getToken() + " could not parse " + duration + " as a valid date.");
		}
		
		/* Clamp the resultant date */
		return (time < MIN_TIMEOUT) ? MIN_TIMEOUT : time;
	}
}
