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

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

// Library classes
import org.apache.commons.lang.Validate;

// Framework classes
import gov.va.med.fw.cache.EntityCacheManager;
import gov.va.med.fw.cache.TriggerEventCacheManager;
import gov.va.med.fw.rule.RuleParameters;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.trigger.TriggerEvent;
import gov.va.med.fw.service.trigger.TriggerIdentity;
import gov.va.med.fw.service.trigger.TriggerRouter;
import gov.va.med.fw.util.StringUtils;

// ESR classes
import gov.va.med.esr.common.model.ee.VerificationInfo;
import gov.va.med.esr.common.model.insurance.InsurancePolicy;
import gov.va.med.esr.common.model.lookup.Country;
import gov.va.med.esr.common.model.lookup.County;
import gov.va.med.esr.common.model.lookup.ZipCode;
import gov.va.med.esr.common.model.lookup.AddressType;
import gov.va.med.esr.common.model.lookup.PhoneType;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.party.Address;
import gov.va.med.esr.common.model.party.Email;
import gov.va.med.esr.common.model.party.Phone;
import gov.va.med.esr.common.model.person.Association;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.rule.data.AddressInputData;
import gov.va.med.esr.common.rule.service.ContactInfoRuleService;
import gov.va.med.esr.service.trigger.FilterSitesPersonTriggerEvent;
import gov.va.med.esr.service.trigger.PersonTrigger;
import gov.va.med.esr.service.trigger.PersonTriggerIdentity;
import gov.va.med.esr.service.LookupService;

/**
 * Project: Common
 *
 * @author DNS   LEV
 * @version 1.0
 */
