/*******************************************************************************
 * Copyright � 2004-2005 EDS. All rights reserved
 ******************************************************************************/
package gov.va.med.esr.service.impl;

// Java Classes
import gov.va.med.esr.UseCaseName;
import gov.va.med.esr.common.infra.ImpreciseDate;
import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.SiteYear;
import gov.va.med.esr.common.model.ee.CDCondition;
import gov.va.med.esr.common.model.ee.CDDescriptor;
import gov.va.med.esr.common.model.ee.CDDiagnosis;
import gov.va.med.esr.common.model.ee.CDProcedure;
import gov.va.med.esr.common.model.ee.CatastrophicDisability;
import gov.va.med.esr.common.model.ee.EligibilityVerification;
import gov.va.med.esr.common.model.ee.IncomingMessageInfo;
import gov.va.med.esr.common.model.ee.PrisonerOfWar;
import gov.va.med.esr.common.model.ee.PurpleHeart;
import gov.va.med.esr.common.model.ee.SHAD;
import gov.va.med.esr.common.model.ee.VerificationInfo;
import gov.va.med.esr.common.model.financials.FinancialStatement;
import gov.va.med.esr.common.model.financials.IncomeTest;
import gov.va.med.esr.common.model.lookup.AACIndicator;
import gov.va.med.esr.common.model.lookup.EnrollmentStatus;

import gov.va.med.esr.common.model.lookup.EligibilityStatus;
import gov.va.med.esr.common.model.lookup.MilitaryServiceQueryStatus;
import gov.va.med.esr.common.model.lookup.RegistryType;
import gov.va.med.esr.common.model.lookup.SSAVerificationStatus;
import gov.va.med.esr.common.model.lookup.SeedStatus;
import gov.va.med.esr.common.model.lookup.SourceDesignation;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.messaging.MessageLogEntry;
import gov.va.med.esr.common.model.messaging.MessageLogEntryLite;
import gov.va.med.esr.common.model.messaging.SeedingLogEntry;
import gov.va.med.esr.common.model.messaging.SiteIdentity;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.PreferredFacility;
import gov.va.med.esr.common.model.person.id.PersonEntityKey;
import gov.va.med.esr.common.model.person.id.VPIDEntityKey;
import gov.va.med.esr.common.persistent.messaging.MessageLogEntryDAO;
import gov.va.med.esr.common.persistent.messaging.SeedingLogEntryDAO;

import gov.va.med.esr.common.rule.service.AssociationRuleService;
import gov.va.med.esr.common.rule.service.ClinicalDeterminationRuleService;
import gov.va.med.esr.common.rule.service.ContactInfoRuleService;
import gov.va.med.esr.common.rule.service.DemographicRuleService;
import gov.va.med.esr.common.rule.service.EligibilityEnrollmentRuleService;
import gov.va.med.esr.common.rule.service.EligibilityFactorRuleService;
import gov.va.med.esr.common.rule.service.EventRuleService;
import gov.va.med.esr.common.rule.service.FinancialInfoRuleService;
import gov.va.med.esr.common.rule.service.InsuranceRuleService;
import gov.va.med.esr.common.rule.service.MilitaryRuleService;
import gov.va.med.esr.common.rule.service.POWRuleService;
import gov.va.med.esr.common.rule.service.PurpleHeartRuleService;
import gov.va.med.esr.service.EEResultInfo;
import gov.va.med.esr.service.EligibilityEnrollmentService;

import gov.va.med.esr.service.GenerateQRYZ11MessagingService;
import gov.va.med.esr.service.MessagingService;
import gov.va.med.esr.service.PersonIdentityTraits;
import gov.va.med.esr.service.PreferredFacilityService;
import gov.va.med.esr.service.RegistryService;
import gov.va.med.esr.service.SystemParameterService;
import gov.va.med.esr.service.UniqueIdentifierGenerator;
import gov.va.med.esr.service.WorkflowService;
import gov.va.med.esr.service.trigger.IncomeYearTriggerEvent;

import gov.va.med.esr.service.trigger.PersonTrigger;
import gov.va.med.esr.service.trigger.PersonTriggerEvent;
import gov.va.med.esr.service.trigger.RetransmitTriggerEvent;

import gov.va.med.fw.cache.EntityCacheManager;
import gov.va.med.fw.cache.TriggerEventCacheManager;
import gov.va.med.fw.model.AbstractEntity;
import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.persistent.DAOException;

import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.config.SingletonApplicationContext;
import gov.va.med.fw.service.pagination.SearchQueryInfo;
import gov.va.med.fw.service.trigger.TriggerEvent;
import gov.va.med.fw.service.trigger.TriggerRouter;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;

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

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.Validate;



/**
 * Provides business methods to process inbound and outbound messages</br>
 * </br>
 * <b>Project:</b> Common </br>
 * <b>Created on: </b> 3:51:14 PM </br>
 *
 * @author ESR Team
 * @history: cleaned up code format and use entity cache manager - VL
 */
