/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
// Package
package gov.va.med.esr.common.rule.parameter;

// Java classes
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.springframework.aop.AfterReturningAdvice;

import gov.va.med.fw.cache.TriggerEventCacheManager;
import gov.va.med.fw.rule.RuleParameters;
import gov.va.med.fw.service.AbstractComponent;
import gov.va.med.fw.service.ServiceConfigurationException;
import gov.va.med.fw.service.trigger.TriggerAware;
import gov.va.med.fw.service.trigger.TriggerEvent;

import gov.va.med.esr.common.rule.UpdateMessageEventInput;
import gov.va.med.esr.service.trigger.PersonTrigger;
import gov.va.med.esr.service.trigger.PersonTriggerIdentity;
import gov.va.med.esr.service.trigger.ProcessTriggerEvent;

/**
 * Intercept to handle trigger aware component to trigger a event
 *
 * Project: Common</br>
 * Created on: 9:20:15 PM </br>
 *
 * @author DNS   LEV
 */
public class TriggerAwareParameterAdvice extends AbstractComponent implements AfterReturningAdvice {

	/**
	 * An instance of cache manager
	 */
	private TriggerEventCacheManager cacheManager;
	
	/**
	 * A default constructor
	 */
	public TriggerAwareParameterAdvice() {
		super();
	}

	/**
	 * @see org.springframework.aop.AfterReturningAdvice#afterReturning(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], java.lang.Object)
	 */
	public void afterReturning(Object returnVal, Method method, Object[] args, Object target) throws Throwable {
		
		// A trigger aware of type UpdateMessageEventInput is a singleton
		// that contains a collection of updated entity keys that are needed 
		// to create trigger events.  For instance, insurance and address update events
		TriggerAware triggerAware = null;
		if( args != null ) {
			for( int index=0; index<args.length; index++ ) {
				if( args[index] instanceof RuleParameters ) {
					RuleParameters ruleParams = (RuleParameters)args[index];
					Collection params = ruleParams.getRuleParameters().values();
					for( Iterator i=params.iterator(); i.hasNext(); ) {
						Object trigger = i.next();
						if( trigger instanceof TriggerAware ) {
							triggerAware = (TriggerAware) trigger;
							synchronized(triggerAware) {
								// A trigger is of type UpdateMessageEventInput,
								// process special events for insurance and addresses
								if( triggerAware instanceof UpdateMessageEventInput ) {
									((UpdateMessageEventInput)triggerAware).updateMessageEvents();
								}							
								cacheTriggerEvents( triggerAware.getTriggerEvents() );
								triggerAware.clearTriggerEvents();								
							}
						}
					}
				}
			}
		}
	}

	/**
	 * @see gov.va.med.fw.service.AbstractComponent#afterPropertiesSet()
	 */
	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();
		if( this.cacheManager == null ) {
			throw new ServiceConfigurationException( "Missing required components");
		}
	}

	/**
	 * @return Returns the cacheManager.
	 */
	public TriggerEventCacheManager getCacheManager() {
		return cacheManager;
	}

	/**
	 * @param cacheManager The cacheManager to set.
	 */
	public void setCacheManager(TriggerEventCacheManager cacheManager) {
		this.cacheManager = cacheManager;
	} 
	
	
	/**
	 * Store trigger events in cache for a particular TriggerIdentity only once.
	 * For special cases like notifying Vista of Insurance updates, there could be multiple
	 * trigger events based on the entity type of the Insurance that got changes. In that case
	 * allow caching of multiple trigger events with same TriggerIdentity.
	 *  
	 * @param triggerEvents
	 */
	private void cacheTriggerEvents(Set triggerEvents)
    {
        Set triggerIdentities = new HashSet();
        Set cacheableTriggerEvents = new HashSet();
        
        if(triggerEvents == null || triggerEvents.isEmpty())
            return;
        
        if (cacheManager.getTriggerEvents() != null)
        {
            for (Iterator iter = cacheManager.getTriggerEvents().iterator(); iter.hasNext();)
            {
                TriggerEvent triggerEvent = (TriggerEvent) iter.next();
                triggerIdentities.add(triggerEvent.getTriggerIdentity());
            }
        }
        
        if (triggerEvents != null)
        {
            for (Iterator iter = triggerEvents.iterator(); iter.hasNext();)
            {
                TriggerEvent triggerEvent = (TriggerEvent) iter.next();
                
                if (triggerIdentities.contains(triggerEvent
                        .getTriggerIdentity()))
                {
                    if (triggerEvent.getTriggerIdentity() instanceof PersonTriggerIdentity)
                    {
                        PersonTriggerIdentity triggerIdentity = (PersonTriggerIdentity) triggerEvent
                                .getTriggerIdentity();
                        if (triggerIdentity.getApplicableDataTypes().contains(
                                PersonTrigger.DataType.INSURANCE))
                        {
                            cacheableTriggerEvents.add(triggerEvent);
                        }
                    }
                    else {
                    	// Allow dups for special triggers that don't have unique identities
                    	if (ProcessTriggerEvent.CREATE_HEC_RECORD.equals(triggerEvent.getName())) {
                    		cacheableTriggerEvents.add(triggerEvent);
                    	}                    	
                    }
                } else
                {
                    triggerIdentities.add(triggerEvent.getTriggerIdentity());
                    cacheableTriggerEvents.add((triggerEvent));
                }
            }
        }

        if (!cacheableTriggerEvents.isEmpty())
            cacheManager.storeTriggerEvents(cacheableTriggerEvents);

    }		
}