public class ContactInfoRuleServiceImpl extends AbstractRuleValidationServiceAwareImpl
      implements ContactInfoRuleService {

   private static final long serialVersionUID = 7538907746205849651L;

   /**
    * An instance of an address rule parameters
    */
   private String addressRuleParameters = null;

   /**
    * An instance of a contact info rule parameters
    */
   private String contactInfoRuleParameters = null;

   /**
    * An instance of entityCacheManager
    */
   private EntityCacheManager cacheManager;

   private TriggerRouter triggerRouter = null;

   private TriggerEventCacheManager triggerEventCacheManager;

   private String handBookEventRuleParameters = null;

  private LookupService lookupService = null;

  public LookupService getLookupService() {
	   return lookupService;
  }

  public void setLookupService(LookupService lookupService) {
	   this.lookupService = lookupService;
  }

   /**
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#processAddresses(
    *      java.util.Set, gov.va.med.esr.common.model.person.Person)
    */
   public void processAddresses(Set incoming, Person onFile, VAFacility sendingFacility) throws ServiceException {

      // Validate input parameters
      Validate.notNull(incoming, "An incoming set of addresses must not be null");

      for( Iterator i = incoming.iterator(); i.hasNext(); ) {
         Object obj = i.next();
         if( obj instanceof Address ) {

        	 if (logger.isDebugEnabled()) {
                 logger.debug("ContactInfoRuleService processAddresses : incoming" + incoming + " onfile: " + onFile);
       			}

             this.doProcessAddress((Address)obj, onFile, this.getRuleParameters( getAddressRuleParameters()), sendingFacility, false,null ) ;
         }
      }
   }

 //CCR11898 Added VerificationInfo
   public void processAddresses(Set incoming, Person onFile, VAFacility sendingFacility, VerificationInfo veriInfo) throws ServiceException {

      // Validate input parameters
      Validate.notNull(incoming, "An incoming set of addresses must not be null");

      for( Iterator i = incoming.iterator(); i.hasNext(); ) {
         Object obj = i.next();
         if( obj instanceof Address ) {
        	 if (logger.isDebugEnabled()) {
                 logger.debug("ContactInfoRuleService processAddresses : incoming" + incoming + " onfile: " + onFile);
       			}
             this.doProcessAddress((Address)obj, onFile, this.getRuleParameters( getAddressRuleParameters()), sendingFacility, false,veriInfo ) ;
         }
      }
   }

   /**
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#manageElectronicAddresses(java.util.Set, gov.va.med.esr.common.model.person.Person)
    */
   public void manageElectronicAddresses(Set emails, Person onFile)
       throws ServiceException {
       this.processElectronicAddresses(emails,onFile,null,UI);
   }

   /**
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#processElectronicAddresses(java.util.Set, gov.va.med.esr.common.model.person.Person)
    */
   public void processElectronicAddresses(Set emails, Person onFile, VAFacility sendingFacility)
         throws ServiceException {
       this.processElectronicAddresses(emails,onFile,sendingFacility, null);
   }

   /**
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#processElectronicAddresses(
    *      java.util.Set, gov.va.med.esr.common.model.person.Person)
    */
   private void processElectronicAddresses(Set emails, Person onFile,VAFacility sendingFacility, String caller)
         throws ServiceException {

      // Validate input parameters. Allowed to be empty.
      Validate.notNull(emails, "An incoming set of electronic addresses is null");

      boolean shouldNotifyVista = false;
      Map updatedKeysAndSitesMap = new HashMap();

      //Person pristinePerson = (Person)cacheManager.getItem(onFile.getEntityKey());

      //change to this so that processVOA() will not get null pristinePerson that causes null pointer exception
      Person pristinePerson = getPristinePerson(onFile);

      Iterator iter = emails.iterator();
      if( emails.isEmpty() ) {
    	  onFile.removeAllEmails();
    	  //CCR11878 - Notify VistA if the email is deleted
          if(pristinePerson.getEmails() != null && !pristinePerson.getEmails().isEmpty()){
	     	 shouldNotifyVista = true;
	     	 if(updatedKeysAndSitesMap.isEmpty())
	              updatedKeysAndSitesMap.put(pristinePerson, sendingFacility);
	       }
      }

      else {
         // Establish a working set
         Set working = new HashSet();
         while( iter.hasNext() ) {
            Email incoming = (Email)iter.next();
            Email found = (Email)findMatchingElement(incoming, onFile.getEmails());
            if( found != null ) {
               // Remove so don't find same one twice
               onFile.removeEmail(found);
            }
            else {
               // Not found so create a new one
               found = new Email();
            }

            //Since there is only one rule to check for triggering ORUZ05s for Phones, it is an overkill to
            //create a separate rule flow. So the Z05 triggering is done in this call
            if (!StringUtils.equals(incoming.getAddress(),found.getAddress()))
            {
                //For UI only if the phone number is changed, set transaction date as the change date
                if(StringUtils.equals(UI,caller)) {
	                incoming.setChangeDate(this.getTransactionDate());
	                shouldNotifyVista = true;
	                //Only need to send one message even if more than one phone changed.If there is already a key
	                //for a previous phone number change,no need to add another
	                if(updatedKeysAndSitesMap.isEmpty())
	                    updatedKeysAndSitesMap.put(found, null);

	            }else
	            {
	                if((incoming.getChangeDate() != null)  &&
	                        ( found.getChangeDate() == null || (incoming.getChangeDate().after(found.getChangeDate()))))
	                {
	                    shouldNotifyVista = true;
		                if(updatedKeysAndSitesMap.isEmpty())
		                    updatedKeysAndSitesMap.put(found, sendingFacility);
	                }
	            }
            }
            this.getMergeRuleService().mergeEmail(incoming, found);
            // Add to working set
            working.add(found);
         }
         onFile.removeAllEmails();
         Iterator iterWorking = working.iterator();
         // Add the updated and new emails to person
         while( iterWorking.hasNext() ) {
            Email updated = (Email)iterWorking.next();
            onFile.addEmail(updated);
         }
      }

      if(shouldNotifyVista)
      {
	      notifyVistaForContactInfo(onFile, updatedKeysAndSitesMap);
      }

   }

   /**
    * Notifies Vista of a Phone or Email change. If a Address change trigger event aleady exists, do not add a new one.
    *
    * @param onFile
    * @param updatedKeysAndSitesMap
    * @throws ServiceException
    */
    private FilterSitesPersonTriggerEvent notifyVistaForContactInfo(Person person, Map updatedKeysAndSitesMap)
    {
        FilterSitesPersonTriggerEvent triggerEvent = new FilterSitesPersonTriggerEvent(
                PersonTrigger.DestinationType.MESSAGING,
                PersonTrigger.TargetType.VISTA,
                PersonTrigger.DispatchType.NOTIFY,
                PersonTrigger.DataType.ADDRESS, updatedKeysAndSitesMap);

        triggerEvent.setPersonId(person.getPersonEntityKey());
        triggerEvent.setIdentityTraits(person.getIdentityTraits());
        triggerEvent.setEntityKeysAndSites(updatedKeysAndSitesMap);

        boolean hasAddressTriggerEvent = false;
        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)triggerEvent.getTriggerIdentity()))
                    {
                        hasAddressTriggerEvent = true;
                        break;
                    }
                }
            }
          }

        if (!hasAddressTriggerEvent)
        {
            // Create the trigger event
            Set triggerEvents = new HashSet();
            triggerEvents.add(triggerEvent);

            triggerEventCacheManager.storeTriggerEvents(triggerEvents);

            return triggerEvent;
        }
        //already has address trigger event, no need to add a new one
        return null;
    }


    //CCR 11789: add fromAddNewPerson flag so if it is true, don't send MVI phone& address true.
    public void managePhones(Set phones, Person onFile, boolean fromAddNewPerson ) throws ServiceException {
        this.processPhones(phones,onFile,null, UI, fromAddNewPerson, null);
    }
    /**
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#managePhones(java.util.Set, gov.va.med.esr.common.model.person.Person)
    */
   public void managePhones(Set phones, Person onFile) throws ServiceException {
       this.processPhones(phones,onFile,null, UI, false, null);
   }

   /**
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#processPhones(java.util.Set, gov.va.med.esr.common.model.person.Person)
    */
   // CCR12176 - change Set Phones to incoming person to check the ssn verfication status NEW from it
   public void processPhones(Person incoming, Person onFile, VAFacility sendingFacility) throws ServiceException {
       this.processPhones(incoming.getPhones(),onFile,sendingFacility, null, false, incoming);
   }

   /**
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#processPhones(java.util.Set, gov.va.med.esr.common.model.person.Person)
    */
   // CCR12176 - adding incoming person to check the ssn verfication status NEW from it
   private void processPhones(Set phones, Person onFile, VAFacility sendingFacility, String caller,  boolean fromAddNewPerson, Person incomingPerson) throws ServiceException {

      // Validate input parameters. Allowed to be empty.
      Validate.notNull(phones, "An incoming set of phones is null");

      boolean triggerZ05 = false;

      //old home phone
      Phone oldHomePhone = onFile.getHomePhone();

      //Map containing entity key as the key and sending facility as value. Used by the Z05 builder
      //to filter out message to sending facility
      Map updatedKeysAndSitesMap = new HashMap();

      //11553
      //Person pristinePerson = (Person)cacheManager.getItem(onFile.getEntityKey());

      //change to this so that processVOA() will not get null pristinePerson that causes null pointer exception
      Person pristinePerson = getPristinePerson(onFile);

      Iterator iter = phones.iterator();
      if( phones.isEmpty() ) {
         onFile.removeAllPhones();
      // CCR11878 - Notify VistA if the phone is deleted
         if(pristinePerson.getPhones()!= null && !pristinePerson.getPhones().isEmpty()){
        	 triggerZ05 = true;
        	 if(updatedKeysAndSitesMap.isEmpty() )
                 updatedKeysAndSitesMap.put(pristinePerson, sendingFacility);
        }
      }
      else {
         // Establish a working set
         Set working = new HashSet();

         //CCR 11402
         String homePhoneCd = PhoneType.CODE_HOME.getCode();

         while( iter.hasNext() ) {
            Phone incoming = (Phone)iter.next();
            Phone found = (Phone)findMatchingElement(incoming, onFile.getPhones());
            if( found != null ) {
               // Remove so don't find same one twice
               onFile.removePhone(found);
            }
            else {
               // Not found so create a new one
               found = new Phone();
            }


            if(! (StringUtils.equals(incoming.getPhoneNumber(),found.getPhoneNumber()) && incoming.getType().equals(found.getType())) )
            {
                //For UI only if the phone number is changed, set transaction date as the change date
                if(StringUtils.equals(UI,caller))
                {
	                incoming.setChangeDate(this.getTransactionDate());
	                triggerZ05 = true;
	                this.getMergeRuleService().mergePhone(incoming, found);
	                // Add to working set
	                working.add(found);
	                //Only need to send one message even if more than one phone changed.If there is already a key
	                //for a previous phone number change,no need to add another
	                if(updatedKeysAndSitesMap.isEmpty())
	                    updatedKeysAndSitesMap.put(found, sendingFacility);


	            }else //for messaging
	            {

                     //	CCR11553
	            	String phoneCode = incoming.getType().getCode();
	            	Phone pristinePhone = null;
	            	pristinePhone = getMostRecentPhone(pristinePerson,phoneCode);
	            	found = (Phone)findMatchingElement(pristinePhone, onFile.getPhones());
		            if( found != null ) {
		                  // Remove so don't find same one twice
		                  onFile.removePhone(found);
		            }
		            else {
		                  // Not found so create a new one
		                  found = new Phone();
		             }

	                if((incoming.getChangeDate() != null)  &&
	                        ( found.getChangeDate() == null || (incoming.getChangeDate().after(found.getChangeDate()))))
	                {
	                    triggerZ05 = true;
		                this.getMergeRuleService().mergePhone(incoming, found);
		                // Add to working set
		                if(!incoming.getPhoneNumber().equals(""))  //this is for valid phone deletion if phone number is "" in HL7 message
		                	working.add(found);
		                if(updatedKeysAndSitesMap.isEmpty())
		                    updatedKeysAndSitesMap.put(found, sendingFacility);

	                }
	                else if(found.getPhoneNumber() != null) {
	                	 working.add(found);
	                }
	             }
              }
              else {
               // Add to working set
               working.add(found);
            }
         }
         //For messaging: the case if there are more than one home or work phones stored in ESR
         if(!StringUtils.equals(UI,caller))
         {
        	 working = processRemainingPhones(pristinePerson.getPhones(), onFile, working);
         }

         onFile.removeAllPhones();
         Iterator iterWorking = working.iterator();
         // Add the updated and new phones to person
         while( iterWorking.hasNext() ) {
            Phone updated = (Phone)iterWorking.next();
            if(updated.getPhoneNumber() != null)
            	onFile.addPhone(updated);
         }
      }

      //new home phone
      Phone newHomePhone = onFile.getHomePhone();

      if(triggerZ05)
      {
	      FilterSitesPersonTriggerEvent evt = notifyVistaForContactInfo(onFile, updatedKeysAndSitesMap);

          //CCR 11402 - update MVI for home phone changes
          if ( !fromAddNewPerson && //CCR11789 if from add a person, don't send MVI phone&addr update
        		evt != null && //avoid duplicate updates if address change event already triggered
        	   ((oldHomePhone == null && newHomePhone != null) || //new home phone added
        		(oldHomePhone != null && newHomePhone == null) || //old home phone removed
        		((oldHomePhone != null && newHomePhone != null)
        				&& (StringUtils.equals(oldHomePhone.getPhoneNumber(),newHomePhone.getPhoneNumber()))))) //home phone changed
          {
        	  //if from VOA, don't send a duplicate update to MVI
        	  if (sendingFacility == null || !VAFacility.CODE_MHV.getCode().equals(sendingFacility.getCode()))
        		  ((PersonTriggerIdentity) evt.getTriggerIdentity()).setIdMAddressPhoneUpdate(true);

        	 if (incomingPerson != null && incomingPerson.isEsrCorrelationAdded()) //CCR12176 dont' send ph/adr update to MVI if new person from Z07)
        		 ((PersonTriggerIdentity) evt.getTriggerIdentity()).setIdMAddressPhoneUpdate(false);

        	 if(!StringUtils.equals(UI,caller) && pristinePerson != null)
        		 evt.setOnFileIdentityTraits(pristinePerson.getIdentityTraits()); //CCR12311
          }
       }
   }

   /*
    * (non-Javadoc)
    *
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#manageAddress(gov.va.med.esr.common.model.party.Address,
    *      gov.va.med.esr.common.model.person.Person)
    */
   public void manageAddress(Address incoming, Person onFile) throws ServiceException {
      this.doProcessAddress(incoming, onFile, this.getRuleParameters( this.getContactInfoRuleParameters() ),  null, false, null ); //CCR11884, changed from true to false
   }

   /*
    * (non-Javadoc)
    *
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#manageHandBookForBadAddressText(Set,
    *      gov.va.med.esr.common.model.person.Person)
    *      Keeping the handbook rules separate from other rules as
    *      It is not running in production as part of 3.5 release.
    *
    */
   public void manageHandBookForBadAddressText(Set incoming, Person onFile) throws ServiceException {
	      // Validate input parameters
	      Validate.notNull(incoming, "An incoming set of addresses must not be null");

	      for( Iterator i = incoming.iterator(); i.hasNext(); ) {
	         Object obj = i.next();
	         if( obj instanceof Address ) {
	        	 manageHandBookForBadAddressText((Address)obj, onFile);
	         }
	      }
	   }
   public void manageHandBookForBadAddressText(Address incoming, Person onFile) throws ServiceException {
	   	  if (logger.isDebugEnabled()) {
	   		  logger.debug("ContactInfoRuleService manageHandbookBadAddress : incoming" + incoming + " onfile: " + onFile);
 			}
	      this.doProcessHandBookForBadAddressTxt(incoming, onFile, this.getRuleParameters( this.getHandBookEventRuleParameters() ),  null );
	   }

   //CCR 11789: pass in the flag fromAddNewPerson so that Add A Person will avoid sending MVI phone&Address update
   public void manageAddresses(Set incoming, Person onFile, boolean fromAddNewPerson) throws ServiceException {
      // Validate input parameters
      Validate.notNull(incoming, "An incoming set of addresses must not be null");

      RuleParameters parameters = this.getRuleParameters( this.getContactInfoRuleParameters());
      for( Iterator i = incoming.iterator(); i.hasNext(); ) {
         Object obj = i.next();
         if( obj instanceof Address ) {
        	 if (fromAddNewPerson)
        	 {
        	 doProcessAddress((Address)obj, onFile, parameters, null, fromAddNewPerson,null );
        	 }
        	 else
        	 {
        		 manageAddress((Address)obj, onFile);
        	 }
        	 fromAddNewPerson = false;
        	 //manageAddress((Address)obj, onFile);
         }
      }
   }
   /*
    * (non-Javadoc)
    *
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#manageAddresses(java.util.Set,
    *      gov.va.med.esr.common.model.person.Person)
    */
   public void manageAddresses(Set incoming, Person onFile) throws ServiceException {
      // Validate input parameters
      Validate.notNull(incoming, "An incoming set of addresses must not be null");

      for( Iterator i = incoming.iterator(); i.hasNext(); ) {
         Object obj = i.next();
         if( obj instanceof Address ) {
            manageAddress((Address)obj, onFile);
         }
      }
   }

   /*
    * (non-Javadoc)
    *
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#manageAddress(gov.va.med.esr.common.model.party.Address,
    *      gov.va.med.esr.common.model.person.Association)
    */
   public void manageAssociationAddress(Association incoming, Person onFile)
         throws ServiceException {
      // Validate input parameter
      Validate.notNull(onFile, "A person on file must not be null");
      Validate.notNull(incoming, "An incoming association must not be null");

      Address incomingAddress = (Address)incoming.getAddress();

      // Find a matching association on file
      Association onFileAssociation = (Association)findMatchingElement(incoming, onFile
            .getAssociations());

      this.doProcessAddress( incomingAddress,
                             onFileAssociation,
                             this.getRuleParameters( this.getContactInfoRuleParameters()) );
   }

   /*
    * (non-Javadoc)
    *
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#manageAddresses(gov.va.med.esr.common.model.party.Address[],
    *      gov.va.med.esr.common.model.person.Association)
    */
   public void manageAssociationAddresses(Set associations, Person onFile)
         throws ServiceException {
      // Validate input parameters
      Validate.notNull(associations, "The incoming associations must not be null");
      for( Iterator i = associations.iterator(); i.hasNext(); ) {
         Object association = i.next();
         Validate.isTrue(( association instanceof Association ),
               "An invalid association type");
         this.manageAssociationAddress((Association)association, onFile);
      }
   }

   /**
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#manageInsuranceAddresses(
    *      java.util.Set, gov.va.med.esr.common.model.person.Person)
    */
   public void manageInsuranceAddresses(Set policies, Person onFile)
         throws ServiceException {

      // Validate input parameters
      Validate.notNull(policies, "The incoming insurance policies must not be null");
      for( Iterator i = policies.iterator(); i.hasNext(); ) {
         Object policy = i.next();
         Validate.isTrue(( policy instanceof InsurancePolicy ),
               "An invalid insurance type");
         this.manageInsuranceAddress((InsurancePolicy)policy, onFile);
      }
   }

   /**
    * @see gov.va.med.esr.common.rule.service.ContactInfoRuleService#manageInsuranceAddress(
    *      gov.va.med.esr.common.model.insurance.InsurancePolicy,
    *      gov.va.med.esr.common.model.person.Person)
    */
   public void manageInsuranceAddress(InsurancePolicy incoming, Person onFile)
         throws ServiceException {

      // Validate input parameter
      Validate.notNull(onFile, "A person on file must not be null");
      Validate.notNull(incoming, "An incoming insurance policy must not be null");

      // Find a matching insurnce policy on file
      InsurancePolicy onFilePolicy = (InsurancePolicy)findMatchingElement(incoming,
            onFile.getInsurances());
      this.getMergeRuleService().mergeInsuranceAddress(incoming.getAddress(),
            onFilePolicy);
   }

   //CCR 11789: added the flag fromAddNewPerson
   //CCR11898: added VerificationInfo, so the tempAddressUpdateMoreRecent flag in VerificationInfo can be set in processAddress rule
   //flow, then pass it to triggerMessage rule flow
   private void doProcessAddress(Address incoming, Person onFile,
         RuleParameters parameters, VAFacility sendingFacility, boolean fromAddNewPerson, VerificationInfo veriInfo) throws ServiceException {
      // Validate input parameters
      Validate.notNull(incoming, "An incoming address must not be null ");
      Validate.notNull(onFile, "A person on file must not be null");
      // NOTE a new person will not have any addresses currently on file

      // Get person from cache
      Person pristine = this.getPristinePerson(onFile);

      /**
       * CCR 10502. Do not upload temporary address , if it is inactive and there is not temp address
       * on file.
       */
      if ( incoming != null && incoming.getType() != null && incoming.isActive() == false) {
    	  AddressType type = incoming.getType();

    	  if ( AddressType.CODE_TEMPORARY_CORRESPONDENCE_ADDRESS.getCode().equals(type.getCode())) {

    		  Address onFileTempAddress = pristine.getTemporaryCorrespondenceAddress();

    		  if ( onFileTempAddress == null ) {
    			  logger.debug("Inactive temporary address without onfile address, so not uploading " );
    			  return;
    		  }

    	  }
      }

     if ( incoming != null && (incoming.getCounty() == null)  )
      {
    	 String incomingZipCode;
    	 ZipCode zipCodeObject=null;
    	 if ( (incomingZipCode = incoming.getZipCode() ) !=null )
      	 {
     			try {
      				zipCodeObject = this.lookupService.getPartialZipCodeByCode(incomingZipCode);
     			} catch (Exception e) { throw new RuntimeException(incomingZipCode, e); }

     			// Get the county
     			if (zipCodeObject !=null){
     				County countyObject = zipCodeObject.getCounty();
     				if (countyObject != null)
     				{
     					incoming.setCounty(countyObject.getName());
     				} 	}
      	  }
       }

     //CCR 13724 - default country to USA if empty and domestic address
     if (incoming != null && (incoming.getCountry() == null || incoming.getCountry().isEmpty())) {

    	 //valid zip then address is domestic
    	 if (this.lookupService.getZipCodeByCode(incoming.getZipCode()) != null) {
    		 incoming.setCountry(Country.CODE_USA.getName());
    	 } else {
    		 //only warn about perm address
    		 if (incoming.getType().getCode().equals(AddressType.CODE_PERMANENT_ADDRESS))
    		 logger.warn("Zip Code: " +  incoming.getZipCode() + " is invalid and Country Code is Empty for person address: " + onFile.getPersonEntityKey().getKeyValueAsString());
    	 }

     }

      // Create an address rule input data
      AddressInputData addressData = new AddressInputData(incoming, onFile, pristine, sendingFacility, veriInfo);
      addressData.setFromAddNewPerson(fromAddNewPerson); //CCR 11789: pass in fromAddNewPerson so Add A Person will avoid sending MVI phone&address update

      // Execute the rules
      this.invokeRuleFlow(parameters, addressData);
   }

