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

// Java classes
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

// Framework classes
import gov.va.med.fw.model.AbstractEntity;
import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.rule.RuleDataAware;
import gov.va.med.fw.util.StringUtils;
import gov.va.med.fw.service.trigger.TriggerEvent;
import gov.va.med.fw.service.trigger.TriggerIdentity;
import gov.va.med.fw.cache.TriggerEventCacheManager;

// ESR classes
import gov.va.med.fw.service.ServiceException;
import gov.va.med.esr.service.PersonService;
import gov.va.med.esr.common.model.lookup.AddressType;
import gov.va.med.esr.common.model.lookup.IncomeTestSource;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.messaging.SiteIdentity;
import gov.va.med.esr.common.model.party.Address;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.ee.IncomingMessageInfo;
import gov.va.med.esr.common.model.financials.IncomeTest;
import gov.va.med.esr.common.rule.data.BaseData;
import gov.va.med.esr.common.rule.data.FinancialInputData;
import gov.va.med.esr.common.rule.UpdateMessageEventInput;
import gov.va.med.esr.common.rule.data.EventInputData;
import gov.va.med.esr.common.rule.data.PersonSignatureInputData;
import gov.va.med.esr.service.trigger.FilterSitesPersonTriggerEvent;
import gov.va.med.esr.service.trigger.PartialPersonTriggerEvent;
import gov.va.med.esr.service.trigger.PersonTrigger;
import gov.va.med.esr.service.trigger.PersonTriggerEvent;
import gov.va.med.esr.service.trigger.IncomeYearTriggerEvent;
import gov.va.med.esr.service.trigger.PersonTriggerIdentity;
import gov.va.med.esr.service.trigger.TargetedPersonTriggerEvent;
import gov.va.med.esr.service.IVMFinancialInfo;

/**
 * This class is responsible for generating events to send various types of HL7 messages.
 *
 * @author Vu Le
 * @version 1.0
 */