public class MessagingServiceImpl extends AbstractRuleAwareServiceImpl
   implements MessagingService {

   /**
    * An instance of serialVersionUID
    */
   private static final long serialVersionUID = -1936162067559837119L;

   private static final String ORUZ12 = "ORUZ12-E";
   private static final String ORUZ13 = "ORUZ13-E";
   private static final String QRYZ11 = "QRYZ11";
   private static final String QRYZ10 = "QRYZ10";
   private static final String RETRANSMIT_EVENT = "messaging.retransmit";
   private static final String MVI_DOD_ACTIVE = "Use MVI Date of Death Service Indicator";


   private static VAFacility VOA_VA_FACILITY = null;

   private static WorkflowService workflowService = null;

   @SuppressWarnings("rawtypes")
   private Map diagnosisMap;

   @SuppressWarnings("rawtypes")
   private Map procedureMap;

   @SuppressWarnings("rawtypes")
   private Map conditionMap;


   /**
    * An instance of generator
    */
   private UniqueIdentifierGenerator generator = null;

   /**
    * An instance of messageLogEntryDAO
    */
   private MessageLogEntryDAO messageLogEntryDAO = null;

   /**
    * An instance of eligibilityEnrollmentService
    */
   private EligibilityEnrollmentService eligibilityEnrollmentService = null;

   private GenerateQRYZ11MessagingService messagingService = null;

   /**
    * An instance of registryService.
    */
   private RegistryService registryService = null;

   /**
    * An instance of triggerRouter
    */
   private TriggerRouter triggerRouter = null;

   /**
    * An instance of triggerEventCacheManager
    */
   private TriggerEventCacheManager triggerEventCacheManager;

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

   /**
    * An instance of seedingLogEntryDAO
    */
   private SeedingLogEntryDAO seedingLogEntryDAO = null;

   /**
    * An instance of systemParameterService
    */
   private SystemParameterService systemParameterService = null;

   private PreferredFacilityService preferredFacilityService = null;


   //VOA forms 1010EZ/R always use previous calendar year
   private static final Integer defaultIncomeYear = new Integer(Calendar.getInstance().get(Calendar.YEAR)-1);



   /**
    * A default constructor
    */
   public MessagingServiceImpl() {
      super();
   }

   /**
    * @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 getEntityCacheManager() {
      return entityCacheManager;
   }

   /**
    * @param entityCacheManager The entityCacheManager to set.
    */
   public void setEntityCacheManager(EntityCacheManager entityCacheManager) {
      this.entityCacheManager = entityCacheManager;
   }

   /**
    * @return a TriggerRouter
    */
   public TriggerRouter getTriggerRouter() {
       return triggerRouter;
   }

   /**
    * @param triggerRouter
    */
   public void setTriggerRouter(TriggerRouter triggerRouter) {
       this.triggerRouter = triggerRouter;
   }
   /**
    * @return a UniqueIdentifierGenerator
    */
   public UniqueIdentifierGenerator getGenerator() {
       return this.generator;
   }

   /**
    * @param generator
    */
   public void setGenerator(UniqueIdentifierGenerator generator) {
       this.generator = generator;
   }

   /**
    * @return a MessageLogEntryDAO
    */
   public MessageLogEntryDAO getMessageLogEntryDAO() {
       return this.messageLogEntryDAO;
   }

   /**
    * @param messageLogEntryDAO
    */
   public void setMessageLogEntryDAO(MessageLogEntryDAO messageLogEntryDAO) {
       this.messageLogEntryDAO = messageLogEntryDAO;
   }

   /**
    * @return Returns the eligibilityEnrollmentService.
    */
   public EligibilityEnrollmentService getEligibilityEnrollmentService() {
      return eligibilityEnrollmentService;
   }

   /**
    * @param eligibilityEnrollmentService
    *           The eligibilityEnrollmentService to set.
    */
   public void setEligibilityEnrollmentService(
         EligibilityEnrollmentService eligibilityEnrollmentService) {
      this.eligibilityEnrollmentService = eligibilityEnrollmentService;
   }


/**
    * @return Returns the registryService.
    */
   public RegistryService getRegistryService() {
      return registryService;
   }

   /**
    * @param registryService The registryService to set.
    */
   public void setRegistryService( RegistryService registryService ) {
      this.registryService = registryService;
   }

   /**
    * @return a SeedingLogEntryDAO
    */
   public SeedingLogEntryDAO getSeedingLogEntryDAO() {
       return this.seedingLogEntryDAO;
   }

   /**
    * @param seedingLogEntryDAO
    */
   public void setSeedingLogEntryDAO(SeedingLogEntryDAO seedingLogEntryDAO) {
       this.seedingLogEntryDAO = seedingLogEntryDAO;
   }

   public SystemParameterService getSystemParameterService() {
       return systemParameterService;
   }

   public void setSystemParameterService(
           SystemParameterService systemParameterService) {
       this.systemParameterService = systemParameterService;
   }


   public PreferredFacilityService getPreferredFacilityService() {
	   return preferredFacilityService;
   }

	public void setPreferredFacilityService(PreferredFacilityService preferredFacilityService) {
		this.preferredFacilityService = preferredFacilityService;
	}



	/**
    * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
    */
   public void afterPropertiesSet() throws Exception {
      super.afterPropertiesSet();
      Validate.notNull(systemParameterService, "systemParameterService is required");
      Validate.notNull(messageLogEntryDAO, "MessageLogEntryDAO is required");
      Validate.notNull(triggerRouter, "triggerRouter is required");
      Validate.notNull(triggerEventCacheManager, "triggerEventCacheManager is required");
      Validate.notNull(entityCacheManager, "entityCacheManager is required");
   }

   /**
    * Implements method specified by MessagingService interface. (non-Javadoc)
    *
    * @see gov.va.med.esr.service.MessagingService#generateControlIdentifier()
    */
   public String generateControlIdentifier() throws ServiceException {
      return this.generator.generate().toString();
   }

   /**
    * @see gov.va.med.esr.service.MessagingService#getIdentity(gov.va.med.esr.common.model.person.Person,
    *      gov.va.med.esr.common.model.lookup.VAFacility)
    */
   @SuppressWarnings("rawtypes")
public SiteIdentity getIdentity( Person person, VAFacility vaFacility )
      throws ServiceException {

      Validate.notNull(vaFacility, "VAFacility can not be null");

      SiteIdentity siteId = null;

      if( person != null ) {

         Set sites = findSitesOfRecord(person);
         for (Iterator iterator = sites.iterator(); iterator.hasNext();) {

            siteId = (SiteIdentity) iterator.next();
            if( siteId.getVaFacility().getCode().equals( vaFacility.getCode() ) ) {
               return siteId;
            }
         }
      }
      // no match found from PSIM, so no DFN will be identified from PSIM,
      // simply create a SiteIdentity with incoming station#, and empty dfn
      siteId = new SiteIdentity();
      siteId.setVaFacility(vaFacility);
      return siteId;
   }

   /**
    * @see gov.va.med.esr.service.MessagingService#getMatchingTraits(gov.va.med.esr.service.PersonIdentityTraits)
    */
   @SuppressWarnings("rawtypes")
public Set getMatchingTraits(PersonIdentityTraits identityTraits)
      throws ServiceException {

      return getPsDelegateService().unattendedSearch(identityTraits);
   }

   /**
    * @see gov.va.med.esr.service.MessagingService#logMessage(gov.va.med.esr.common.model.messaging.MessageLogEntry)
    */
   public void logMessage(MessageLogEntry entry) throws ServiceException {

      // Persist the message log entry
      try {

         if (entry.getEntityKey() == null) {
            messageLogEntryDAO.saveObject(entry);
         }
         else {
            MessageLogEntry cachedEntry =
               (MessageLogEntry)messageLogEntryDAO.getByKey(entry.getEntityKey());

            cachedEntry.setAckDate(entry.getAckDate());
            cachedEntry.setAckType(entry.getAckType());
            cachedEntry.setErrorText(entry.getErrorText());
            cachedEntry.setInternalErrorText(entry.getInternalErrorText());
            cachedEntry.setStatus(entry.getStatus());

            messageLogEntryDAO.saveObject(cachedEntry);
         }
      }
      catch (DAOException ex) {
         throw new ServiceException("Failed to log message ", ex);
      }
   }

   /**
    * @see gov.va.med.esr.service.MessagingService#getMessageLogEntryById(java.math.BigDecimal)
    */
   public MessageLogEntry getMessageLogEntryById( BigDecimal id)
      throws ServiceException {

      MessageLogEntry mle = null;
      try {
         mle = messageLogEntryDAO.getById( id );
      }
      catch( DAOException e ) {

         throw new ServiceException("Failed to get message log entry by id " + id, e);
      }
      return mle;
   }

   /**
    * @see gov.va.med.esr.service.MessagingService#getMessageLogEntryLiteById(java.math.BigDecimal)
    */
   public MessageLogEntryLite getMessageLogEntryLiteById( BigDecimal id )
      throws ServiceException {

      MessageLogEntryLite mle = null;
      try {
         mle = messageLogEntryDAO.getMessageLogEntryLiteById( id );
      }
      catch( DAOException e ) {

         throw new ServiceException( "Failed to get message log entry lite by id " + id, e );
      }
      return mle;
   }

   /**
    * @see gov.va.med.esr.service.MessagingService#getMessageLogEntry(java.lang.String)
    */
   public MessageLogEntry getMessageLogEntry( String id )
      throws ServiceException {

      MessageLogEntry mle = null;
      try {
         mle = messageLogEntryDAO.getByControlId( id );
      }
      catch( DAOException e ) {

         throw new ServiceException( "Failed to get message log entry by id " + id, e );
      }
      return mle;
   }

    /**
     * @see gov.va.med.esr.service.MessagingService#getMessageLogEntry(java.lang.String)
     */
    public MessageLogEntry getMessageLogEntryByBatchControlNumber( String id )
       throws ServiceException {

       MessageLogEntry mle = null;
       try {
          mle = messageLogEntryDAO.getByBatchControlId( id );
       }
       catch( DAOException e ) {
          throw new ServiceException( "Failed to get message log entry by batch id " + id, e );
       }
       return mle;
    }

    /**
     * @see gov.va.med.esr.service.MessagingService#findSitesOfRecord(gov.va.med.esr.common.model.person.Person)
     */
    @SuppressWarnings("rawtypes")
	public Set findSitesOfRecord(Person person) throws ServiceException {
       return person == null ? new HashSet() : getPsDelegateService().getSites( person.getVPIDEntityKey() );
    }

    /**
     * Retrieves the messages matching the criteria.
     *
     * @param criteria the criteria object
     * @return
     * @throws ServiceException
     */
    @SuppressWarnings("rawtypes")
	public List search(SearchQueryInfo criteria) throws ServiceException {

       List list = null;
       try {
          list = messageLogEntryDAO.find(criteria);
       }
       catch (DAOException ex) {
          throw new ServiceException("Failed to get the Messages matchong the criteria", ex);
       }
       return list;
    }

    /**
     * This method implements the following use cases <br>
     *
     * <b>[UC35] Process Person VBA Data [1059] </b></br>
     * </br>
     * <b>[2018UC35.1]</b> The purpose of this use case is to describe </br>
     * how the system shall process specific person Veteran Benefits Administration (VBA)</br>
     * award data received via an HL7 message</br>
     * </br>
     * <b>3172 [UC23.5]</b> Determine Eligibility </br>
     * <b>4354 [UC23.6]</b> Trigger Letter/Email Bulletin </br>
     * @see gov.va.med.esr.service.MessagingService#processNonQueryZ11(
     * gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.ee.VerificationInfo)
     */
    public Person processNonQueryZ11( Person incoming, VerificationInfo verificationInfo ) throws ServiceException {

       Validate.notNull( incoming, "A person to be updated must not be null " );
       Validate.notNull( verificationInfo, "A verification info must not be null " );

       // No Optimistic Lock check is done for messaging
       Person onFile = this.getPersonService().getPerson( incoming.getPersonEntityKey() );

       //Proceed only if there is a valid enrollment record. If not, throw exception and
       //stop processing
       validateEnrollmentDetermination(onFile);

       // Check the onfile person for presence of qualifying MSE
       boolean qmse = this.getMilitaryRuleService().hasQualifyingMse(onFile, verificationInfo);
       verificationInfo.setHasQualifyingMse(qmse); // Inform the rules about qmse state

       // Step 1: Process VBA
       Person updated = this.getProcessVBARuleService().verifyEligibility( incoming, onFile, verificationInfo  );

       //
  	   // In Process Person VBA Data use case, the following 6463 requirement states:
 	   // 6463 [UC52.6.23] Scenario 8 - VBA Data contains Combined SC % less than the SC % currently on file
 	   // If the incoming message is a HINQ response (HINQ ORU~Z11), do not process the VBA data and EXIT the use case
 	   //
 	   // ProcessVBAFromMVR.irl rule flow correctly executes a rule 6463_isCombinedSCLessThanOnFile to ignore VBA data
 	   // and set the trigger enrollment processing flag to false.  When the control comes back to this method, because
 	   // a pristine person (a current person in ESR) is different from an updated person (an incoming person), the
 	   // curent logic below saves the person into a database.  This lead to a CodeCR: REEG_00006383.
 	   //
 	   // Also in Process Person VBA Data use case, the following requirements dictates an update of ESR data
 	   // in the "special requirements section".
 	   //
 	   // 4190 [UC52.5.1.10] Patient Sensitivity Information
 	   // 4193 [UC52.5.1.7] Claim Folder Number and Claim Folder Location
 	   // 6394 [UC52.5.1.6] Guardian
 	   // 6393 [UC52.5.1.5] Power of Attorney
 	   //
 	   // What was not clear was whether or not requirement 6463 overrules requirements 4190, 4193, 6394, and 6393
       // After having consulted with the BA, the decision was made to let 6463 take precedent over 4190, 4193, 6394
       // and 6393, - VL

       // Step 2: Get an enrollment service to determine eligibility.
       if( verificationInfo.isTriggerEnrollmentProcess() ) {

           updated = this.getEligibilityEnrollmentService().assessEEImpact( updated, false, false );

          // Step 3: Send update Z11
          this.getEventRuleService().manageMessageEvents(updated);


          //delete any in-process financials
          updated.removeAllInProcessFinancials();

          Person pristinePerson = (Person)entityCacheManager.getItem(updated.getEntityKey());

          //If the incomming message is the same as the one on file, do not perist
          if(AbstractEntity.matchesDomainValues(pristinePerson, updated)) {
             triggerEventCacheManager.removeTriggerEvents();
          }
          else {

          	// INC000001003363   if ZSP.seq7 == "" then delete unemployable=null for this person
        	  if (incoming.getServiceConnectionAward() != null
	      			  && updated.getServiceConnectionAward() != null
	      			  && incoming.getServiceConnectionAward().getUnemployable() == null) {
        		  updated.getServiceConnectionAward().setUnemployable(null);
        	  }

        	  // call the handbookTrigger rule flow.
        	  EligibilityEnrollmentRuleService ers = this.getEnrollmentRuleService();
        	  ers.processHandBookTriggers(updated, onFile, null, verificationInfo);


              // Step 4: Save

             updated = getPersonService().save( updated );
          }
       }
       this.getEventRuleService().processPersonEventsForPensionBulletin(updated);

		//CCR31095: apply VOA Specific Logic if enrollment determination != null and
		//VOA_IND=2 (VOA submission, person not found, and person is added explicitly)

		return updated;
    }

    /**
     * @see gov.va.med.esr.service.MessagingService#processORUZ04(gov.va.med.esr.common.model.person.Person)
     */
    public Person processORUZ04(Person person) throws ServiceException {
        // TODO Auto-generated method stub
        return person;
    }

    /**
     * @see gov.va.med.esr.service.MessagingService#processORUZ05(gov.va.med.esr.common.model.person.Person)
     */
    public Person processORUZ05(Person incoming) throws ServiceException {

       Validate.notNull(incoming, "A veteran must not be null");

       // No Optimistic Lock check is done for messaging
       Person onFile = this.getPersonService().getPerson( incoming.getPersonEntityKey() );

       //Proceed only if there is a valid enrollment record. If not, throw exception and
       //stop processing
       validateEnrollmentDetermination(onFile);

       // Execute Rules
       this.getContactInfoRuleService().processAddresses( incoming.getAddresses(), onFile, null);

       Person pristinePerson = (Person)entityCacheManager.getItem(onFile.getEntityKey());
       if (AbstractEntity.matchesDomainValues( pristinePerson, onFile )) {
          triggerEventCacheManager.removeTriggerEvents();
       }
       else {
          // Persist person
          this.getPersonService().save(onFile);

       }
       return onFile;
    }
// Commented out since behavior for Add a Person is not same as process Z07.
//    private Person processFullPersonData(Person person, VAFacility sendingFacility, VerificationInfo verificationInfo, boolean isAddPerson) throws ServiceException {
//
//        Validate.notNull(person, "A veteran must not be null");
//
//        PersonEntityKey personKey = null;
//
//        if (isAddPerson) {
//        	// Update person profile for ESR200 Correlation.
//	        this.getPersonService().addESRCorrelation(person);
//
//	        // Add correlation for the preferred facility
//	        this.getPersonService().addPreferredFacilityCorrelation(person);
//
//	        // Once above addESRCorrelation succeeded, this person now should have the vpid populated
//	        // use that vpid to persist a stub of the new person
//	        personKey = this.getPersonService().addPersonStub(person.getVPIDEntityKey());
//        } else {
//        	// existing person for process Z07
//        	personKey = person.getPersonEntityKey();
//        }
//
//        // 10471 -- start of original processZ07 logic
//
//        // No Optimistic Lock check is done for messaging
//        Person onFile = this.getPersonService().getPerson( personKey );
//
//        if (onFile == null) {
//        	throw new ServiceException("Cannot process person. onFile person not found for personKey " + personKey.getKeyValueAsString());
//        }
//
//        // Determine if a PSD demographic update has occurred
//        boolean psdDemographicUpdate = determinePsdDemographicUpdate(person, onFile);
//
//        String dfn = (verificationInfo != null) ? verificationInfo.getDfn() : null;
//
//        // Process rules related to a new person
//        EligibilityFactorRuleService efrs = this.getEligibilityFactorRuleService();
//        efrs.processNewVeteran(person, onFile);
//
//        // Process rules related to addresses and phones
//        ContactInfoRuleService contactInfoRuleservice = this.getContactInfoRuleService();
//        contactInfoRuleservice.processAddresses( person.getAddresses(), onFile, sendingFacility );
//        contactInfoRuleservice.processPhones( person.getPhones(), onFile, sendingFacility );
//        contactInfoRuleservice.processElectronicAddresses(person.getEmails(), onFile, sendingFacility);
//
//        // Process rules related to insurance
//        InsuranceRuleService insRuleService = this.getInsuranceRuleService();
//        insRuleService.processInsurancePolicies( person.getInsurances(), onFile , sendingFacility);
//        insRuleService.processMedicaidFactor(person.getMedicaidFactor(), onFile);
//
//        // Process rules related to association
//        AssociationRuleService associationRuleService = this.getAssociationRuleService();
//        associationRuleService.processAssociations( person.getAssociations(), onFile, sendingFacility);
//
//        // Process rules related to pow
//        POWRuleService powRuleService = this.getPowRuleService();
//        powRuleService.processPOW(person, onFile, sendingFacility);
//        // Process rules related to purple heart
//        PurpleHeartRuleService phRuleService = this.getPurpleHeartRuleService();
//        phRuleService.processPurpleHeart(person, onFile, verificationInfo,sendingFacility);
//
//        // Link Person Registries.
//        this.getRegistryService().linkPersonRegistries(person, onFile, false);
//        // Process rules related to clinical determination
//        ClinicalDeterminationRuleService cdrs = this.getClinicalDeterminationRuleService();
//        cdrs.processClinicalDetermination(person, onFile,sendingFacility,null);
//
//        // Process rules related enrollment
//        EligibilityEnrollmentRuleService ers = this.getEnrollmentRuleService();
//        // CodeCR 7327 - apply rules before accepting received enrollment and application
//        //ers.processReceivedEnrollment(person.getReceivedEnrollment(), onFile);
//        //ers.processApplication(person.getApplication(), onFile);
//        ers.processPerson(person, onFile, sendingFacility, verificationInfo,false);
//        ers.processReceivedEligibilities(person, onFile, verificationInfo, false);
//        ers.processIneligibility(person, onFile, sendingFacility, verificationInfo,false);
//
//        MilitaryRuleService mrs = this.getMilitaryRuleService();
//        mrs.processMilitaryService(sendingFacility,dfn,person.getMilitaryService(), onFile,null);
//        //This call calculates the POS from an updated person and sets it to on file person.
//        mrs.calculatePeriodOfService(person,onFile,UseCaseName.MS);
//
//        // Process rules related to demographic
//        DemographicRuleService drs = this.getDemographicRuleService();
//        drs.processDemographicInfo(sendingFacility, person, onFile,false);
//
//        Map incomeTests = person.getIncomeTests();
//        Map financialStatements = person.getFinancialStatements();
//        Map patientVisitSummaries = person.getPatientVisitSummaries();
//        Map beneficiaryTravels = person.getBeneficiaryTravels();
//
//        FinancialInfoRuleService frs = this.getFinancialInfoRuleService();
//        EEResultInfo info = new EEResultInfo();
//        if( MapUtils.isNotEmpty( incomeTests ) ||
//            MapUtils.isNotEmpty(financialStatements) ||
//            MapUtils.isNotEmpty(patientVisitSummaries) ||
//            MapUtils.isNotEmpty(beneficiaryTravels) ) {
//
//           Integer incomeYear =  getIncomeYear(incomeTests, financialStatements, patientVisitSummaries, beneficiaryTravels);
//           //frs.processFinancialInfo( incomeYear, sendingFacility, person, onFile, info);
//           //Fix for CR_9070
//           frs.processFinancialInfo( incomeYear, sendingFacility, person, onFile, info, true);
//        }
//
//        //Process Fee Bases
//        frs.processFeeBasis(sendingFacility, person, onFile,false);
//
//        // Process VBA data coming from ORUZ07
//        this.getProcessVBARuleService().processVBAData( person, onFile, verificationInfo );
//
//        /*
//         * CCR 10689 Agent Orange deleted from Verified record
//         * From this point forward, any rule that considers Veteran Indicator should be using
//         * the updated person. The reason is Process VBA rules figure out whether a Veteran Indicator from
//         * site will be accepted.  The incoming person needs to be updated by next step with appropriate
//         * value in order to minimize ILOG rules changes.
//         */
//        person.setVeteran(onFile.getVeteran()); // Synchronize Veteran Indicator with updated value
//
//        // Process rules related to eligibiility factor
//        efrs.processSpecialFactors(person, onFile,false);
//        efrs.processIncompetenceRuling(person.getIncompetenceRuling(), onFile);
//        efrs.processEmergencyResponseIndicator(person, onFile);
//
//        // Provide the onFile person (data all merged, etc.) and calculate E&E. We
//        // want to trigger letters so we invoke assessEEImpact by passing false.
//        Person calculatedPerson = this.getEligibilityEnrollmentService().assessEEImpact(onFile, false, false, sendingFacility);
//
//        // Trigger QRYZ11 / ORUZ11 if needed
//        this.getEventRuleService().processMessageEvents(onFile, verificationInfo, info.isZ10Sent());
//
//        //Remove all in process data whenever Z07 or Z11 is recieved.
//        calculatedPerson.removeAllInProcessFinancials();
//
//
//        Person pristinePerson = (Person)entityCacheManager.getItem(calculatedPerson.getEntityKey());
//
//        //If the incomming message is the same as the one on file, do not perist
//        if(AbstractEntity.matchesDomainValues(pristinePerson, calculatedPerson)) {
//           triggerEventCacheManager.removeTriggerEvents();
//        }
//        else {
//
//        	//For MSDS - needed because may receive Z07 for person after initial seeding rules were applied BUT not before broker is
//        	//turned on
//
//        	if(getSystemParameterService().getMSDSReconIndicator().booleanValue()){
//        		if(buildMSDRecord(pristinePerson,calculatedPerson)){
//        			logger.info("Reconciliation taking place");
//                	mrs.processSeedingMilitaryService(onFile.getMilitaryService(), onFile);
//                	updateSeedingLogEntry(calculatedPerson,SeedStatus.COMPLETE.getCode(), verificationInfo.getMessageControlId());
//                	logger.info("Completed Reconciliation");
//                }
//        	}
//
//        	 //calling processPerson for handbook trigger rule flow
//             ers.processHandBookTriggers(calculatedPerson, onFile, null, verificationInfo);
//
//
//            //CCR10679 used to undo
//            //mrs.processCreateHECRecord(person.getMilitaryService(), onFile);
//
//        	// Persist person
//        	onFile = this.getPersonService().save(calculatedPerson);
//
//        	// Notify PSD in case a demographic update occurred.  This should be done after all rules
//        	// have been validated and the person has been saved so we don't accidentally notify PSD
//        	// before we successfully persist our changes.
//        	notifyPsdOfDemographicUpdate(psdDemographicUpdate, onFile);
//        }
//
//        if(logger.isDebugEnabled()) {
//        	   logger.debug( "MessagingServiceImpl.processORUZ07(): End processing rules for person " +
//        	                 person.getPersonEntityKey().getKeyValueAsString() );
//        }
//        return onFile;
//
//    }

    /**
     * Process VOA Application
     * @param incoming the person object from VOA Web Service
     * @param sendingFacility
     * @param verificationInfo
     * @return
     * @throws ServiceException
     */
    @SuppressWarnings({ "rawtypes", "static-access" })
	public Person processVOA(Person incoming, VAFacility sendingFacility, VerificationInfo verificationInfo, boolean isAnonymoysLOA1)
			throws ServiceException {

        Validate.notNull(incoming, "A incoming Person must not be null");
        Validate.notNull(sendingFacility, "A sending facility must not be null");

        // existing person for process VOA
        Person onFile = null;

        //in case it's brand new add and PV not there yet, use ESR person added which is identical
        try {
        	onFile = this.getPersonService().getPerson( incoming.getPersonEntityKey() );
        } catch (Exception ex) {
        	logger.error("PV Retrieve Failed, trying with ESR Person for " + incoming.getPersonEntityKey());
        }

        if (onFile == null) {
        	onFile = this.getPersonService().get200ESRPerson(incoming.getPersonEntityKey());
        }

        if (onFile == null) {
        	throw new ServiceException("ProcessVOA Failed to Retrieve onFile person from MVI:" + incoming.getPersonEntityKey().getKeyValueAsString());
        }

        boolean isNewPerson = false;
        /**
         * RTC # 372038 - If record exist in the ES with a Non Veteran Primary Eligibility Code
         * then system should treat as a new applicant for VA health care and make queries to VBA and MSDS
         * (SDM Tkt # I9613978FY16)
         *
         * If Veteran exist in the ES (i.e. onFile veteran) for incoming VOA application request with a Non Veteran Primary Eligibility Code
         * then system should treat as a new applicant for VA health care and make queries to VBA and MSDS.
         */
    	boolean isNonVeteran = this.getHelperService().isNonVeteranPrimaryEligibility(onFile);
    	if (isNonVeteran){
    		incoming.setJustAdded(true);
    		onFile.setJustAdded(true);
    		isNewPerson = true;
    	}

        if (onFile.getEnrollmentDeterminations() == null || onFile.getEnrollmentDeterminations().size() == 0){
        	isNewPerson = true;
        	onFile.setVeteran(Boolean.TRUE);

        	//set default pending eligibility and ssn status
        	EligibilityVerification ev = new EligibilityVerification();
        	try {
    			ev.setEligibilityStatus(this.getLookupService().getEligibilityStatusByCode(
    					EligibilityStatus.CODE_PENDING_VERIFICATION.getCode()));
    			ev.setEligibilityStatusDate(new ImpreciseDate(new Date()));
    		} catch (Exception e) {
    			logger.error(e);
    		}
        	onFile.setEligibilityVerification(ev);

        	SSAVerificationStatus ssnNew = new SSAVerificationStatus();
        	try {
         		ssnNew = this.getLookupService().
        			getSSAVerificationStatusByCode(SSAVerificationStatus.NEW_RECORD.getCode());
         	} catch (Exception e){

         	}

        	if (onFile.getIdentityTraits() != null && onFile.getIdentityTraits().getSsn() != null) {
        		onFile.getIdentityTraits().getSsn().setSsaVerificationStatus(ssnNew);
        	}

        }

        //current person is cancel decline and reapply from VOA
        //remove cancel/decline and set new app date
        boolean isEnrollStatusCancel = false;
        if (onFile.getEnrollmentDetermination() != null && onFile.getEnrollmentDetermination().getEnrollmentStatus() != null) {

	        String statusCd = onFile.getEnrollmentDetermination().getEnrollmentStatus().getCode();

	        isEnrollStatusCancel = EnrollmentStatus.CODE_CANCELLED_DECLINED.getCode().equals(statusCd);
        }

        if (isEnrollStatusCancel) {
        	onFile.setCancelDecline(null);
        	onFile.getApplication().setApplicationDate(new Date());
        	incoming.getApplication().setApplicationDate(new Date());
        	isNewPerson = true;
        }

        if (onFile.getPreferredFacility() == null) {

        	PreferredFacility pf = new PreferredFacility();
            pf.setAssignmentDate(new Date());
            pf.setFacility(incoming.getPreferredFacility());
            pf.setSourceDesignation(this.getLookupService().getSourceDesignationByCode(SourceDesignation.CODE_ESR.getCode()));
        	onFile.addPreferredFacility(pf);

        }

        //do not change verified vet status based on incoming submit
        if (onFile.getEligibilityVerification() != null &&
        		onFile.getEligibilityVerification().getEligibilityStatus() != null &&
        		onFile.getEligibilityVerification().getEligibilityStatus().getCode() != null &&
        		onFile.getEligibilityVerification().getEligibilityStatus().getCode().equals(EligibilityStatus.CODE_VERIFIED.getCode()) &&
        		onFile.isVeteran() != null &&
        		incoming.isVeteran() != null &&
        		(onFile.isVeteran().booleanValue() != incoming.isVeteran().booleanValue())) {
        	incoming.setVeteran(onFile.isVeteran());
        }

        EligibilityFactorRuleService efrs = this.getEligibilityFactorRuleService();

        try {
	        // Process rules related to a new person
	        efrs.processNewVeteran(incoming, onFile);
        } catch (Exception ex) {
        	throw new ServiceException("ProcessVOA Failed to invoke EligibilityFactorRuleService:" + ex.getMessage(), ex);
        }


        try {
	        // Process rules related to addresses and phones
	        ContactInfoRuleService contactInfoRuleservice = this.getContactInfoRuleService();
	        contactInfoRuleservice.processAddresses( incoming.getAddresses(), onFile, sendingFacility, verificationInfo);
	        contactInfoRuleservice.processPhones( incoming, onFile, sendingFacility );
	        contactInfoRuleservice.processElectronicAddresses(incoming.getEmails(), onFile, sendingFacility);
        } catch (Exception ex) {
        	throw new ServiceException("ProcessVOA Failed to invoke ContactInfoRuleService:" + ex.getMessage(), ex);
        }

        try {
	        // Process rules related to insurance
	        InsuranceRuleService insRuleService = this.getInsuranceRuleService();
	        //4.2.7.	SUC2031.3.7 - [1729] Process Person Insurance Data
	        //CCR12075 - changed from processInsurancePolicies to manageInsurancePolicies(used by UI) so that Z04 can be triggered
	        insRuleService.processInsurancePolicies( incoming.getInsurances(), onFile , sendingFacility);
	        insRuleService.processMedicaidFactor(incoming.getMedicaidFactor(), onFile);
        } catch (Exception ex) {
        	throw new ServiceException("ProcessVOA Failed to invoke InsuranceRuleService:" + ex.getMessage(), ex);
        }


        try {

	        // Process rules related to pow
	        POWRuleService powRuleService = this.getPowRuleService();
	        powRuleService.processPOW(incoming, onFile, sendingFacility);
	        // Process rules related to purple heart
	        PurpleHeartRuleService phRuleService = this.getPurpleHeartRuleService();
	        phRuleService.processPurpleHeart(incoming, onFile, verificationInfo,sendingFacility);
        } catch (Exception ex) {
        	throw new ServiceException("ProcessVOA Failed to invoke POW/PH Rule Service:" + ex.getMessage(), ex);
        }


        EligibilityEnrollmentRuleService ers = this.getEnrollmentRuleService();
        try {
	        // Process rules related enrollment
	        ers.processPerson(incoming, onFile, sendingFacility, verificationInfo,false);
	        ers.processReceivedEligibilities(incoming, onFile, verificationInfo, false);
	        ers.processIneligibility(incoming, onFile, sendingFacility, verificationInfo,false);
        } catch (Exception ex) {
        	throw new ServiceException("ProcessVOA Failed to invoke EligibilityEnrollmentRuleService:" + ex.getMessage(), ex);
        }


        try  {
	        MilitaryRuleService mrs = this.getMilitaryRuleService();
	        mrs.processMilitaryService(sendingFacility,null, incoming.getMilitaryService(), onFile,null);

	        mrs.calculatePeriodOfService(incoming,onFile,UseCaseName.MS, sendingFacility);
        } catch (Exception ex) {
        	throw new ServiceException("ProcessVOA Failed to invoke MilitaryRuleService:" + ex.getMessage(), ex);
        }


        try {
	        // Process rules related to demographic
	        DemographicRuleService drs = this.getDemographicRuleService();

	        //RTC WI 348581, filter sites by parameter list during MVI authority rollout
	        String mviActiveForSite = "N";
	        String[] siteList = this.getSystemParameterService().getByName("VISTA_ROLLOUT_SITE_LIST").getValue().split(",");

	        if (this.getSystemParameterService().getByName(MVI_DOD_ACTIVE).getValue().equalsIgnoreCase("Y") ||
	        		Arrays.asList(siteList).contains(sendingFacility.getStationNumber())) {
	        	mviActiveForSite = "Y";
	        }

	        drs.processDemographicInfo(sendingFacility, incoming, onFile,false, mviActiveForSite);
        } catch (Exception ex) {
        	throw new ServiceException("ProcessVOA Failed to invoke DemographicRuleService:" + ex.getMessage(), ex);
        }


        Map incomeTests = incoming.getIncomeTests();
        Map financialStatements = incoming.getFinancialStatements();
        Map patientVisitSummaries = incoming.getPatientVisitSummaries();
        Map beneficiaryTravels = incoming.getBeneficiaryTravels();

        FinancialInfoRuleService frs = this.getFinancialInfoRuleService();
        EEResultInfo info = new EEResultInfo();


        Integer incomeYear = defaultIncomeYear;

        if( MapUtils.isNotEmpty( incomeTests ) ||
            MapUtils.isNotEmpty(financialStatements) ||
            MapUtils.isNotEmpty(patientVisitSummaries) ||
            MapUtils.isNotEmpty(beneficiaryTravels) ) {

           incomeYear =  getIncomeYear(incomeTests, financialStatements, patientVisitSummaries, beneficiaryTravels);

           if (incomeYear != null && incomeYear.intValue() == 0) //empty value can defulat to 0
           {
        	   //VOA forms 1010EZ/R always use previous calendar year
        	   //set back the income year. Follow the logic in the method getIncomeYear() above
    	      if (MapUtils.isNotEmpty(incomeTests)) {
    	    	  IncomeTest test = incoming.getIncomeTest(incomeYear);
    	    	  test.setIncomeYear(defaultIncomeYear);
    	    	  incoming.setIncomeTest(incomeYear, null); //remove incomeYear 0
    	    	  incoming.setIncomeTest(defaultIncomeYear, test);

    	       } else if (MapUtils.isNotEmpty(financialStatements)) {
    	    	  FinancialStatement stmt = incoming.getFinancialStatement(incomeYear);
    	    	  stmt.setIncomeYear(defaultIncomeYear);
    	    	  incoming.setFinancialStatement(incomeYear, null); //remove incomeYear 0
    	    	  incoming.setFinancialStatement(defaultIncomeYear, stmt);
    	       } else if (MapUtils.isNotEmpty(patientVisitSummaries)) {
    	          ((SiteYear) patientVisitSummaries.keySet().iterator().next()).setYear(defaultIncomeYear);
    	       }
    	       else{
    	          ((SiteYear) beneficiaryTravels.keySet().iterator().next()).setYear(defaultIncomeYear);
    	       }
    	      incomeYear = defaultIncomeYear;
           }

           	Date origEffectiveDate = new Date();

           	if (incoming.getIncomeTest(incomeYear) != null) {
           		origEffectiveDate = incoming.getIncomeTest(incomeYear).getEffectiveDate();

           	}

        	try {
        	   frs.processFinancialInfo( incomeYear, sendingFacility, incoming, onFile, info, false);
        	} catch (Exception ex) {
        		throw new ServiceException("ProcessVOA Failed to invoke FinancialsRuleService:" + ex.getMessage(), ex);
        	}

        	   //after rules run, set back the orig effective and completed dates that we took before removing on file test
        	   if (onFile.getIncomeTest(incomeYear) != null) {
        		   onFile.getIncomeTest(incomeYear).setEffectiveDate(origEffectiveDate);
        		   onFile.getIncomeTest(incomeYear).setAgreesToPayDeductible(Boolean.TRUE);
        		   if (onFile.getIncomeTest(incomeYear).getIncomeTestStatus() != null) {
        			   onFile.getIncomeTest(incomeYear).getIncomeTestStatus().setCompletedDate(origEffectiveDate);
        		   }
        	   }
        	}


    	if (isNewPerson) {
    		incoming.setJustAdded(true);
    	}


        // Process rules related to eligibility factor
        EEResultInfo clvInfo = new EEResultInfo();
        try {
	        efrs.processSpecialFactors(incoming, sendingFacility, onFile,false, clvInfo);

	        efrs.processIncompetenceRuling(incoming.getIncompetenceRuling(), onFile);
	        efrs.processEmergencyResponseIndicator(incoming, onFile);
        } catch (Exception ex) {
        	throw new ServiceException("ProcessVOA Failed to invoke Special Factors:" + ex.getMessage(), ex);
        }

        // Provide the onFile person (data all merged, etc.) and calculate E&E. We
        // want to trigger letters so we invoke assessEEImpact by passing false.
        Person calculatedPerson = this.getEligibilityEnrollmentService().assessEEImpact(onFile, false, false, sendingFacility);

        //trigger msds if not any existing response

        try {
	    	if (isNewPerson || calculatedPerson.getMilitaryService().getMilitaryServiceQueryStatus() == null ||
	    			!calculatedPerson.getMilitaryService().getMilitaryServiceQueryStatus().getCode().
	    			equals(MilitaryServiceQueryStatus.MILITARY_SERVICE_DATA_RECEIVED.getCode())) {
	    		this.processTriggerBroker(calculatedPerson);


	    		calculatedPerson.getMilitaryService().setMilitaryServiceQueryStatus(
						this.getLookupService().getMilitaryServiceQueryStatusByCode(MilitaryServiceQueryStatus.QUERIED_PENDING_RESPONSE.getCode()));

	    	}
        } catch (Exception ex) {
        	logger.error("Failed to trigger MSDS query for person:" + calculatedPerson.getPersonEntityKey().getKeyValueAsString());
        }


        //trigger vba if not any good response
        try {

        	if (isNewPerson || calculatedPerson.getEligibilityVerification() == null || calculatedPerson.getEligibilityVerification().getAacIndicator() == null ||
        			(!calculatedPerson.getEligibilityVerification().getAacIndicator().getCode().equals(AACIndicator.CODE_DATA_ACCEPTED.getCode()) &&
        			!calculatedPerson.getEligibilityVerification().getAacIndicator().getCode().equals(AACIndicator.CODE_DATA_RECEIVED.getCode()))) {

        		this.getMessagingService().generateMessage(calculatedPerson.getPersonEntityKey(),
                		calculatedPerson.getIdentityTraits().getVpid().getKeyValueAsString());

        		if (calculatedPerson.getEligibilityVerification() == null) {
        			EligibilityVerification eligibilityVerification = new EligibilityVerification();
        			calculatedPerson.setEligibilityVerification(eligibilityVerification);
        		}

        		calculatedPerson.getEligibilityVerification().setAacIndicator(this.getLookupService().
    					getAACIndicatorByCode(AACIndicator.CODE_AAC_QUERIED_PENDING.getCode()));


        	}

        } catch (Exception ex) {
        	logger.error("Failed to trigger VBA query for person:" + calculatedPerson.getPersonEntityKey().getKeyValueAsString());
        }

        EventRuleService eventRuleService = this.getEventRuleService();

        //force the z10 in the message event
        eventRuleService.processMessageEvents(onFile, verificationInfo, false, false);
        //trigger explicit Z05 for new person
        if (isNewPerson) {
        	getTriggerRouter().processTriggerEvent(getTriggerEvent(onFile.getPersonEntityKey()));
        }

        eventRuleService.managePersonEvents(calculatedPerson);

        //calling processPerson for handbook trigger rule flow
        ers.processHandBookTriggers(calculatedPerson, onFile, null, verificationInfo);

         //make a backward compatibility when there was just on PF in old times
         if(calculatedPerson.getMostRecentPreferredFacility() == null)
         {
        	 Set pfs = calculatedPerson.getPreferredFacilities();
        	 if (pfs != null && pfs.size() > 0)
        		 this.getPreferredFacilityService().setCalculatedMostRecentPreferredFacility(calculatedPerson);
         }

        //update VOA_INDICATOR from incoming person if not null
         if (incoming.getVOAIndicator() != null) {
        	 calculatedPerson.setVOAIndicator(incoming.getVOAIndicator());
         }

         try {
      	   // Persist person
           onFile = this.getPersonService().save(calculatedPerson);
         }
         catch(Exception e){
        	 logger.error("Exception occurred while trying to save the person in ADR: " + e);
         }


     	if (isNewPerson){
     		this.getHelperService().addToSSNVerificationQueue(incoming);
        }


        if(logger.isDebugEnabled() && incoming != null) {
        	logger.debug( "MessagingServiceImpl.processVOA(): END processing VOA rules for person VPID= " + incoming.getVPIDValue());
        }

        if (isNewPerson) {
        	onFile.setJustAdded(true);
        } else {
        	onFile.setJustAdded(false);
        }

        return onFile;
    }

    private TriggerEvent getTriggerEvent(PersonEntityKey key)
	{
		PersonTriggerEvent triggerEvent = new PersonTriggerEvent(PersonTrigger.DestinationType.MESSAGING,
                PersonTrigger.TargetType.VISTA, PersonTrigger.DispatchType.NOTIFY,  PersonTrigger.DataType.ADDRESS);
		triggerEvent.setPersonId(key);
		return triggerEvent;

	}

    /**
     * @see gov.va.med.esr.service.MessagingService#processZ07(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.lookup.VAFacility, gov.va.med.esr.common.model.ee.VerificationInfo)
     */
    @SuppressWarnings({ "unused", "rawtypes" })
	public Person processZ07(Person person, VAFacility sendingFacility, VerificationInfo verificationInfo) throws ServiceException {
        if(logger.isDebugEnabled())
        	logger.debug("MessagingServiceImpl.processORUZ07(): Start processing rules for person " +person.getPersonEntityKey().getKeyValueAsString());

       Validate.notNull(person, "A veteran must not be null");

        PersonEntityKey personKey = null;

        // existing person for process Z07
        personKey = person.getPersonEntityKey();

        // 10471 -- start of original processZ07 logic

        // No Optimistic Lock check is done for messaging
        Person onFile = this.getPersonService().getPerson( personKey );

        //CCR 11666:
        boolean isNewPerson = false;
        if (onFile.getEnrollmentDeterminations() == null || onFile.getEnrollmentDeterminations().size() == 0)
        {
        	isNewPerson = true;
        }

        if (onFile == null) {
        	throw new ServiceException("Cannot process person. onFile person not found for personKey " + personKey.getKeyValueAsString());
        }

        String dfn = (verificationInfo != null) ? verificationInfo.getDfn() : null;

        //CCR 13960, new reg z07, default PF to sending site if not present
        if (isNewPerson) {

            /* I7276837FY16 - changing to default to incoming preferred facility
            if (onFile.getPreferredFacility() == null) {

                     logger.info("Defaulting PF for new Z07 for person: " + person.getPersonEntityKey().getKeyValueAsString());

                     PreferredFacility pf = new PreferredFacility();
                     pf.setAssignmentDate(new Date());
                     pf.setFacility(sendingFacility);
                     pf.setSourceDesignation(this.getLookupService().getSourceDesignationByCode(SourceDesignation.CODE_ESR.getCode()));
                  onFile.addPreferredFacility(pf);
              }
              */

           if(person.getPreferredFacility() != null){

               logger.info("Defaulting PF for new Z07 for person: " + person.getPersonEntityKey().getKeyValueAsString());

               PreferredFacility pf = new PreferredFacility();
               pf.setAssignmentDate(new Date());
               pf.setFacility(person.getPreferredFacility());
               pf.setSourceDesignation(this.getLookupService().getSourceDesignationByCode(SourceDesignation.CODE_ESR.getCode()));
               onFile.addPreferredFacility(pf);

           }
        }
        else{ // I7276837FY16 - changing to default to incoming preferred facility
            if(person.getPreferredFacility() != null){
                if(onFile.getPreferredFacility() == null ||
                        (!onFile.getPreferredFacility().getCode().equals(person.getPreferredFacility().getCode()))){

                	logger.info("Defaulting PF for new Z07 for person: " + person.getPersonEntityKey().getKeyValueAsString());

                	PreferredFacility pf = new PreferredFacility();
                    pf.setAssignmentDate(new Date());
                    pf.setFacility(person.getPreferredFacility());
                    pf.setSourceDesignation(this.getLookupService().getSourceDesignationByCode(SourceDesignation.CODE_ESR.getCode()));
                    onFile.addPreferredFacility(pf);
                }
            }
        }

        /***
	        RTC Defect # 288240 - Not allow VistA to change on file Non Veteran status to Veteran status as part of Inbound Z07 to ESR (REEG_00010492)
	        RTC Defect # 343934 - Not allow VistA to change on file Veteran if Eligibility is verified as part of Inbound Z07 to ESR (SDM # I8733636FY16)
	        This is only applicable if eligibility status is Verified.
	        Instead incoming veteran status is updated as per on file veteran status if eligibility is verified.
	        If eligibility is non verified then processing of Z07 will happen as usual i.e. allow VistA to change veteran status within ESR.
        ***/
        if (onFile.getEligibilityVerification() != null &&
        		onFile.getEligibilityVerification().getEligibilityStatus() != null &&
        		onFile.getEligibilityVerification().getEligibilityStatus().getCode() != null &&
        		onFile.getEligibilityVerification().getEligibilityStatus().getCode().equals(EligibilityStatus.CODE_VERIFIED.getCode()) &&
        		onFile.isVeteran() != null &&
        		person.isVeteran() != null &&
        		(onFile.isVeteran().booleanValue() != person.isVeteran().booleanValue())) {
        	logger.info("VistA is not allowed to change on file Non Veteran status to Veteran status. Changing incoming person veteran status to on file veteran status..");
        	//throw new ServiceException("VistA is not allowed to change on file Non Veteran status to Veteran status for personKey " + personKey.getKeyValueAsString());
        	//Update incoming person veteran status to on file veteran status
        	person.setVeteran(onFile.isVeteran());
        }

        // Process rules related to a new person
        EligibilityFactorRuleService efrs = this.getEligibilityFactorRuleService();
        efrs.processNewVeteran(person, onFile);

        // Process rules related to addresses and phones

        ContactInfoRuleService contactInfoRuleservice = this.getContactInfoRuleService();
        //CCR11898 Added verificationInfo
        if(verificationInfo == null)
        	verificationInfo = new VerificationInfo();
        contactInfoRuleservice.processAddresses( person.getAddresses(), onFile, sendingFacility, verificationInfo);
        contactInfoRuleservice.processPhones( person, onFile, sendingFacility );
        contactInfoRuleservice.processElectronicAddresses(person.getEmails(), onFile, sendingFacility);

        // Process rules related to insurance
        InsuranceRuleService insRuleService = this.getInsuranceRuleService();
        insRuleService.processInsurancePolicies( person.getInsurances(), onFile , sendingFacility);
        insRuleService.processMedicaidFactor(person.getMedicaidFactor(), onFile);

        // Process rules related to association
        AssociationRuleService associationRuleService = this.getAssociationRuleService();
        // RTC Defect # 191512 - The Z05 does not include a ZCT deleted for contacts/associates
        // VistA is not able to handle remove associates even if ESR sends it to VistA through outbound Z05
        // Till VistA implements the functionality to delete associate based upon empty ZCT associate segment values
        // code is commented out to not to send delete associate to VistA through outbound Z05
        // flag is set from true to false to revert to ESR production behaviour
        boolean isAssociationsToBeRemoved = associationRuleService.processAssociations( false, person.getAssociations(), onFile, sendingFacility);

        // Process rules related to pow
        POWRuleService powRuleService = this.getPowRuleService();
        powRuleService.processPOW(person, onFile, sendingFacility);
        // Process rules related to purple heart
        PurpleHeartRuleService phRuleService = this.getPurpleHeartRuleService();
        phRuleService.processPurpleHeart(person, onFile, verificationInfo,sendingFacility);

        // Link Person Registries.
        this.getRegistryService().linkPersonRegistries(person, onFile, false);
        // Process rules related to clinical determination
        // We have to do on-the-fly CD conversion for sites that are sending old conditions/procedures/diagnoses records
        CatastrophicDisability cd = person.getCatastrophicDisability();
        if (cd != null && cd.isCatastrophicallyDisabled() != null && cd.isCatastrophicallyDisabled().booleanValue() &&
        		cd.getCDDescriptors().isEmpty()) {
        	this.convertCDOnly(person);
        }
        ClinicalDeterminationRuleService cdrs = this.getClinicalDeterminationRuleService();
        cdrs.processClinicalDetermination(person, onFile,sendingFacility,null);

        // Process rules related enrollment
        EligibilityEnrollmentRuleService ers = this.getEnrollmentRuleService();
        // CodeCR 7327 - apply rules before accepting received enrollment and application
        //ers.processReceivedEnrollment(person.getReceivedEnrollment(), onFile);
        //ers.processApplication(person.getApplication(), onFile);
        ers.processPerson(person, onFile, sendingFacility, verificationInfo,false);
        ers.processReceivedEligibilities(person, onFile, verificationInfo, false);
        ers.processIneligibility(person, onFile, sendingFacility, verificationInfo,false);

        MilitaryRuleService mrs = this.getMilitaryRuleService();
        mrs.processMilitaryService(sendingFacility,dfn,person.getMilitaryService(), onFile,null);
        //This call calculates the POS from an updated person and sets it to on file person.
        mrs.calculatePeriodOfService(person,onFile,UseCaseName.MS, null);

        // Process rules related to demographic
        DemographicRuleService drs = this.getDemographicRuleService();

        //RTC WI 348581, filter sites by parameter list during MVI authority rollout
        String mviActiveForSite = "N";
        String[] siteList = this.getSystemParameterService().getByName("VISTA_ROLLOUT_SITE_LIST").getValue().split(",");

        if (this.getSystemParameterService().getByName(MVI_DOD_ACTIVE).getValue().equalsIgnoreCase("Y") ||
        		Arrays.asList(siteList).contains(sendingFacility.getStationNumber())) {
        	mviActiveForSite = "Y";
        }

        drs.processDemographicInfo(sendingFacility, person, onFile,false, mviActiveForSite);

        Map incomeTests = person.getIncomeTests();
        Map financialStatements = person.getFinancialStatements();
        Map patientVisitSummaries = person.getPatientVisitSummaries();
        Map beneficiaryTravels = person.getBeneficiaryTravels();

        FinancialInfoRuleService frs = this.getFinancialInfoRuleService();
        EEResultInfo info = new EEResultInfo();
        if( MapUtils.isNotEmpty( incomeTests ) ||
            MapUtils.isNotEmpty(financialStatements) ||
            MapUtils.isNotEmpty(patientVisitSummaries) ||
            MapUtils.isNotEmpty(beneficiaryTravels) ) {

           Integer incomeYear =  getIncomeYear(incomeTests, financialStatements, patientVisitSummaries, beneficiaryTravels);
           //frs.processFinancialInfo( incomeYear, sendingFacility, person, onFile, info);
           //Fix for CR_9070
           frs.processFinancialInfo( incomeYear, sendingFacility, person, onFile, info, true);
        }

        //Process Fee Bases
        frs.processFeeBasis(sendingFacility, person, onFile,false);

        // Process VBA data coming from ORUZ07
        this.getProcessVBARuleService().processVBAData( person, sendingFacility, onFile, verificationInfo );

        /*
         * CCR 10689 Agent Orange deleted from Verified record
         * From this point forward, any rule that considers Veteran Indicator should be using
         * the updated person. The reason is Process VBA rules figure out whether a Veteran Indicator from
         * site will be accepted.  The incoming person needs to be updated by next step with appropriate
         * value in order to minimize ILOG rules changes.
         */
        person.setVeteran(onFile.getVeteran()); // Synchronize Veteran Indicator with updated value

        // Process rules related to eligibiility factor
        efrs.processSpecialFactors(person, sendingFacility, onFile,false);
        efrs.processIncompetenceRuling(person.getIncompetenceRuling(), onFile);
        efrs.processEmergencyResponseIndicator(person, onFile);

        // Provide the onFile person (data all merged, etc.) and calculate E&E. We
        // want to trigger letters so we invoke assessEEImpact by passing false.
        Person calculatedPerson = this.getEligibilityEnrollmentService().assessEEImpact(onFile, false, false, sendingFacility);

        // Trigger QRYZ11 / ORUZ11 if needed
        this.getEventRuleService().processMessageEvents(onFile, sendingFacility, verificationInfo, info.isZ10Sent());

        //Remove all in process data whenever Z07 or Z11 is recieved.
        calculatedPerson.removeAllInProcessFinancials();


        Person pristinePerson = (Person)entityCacheManager.getItem(calculatedPerson.getEntityKey());

        //If the incomming message is the same as the one on file, do not perist
        if(AbstractEntity.matchesDomainValues(pristinePerson, calculatedPerson)) {
           triggerEventCacheManager.removeTriggerEvents();
        }
        else {

        	// INC000001003363   if ZSP.seq7 == "" then delete unemployable=null for this person
	      	  if (person.getServiceConnectionAward() != null
	      			  && calculatedPerson.getServiceConnectionAward() != null
	      			  && person.getServiceConnectionAward().getUnemployable() == null) {
	      		calculatedPerson.getServiceConnectionAward().setUnemployable(null);
	      	  }

        	//For MSDS - needed because may receive Z07 for person after initial seeding rules were applied BUT not before broker is
        	//turned on

        	if(getSystemParameterService().getMSDSReconIndicator().booleanValue()){
        		if(buildMSDRecord(pristinePerson,calculatedPerson)){
        			logger.info("Reconciliation taking place");
                	mrs.processSeedingMilitaryService(onFile.getMilitaryService(), onFile);
                	updateSeedingLogEntry(calculatedPerson,SeedStatus.COMPLETE.getCode(), verificationInfo.getMessageControlId());
                	logger.info("Completed Reconciliation");
                }
        	}

        	this.getEventRuleService().processPersonEventsForPensionBulletin(calculatedPerson);

        	 //calling processPerson for handbook trigger rule flow
             ers.processHandBookTriggers(calculatedPerson, onFile, null, verificationInfo);

             //CCR 11666/CCR 13395: insert into SSA verification queue
            if (isNewPerson) {
            	this.getHelperService().addToSSNVerificationQueue(person);
            }


            //CCR10679 used to undo
            //mrs.processCreateHECRecord(person.getMilitaryService(), onFile);

            // RTC Defect # 191512 - The Z05 does not include a ZCT deleted for contacts/associates
            // VistA is not able to handle remove associates even if ESR sends it to VistA through outbound Z05
            // Till VistA implements the functionality to delete associate based upon empty ZCT associate segment values
            // code is commented out to not to send delete associate to VistA through outbound Z05
            // flag is set from true to false to revert to ESR production behaviour
            /**
        	if (isAssociationsToBeRemoved) {
                //Process Z05 delete trigger is applicable
        		Person updatedPerson = (Person)calculatedPerson.clone();
        		associationRuleService.upddateAssociations( person.getAssociations(), updatedPerson, sendingFacility);

        		associationRuleService.triggerZ05(sendingFacility, updatedPerson);

        		//Remove associations
        		associationRuleService.removeAssociations( person.getAssociations(), calculatedPerson, sendingFacility);
        	}
        	**/

            // Persist person
        	onFile = this.getPersonService().save(calculatedPerson);

        	//RTC Defect # 355422 Added calculate Most recent preferred facility
        	//Update's the calculated mostRecentPreferredFacility on Person
        	this.getPreferredFacilityService().setCalculatedMostRecentPreferredFacility(onFile);
        }

        if(logger.isDebugEnabled()) {
        	   logger.debug( "MessagingServiceImpl.processORUZ07(): End processing rules for person " +
        	                 person.getPersonEntityKey().getKeyValueAsString() );
        }
        return onFile;
    }

    /*
     * CCR 10892
     * This method provides behavior specific to Add a Person.
     *
     * @see gov.va.med.esr.service.MessagingService#processAddPerson(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.lookup.VAFacility, gov.va.med.esr.common.model.ee.VerificationInfo)
     */
    @SuppressWarnings("rawtypes")
	public Person processAddPerson(Person incoming, VAFacility sendingFacility, VerificationInfo verificationInfo) throws ServiceException {
        Validate.notNull(incoming, "A veteran must not be null");
        Validate.notNull(sendingFacility, "A sending facility must not be null");

        // No Optimistic Lock check is done for messaging
        //CCR 11788: get the person with 200ESR Correlation so in case the PV is not synced up with the
        //add a person update yet due to MVI delay
        Person onFile = this.getPersonService().get200ESRPerson(incoming.getPersonEntityKey() );

        if (onFile == null) {
        	throw new ServiceException("Cannot process person. onFile person not found for" + incoming.getPersonEntityKey());
        }

        if (sendingFacility.getCode().equals("200ESR")) {
        	sendingFacility = this.getLookupService().getVaFacilityByCode(VAFacility.CODE_HEC.getCode());
        }
        // This won't get transferred unless we do it here.
        onFile.setVeteran(incoming.getVeteran());

        EligibilityEnrollmentRuleService ers = this.getEnrollmentRuleService();
        ers.processServiceConnectedIndicator(incoming);
        ers.manageReceivedEligibilities(incoming, onFile);
        ers.manageIneligibility(incoming, onFile);
        ers.manageApplication(incoming.getApplication(), onFile);
        //CCR16024
        ers.manageHealthBenefitProfile(incoming, onFile);

        // Rules related to Eligibility Factors
        EligibilityFactorRuleService efrs = this.getEligibilityFactorRuleService();
        efrs.manageSpecialFactors(incoming, onFile);
        efrs.processIncompetenceRuling(incoming.getIncompetenceRuling(), onFile);
        efrs.processEmergencyResponseIndicator(incoming, onFile);
        // Handle Eligibility Verification data, etc.
        efrs.manageNewVeteranFromAddPerson(incoming, onFile);
        efrs.manageMonetaryBenefitAward(incoming.getMonetaryBenefitAward(), onFile);
        // Merge the Service Connection Award data from ESR UI
        efrs.processServiceConnectionAward(incoming.getServiceConnectionAward(), onFile);

        // Update PH only when something is changed.
        if (!AbstractEntity.matchesDomainValues(incoming.getPurpleHeart(), onFile.getPurpleHeart()))
        {
            this.getPurpleHeartRuleService().managePurpleHeart(incoming, onFile);
    	    //Step 4:Link purpleHeart registry.
            PurpleHeart incomingPurpleHeart = incoming.getPurpleHeart();
    	    if(incomingPurpleHeart != null && onFile != null && incomingPurpleHeart.getRegistryTrait() == null)
    	    {
    	    	RegistryType regType=(RegistryType)this.getLookupService().getByCode(RegistryType.class,
    	    			RegistryType.CODE_PH_REGISTRY.getCode());
    	    	this.getRegistryService().linkPersonRegistry(incomingPurpleHeart,
    	    			onFile.getPurpleHeart(), incoming, onFile, regType, true);
    	    }
        }

        // If any part of the permanent address updatable fields changed, update the change date
        // with the transaction timestamp
        if (!AbstractEntity.matchesDomainValues(incoming.getPermanentAddress(), onFile.getPermanentAddress())) {
        	// CCR 11105 - AAP always provides Perm Address, but just in case someone is trying to break things I put a guard
        	if (incoming.getPermanentAddress() != null) {
                incoming.getPermanentAddress().setChangeDate(
                        new Date(getTimestampManager().getTransactionTimestamp().getTime()));
        	}
        }

        if (!AbstractEntity.matchesDomainValues(incoming.getResidentialAddress(), onFile.getResidentialAddress())) {
        	// CCR 11105 - AAP always provides Perm Address, but just in case someone is trying to break things I put a guard
        	if (incoming.getResidentialAddress() != null) {
                incoming.getResidentialAddress().setChangeDate(
                        new Date(getTimestampManager().getTransactionTimestamp().getTime()));
        	}
        }
        if (!AbstractEntity.matchesDomainValues(incoming.getTemporaryCorrespondenceAddress(), onFile.getTemporaryCorrespondenceAddress())) {
        	// CCR 11105 - AAP always provides Perm Address, but just in case someone is trying to break things I put a guard
        	if (incoming.getTemporaryCorrespondenceAddress() != null) {
                incoming.getTemporaryCorrespondenceAddress().setChangeDate(
                        new Date(getTimestampManager().getTransactionTimestamp().getTime()));
        	}
        }
        if (!AbstractEntity.matchesDomainValues(incoming.getConfidentialAddress(), onFile.getConfidentialAddress())) {
        	// CCR 11105 - AAP always provides Perm Address, but just in case someone is trying to break things I put a guard
        	if (incoming.getConfidentialAddress() != null) {
                incoming.getConfidentialAddress().setChangeDate(
                        new Date(getTimestampManager().getTransactionTimestamp().getTime()));
        	}
        }
        
     
        // Rules related to Contact Information
        ContactInfoRuleService contactInfoRuleservice = this.getContactInfoRuleService();
        contactInfoRuleservice.manageAddresses(incoming.getAddresses(), onFile, true); //CCR 11789: add the flag
        contactInfoRuleservice.managePhones(incoming.getPhones(), onFile, true); //CCR 11789: add the flag
        contactInfoRuleservice.manageElectronicAddresses(incoming.getEmails(), onFile);

        // Rules related to Insurance
        InsuranceRuleService insRuleService = this.getInsuranceRuleService();
        insRuleService.manageInsurancePolicies( incoming.getInsurances(), onFile , sendingFacility);
        insRuleService.manageMedicaidFactor(incoming.getMedicaidFactor(), onFile);

        // Decided to leave this as-is since it's possible multiple associations provided
        AssociationRuleService associationRuleService = this.getAssociationRuleService();
        associationRuleService.processAssociations( incoming.getAssociations(), onFile, sendingFacility);

        // Rules related to Prisoner of War
        PrisonerOfWar incomingPOW = incoming.getPrisonerOfWar();
        this.getPowRuleService().managePOW(incoming, onFile);

        // Link POW Registry
        if (incomingPOW != null && onFile != null && incomingPOW.getRegistryTrait() == null)
        {
            RegistryType regType = (RegistryType)this.getLookupService()
                .getByCode(RegistryType.class, RegistryType.CODE_POW_REGISTRY.getCode());
            this.getRegistryService().linkPersonRegistry(incomingPOW, onFile.getPrisonerOfWar(), incoming, onFile, regType, true);
        }

        // These rules will not do anything for Add a Person
        // since this data only is received from Z07
        ClinicalDeterminationRuleService cdrs = this.getClinicalDeterminationRuleService();
        cdrs.processClinicalDetermination(incoming, onFile,sendingFacility,null);

        // Rules for Military Service
        MilitaryRuleService mrs = this.getMilitaryRuleService();
        onFile = mrs.manageMilitaryService(incoming.getMilitaryService(), onFile);

        // This call calculates the POS from an updated person and sets it to on file person.
        mrs.calculatePeriodOfService(incoming,onFile,UseCaseName.MS, null);

        // Link Shad registry.
        SHAD incomingShad = incoming.getShad();
        if(incomingShad != null &&
        		onFile != null &&
        		incomingShad.getRegistryTrait() == null)
        {
        	RegistryType regType=(RegistryType)this.getLookupService().getByCode(RegistryType.class,
        			RegistryType.CODE_SHAD_REGISTRY.getCode());
        	this.getRegistryService().linkPersonRegistry(incomingShad, onFile.getShad(),
        			incoming, onFile, regType, true);
        }

        // Rules related to Demographics
        DemographicRuleService drs = this.getDemographicRuleService();
        drs.manageDemographicInfo(incoming, onFile);

        // Rules related to Financials
        Map incomeTests = incoming.getIncomeTests();
        Map financialStatements = incoming.getFinancialStatements();
        Map patientVisitSummaries = incoming.getPatientVisitSummaries();
        Map beneficiaryTravels = incoming.getBeneficiaryTravels();

        // The approach used here is based on assumption that Add a Person will only be called for
        // a new person. Therefore, only one income year will be found in the data and that year
        // will be used for calculations.
        EEResultInfo info = new EEResultInfo();
        if( MapUtils.isNotEmpty( incomeTests ) ||
              MapUtils.isNotEmpty(financialStatements) ||
              MapUtils.isNotEmpty(patientVisitSummaries) ||
              MapUtils.isNotEmpty(beneficiaryTravels) ) {
            FinancialInfoRuleService frs = this.getFinancialInfoRuleService();
            Integer incomeYear =  getIncomeYear(incomeTests, financialStatements, patientVisitSummaries, beneficiaryTravels);
            if (incomeYear != null) {
                frs.manageFinancialAssessment(incomeYear, sendingFacility, incoming, onFile, info);
            }
        }

        // Calculate EE. Notice that data is FROM the ESR UI.
        Person calculatedPerson = this.getEligibilityEnrollmentService().assessEEImpact(onFile, true, false, sendingFacility);

        // Registration Events: Trigger QRYZ11 / ORUZ11 if needed AND Bulletins
        EventRuleService eventRuleService = this.getEventRuleService();
        verificationInfo.setUnsolicitedType(null); // don't take message path
        eventRuleService.processMessageEvents(onFile, verificationInfo, info.isZ10Sent(), true);
        eventRuleService.managePersonEvents(onFile);

        Person pristinePerson = (Person)entityCacheManager.getItem(calculatedPerson.getEntityKey());

    	//CCR 11666: insert into SSA verification queue
    	this.getHelperService().addToSSNVerificationQueue(pristinePerson);

        //If the incomming message is the same as the one on file, do not perist
        if(AbstractEntity.matchesDomainValues(pristinePerson, calculatedPerson)) {
           triggerEventCacheManager.removeTriggerEvents();
        }
        else {
        	 //calling processPerson for handbook trigger rule flow
             ers.processHandBookTriggers(incoming, onFile, null, verificationInfo);

        	// Persist person
        	onFile = this.getPersonService().save(calculatedPerson, true);

        }

        return onFile;
    }

    /**
     * @see gov.va.med.esr.service.MessagingService#processORUZ12(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.lookup.VAFacility)
     */
    public Person processORUZ12(Person person, VAFacility sendingFacility) throws ServiceException {
        return this.receiveZ12Z13Data(person, sendingFacility, ORUZ12);
    }

    /**
     * @see gov.va.med.esr.service.MessagingService#processORUZ13(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.lookup.VAFacility)
     */
    public Person processORUZ13(Person person, VAFacility sendingFacility) throws ServiceException {
        return this.receiveZ12Z13Data(person, sendingFacility, ORUZ13);
    }

    /**
     * @see gov.va.med.esr.service.MessagingService#processQRYZ10(gov.va.med.esr.common.model.person.Person, java.lang.Integer, gov.va.med.esr.common.model.ee.IncomingMessageInfo)
     */
    public void processQRYZ10(Person person, Integer incomeYear, IncomingMessageInfo incomingMessageInfo) throws ServiceException {
        this.processQRYMessages(person, incomeYear, incomingMessageInfo, QRYZ10);
    }

    /**
     * @see gov.va.med.esr.service.MessagingService#processQRYZ11(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.ee.IncomingMessageInfo)
     */
    public void processQRYZ11( Person person, IncomingMessageInfo incomingMessageInfo) throws ServiceException {
        this.processQRYMessages(person, null, incomingMessageInfo, QRYZ11);
    }

    /**
     * Retransmits a message.
     *
     * @param identifier message log entry id.
     * @throws ServiceException
     */
    public void triggerRetransmission(BigDecimal identifier) throws ServiceException {

        RetransmitTriggerEvent event = new RetransmitTriggerEvent(identifier, true);
        event.setName(RETRANSMIT_EVENT);

        // Process the event
        triggerRouter.processTriggerEvent(event);
    }

    /**
     * @see gov.va.med.esr.service.MessagingService#triggerQRYZ07(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.lookup.VAFacility, java.lang.Integer, boolean)
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
	public void triggerQRYZ07( Person person, VAFacility facility, Integer incomeYear, boolean handleAfterCommit) throws ServiceException {

       Validate.notNull(facility, "Facility can not be null");
       Validate.notNull(incomeYear, "Income Year can not be null");
       Validate.notNull(person, "Person can not be null");

       // Create the VAFacility Site List
       Set sites = new HashSet(1);
       sites.add(facility);

       // Create the trigger event
       IncomeYearTriggerEvent incomeEvent =
          new IncomeYearTriggerEvent( PersonTrigger.DestinationType.MESSAGING,
                                      PersonTrigger.TargetType.VISTA,
                                      PersonTrigger.DispatchType.QUERY,
                                      PersonTrigger.DataType.FULL_DATA );

       incomeEvent.setIncomeYear(incomeYear);
       incomeEvent.setPersonId(person.getPersonEntityKey());
       incomeEvent.setIdentityTraits(person.getIdentityTraits());
       incomeEvent.setTargetSites(sites);

       // Process the event
       if( handleAfterCommit ) {
          Set events = new HashSet(1);
          events.add(incomeEvent);
          triggerEventCacheManager.storeTriggerEvents(events);
       }
       else {
          triggerRouter.processTriggerEvent(incomeEvent);
       }
    }

    /**
     * Trigger a QRYZ07 message instantly
     *
     * @param person The person whose data is being queries
     * @param facility The facility to send the message to
     * @param incomeYear The income year to query on
     * @throws ServiceException
     */
    public void triggerQRYZ07(Person person, VAFacility facility, Integer incomeYear) throws ServiceException {
       triggerQRYZ07(person, facility, incomeYear, false);
    }

    /**
     * @see gov.va.med.esr.service.MessagingService#findSiteLastTransmittedMsg(gov.va.med.fw.model.EntityKey)
     */
    public VAFacility findSiteLastTransmittedMsg(EntityKey personId) throws ServiceException {

       try {
          String siteCode = getMessageLogEntryDAO().findSiteLastTransmittedMsg(personId);
          if (siteCode != null){
              return getLookupService().getVaFacilityByCode(siteCode);
          }
          return null;
       }
       catch (DAOException e) {
          throw new ServiceException( "Failed to Retrieve the Site that last trsmitted the Message",e );
       }
    }


	public boolean processCDConversion(Person incoming) throws ServiceException {
		if (logger.isDebugEnabled())
			logger.debug("MessagingServiceImpl.processCDConversion(): Start CD conversion for person "
					+ incoming.getPersonEntityKey().getKeyValueAsString());

		Validate.notNull(incoming, "A veteran must not be null");

		PersonEntityKey personKey = incoming.getPersonEntityKey();
		Person onFile = this.getPersonService().getPerson(personKey);

		if (onFile == null) {
			throw new ServiceException("Cannot process person. onFile person not found for personKey " + personKey.getKeyValueAsString());
		}

		// z11Required=true means that conversion produced CDDescriptor(s)or it already existed.
		boolean z11Required = convertCDOnly(onFile);
		if (z11Required) {
			this.getEventRuleService().processMessageEvents(onFile);
		}

		// Persist person
		this.getPersonService().save(onFile);

		return z11Required;
	}


	/**
	 * Convert the CD to use descriptors. Leaves the existing CDP sets untouched.
	 * Return true if descriptor added/exists
	 */
	@SuppressWarnings("unchecked")
	public boolean convertCDOnly(Person person) throws ServiceException {
		CatastrophicDisability cd = person.getCatastrophicDisability();
		Set<CDDescriptor> descriptorsToAdd = new HashSet<CDDescriptor>();
		boolean existsAlready = false;

		if (cd != null) {

			Set<CDDescriptor> descriptors = cd.getCDDescriptors();
			if (descriptors.size() > 0) {
				existsAlready = true;
			}

			Set<CDCondition> conditions = cd.getConditions();
			for (CDCondition cdCondition : conditions) {
				String code = cdCondition.getCondition().getCode();
				String translated = (String)getConditionMap().get(code);
				if (translated != null) {
					descriptorsToAdd.add(buildCDDescriptor(translated));
				}
			}
			Set<CDProcedure> procedures = cd.getProcedures();
			for (CDProcedure cdProcedure : procedures) {
				String code = cdProcedure.getProcedure().getCode();
				String translated = (String)getProcedureMap().get(code);
				if (translated != null) {
					descriptorsToAdd.add(buildCDDescriptor(translated));
				}
			}
			Set<CDDiagnosis> diagnoses = cd.getDiagnoses();
			for (CDDiagnosis cdDiagnosis : diagnoses) {
				String code = cdDiagnosis.getDiagnosis().getCode();
				String translated = (String)getDiagnosisMap().get(code);
				if (translated != null) {
					descriptorsToAdd.add(buildCDDescriptor(translated));				}
			}
			for (CDDescriptor descriptorToAdd : descriptorsToAdd) {
				boolean exists = false;
				for (CDDescriptor descriptor : descriptors){
					if (descriptor.getDescriptorType().getCode().equals(
							descriptorToAdd.getDescriptorType().getCode())) {
						exists = true;
						break;
					}
				}
				if (!exists) {
					cd.addCDDescriptor(descriptorToAdd);
				}
			}
		}
		// We want to force Z11 if descriptor exists
		return descriptorsToAdd.size() > 0 || existsAlready;
	}

	private CDDescriptor buildCDDescriptor(String code) throws ServiceException {
		CDDescriptor cdd = new CDDescriptor();
		cdd.setDescriptorType(this.getLookupService().getDescriptorTypeByCode(code));
		return cdd;
	}

	/**

	public boolean processCDConversion(Person incoming) throws ServiceException {
		if (logger.isDebugEnabled())
			logger.debug("MessagingServiceImpl.processCDConversion(): Start CD conversion for person "
					+ incoming.getPersonEntityKey().getKeyValueAsString());

		Validate.notNull(incoming, "A veteran must not be null");

		PersonEntityKey personKey = incoming.getPersonEntityKey();
		Person onFile = this.getPersonService().getPerson(personKey);

		if (onFile == null) {
			throw new ServiceException("Cannot process person. onFile person not found for personKey " + personKey.getKeyValueAsString());
		}

		// z11Required=true means that conversion produced CDDescriptor(s)or it already existed.
		boolean z11Required = convertCDOnly(onFile);
		if (z11Required) {
			this.getEventRuleService().processMessageEvents(onFile);
		}

		// Persist person
		this.getPersonService().save(onFile);

		return z11Required;
	}


	/**
	 * Convert the CD to use descriptors. Leaves the existing CDP sets untouched.
	 * Return true if descriptor added/exists


	/**
     * Apply use case rules for receving z12 and z13.
     *
     * @param person
     * @param sendingFacility
     * @param context
     * @return
     * @throws ServiceException
     */
    private Person receiveZ12Z13Data(Person person, VAFacility sendingFacility, String context ) throws ServiceException {

       Validate.notNull(person, "A veteran must not be null");

       // No Optimistic Lock check is done for messaging
       Person onFile = this.getPersonService().getPerson( person.getPersonEntityKey() );

       //Proceed only if there is a valid enrollment record. If not, throw exception and
       //stop processing
       validateEnrollmentDetermination(onFile);

       VerificationInfo vi = new VerificationInfo(null, context, null);

       this.getDemographicRuleService().processDemographicInfo(sendingFacility, person, onFile, vi);
       Person calculated = this.getEligibilityEnrollmentService().assessEEImpact(onFile, false, false);

       // Trigger messages as needed
       EventRuleService ers = this.getEventRuleService();
       ers.processMessageEvents(calculated, vi);
       ers.processPersonEvents(calculated);
       //ccr11159 handbook
       ers.manageHandBookEvents(calculated);

       Person pristinePerson = (Person)entityCacheManager.getItem(onFile.getEntityKey());
       if (AbstractEntity.matchesDomainValues(pristinePerson,calculated)) {
          triggerEventCacheManager.removeTriggerEvents();
       }
       else {
          onFile = this.getPersonService().save(calculated);
       }
       return onFile;
    }

    /**
     * @param person
     * @param incomeYear
     * @param incomingMessageInfo
     * @param context
     * @throws ServiceException
     */
    private void processQRYMessages(Person person, Integer incomeYear, IncomingMessageInfo incomingMessageInfo, String context) throws ServiceException {

       Validate.notNull(person, "A veteran must not be null");

       // No Optimistic Lock check is done for messaging
       Person onFile = this.getPersonService().getPerson( person.getPersonEntityKey() );
       VerificationInfo vi = new VerificationInfo(null, context, null);
       this.getEventRuleService().processMessageEvents(onFile, incomeYear, incomingMessageInfo, vi);
    }

   /**
    * @param incomeTests
    * @param financialStatements
    * @param patientVisitSummaries
    * @param beneficiaryTravels
    * @return
    */
   @SuppressWarnings("rawtypes")
private Integer getIncomeYear(Map incomeTests, Map financialStatements, Map patientVisitSummaries, Map beneficiaryTravels) {

      Integer incomeYear;
      if (MapUtils.isNotEmpty(incomeTests)) {
         incomeYear = (Integer) incomeTests.keySet().iterator().next();
      }
      else if (MapUtils.isNotEmpty(financialStatements)) {
         incomeYear = (Integer) financialStatements.keySet().iterator().next();
      }
      else if (MapUtils.isNotEmpty(patientVisitSummaries)) {
         incomeYear = ((SiteYear) patientVisitSummaries.keySet().iterator().next()).getYear();
      }
      else{
         incomeYear = ((SiteYear) beneficiaryTravels.keySet().iterator().next()).getYear();
      }
      return incomeYear;
   }

	/** Specific check to determine if already processed an inbound message */
	public boolean hasProcessedInboundMessage(String messageControlNumber, String stationNumber) throws ServiceException {
		try {
			return messageLogEntryDAO.hasProcessedInboundMessage(messageControlNumber, stationNumber);
		} catch(DAOException e) {
			throw new ServiceException("Unable to execute DAO", e);
		}
	}

    /**
     * find message log entries for VOA that has AA Ack sent, given a person id, in decending datetime order (latest first)
     *
     * @param identifier person id.
     * @return list of log entries
     * @throws ServiceException
     */
	@SuppressWarnings("rawtypes")
	public List findVoaAaAckLogEntryByPersonId(EntityKey personId) throws ServiceException
	{
		try {
			return messageLogEntryDAO.findVoaAaAckLogEntryByPersonId(personId);
		} catch(DAOException e) {
			throw new ServiceException("Unable to execute DAO", e);
		}
	}

	//MSDS Seeding change

	/**
     * @see gov.va.med.esr.service.MessagingService#processK21(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.lookup.VAFacility, gov.va.med.esr.common.model.ee.VerificationInfo)
     */
    public Person processK21(Person person, VAFacility sendingFacility, VerificationInfo verificationInfo) throws ServiceException {
        if(logger.isDebugEnabled())
            logger.debug("MessagingServiceImpl.processRSPK21(): Start processing rules for person " +person.getPersonEntityKey().getKeyValueAsString());

        Validate.notNull(person, "A veteran must not be null");

        // No Optimistic Lock check is done for messaging
        Person onFile = this.getPersonService().getPerson( person.getPersonEntityKey() );

        // Determine if a PSD demographic update has occurred
        //boolean psdDemographicUpdate = determinePsdDemographicUpdate(person, onFile);

        String dfn = (verificationInfo != null) ? verificationInfo.getDfn() : null;

        // Process rules related to a new person -- VERIFY IF THERE IS A NEED TO HANDLE NEW VETERANS
        /*EligibilityFactorRuleService efrs = this.getEligibilityFactorRuleService();
        efrs.processNewVeteran(person, onFile);*/

        /* I NEED TO CREATE MY NEW SEEDING PROCESS RULE Passing in sendingFacility, person.getMilitaryService(), onFile
        MilitaryRuleService mrs = this.getMilitaryRuleService();
        mrs.processMilitaryService(sendingFacility,dfn,person.getMilitaryService(), onFile);
        This call calculates the POS from an updated person and sets it to on file person.
        mrs.calculatePeriodOfService(person,onFile,UseCaseName.MS);

        SeedingRuleService srs = this.getSeedingRuleService();
        srs.processSeedingService(sendingFacility,dfn,person.getMilitaryService(), onFile);*/

        MilitaryRuleService mrs = this.getMilitaryRuleService();
        mrs.processMilitaryService(sendingFacility, dfn, person.getMilitaryService(), onFile,null);
        mrs.processSeedingMilitaryService(person.getMilitaryService(), onFile);



        Person pristinePerson = (Person)entityCacheManager.getItem(onFile.getEntityKey());
        if (AbstractEntity.matchesDomainValues( pristinePerson, onFile )) {
           triggerEventCacheManager.removeTriggerEvents();
        }
        else {
           // Persist person
           this.getPersonService().save(onFile);

           // Notify PSD in case a demographic update occurred. This should be
           // done after all rules
           // have been validated and the person has been saved so we don't
           // accidentally notify PSD
           // before we successfully persist our changes.
           //notifyPsdOfDemographicUpdate(psdDemographicUpdate, onFile);
        }
        return onFile;
    }

    public void triggerQRYZ21(PersonEntityKey key, String VPID) throws ServiceException{

    	Validate.notNull(key, "PersonEntityKey must not be null");
        Validate.notNull(VPID, "VPID must not be null");

 	    VPIDEntityKey entityKey =
            CommonEntityKeyFactory.createVPIDEntityKey(VPID);
        PersonIdentityTraits identityTraits = getPsDelegateService().getIdentityTraits(entityKey);
        // The personTrigger.DataType should be a newly generated one perhaps seeding hence PersonTrigger.DataType.SEEDING
        PersonTriggerEvent personTriggerEvent = new PersonTriggerEvent(PersonTrigger.DestinationType.MESSAGING,
                 PersonTrigger.TargetType.VISTA, PersonTrigger.DispatchType.QUERY,  PersonTrigger.DataType.ELIGIBILITY);
        personTriggerEvent.setPersonId(key);
        personTriggerEvent.setIdentityTraits(identityTraits);

        // Trigger QRYZ21
        getTriggerRouter().processTriggerEvent(personTriggerEvent);
    }

    @SuppressWarnings("rawtypes")
	private boolean buildMSDRecord(Person pristinePerson, Person calculatedPerson) throws ServiceException{
    	boolean seed = false;
    	List seedingLogEntry = getSeedingLogEntrybyPerson(calculatedPerson);

    	if(pristinePerson.getEnrollmentDetermination()== null ){
    		if(calculatedPerson.isVeteran().booleanValue()){
    			seed = true;
    		}
    	}
    	else{
    		if(seedingLogEntry != null && !seedingLogEntry.isEmpty()){
        		if(!AbstractEntity.matchesDomainValues(pristinePerson.getMilitaryService(), calculatedPerson.getMilitaryService())){
        			seed = true;
        		}
        	}

    	}
    	return seed;
    }

	@SuppressWarnings("rawtypes")
	private List getSeedingLogEntrybyPerson(Person person)throws ServiceException{

		List seedingLogEntry = null;
		try {
			seedingLogEntry = seedingLogEntryDAO.find(person);
		} catch (DAOException ex) {
			throw new ServiceException("Failed to log seeding  ", ex);
		}
		return seedingLogEntry;
	}

	private void updateSeedingLogEntry(Person person,String code,String messageControlId)throws ServiceException {

		SeedingLogEntry sle ;

		if(getSeedingLogEntrybyPerson(person) == null || getSeedingLogEntrybyPerson(person).isEmpty()){
			sle = new SeedingLogEntry();
			sle.setPersonId((BigDecimal)person.getPersonEntityKey().getKeyValue());
		}
		else{
			sle = (SeedingLogEntry)getSeedingLogEntrybyPerson(person).get(0);
		}
		sle.setOriginatingProcess("ProcessZ07:" + messageControlId);
		sle.setStatus(getLookupService().getSeedStatusByCode(code));
		try {
			seedingLogEntryDAO.saveObject(sle);
		} catch (DAOException ex) {
			throw new ServiceException("Failed to get seedingDAO  ", ex);
		}

	}

	private WorkflowService getWorkflowService() throws ServiceException {
		// workflowService can not be injected from Spring, use
		// static variable and getComponent()
		if (workflowService == null) {
			workflowService = (WorkflowService) this.getComponent("workflowService", WorkflowService.class);
		}

		return workflowService;
	}

    /**
     * Meena TODO: Place holder for broker message trigger to be utilized by UI request
     * @see gov.va.med.esr.service.MessagingService#triggerBroker(gov.va.med.esr.common.model.person.Person)
     */
    public void processTriggerBroker( Person person) throws ServiceException {

       Validate.notNull(person, "Person can not be null");
       Person onFile = this.getPersonService().getPerson( person.getPersonEntityKey() );
       this.getEventRuleService().processMessageEvents(onFile, true);

    }

	@SuppressWarnings("rawtypes")
	public Map getDiagnosisMap() {
		return diagnosisMap;
	}

	@SuppressWarnings("rawtypes")
	public void setDiagnosisMap(Map diagnosisMap) {
		this.diagnosisMap = diagnosisMap;
	}

	@SuppressWarnings("rawtypes")
	public Map getProcedureMap() {
		return procedureMap;
	}

	@SuppressWarnings("rawtypes")
	public void setProcedureMap(Map procedureMap) {
		this.procedureMap = procedureMap;
	}

	@SuppressWarnings("rawtypes")
	public Map getConditionMap() {
		return conditionMap;
	}

	@SuppressWarnings("rawtypes")
	public void setConditionMap(Map conditionMap) {
		this.conditionMap = conditionMap;
	}

	public GenerateQRYZ11MessagingService getMessagingService() {
		if (messagingService == null) {
			messagingService = (GenerateQRYZ11MessagingService) SingletonApplicationContext
					.getInstance().getSingletonContext()
					.getBean("messagingService");
		}
		return messagingService;
	}


	public void setMessagingService(GenerateQRYZ11MessagingService messagingService) {
		this.messagingService = messagingService;
	}

}