private void doProcessHandBookForBadAddressTxt(Address incoming, Person onFile,
	         RuleParameters parameters, VAFacility sendingFacility) throws ServiceException {
	      // Validate input parameters
	      Validate.notNull(incoming, "An incoming address must not be null ");
	      Validate.notNull(onFile, "A person on file must not be null");
	      // NOTE a new person will not have any addresses currently on file

	      // Get person from cache
	      Person pristine = this.getPristinePerson(onFile);

	      // Create an address rule input data
	      AddressInputData addressData = new AddressInputData(incoming, onFile, pristine, sendingFacility);

	      // Execute the rules
	      this.invokeRuleFlow(parameters, addressData);
	   }
   private void doProcessAddress(Address incoming, Association onFileAssociation,
         RuleParameters parameters) throws ServiceException {
      // Validate input parameters
      Validate.notNull(incoming, "An incoming address must not be null ");
      Validate.notNull(onFileAssociation, "An association on file must not be null");

      // Create an address rule input data
      AddressInputData addressData = new AddressInputData(incoming, onFileAssociation);

      // Execute the rules
      this.invokeRuleFlow(parameters, addressData);
   }

   /**
    * @see gov.va.med.esr.common.rule.service.impl.AbstractRuleValidationServiceAwareImpl#afterPropertiesSet()
    */
   public void afterPropertiesSet() throws Exception {
      super.afterPropertiesSet();
   }

   /**
    * @return Returns the addressRuleParameters.
    */
   public String getAddressRuleParameters() {
      return addressRuleParameters;
   }

   /**
    * @param addressRuleParameters
    *           The addressRuleParameters to set.
    */
   public void setAddressRuleParameters(String addressRuleParameters) {
      this.addressRuleParameters = addressRuleParameters;
   }

   /**
    * @return Returns the contactInfoRuleParameters.
    */
   public String getContactInfoRuleParameters() {
      return contactInfoRuleParameters;
   }

   /**
    * @param contactInfoRuleParameters
    *           The contactInfoRuleParameters to set.
    */
   public void setContactInfoRuleParameters(String contactInfoRuleParameters) {
      this.contactInfoRuleParameters = contactInfoRuleParameters;
   }


	/**
	 * @return Returns the triggerRouter.
	 */
	public TriggerRouter getTriggerRouter()
	{
	    return triggerRouter;
	}
	/**
	 * @param triggerRouter The triggerRouter to set.
	 */
	public void setTriggerRouter(TriggerRouter triggerRouter)
	{
	    this.triggerRouter = triggerRouter;
	}


	/**
	 * @return Returns the triggerEventCacheManager.
	 */
    public TriggerEventCacheManager getTriggerEventCacheManager()
    {
        return triggerEventCacheManager;
    }

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


	public EntityCacheManager getCacheManager() {
		return cacheManager;
	}


	public void setCacheManager(EntityCacheManager cacheManager) {
		this.cacheManager = cacheManager;
	}
	/**
     * @return Returns the HandBookEventRuleParameters.
     */
    public String getHandBookEventRuleParameters() {
        return handBookEventRuleParameters;
    }
    /**
     * @param handBookEventRuleParameters The handBookEventRuleParameters to set.
     */
    public void setHandBookEventRuleParameters(
          String handBookEventRuleParameters) {
        this.handBookEventRuleParameters = handBookEventRuleParameters;
    }

    //CCR11553 this is for the case when ESR has more than one home/work phones
    private Set processRemainingPhones(Set pristinePhones, Person onFile, Set working)
    {
  	  // Validate input parameters. Allowed to be empty.
        Validate.notNull(pristinePhones, "set of phones stored in pristinePhones is null");

      	 Iterator iter = pristinePhones.iterator();
      	 while(iter.hasNext())
      	 {
      		Phone pristinePhone = (Phone)iter.next();
      		Phone found = (Phone)findMatchingElement(pristinePhone, onFile.getPhones());
           	if( found != null ) {
           		onFile.removePhone(found);
      		    working.add(found);
           	}
      	 }
      return working;

    }
    //CCR11553 this is for the case when ESR has more than one home/work phones
    private Phone getMostRecentPhone(Person pristinePerson,String phoneCode)
    {
  	  Set pristinePhones = pristinePerson.getPhones();
  	  Iterator iter = pristinePhones.iterator();
  	  Phone mostRecentPhone = new Phone();
   	  while(iter.hasNext())
   	  {
   		Phone pristinePhone = (Phone)iter.next();
   		if( (pristinePhone.getType().getCode().equals(phoneCode)) && (pristinePhone.getChangeDate() != null) && ( mostRecentPhone.getChangeDate() == null || (pristinePhone.getChangeDate().after(mostRecentPhone.getChangeDate()))))
   				mostRecentPhone = pristinePhone;

        }
   	  return mostRecentPhone;
    }
 }