package gov.va.med.fw.scheduling;

import java.util.Date;

import org.quartz.Calendar;
import org.quartz.CronTrigger;
import org.springframework.core.Constants;
import org.springframework.scheduling.quartz.CronTriggerBean;

import gov.va.med.fw.util.DateUtils;

/**
 * Allows for customization of Quartz CronTrigger component.
 * 
 * <p>Typical override may be for Misfire instructions.
 * 
 * @author DNS   BOHMEG
 *
 */
public class CustomCronTriggerBean extends CronTriggerBean {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1766902112808867367L;
	
	private int MISFIRE_INSTRUCTION_SMART_POLICY_WITHIN_HOUR = 2001;
	private int MISFIRE_INSTRUCTION_SMART_POLICY_WITHIN_HALFTIME_OF_NEXT_FIRE = 2002;
	
	private int defaultMisfireInstuctionSmartPolicy = MISFIRE_INSTRUCTION_SMART_POLICY_WITHIN_HOUR;
    
	
	/*
	 * Rdefines "smart" policy
	 * 
	 * @see org.quartz.CronTrigger#updateAfterMisfire(org.quartz.Calendar)
	 */
	public void updateAfterMisfire(Calendar cal) {
		int instr = getMisfireInstruction();
		
		if (instr == CronTrigger.MISFIRE_INSTRUCTION_SMART_POLICY) {
            instr = CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING;
			Date now = new Date();
			Date fireTimeThatMisfired = super.getNextFireTime(); // should be in past
			
			if(defaultMisfireInstuctionSmartPolicy == MISFIRE_INSTRUCTION_SMART_POLICY_WITHIN_HALFTIME_OF_NEXT_FIRE) {
				Date nextFireTime = super.getFireTimeAfter(fireTimeThatMisfired);
				long spanTime = nextFireTime.getTime()
						- fireTimeThatMisfired.getTime();
				if ((spanTime / 2.0) > (now.getTime() - fireTimeThatMisfired.getTime())) {
                    instr = CronTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
				}
			} else if(defaultMisfireInstuctionSmartPolicy == MISFIRE_INSTRUCTION_SMART_POLICY_WITHIN_HOUR) {
				if(now.getTime() - fireTimeThatMisfired.getTime() < DateUtils.MILLIS_PER_HOUR) {
                    instr = CronTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
                    
				}
			}
		} 
		
		// code below copied from CronTrigger.updateAfterMisfire(Calendar) method
		if (instr == CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING) {
			Date newFireTime;
			for (newFireTime = getFireTimeAfter(new Date()); newFireTime != null
					&& cal != null
					&& !cal.isTimeIncluded(newFireTime.getTime()); newFireTime = getFireTimeAfter(newFireTime))
				;
			setNextFireTime(newFireTime);			
		} else if (instr == CronTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) {
			setNextFireTime(new Date());
		}
	}

	public int getDefaultMisfireInstuctionSmartPolicy() {
		return defaultMisfireInstuctionSmartPolicy;
	}

	public void setDefaultMisfireInstuctionSmartPolicy(
			int defaultMisfireInstuctionSmartPolicy) {
		this.defaultMisfireInstuctionSmartPolicy = defaultMisfireInstuctionSmartPolicy;
	}

}
