// Created Apr 27, 2005 4:53:42 PM

/********************************************************************
 * Copyright © 2005 VHA. All rights reserved
 ********************************************************************/

package gov.va.med.fw.service.trigger;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.InvalidConfigurationException;

/**
 * Default implementation of TriggerRouter that maps TriggerEvent's to
 * TriggerableService's. Applications can use this default implementation by
 * injecting the desired triggerableServices property.
 * 
 * @author DNS   BOHMEG
 */
public class TriggerRouterImpl implements TriggerRouter {
	/**
	 * Configuration injected as such: Map<TriggerIdentity.DestinationType, TriggerableService>
	 */
	private Map triggerableServices;
	
	private TriggerableService defaultTriggerableService;

	protected Log logger = LogFactory.getLog(getClass());

	/** Must be implemented to determine how to handle a TriggerAware containing multiple TriggerEvent's.
	 * 		@return True if all TriggerEvent's were properly routed.  False if a repeat TriggerEvent was encountered.
	 *  */	
	public boolean processTriggerAware(TriggerAware triggerAware)
			throws ServiceException {
		boolean allRouted = false;
		if (triggerAware != null) {
			/* while we are iterating and clearing, synchronize this TriggerAware just
			 in case it is used across more than one thread */
			synchronized(triggerAware) {
				allRouted = true;
				Set triggerEvents = triggerAware.getTriggerEvents();
				Iterator itr = (triggerEvents != null) ? triggerEvents.iterator()
						: null;
				while (itr != null && itr.hasNext()) {
					if(!processTriggerEvent((TriggerEvent) itr.next()))
						allRouted = false;
				}
				triggerAware.clearTriggerEvents();
			}
		} else {
			logger.warn("Null TriggerAware object, ignoring");
		}
		return allRouted;
	}

	/**
	 * @return Returns the triggerableServices.
	 */
	public Map getTriggerableServices() {
		return triggerableServices;
	}

	/**
	 * @param triggerableServices
	 *            The triggerableServices to set.
	 */
	public void setTriggerableServices(Map triggerableServices) {
		this.triggerableServices = triggerableServices;
	}

	public void afterPropertiesSet() {
		if (triggerableServices == null)
			throw new InvalidConfigurationException(
					"Map of TriggerableService's must be set on "
							+ getClass().getName());
	}

	/** Must be implemented to determine how to handle a single TriggerEvent.
	 * 	@return True if the TriggerEvent was properly routed.
	 * */
	public boolean processTriggerEvent(TriggerEvent triggerEvent)
			throws ServiceException {		
		TriggerableService service = getTriggerableService(triggerEvent);
		if (service == null) {
			throw new InvalidConfigurationException(triggerEvent
					+ " not mapped to a TriggerableService");
		}
		// allow specialized behavior to alter TriggerEvent for servicing
		triggerEvent.preTriggerInit();
		if (logger.isDebugEnabled()) {
			logger.debug("Executing " + service.getClass().getName()
					+ " for TriggerEvent" + triggerEvent);
		}		
		service.trigger(triggerEvent);
		return true;
	}
	
	/** Default implementation - can be overridden by subclasses */
	protected TriggerableService getTriggerableService(TriggerEvent triggerEvent) {
		TriggerableService service = (TriggerableService) triggerableServices.get(triggerEvent.getTriggerIdentity().getDestinationType());
		if(service == null)
			service = defaultTriggerableService;
		
		if(service == null)
			throw new InvalidConfigurationException("Unable to route TriggerEvent, there is no TriggerableService defined for triggerEvent: " + triggerEvent);
		
		return service;
	}

	/**
	 * @return the defaultTriggerableService
	 */
	public TriggerableService getDefaultTriggerableService() {
		return defaultTriggerableService;
	}

	/**
	 * @param defaultTriggerableService the defaultTriggerableService to set
	 */
	public void setDefaultTriggerableService(
			TriggerableService defaultTriggerableService) {
		this.defaultTriggerableService = defaultTriggerableService;
	}
}