public class MessageEventInputParameter extends AbstractTriggerAwareInputParameter
      implements UpdateMessageEventInput {

   private static final long serialVersionUID = 792324279259355480L;

   private Set updatedInsuranceEntityKeys = new HashSet();

   private Map updatedAssociationEntityKeysAndSites = new HashMap();

   private Map updatedAddressEntityKeysAndSites = new HashMap();

   private boolean sendingZ11 = false;

   private boolean z11Sent = false;

   private boolean z10Sent = false;

   private boolean fromAddNewPerson= false;

   private PersonService personService = null;

   private TriggerEventCacheManager triggerEventCacheManager;


   /**
    * @see gov.va.med.esr.service.trigger.FrequentPersonTriggers#notifyVistaForSpouseDependentsSSNValidationInfo()
    */
   public void notifyVistaForSpouseDependentsSSNValidationInfo() {
      // Trigger a ORUZ10 for the current income year
      IncomeYearTriggerEvent incomeYearTriggerEvent = new IncomeYearTriggerEvent(
            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
            PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.FINANCIAL);

      Person person = this.getResultPerson();
      IncomeTest incomeTest = getHelperService().getCurrentIncomeTest(person);
      if (incomeTest != null)
      {
            incomeYearTriggerEvent.setIncomeYear(incomeTest.getIncomeYear());
            addTriggerEvent(incomeYearTriggerEvent);
       }
   }

   /* (non-Javadoc)
    * @see gov.va.med.esr.common.rule.MessageEventInput#queryMSDSforMSdata()
    */
   public void queryMSDSforMSdata() {
	   logger.info("Adding the msds trigger event to the queue");
	   addTriggerEvent(new PersonTriggerEvent(PersonTrigger.DestinationType.MESSAGING,
			   PersonTrigger.TargetType.VISTA, PersonTrigger.DispatchType.QUERY,
			   PersonTrigger.DataType.ELIGIBILITY));
	   logger.info("Added the msds trigger event to the queue");

   }

   /**
    * @see gov.va.med.esr.service.trigger.FrequentPersonTriggers#queryVBAForEligibility()
    */
   public void queryVBAForEligibility() {
	   addTriggerEvent(new PersonTriggerEvent(PersonTrigger.DestinationType.MESSAGING,
			   PersonTrigger.TargetType.VBA, PersonTrigger.DispatchType.QUERY,
			   PersonTrigger.DataType.ELIGIBILITY));
   }

   /**
    * @see gov.va.med.esr.service.trigger.FrequentPersonTriggers#respondVistaForEligibility()
    */
   public void respondVistaForEligibility() {
      addTriggerEvent(new PersonTriggerEvent(PersonTrigger.DestinationType.MESSAGING,
            PersonTrigger.TargetType.VISTA, PersonTrigger.DispatchType.RESPOND,
            PersonTrigger.DataType.ELIGIBILITY));
   }

   /**
    * @see gov.va.med.esr.service.trigger.FrequentPersonTriggers#notifyVistaForEligibility()
    */
   public void notifyVistaForEligibility() {
      addTriggerEvent(new PersonTriggerEvent(PersonTrigger.DestinationType.MESSAGING,
            PersonTrigger.TargetType.VISTA, PersonTrigger.DispatchType.NOTIFY,
            PersonTrigger.DataType.ELIGIBILITY));
   }

   /**
    * @see gov.va.med.esr.service.trigger.FrequentPersonTriggers#queryVistaForFullData()
    */
   public void queryVistaForFullData() {
      addTriggerEvent(new PersonTriggerEvent(PersonTrigger.DestinationType.MESSAGING,
            PersonTrigger.TargetType.VISTA, PersonTrigger.DispatchType.QUERY,
            PersonTrigger.DataType.FULL_DATA));
   }

   /**
    * @see gov.va.med.esr.service.trigger.FrequentPersonTriggers#notifyVistaForInsurance()
    */
   public void notifyVistaForInsurance(Set updatedKeys) {

      addTriggerEvent(new PartialPersonTriggerEvent(
            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
            PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.INSURANCE,
            updatedKeys));
   }

   /**
    * Triggers an event to notify vista of Guardian info update.
    */
   public void notifyVistaForAssociation(Map updatedKeys) {
      addTriggerEvent(new FilterSitesPersonTriggerEvent(
            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
            PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.GUARDIAN_INFO,
            updatedKeys));
   }

   /**
    * @see gov.va.med.esr.service.trigger.FrequentPersonTriggers#notifyVistaForAddress()
    */
   public void notifyVistaForAddress(Map updatedKeys) {
	   FilterSitesPersonTriggerEvent evt = new FilterSitesPersonTriggerEvent(
	            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
	            PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.ADDRESS,
	            updatedKeys);

	   //CCR11402
	   boolean updateAddrToMVI = isUpdateAddressToMVI();
	   ((PersonTriggerIdentity)evt.getTriggerIdentity()).setIdMAddressPhoneUpdate(updateAddrToMVI);

	   //CCR 11758, for message, get the onFile person traits
	   if (updateAddrToMVI && !isFromUI())
		   evt.setOnFileIdentityTraits(this.getPristinePerson().getIdentityTraits()); //CCR12311

	   //CCR11673 in case if phone has been updated, then skip add another trigger event
	   boolean hasPersonTriggerEvent = false;
	   evt.setPersonId(this.getPersonId());
       evt.setIdentityTraits(this.getPersonIdentityTraits());
       evt.setEntityKeysAndSites(updatedKeys);

       if (triggerEventCacheManager.getTriggerEvents() != null)
       {
           for (Iterator iter = triggerEventCacheManager.getTriggerEvents().iterator(); iter.hasNext();)
           {
               TriggerEvent currentEvent = (TriggerEvent) iter.next();
               TriggerIdentity identity = currentEvent.getTriggerIdentity();

               if (identity instanceof PersonTriggerIdentity)
               {
              	 if(((PersonTriggerIdentity)identity).matchTriggerCriteria((PersonTriggerIdentity)evt.getTriggerIdentity()))
                   {
              		hasPersonTriggerEvent = true;
//              	CCR11696
              		if( !(((PersonTriggerIdentity)identity).isIdMAddressPhoneUpdate()) && ((PersonTriggerIdentity)evt.getTriggerIdentity()).isIdMAddressPhoneUpdate())
              			((PersonTriggerIdentity)identity).setIdMAddressPhoneUpdate(true);
                   }
               }
           }
         }
      if(!hasPersonTriggerEvent)
    	  addTriggerEvent(evt);
   }

   /**
    * @see gov.va.med.esr.common.rule.MessageEventInput#notifyVistaForAddress()
    */
   public void notifyVistaForAddress() {
	   PartialPersonTriggerEvent evt = new PartialPersonTriggerEvent(
	            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
	            PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.ADDRESS);
	   //CCR11402
	   boolean updateAddrToMVI = isUpdateAddressToMVI();
	   ((PersonTriggerIdentity)evt.getTriggerIdentity()).setIdMAddressPhoneUpdate(updateAddrToMVI);

	   //CCR 11758, for message, get the onFile person traits
	   if (updateAddrToMVI && !isFromUI())
		   evt.setOnFileIdentityTraits(this.getPristinePerson().getIdentityTraits()); //CCR12311

	   addTriggerEvent(evt);
   }

   /**
	* The Send Update Message Z10 of financial update. This method should be called to send financial data update
    * in Z10. When income test is deleted call the other method specifically written for the purpose.
    */
   public void notifyVistaForFinancial()
    {
       Integer incomeYear=this.getIncomingIncomeYear();
       if(incomeYear!=null)
       {
           IncomeYearTriggerEvent triggerEvent = IncomeYearTriggerEvent.notifyVistaForFinancial(incomeYear);
           notifyVistaForFinancial(triggerEvent);
       }
    }

   public void notifyVistaForIVMFinancial() {
	   FinancialInputData data = this.getFinancialInputData();
	   if (data != null && data.getIvmFinancialInfo() != null) {
		   IVMFinancialInfo info = data.getIvmFinancialInfo();

		   Integer incomeYear = info.getIncomeYear();

		   if(info.getIvmActionCode().equalsIgnoreCase("C")){
			   if(data.isMTConversion()){
				   IncomeYearTriggerEvent incomeYearTriggerEvent = new IncomeYearTriggerEvent(
				            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
				            PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.MEANS_TEST);
				   incomeYearTriggerEvent.setIncomeYear(incomeYear);
				   incomeYearTriggerEvent.setActionCode(info.getIvmActionCode());
				   incomeYearTriggerEvent.setType("MT");
				   addTriggerEvent(incomeYearTriggerEvent);
			   }
			   if(data.isRXConversion()){
				   IncomeYearTriggerEvent incomeYearTriggerEvent = new IncomeYearTriggerEvent(
				            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
				            PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.RX_TEST);
				   incomeYearTriggerEvent.setIncomeYear(incomeYear);
				   incomeYearTriggerEvent.setActionCode(info.getIvmActionCode());
				   incomeYearTriggerEvent.setType("RX");
				   addTriggerEvent(incomeYearTriggerEvent);
			   }
		   }
		   else if(info.getIvmActionCode().equalsIgnoreCase("R")){
			   IncomeTest deletedIncomeTest = this.getDeletedIncomeTest(incomeYear);
			   IncomeTest deletedIncomeTestCopy = (deletedIncomeTest != null) ? (IncomeTest)deletedIncomeTest.clone() : null;
		       if(deletedIncomeTestCopy != null) {
		           deletedIncomeTestCopy.setPerson(null);
		           deletedIncomeTestCopy.setGmtAddress(null);
		       }

			   if(data.isMTReversal()){
				   IncomeYearTriggerEvent incomeYearTriggerEvent = new IncomeYearTriggerEvent(
				            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
				            PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.MEANS_TEST);
				   incomeYearTriggerEvent.setIncomeYear(incomeYear);
				   incomeYearTriggerEvent.setDeletedIncomeTest(deletedIncomeTestCopy);
				   incomeYearTriggerEvent.setActionCode(info.getIvmActionCode());
				   incomeYearTriggerEvent.setType("MT");
				   addTriggerEvent(incomeYearTriggerEvent);
			   }
			   if(data.isRXReversal()){
				   IncomeYearTriggerEvent incomeYearTriggerEvent = new IncomeYearTriggerEvent(
				            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
				            PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.RX_TEST);
				   incomeYearTriggerEvent.setIncomeYear(incomeYear);
				   incomeYearTriggerEvent.setDeletedIncomeTest(deletedIncomeTestCopy);
				   incomeYearTriggerEvent.setActionCode(info.getIvmActionCode());
				   incomeYearTriggerEvent.setType("RX");
				   addTriggerEvent(incomeYearTriggerEvent);
			   }

			   // CCR 12425 Handle Z10 if needed for reversal scenario where clone was involved
			   Integer conversionIY = info.getIncomeYear();
			   Integer currentIYCloneScenarioReversal = info.getCurrentIncomeYear();
			   if(conversionIY != null && currentIYCloneScenarioReversal != null) {
				   // The currentIYAfterReversal is only non-null in clone scenario
				   // and is NOT set in the existing test scenario.
				   boolean send = currentIYCloneScenarioReversal.compareTo(conversionIY) < 0;
				   if (send) {
					   IncomeYearTriggerEvent triggerEvent = IncomeYearTriggerEvent.notifyVistaForFinancial(
							   currentIYCloneScenarioReversal);
					   notifyVistaForFinancial(triggerEvent);
				   }
			   }
		   }
	   }
   }

   /**
    * This method is used to send a specific year income test to Vista
    */
   public void notifyVistaForFinancialByIncomeYear(Integer incomeYear) {
       if(incomeYear!=null)
       {
           IncomeYearTriggerEvent triggerEvent = IncomeYearTriggerEvent.notifyVistaForFinancial(incomeYear);
           notifyVistaForFinancial(triggerEvent);
       }
   }

   /**
    * The Send Update Message Z10 of income test delete. This method should be called only triggering Z10 when the
    * income test is deleted. Saves the deleted income test obtained the pristine person which will be used while
    * building ZMT segments. This is neccessary because the income test is already deleted.
    */
   public void notifyVistaForIncomeTestDelete()
    {
       Integer incomeYear = null;
       FinancialInputData financialInputData = this.getFinancialInputData();
       // Check if in financials context
       if( financialInputData != null ) {
           incomeYear= financialInputData.getIncomingIncomeYear();
       }
       IncomeTest deletedIncomeTest = this.getDeletedIncomeTest(incomeYear);

       //Make a copy of the income test and set its parent(person) to null
       IncomeTest deletedIncomeTestCopy = (deletedIncomeTest != null) ? (IncomeTest)deletedIncomeTest.clone() : null;
       if(deletedIncomeTestCopy != null) {
           deletedIncomeTestCopy.setPerson(null);
           deletedIncomeTestCopy.setGmtAddress(null);
       }
       IncomeYearTriggerEvent triggerEvent = IncomeYearTriggerEvent.notifyVistaForFinancial(incomeYear,deletedIncomeTestCopy);
       notifyVistaForFinancial(triggerEvent);
    }

   /**
    * 6302[UC54.10.1]
    * The Send Update Message Z10 Use Case will not be executed if the Source of Test (MT or Rx Copay Test) is IVM.
    * This is called from different uses cases. Instead of duplicating across different rule flows, put the rule in here.
    */
   private void notifyVistaForFinancial(IncomeYearTriggerEvent triggerEvent)
    {
       IncomeTestSource incomeTestSource=getIncomeTestSource(triggerEvent.getIncomeYear());
       //Send out if income test source is null(no income test present) or non IVM source
       if(incomeTestSource == null
               || (!StringUtils.equals(IncomeTestSource.CODE_IVM.getCode(), incomeTestSource.getCode())))
       {
            addTriggerEvent(triggerEvent);
            BaseData bd = this.getBaseData();
            if (bd != null) {
                bd.setZ10Sent(true);
            }
       }
    }

   /**
    * Triggers a Z10 message for future dated test to the sending site.
    */
   public void notifySendingVistaForFutureDatedTest(VAFacility facility, String dfn)
   {
       IncomeYearTriggerEvent incomeYearTriggerEvent = new IncomeYearTriggerEvent(
               PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
               PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.FINANCIAL);

       Person person = this.getResultPerson();
       IncomeTest futureTest = getHelperService().getLatestIncomeTest(person);
       if (futureTest != null)
       {
           incomeYearTriggerEvent.setIncomeYear(futureTest.getIncomeYear());
           incomeYearTriggerEvent.setTargetSites(createTargetSites(facility, dfn));
           addTriggerEvent(incomeYearTriggerEvent);
       }
   }

   /**
    * @see gov.va.med.esr.service.trigger.FrequentPersonTriggers#respondVistaForFinancial()
    */
   public void respondVistaForFinancial() {
      /*
       * FIXME Commented out RP 10/20 to prevent triggering of ORUZ10s addTriggerEvent(new
       * PersonTriggerEvent( PersonTrigger.DestinationType.MESSAGING,
       * PersonTrigger.TargetType.VISTA, PersonTrigger.DispatchType.RESPOND,
       * PersonTrigger.DataType.FINANCIAL));
       */
   }

   /**
    * CCR 13856
    * send concurrent 1302 death update notification to MVI whenever
    * vista is notified according to existing business rules
    * This is part of phased rollout of MVI becoming authoritative source for death
    * These duplicate/concurrent notifications are temporary
    * Full rollout will disable these below vista notifications in the rule flow
    * Full rollout includes separate ilog rule to notify mvi only
    */
   private void notifyMVIforDateOfDeath() {
	   try {
		   this.getPersonService().updateProfileForDeathEvent(this.getResultPerson());

	   } catch (ServiceException ex) {
		   logger.error("Death Notification to MVI failed for person: " + this.getPersonId().getKeyValueAsString(), ex);
	   }

   }

   //RTC WI 348581, filter sites by parameter list during MVI authority rollout
   private ArrayList<VAFacility> getFilteredDeathSites() {
	   String[] siteList = null;

	   ArrayList<VAFacility> filterList = new ArrayList<VAFacility>();

	   try {
		   siteList = this.getSystemParameterService().getByName("VISTA_ROLLOUT_SITE_LIST").getValue().split(",");
	   } catch (Exception ex) {
		   logger.error("Unknown exception retrieving site list parameter during notifyVistaForDateofDeath:" + ex.getMessage());
		   return null;
	   }

	   if (siteList != null) {
		   if (!siteList[0].trim().equalsIgnoreCase("NONE")) {
			   for (int i = 0; i < siteList.length; i++) {
				   VAFacility facility = null;
				   try {
					    facility = (VAFacility)this.getLookupService().getByCode(VAFacility.class, siteList[i]);
				   } catch (Exception ex) {
					   logger.error("Unknown exception during lookup of VA Facility with station:" + siteList[i] + " during notifyVistaForDateofDeath:" + ex.getMessage());
				   }
				   if (facility != null) {
					   filterList.add(facility);
				   }
			   }
		   }
	   }

	   return filterList;
   }

   /**
    * Trigers a Z05 message event to notify a death record update.
    */
   public void notifyVistaForDateOfDeath() {
	   //CCR13856
	   notifyMVIforDateOfDeath();

	   /*addTriggerEvent(new PersonTriggerEvent(PersonTrigger.DestinationType.MESSAGING,
            PersonTrigger.TargetType.VISTA, PersonTrigger.DispatchType.NOTIFY,
            PersonTrigger.DataType.DATE_OF_DEATH));*/

	 //RTC WI 348581, filter sites by parameter list during MVI authority rollout
	   TargetedPersonTriggerEvent triggerEvent = new TargetedPersonTriggerEvent(
               PersonTrigger.DestinationType.MESSAGING,
               PersonTrigger.TargetType.VISTA,
               PersonTrigger.DispatchType.NOTIFY,
               PersonTrigger.DataType.DATE_OF_DEATH);

       Set sites = new HashSet();
       sites.addAll(getFilteredDeathSites());
       triggerEvent.setFilterSites(sites);

       addTriggerEvent(triggerEvent);


   }

   /**
    * Trigers a Z05 message event to notify a death record update.
    */
   @SuppressWarnings("unchecked")
public void notifyVistaForDateOfDeath(VAFacility sendingFacility)
    {
	   //CCR13856
	   notifyMVIforDateOfDeath();

	   TargetedPersonTriggerEvent triggerEvent = new TargetedPersonTriggerEvent(
                PersonTrigger.DestinationType.MESSAGING,
                PersonTrigger.TargetType.VISTA,
                PersonTrigger.DispatchType.NOTIFY,
                PersonTrigger.DataType.DATE_OF_DEATH);

	   //RTC WI 348581, filter sites by parameter list during MVI authority rollout
        Set sites = new HashSet();
        sites.add(sendingFacility);
        sites.addAll(getFilteredDeathSites());
        triggerEvent.setFilterSites(sites);

        addTriggerEvent(triggerEvent);
    }

   /**
    * @see gov.va.med.esr.common.rule.UpdateMessageEventInput#addUpdatedInsuranceEntityKeys(gov.va.med.fw.model.EntityKey)
    */
   public void addUpdatedInsuranceEntityKeys(EntityKey key) {
	   if (key != null)
		   this.updatedInsuranceEntityKeys.add(key);
   }

   /**
    * @see gov.va.med.esr.common.rule.UpdateMessageEventInput#addUpdatedInsuranceEntityKeys(gov.va.med.fw.model.AbstractEntity)
    */
   public void addUpdatedInsuranceEntityKeys(AbstractEntity entity) {
      this.updatedInsuranceEntityKeys.add(entity);
   }

   /**
    * Adds an entity key of the updated association.
    */
   public void addUpdatedAssociationEntityKeys(EntityKey key) {
       if(key != null)
           this.updatedAssociationEntityKeysAndSites.put(key, null);
   }

   /**
    * Adds an entity key of the updated association.
    */
   public void addUpdatedAssociationEntityKeys(AbstractEntity entity) {
      if(entity != null)
          this.updatedAssociationEntityKeysAndSites.put(entity, null);
   }

   /*
    * (non-Javadoc)
    *
    * @see gov.va.med.esr.common.rule.UpdateMessageEventInput#addUpdatedAssociationEntityKeys(gov.va.med.fw.model.EntityKey,
    *      gov.va.med.esr.common.model.lookup.VAFacility)
    */
    public void addUpdatedAssociationEntityKeys(AbstractEntity entity,
            VAFacility sendingFacility)
    {
        if (entity != null)
            this.updatedAssociationEntityKeysAndSites.put(entity, sendingFacility);
    }

    /**
     * @see gov.va.med.esr.common.rule.UpdateMessageEventInput#addDeferredAddressEntityKeys()
     */
    public void addDeferredAddressEntityKeys() {
    	Set addresses = this.getResultPerson().getAddresses();
    	for( Iterator i = addresses.iterator(); i.hasNext(); ) {
    		Address adr = (Address)i.next();
    		addUpdatedAddressEntityKeys(adr.getEntityKey());
    	}
    }

    /**
     * @see gov.va.med.esr.common.rule.UpdateMessageEventInput#addDeferredTempAddressEntityKeys()
     */
    //This method is shared by the case of new person from Z07 and the case with existing person with new temp address
    //Since all address change will trigger the same Z05 message, it is not necessary to create a new method for existing
    //person with new temp address
    public void addDeferredAddressEntityKeysForMessaging() {
    	Set addresses = this.getResultPerson().getAddresses();
    	for( Iterator i = addresses.iterator(); i.hasNext(); ) {
    		Address adr = (Address)i.next();
    		if(this.updatedAddressEntityKeysAndSites.isEmpty())
    		   addUpdatedAddressEntityKeys(adr.getEntityKey());
    	}
    }

	/**
	 * @see gov.va.med.esr.common.rule.UpdateMessageEventInput#addUpdatedAddressEntityKeys(gov.va.med.fw.model.EntityKey)
	 */
	public void addUpdatedAddressEntityKeys(EntityKey key) {
		if( key != null ) {
			this.updatedAddressEntityKeysAndSites.put(key, null);
		}
	}

	public void addUpdatedAddressEntityKeys( EntityKey key, VAFacility sendingFacility )
	{
	      if( key != null ) {
	          this.updatedAddressEntityKeysAndSites.put(key, sendingFacility);
	       }
	}
	//CCR 11402 -Provide Updates for Address and Telephone Number to MVI Service
	private boolean isUpdateAddressToMVI()
	{

		//CCR 11789: if from Add A Person or VOA, don't send MVI phone&addr update
		if (this.getBaseData().isFromAddNewPerson() ||
				( getSendingFacility() != null && VAFacility.CODE_MHV.getCode().equals(getSendingFacility().getCode())) ||
				this.getIncomingPerson().isEsrCorrelationAdded()) //CCR 12176 - don't trigger ph/adr MVI update for new person from Z07
			return false;

		//Z05 include other addresss type changes. MVI only needs Permanent Address change
		Address newAddr = this.getResultPerson().getPermanentAddress();
		Address oldAddr = this.getPristinePerson().getPermanentAddress();

		if (newAddr == null && oldAddr == null)
			return false; //both null, nothing to do

		if (newAddr != null && oldAddr != null)
		{
		    if (isEqual(newAddr.getLine1(), oldAddr.getLine1()) &&
		             isEqual(newAddr.getLine2(), oldAddr.getLine2()) &&
		             isEqual(newAddr.getLine3(), oldAddr.getLine3()) &&
		             isEqual(newAddr.getCity(), oldAddr.getCity()) &&
		             isEqual(newAddr.getState(), oldAddr.getState()) &&
		             isEqual(newAddr.getZipCode(), oldAddr.getZipCode()) &&
		             isEqual(newAddr.getCountry(), oldAddr.getCountry()) &&
		             isEqual(newAddr.getBadAddressReason(), oldAddr.getBadAddressReason()))
		    	return  false; //new address and old address are the same
		}

		//if reaches here, permanent address has changed, send update to MVI

		return true;
//		try
//		{
//			this.getPersonService().updateProfileForESRCorrelation(this.getResultPerson());
//		} catch (ServiceException se)
//		{
//	          this.logger.error("### Failed to update Phone And Address to MVI ###", se);
//		}

	}

    /**
     * Triggers a Z05 in response to a QRYZ11 back to the sending site.
     */
	public void notifyVistaForAddress(VAFacility facility, String dfn)
	{
	      Set sites = new HashSet();
	      TargetedPersonTriggerEvent targetedPersonTriggerEvent = new TargetedPersonTriggerEvent(
	            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
	            PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.ADDRESS);
	      sites.add(facility);
	      targetedPersonTriggerEvent.setTargetSites(createTargetSites(facility, dfn));

		   //CCR11402
		   boolean updateAddrToMVI = isUpdateAddressToMVI();
		   ((PersonTriggerIdentity)targetedPersonTriggerEvent.getTriggerIdentity()).setIdMAddressPhoneUpdate(updateAddrToMVI);

		   //CCR 11758, for message, get the onFile person traits
		   if (updateAddrToMVI && !isFromUI())
			   targetedPersonTriggerEvent.setOnFileIdentityTraits(this.getPristinePerson().getIdentityTraits()); //CCR12311
	      addTriggerEvent(targetedPersonTriggerEvent);

	}


   /**
    * @see gov.va.med.esr.common.rule.UpdateMessageEventInput#updateMessageEvents()
    */
   public void updateMessageEvents() {

      // Copy keys from internal list to new lists to pass
      // to trigger event so we can quickly clear our internal
      // list. Sycnrhonize is use to ensure that our
      // internal id keys are not altered during this process

      // Trigger address update event
      if( !this.updatedAddressEntityKeysAndSites.isEmpty() ) {
         final Map updatedAddressKeysAndSites = new HashMap();
         updatedAddressKeysAndSites.putAll(this.updatedAddressEntityKeysAndSites);
         this.notifyVistaForAddress(updatedAddressKeysAndSites);
         this.updatedAddressEntityKeysAndSites.clear();
      }

      // Trigger an insurance update event
      if( !this.updatedInsuranceEntityKeys.isEmpty() ) {

         // TODO: this fix needs to be moved to ILOG
         if( this.isFromUI() ) {
            final HashSet updatedInsuranceKeys = new HashSet();
            updatedInsuranceKeys.addAll(this.updatedInsuranceEntityKeys);
            this.notifyVistaForInsurance(updatedInsuranceKeys);
         }
         this.updatedInsuranceEntityKeys.clear();
      }

      // Trigger an association update event
      if( !this.updatedAssociationEntityKeysAndSites.isEmpty() ) {
         RuleDataAware data = this.getRuleDataAware();
         if( data instanceof BaseData ) {
            final Map updatedAssociationKeysWithSites = new HashMap();
            updatedAssociationKeysWithSites.putAll(this.updatedAssociationEntityKeysAndSites);
            this.notifyVistaForAssociation(updatedAssociationKeysWithSites);
         }
         this.updatedAssociationEntityKeysAndSites.clear();
      }
   }

   /**
    * For use in Person Signature context
    * @deprecated All Z06 info is now sent in Z10s.
    * @see gov.va.med.esr.common.rule.MessageEventInput#notifyVistaForSignatureInformation()
    */
   public void notifyVistaForSignatureInformation() {
      Set sites = new HashSet();
      IncomeYearTriggerEvent incomeYearTriggerEvent = new IncomeYearTriggerEvent(
            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
            PersonTrigger.DispatchType.NOTIFY, PersonTrigger.DataType.MEANS_TEST);

      sites.add(this.getPrimaryFinancialAssessmentSite());
      incomeYearTriggerEvent.setIncomeYear(this.getIncomingIncomeYear());
      incomeYearTriggerEvent.setTargetSites(sites);
      addTriggerEvent(incomeYearTriggerEvent);
   }

   /**
    * @deprecated - A Z10 is now sent out to all sites.
    * @see gov.va.med.esr.common.rule.MessageEventInput##notifyVistaForFinancialExceptPrimary()
    */
   public void notifyVistaForFinancialExceptPrimary()
    {
	   Set sites = new HashSet();
        IncomeYearTriggerEvent triggerEvent = IncomeYearTriggerEvent
                .notifyVistaForFinancial(this.getIncomingIncomeYear());
        sites.add(this.getPrimaryFinancialAssessmentSite());
        triggerEvent.setFilterSites(sites);
        addTriggerEvent(triggerEvent);
        BaseData bd = this.getBaseData();
        if (bd != null) {
            bd.setZ10Sent(true);
        }
    }

   /**
    * @see gov.va.med.esr.common.rule.MessageEventInput#generateORFZ10(gov.va.med.fw.model.EntityKey,
    *      gov.va.med.esr.common.model.lookup.VAFacility, java.lang.Integer)
    */
   public void generateORFZ10(VAFacility facility, String dfn, Integer incomeYear) {
      IncomeYearTriggerEvent incomeYearTriggerEvent = new IncomeYearTriggerEvent(
            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
            PersonTrigger.DispatchType.RESPOND, PersonTrigger.DataType.FINANCIAL);

      incomeYearTriggerEvent.setIncomeYear(incomeYear);
      incomeYearTriggerEvent.setIncomingMessageInfo(this.getIncomingMessageInfo());
      incomeYearTriggerEvent.setTargetSites(createTargetSites(facility, dfn));
      addTriggerEvent(incomeYearTriggerEvent);
   }

   /**
    * @see gov.va.med.esr.common.rule.MessageEventInput#generateORFZ11(gov.va.med.fw.model.EntityKey,
    *      gov.va.med.esr.common.model.lookup.VAFacility)
    */
   public void generateORFZ11(VAFacility facility, String dfn) {
      TargetedPersonTriggerEvent targetedPersonTriggerEvent = new TargetedPersonTriggerEvent(
            PersonTrigger.DestinationType.MESSAGING, PersonTrigger.TargetType.VISTA,
            PersonTrigger.DispatchType.RESPOND, PersonTrigger.DataType.ELIGIBILITY);

      targetedPersonTriggerEvent.setTargetSites(createTargetSites(facility, dfn));
      targetedPersonTriggerEvent.setIncomingMessageInfo(this.getIncomingMessageInfo());
      addTriggerEvent(targetedPersonTriggerEvent);
   }

   /**
    * Create the target site data for the outbound ORFZ10/Z11 payload.
    *
    * @param facility
    * @param dfn
    * @return set of size 1
    */
   private Set createTargetSites(VAFacility facility, String dfn) {
      Set sites = new HashSet();
      if (dfn != null) {
          // CR6852: for QRY->ORFZ10/Z11, inbound facility and dfn is used directly,
          // so bypass PSIm lookup, and create a siteIdentity and pass that into
    	  // the outbound message

    	  // CR7165: do the same for the outbound Z05 that accompanies the ORFZ11
    	  SiteIdentity siteIdentity = new SiteIdentity();
    	  siteIdentity.setVaFacility(facility);
    	  siteIdentity.setDfn(dfn);
    	  sites.add(siteIdentity);
      } else {
    	  sites.add(facility);
      }
      return sites;
  }

   /**
    * @see gov.va.med.esr.common.rule.UpdateMessageEventInput#triggerMFNZEGMessage()
    */
   public void triggerMFNZEGMessage() {
      // TODO Auto-generated method stub

   }

   private IncomingMessageInfo getIncomingMessageInfo() {
      if( this.getRuleDataAware() instanceof EventInputData ) {
         return ( (EventInputData)this.getRuleDataAware() ).getIncomingMessageInfo();
      }
      return null;
   }

   private FinancialInputData getFinancialInputData() {
      if( this.getRuleDataAware() instanceof FinancialInputData ) {
         return (FinancialInputData)this.getRuleDataAware();
      }
      return null;
   }


   private Integer getIncomingIncomeYear() {

      // Use in person signature context
      if( this.getPersonSignatureInputData() != null ) {
         // Try getting from signature image
         Integer year = this.getPersonSignatureInputData().getSignatureImage() != null ? this
               .getPersonSignatureInputData().getSignatureImage().getIncomeYear()
               : null;
         if( year != null ) {
            return year;
         }
      }

      // Default is to get it from the incoming test
      IncomeTest primary = this.getOnFilePrimaryTest();
      return ( primary != null ) ? primary.getIncomeYear() : null;
   }

   public IncomeTest getOnFilePrimaryTest() {
       return this.getHelperService().getCurrentIncomeTest(this.getResultPerson());
    }

   private IncomeTest getDeletedIncomeTest(Integer incomeYear) {
       FinancialInputData financialInputData = this.getFinancialInputData();
       // Get the income test from pristine person.
       if( financialInputData != null && incomeYear != null) {
           Person person = (Person)financialInputData.getPristineData();
           return person.getIncomeTest(incomeYear);
       }
       return null;
    }

   private IncomeTestSource getIncomeTestSource(Integer incomeYesr)
   {
	   Person person = getIncomingPerson();

	   IncomeTest incomeTest = person.getIncomeTest(incomeYesr);

	   return incomeTest == null ? null : incomeTest.getSource();

   }

   private VAFacility getPrimaryFinancialAssessmentSite() {
      IncomeTest primary = this.getPrimaryTest();
      return ( primary != null ) ? primary.getSiteConductingTest() : null;
   }

   private PersonSignatureInputData getPersonSignatureInputData() {
      if( this.getRuleDataAware() instanceof PersonSignatureInputData ) {
         return (PersonSignatureInputData)this.getRuleDataAware();
      }
      return null;
   }

   /**
    * @see gov.va.med.esr.common.rule.UpdateMessageEventInput#isMFKZEGRecievedForCurrentEGT()
    */
   public boolean isMFKZEGRecievedForCurrentEGT() {
      // TODO Auto-generated method stub
      return false;
   }

   /**
    * @see gov.va.med.esr.common.rule.MessageEventInput#isZ10Sent()
    */
   public boolean isZ10Sent() {
		BaseData bd = this.getBaseData();
		return (bd != null) ? bd.isZ10Sent() : false;
	}


	//The following are enhancements to ESR 3.1 VOA, please see
    //SUC_[676] Send Update Message (Z11)

	public boolean isSendingZ11() {
		return sendingZ11;
	}

	public void setSendingZ11(boolean sendingZ11) {
		this.sendingZ11 = sendingZ11;
	}

	public boolean isZ11Sent() {
		return z11Sent;
	}

	public void setZ11Sent(boolean z11Sent) {
		this.z11Sent = z11Sent;
	}

	public void sendVOAFinalStatusToMHV() {
	      addTriggerEvent(new PersonTriggerEvent(PersonTrigger.DestinationType.MESSAGING,
	              PersonTrigger.TargetType.MHV, PersonTrigger.DispatchType.RESPOND,
	              PersonTrigger.DataType.VOA));
	}

//	Seeding
	public boolean getQueryMSDSIndicator(){
    	return this.getEventInputData().isTriggerMSDSBroker();

    }

	public boolean isValidQueryInfo() {

		if (passedNullValidation()) {
			if (invalidQuery()) {
				return false;
			} else
				return true;

		} else
			return false;

	}

	private boolean invalidQuery(){

		if (this.getIncomingPerson().getIdentityTraits().getLegalName()
				.getGivenName()== null
				|| this.getIncomingPerson().getIdentityTraits().getLegalName()
						.getGivenName().length() == 0
				|| this.getIncomingPerson().getIdentityTraits().getLegalName()
						.getGivenName().trim().length() == 0

				|| this.getIncomingPerson().getIdentityTraits().getSsnText()== null
				|| this.getIncomingPerson().getIdentityTraits().getSsnText()
						.length() == 0
				|| this.getIncomingPerson().getIdentityTraits().getSsnText()
						.trim().length() == 0

				|| this.getIncomingPerson().getIdentityTraits()
						.getLegalName().getFamilyName()== null
				|| this.getIncomingPerson().getIdentityTraits()
						.getLegalName().getFamilyName().length()== 0
				|| this.getIncomingPerson().getIdentityTraits()
						.getLegalName().getFamilyName().trim().length()== 0
				/*CCR13500, allow null gender to be sent
				|| this.getIncomingPerson().getIdentityTraits().getGender()
						.getCode() == null
				|| this.getIncomingPerson().getIdentityTraits().getGender()
						.getCode().length()== 0
						|| this.getIncomingPerson().getIdentityTraits().getGender()
						.getCode().trim().length()== 0*/

				|| this.getIncomingPerson().getIdentityTraits()
						.getBirthRecord().getBirthDate() == null){
			return true;

		}
		else
			return false;

	}

	public boolean passedNullValidation() {
		if (this.getIncomingPerson().getIdentityTraits() != null
				&& this.getIncomingPerson().getIdentityTraits().getLegalName() != null
				//CCR13500, allow null gender to be sent
				//&& this.getIncomingPerson().getGender() != null
				&& this.getIncomingPerson().getBirthRecord() != null) {
			return true;

		} else
			return false;

	}

	public PersonService getPersonService() {
		return personService;
	}

	public void setPersonService(PersonService personService) {
		this.personService = personService;
	}/**
	 * @return Returns the triggerEventCacheManager.
	 */
	public TriggerEventCacheManager getTriggerEventCacheManager() {
		return triggerEventCacheManager;
	}

	/**
	 * @param triggerEventCacheManager The triggerEventCacheManager to set.
	 */
	public void setTriggerEventCacheManager(
			TriggerEventCacheManager triggerEventCacheManager) {
		this.triggerEventCacheManager = triggerEventCacheManager;
	}

	private boolean isFromUI()
	{
		 RuleDataAware data = this.getRuleDataAware();
         if( data instanceof BaseData && ( (BaseData)data ).isDataFromGUI())
        	 return true;

         return false;
	}
//CCR11898
	public boolean isTempAddressUpdateRecent() {
		return this.getEventInputData().getVerificationInfo().isTempAddressUpdateMoreRecent();
	}

}