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

// Java classes

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

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

// ESR classes
import gov.va.med.esr.common.model.ee.POWEpisode;
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.PurpleHeartDocument;
import gov.va.med.esr.common.model.ee.SHADDocument;
import gov.va.med.esr.common.model.ee.SHAD;
import gov.va.med.esr.common.model.lookup.NameType;
import gov.va.med.esr.common.model.lookup.RegistryType;
import gov.va.med.esr.common.model.lookup.ReportExceptionType;
import gov.va.med.esr.common.model.person.Name;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.PersonLockedReason;
import gov.va.med.esr.common.model.person.SSN;
import gov.va.med.esr.common.model.person.id.PersonEntityKey;
import gov.va.med.esr.common.model.registry.RegistryLoadStatistics;
import gov.va.med.esr.common.model.registry.Registry;
import gov.va.med.esr.common.model.registry.RegistryTrait;
import gov.va.med.esr.common.persistent.history.HistoryDAO;
import gov.va.med.esr.common.persistent.registry.RegistryDAO;
import gov.va.med.esr.service.EligibilityEnrollmentService;
import gov.va.med.esr.service.LoadRegistryResult;
import gov.va.med.esr.service.PersonIdentityTraits;
import gov.va.med.esr.service.RegistrySearchCriteria;
import gov.va.med.esr.service.RegistrySearchResultBean;
import gov.va.med.esr.service.RegistryService;
import gov.va.med.esr.service.trigger.PersonTrigger;
import gov.va.med.esr.service.trigger.PersonTriggerEvent;
import gov.va.med.fw.service.MultipleRecordsFoundException;
import gov.va.med.fw.service.ServiceOptimisticLockException;
import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.model.VersionedEntityKey;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.persistent.MaxRecordsExceededException;
import gov.va.med.fw.service.ServiceException;
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 gov.va.med.fw.util.StringUtils;

import gov.va.med.fw.validation.ValidationMessage;
import gov.va.med.fw.validation.ValidationMessages;
import gov.va.med.fw.validation.ValidationServiceException;

/**
 * Registry service implementation.
 * 
 * @author Muddaiah Ranga
 * @version 3.0
 */ 
/**
 * @author DNS   KULLAV
 *
 */
public class RegistryServiceImpl extends AbstractHistoricalInfoServiceImpl implements RegistryService {

    private static final long serialVersionUID = 2120843322370727312L;
    private static final String VETERAN_ALREADY_HAS_REGISTRY_DATA = "VETERAN_ALREADY_HAS_REGISTRY_DATA";
    private static final String MULTIPLE_PERSON_RECORDS_FOUND = "MULTIPLE_PERSON_RECORDS_FOUND";

    private EligibilityEnrollmentService eligibilityEnrollmentService = null;
    private String eligibilityEnrollmentServiceName = null;
    private RegistryDAO registryDAO;
    private Map historyDAOs;
    
    private TriggerRouter triggerRouter;

    /* (non-Javadoc)
     * @see gov.va.med.esr.service.RegistryService#getRegistryById(java.math.BigDecimal, gov.va.med.esr.common.model.lookup.RegistryType)
     */
    public Registry getRegistryById(EntityKey entityKey,RegistryType registryType) throws ServiceException {
        try {
            Registry onFileRegistry = this.registryDAO.getById(entityKey,registryType);
            
            Integer incomingVersion = (entityKey instanceof VersionedEntityKey) ? ((VersionedEntityKey)entityKey).getVersion() : null;
                    
            //verify against VersionedEntityKey (if specified)
            if(onFileRegistry != null) {
                if (incomingVersion != null  && !incomingVersion.equals(onFileRegistry.getVersion()))
                    throw new ServiceOptimisticLockException("Version number specified in EntityKey does not match on file Registry",null,onFileRegistry);
            }
            return onFileRegistry;
        } catch(DAOException ex) {
            throw new ServiceException(ex);
        }
    }
    
    /* (non-Javadoc)
     * @see gov.va.med.fw.service.pagination.PaginatedSearchService#search(gov.va.med.fw.service.pagination.SearchQueryInfo)
     */
    public List search(SearchQueryInfo searchCriteria) throws ServiceException {
        List list = null;
        try {
            list = registryDAO.find(searchCriteria);
        }
        catch (DAOException ex) {
            throw new ServiceException("Failed to get the Registry matching the criteria", ex);
        }
        return list;
    }

    /* (non-Javadoc)
     * @see gov.va.med.esr.service.RegistryService#updateRegistry(gov.va.med.esr.common.model.registry.Registry)
     */
    public void updateRegistry(Registry registry) throws ServiceException {
        Validate.notNull(registry, "Registry cannot be null");
        Validate.notNull(registry.getRegistryTrait(), "RegistryTrait() cannot be null");
        try {
            EntityKey registryKey = registry.getEntityKey();
            RegistryType registryType = registry.getRegistryTrait().getRegistryType();
            Registry registryOnFile = null;
            if(registryKey != null) {
                registryOnFile = this.getRegistryById(registry.getEntityKey(),registryType);

                // Ensure that something changed on the incoming entity as compared to the onFile entity
                ensureEntityChanged(registry, registryOnFile);
            } else {
                RegistryTrait onFileTrait = registryDAO.getRegistryTrait(registry);
                if(onFileTrait == null) {
                    registryOnFile = createNewRegistry(registryType);
                } else {
                    ValidationMessages messages = new ValidationMessages();
                    ValidationMessage msg = new ValidationMessage(VETERAN_ALREADY_HAS_REGISTRY_DATA,registryType.getDescription());
                    messages.add(msg);
                    throw new ValidationServiceException(messages);
                }
            }
            registryOnFile = this.getRegistryRuleService().manageRegistry(registry,registryOnFile);
            
            //If the registry is not yet linked to veteran eligibility record, make a link.
            Person person = null;
            try {
                if(registryOnFile.getPerson() == null) {
                     person = this.findMatchingPerson(registryOnFile);
                     //Don't link/add/update registry if an enrolled veteran exists
                     if(person != null) {
                         ValidationMessages messages = new ValidationMessages();
                         ValidationMessage msg = new ValidationMessage(VETERAN_ALREADY_HAS_REGISTRY_DATA,registryType.getDescription());
                         messages.add(msg);
                         throw new ValidationServiceException(messages);
                     }
                }
            }catch(MultipleRecordsFoundException ex) {
                ValidationMessages messages = new ValidationMessages();
                ValidationMessage msg = new ValidationMessage(MULTIPLE_PERSON_RECORDS_FOUND,registryType.getDescription());
                messages.add(msg);                
                throw new ValidationServiceException(messages);
            }
            
            //If the registry is linked to a veteran, perfom EE impact and save the changes
            Person personOnFile = registryOnFile.getPerson();
            if(personOnFile != null) {
                personOnFile = this.getEligibilityEnrollmentService().assessEEImpact(personOnFile, false );
                this.getPersonService().save(personOnFile);
            } else {
                this.registryDAO.save(registryOnFile);
            }
        } catch(DAOException ex) {
            throw new ServiceException(ex);
        }
    }
    
    /**
     * @see gov.va.med.esr.service.RegistryService#processRegistry(gov.va.med.esr.common.model.registry.Registry)
     */
    public LoadRegistryResult processRegistry(Registry registry) throws ServiceException {
        Validate.notNull(registry, "Registry cannot be null");
        Validate.notNull(registry.getRegistryTrait(), "RegistryTrait() cannot be null");

        LoadRegistryResult loadRegistryResult = new LoadRegistryResult();
        
       try {
            boolean exactMatch = false;
            boolean exactMatchSameEpisode = false;            
            boolean ambiguousMatch = false;
            boolean exactMatchDifferentEpisode = false;
            
            // 1. Exact match 
            Registry registryOnFile = findExactMatch(registry); 

            if (registryOnFile == null) {
                RegistryType registryType = registry.getRegistryTrait().getRegistryType(); 
                registryOnFile = createNewRegistry(registryType);                
            } else {

                //2. Exact episode match and Exact match with different episode
                exactMatch = true;
                if (isSameEpisodeMatch(registry, registryOnFile))
                    exactMatchSameEpisode = true;
                else
                    exactMatchDifferentEpisode = true; 
            }
            
            
            // 3. Ambiguous
            ambiguousMatch = isAmbiguousMatch(registry); 
            
	        // Run rules
            loadRegistryResult.setAmbiguousMatch(ambiguousMatch);
            loadRegistryResult.setExactMatch(exactMatch);
            loadRegistryResult.setExactMatchSameEpisode(exactMatchSameEpisode);
            loadRegistryResult.setExactMatchDifferentEpisode(exactMatchDifferentEpisode);
            registryOnFile = this.getRegistryRuleService().processRegistry(registry, registryOnFile, loadRegistryResult);
            
            if(exactMatchDifferentEpisode || (!ambiguousMatch && loadRegistryResult.isNewRegistry()) ) {
                //If the person exists in ESR with the same identity traits, make a link.
                try {
                	if(registryOnFile.getPerson() == null) {	
                        Person person = this.findMatchingPerson(registryOnFile);
                        if(person != null) {
                            RegistryType registryType = registry.getRegistryTrait().getRegistryType();
                            if(registryType.isPurpleHeart()) {
                                person.setPurpleHeart((PurpleHeart)registryOnFile);
                            } else if(registryType.isPrisonerOfWar()) {
                                person.setPrisonerOfWar((PrisonerOfWar)registryOnFile);
                            } else if(registryType.isSHAD()) {
                                person.setShad((SHAD)registryOnFile);
                            }
                        }else{
                        	//P1- Person ID not Assigned : No Match to Person table
                            loadRegistryResult.setExceptionType(
                            		(ReportExceptionType)getLookupService().getByCode(ReportExceptionType.class, ReportExceptionType.P1));                    
                        }
                    }  
                    
                    //If the registry is linked to a veteran, perfom EE impact and save the changes
                    Person personOnFile = registryOnFile.getPerson();
                    if(personOnFile != null) {
                        personOnFile = this.getEligibilityEnrollmentService().assessEEImpact(personOnFile, false );
                        this.getPersonService().save(personOnFile);
                        
                        //DNS   doank CCR8820: send out Z11 messages
                        getTriggerRouter().processTriggerEvent(getTriggerEvent(personOnFile.getPersonEntityKey()));
                		if(this.logger.isDebugEnabled())
                		{
                			this.logger.debug("Triggering ORUZ11 message for person ID " + registry.getPerson().getPersonEntityKey().getKeyValue() + " in " + this.getBeanName());
                        }
                    } else {
                        this.registryDAO.save(registryOnFile);
                    }
                }catch(MultipleRecordsFoundException ex) 
                {
                    loadRegistryResult.setExceptionType(
                    		(ReportExceptionType)getLookupService().getByCode(ReportExceptionType.class, ReportExceptionType.P2));                    
                	//Reset these values so that they do not show up in the statistics report as a new inserted record
                	loadRegistryResult.setNewRegistry(false);
                	loadRegistryResult.setExactMatchDifferentEpisode(false);
                }                
            }
            // Set result
            loadRegistryResult.setResultRegistry(registryOnFile);
            
        } catch(DAOException ex) {
            throw new ServiceException(ex);
        }        
        return loadRegistryResult;
    }

    /* (non-Javadoc)
     * @see gov.va.med.esr.service.RegistryService#getLatestRegistrySummaryByType(gov.va.med.esr.common.model.lookup.RegistryType)
     */
    public RegistryLoadStatistics getMostRecentRegistrySummaryByType(
            RegistryType registryType) throws ServiceException
    {
        try
        {
            return getRegistryDAO().getMostRecentRegistrySummaryByType(
                    registryType);
        } catch (DAOException e)
        {
            throw new ServiceException(
                    "Could not rerieve MostRecentRegistrySummary By Registry Type "
                            + registryType.getCode());
        }
    }    
    
    
    
    // ****************************************** Private Methods ************************************ /


    private Registry createNewRegistry(RegistryType registryType) {
        Registry registry = null;
        if(StringUtils.equals(registryType.getCode(),RegistryType.CODE_PH_REGISTRY.getCode())) {
            registry = new PurpleHeart();
        } else if(StringUtils.equals(registryType.getCode(),RegistryType.CODE_POW_REGISTRY.getCode())) {
            registry = new PrisonerOfWar();
        } else if(StringUtils.equals(registryType.getCode(),RegistryType.CODE_SHAD_REGISTRY.getCode())) {
            registry = new SHAD();
        }
        if(registry != null) registry.setRegistryTrait(new RegistryTrait());
        return registry;
    } 
    
    private Registry findExactMatch(Registry registry)
            throws ServiceException, DAOException
    {
        Registry exactMatch = null;
        RegistrySearchCriteria queryCriteria = new RegistrySearchCriteria();

        if (registry.getRegistryTrait() != null)
        {
            //For POW, Search by ICN first
            if (StringUtils.equals(registry.getRegistryTrait()
                    .getRegistryType().getCode(),
                    RegistryType.CODE_POW_REGISTRY.getCode()))
            {
                PrisonerOfWar pow = (PrisonerOfWar)registry;
                exactMatch = getRegistryDAO().getPOWRegistryByIcn(pow.getIcn());
            }
            
            if (exactMatch == null)
            {            
	            //Match by SSN, Last name, First Name
	            queryCriteria.setSsn(registry.getRegistryTrait().getSsn());
	            queryCriteria
	                    .setLastName(registry.getRegistryTrait().getLastName());
	            queryCriteria.setFirstName(registry.getRegistryTrait()
	                    .getFirstName());
	            queryCriteria.setRegistryType(registry.getRegistryTrait()
	                    .getRegistryType());
	            
	            exactMatch = getRegistryFromQueryCriteria(queryCriteria);
	
            }

            //If no match, then search by SSN, Last name
            if (exactMatch == null)
            {
                queryCriteria.setSsn(registry.getRegistryTrait().getSsn());
                queryCriteria.setLastName(registry.getRegistryTrait()
                        .getLastName());
                queryCriteria.setRegistryType(registry.getRegistryTrait()
                        .getRegistryType());

	            exactMatch = getRegistryFromQueryCriteria(queryCriteria);
            }

            //If no match, then search  SSN, First name
            if (exactMatch == null)
            {
                queryCriteria.setSsn(registry.getRegistryTrait().getSsn());
                queryCriteria.setFirstName(registry.getRegistryTrait()
                        .getFirstName());
                queryCriteria.setRegistryType(registry.getRegistryTrait()
                        .getRegistryType());

	            exactMatch = getRegistryFromQueryCriteria(queryCriteria);
            }
        }

        return exactMatch;

    }
    /**
     * @param exactMatch
     * @param matchedregistryData
     * @return
     * @throws DAOException
     * @throws MaxRecordsExceededException
     */
    private Registry getRegistryFromQueryCriteria(
            RegistrySearchCriteria queryCriteria)
            throws MaxRecordsExceededException, DAOException
    {
        Registry exactMatch = null;
        
        List matchedregistryData = registryDAO.find(queryCriteria);

        if (matchedregistryData != null & matchedregistryData.size() == 1)
        {
            RegistrySearchResultBean searchResult = (RegistrySearchResultBean) matchedregistryData
                    .iterator().next();
            exactMatch = getRegistryDAO().getById(searchResult.getEntityKey(),
                    searchResult.getRegistryType());
        }
        return exactMatch;
    }

    /**
     * Refactor to use a query
     * 
     * @param incomingRegistry
     * @return
     * @throws ServiceException
     * @throws DAOException
     */
    private boolean isAmbiguousMatch(Registry incomingRegistry) throws ServiceException, DAOException
    {
        if (incomingRegistry == null)
            return false;
        
        boolean isAmbiguousMatch = false;
        List matchedregistryData = null;        
        //ChangeRequest-4831. Change such that a NULL SSN will not be 
        //considered for an "ambiguous match".
        if (incomingRegistry.getRegistryTrait() != null 
        		&& !StringUtils.isEmpty(incomingRegistry.getRegistryTrait().getSsn()))
        {
            RegistrySearchCriteria queryCriteria = new RegistrySearchCriteria();
            //Get all records for the ssn and specific registry type
            queryCriteria.setSsn(incomingRegistry.getRegistryTrait().getSsn());
            queryCriteria.setRegistryType(incomingRegistry.getRegistryTrait().getRegistryType());

            matchedregistryData = registryDAO.find(queryCriteria);
        }
        
        RegistryTrait incomingRegistryTrait = incomingRegistry.getRegistryTrait();
        //SSN matched but not any of the other identifying criteria
        if(matchedregistryData != null && !matchedregistryData.isEmpty())
        {
            //Ideally there should be only one record for a SSN. To handle bad data where there are 
        	//multiple SSNs for the same registry type, loop through the search results.
        	//If any one of them is a ambiguous match, then consider the incoming record as ambigous match.
        	for (Iterator iter = matchedregistryData.iterator(); iter.hasNext();) 
        	{
            	RegistrySearchResultBean searchResult = 
            		(RegistrySearchResultBean) iter.next();            

            	Registry matchedRegistry = getRegistryDAO().getById(
                        searchResult.getEntityKey(),
                        searchResult.getRegistryType());
                
                RegistryTrait matchedRegistryTrait = matchedRegistry.getRegistryTrait();
                
                if(!isNameEqual(matchedRegistryTrait.getLastName(), incomingRegistryTrait.getLastName()) &&
    	           !isNameEqual(matchedRegistryTrait.getFirstName(), incomingRegistryTrait.getFirstName()) &&
                   !(matchedRegistryTrait.getDeathDate() != null && incomingRegistry != null && 
                		   matchedRegistryTrait.getDeathDate().equals(incomingRegistryTrait.getDeathDate())))
    	        {
                    isAmbiguousMatch = true;
                    break;
                }
				
			}
        }
        return isAmbiguousMatch;
    }
    
    private boolean isNameEqual(String trait1, String trait2)
    { 
    	//Consider two values as equal only if both of them are not null. 
    	return (trait1 != null && trait2 != null && StringUtils.equalsIgnoreCase(trait1, trait2)); 
    }
    
    private boolean isSameEpisodeMatch(Registry incoming, Registry onFile)
    {
        boolean  isSameEpisodeMatch = false;
        
        if(incoming.getRegistryTrait() != null)
        {
            RegistryType registryType = incoming.getRegistryTrait().getRegistryType();
            
            if (StringUtils.equals(registryType.getCode(),
                    RegistryType.CODE_PH_REGISTRY.getCode()))
            {
                isSameEpisodeMatch = isPurpleHeartDocumentMatch(incoming, onFile);
                
            } else if (StringUtils.equals(registryType.getCode(),
                    RegistryType.CODE_POW_REGISTRY.getCode()))
            {
                isSameEpisodeMatch = isPrisonerOfWarEpisodeMatch(incoming, onFile);
                
            } else if (StringUtils.equals(registryType.getCode(),
                    RegistryType.CODE_SHAD_REGISTRY.getCode()))
            {
                isSameEpisodeMatch = isSHADDocumentMatch(incoming, onFile);                
            }
        }
        
        return isSameEpisodeMatch;
    }    

    
    /**
     * @param incoming
     * @param onFile
     * @param isSameEpisodeMatch
     * @return
     */
    private boolean isSHADDocumentMatch(Registry incoming, Registry onFile)
    {
        boolean isSameEpisodeMatch = false;
        
        SHAD incomingRegistry = (SHAD)incoming;                
        SHAD onFileRegistry = (SHAD)onFile;                

        //Incoming can have only one document
        SHADDocument episode = (incomingRegistry.getDocuments() == null || incomingRegistry
                .getDocuments().isEmpty()) ? null
                : (SHADDocument) incomingRegistry.getDocuments()
                        .iterator().next();
        
        if(onFileRegistry.getDocuments() != null && !onFileRegistry.getDocuments().isEmpty()) 
        {
            for (Iterator iter = onFileRegistry.getDocuments().iterator(); iter.hasNext();)
            {
                SHADDocument currrentDocument = (SHADDocument) iter.next();
                if(getMergeRuleService().getMatchRuleService().match(episode, currrentDocument))
                {
                    isSameEpisodeMatch = true;
                	break;
                }
            }
        }
        
        return isSameEpisodeMatch;
    }

    /**
     * @param incoming
     * @param onFile
     * @param isSameEpisodeMatch
     * @return
     */
    private boolean isPrisonerOfWarEpisodeMatch(Registry incoming, Registry onFile)
    {
        boolean isSameEpisodeMatch = false;
        
        PrisonerOfWar incomingRegistry = (PrisonerOfWar)incoming;                
        PrisonerOfWar onFileRegistry = (PrisonerOfWar)onFile;                

        //Incoming can have only one episode
        POWEpisode episode = (incomingRegistry.getEpisodes() == null || incomingRegistry
                .getEpisodes().isEmpty()) ? null
                : (POWEpisode) incomingRegistry.getEpisodes()
                        .iterator().next();
        if(onFileRegistry.getEpisodes() != null && !onFileRegistry.getEpisodes().isEmpty()) 
        {
            for (Iterator iter = onFileRegistry.getEpisodes().iterator(); iter.hasNext();)
            {
                POWEpisode currrentDocument = (POWEpisode) iter.next();
                if(getMergeRuleService().getMatchRuleService().match(episode, currrentDocument))
                {
                    isSameEpisodeMatch = true;
                	break;
                }
            }
        }
        return isSameEpisodeMatch;
    }

    /**
     * @param incoming
     * @param isSameEpisodeMatch
     * @return
     */
    private boolean isPurpleHeartDocumentMatch(Registry incoming, Registry onFile)
    {
        boolean isSameEpisodeMatch = false;
        PurpleHeart incomingRegistry = (PurpleHeart)incoming;                
        PurpleHeart onFileRegistry = (PurpleHeart)onFile;                

        //Incoming can have only one document
        PurpleHeartDocument episode = (incomingRegistry.getDocuments() == null || incomingRegistry
                .getDocuments().isEmpty()) ? null
                : (PurpleHeartDocument) incomingRegistry.getDocuments()
                        .iterator().next();
        
        //if(onFileRegistry.getDocuments() != null) && onFileRegistry.getDocuments().contains(episode))
        if(onFileRegistry.getDocuments() != null && !onFileRegistry.getDocuments().isEmpty()) 
        {
            for (Iterator iter = onFileRegistry.getDocuments().iterator(); iter.hasNext();)
            {
                PurpleHeartDocument currrentDocument = (PurpleHeartDocument) iter.next();
                if(getMergeRuleService().getMatchRuleService().match(episode, currrentDocument))
                {
                    isSameEpisodeMatch = true;
                	 break;
                }
            }
        }
        
        return isSameEpisodeMatch;
    }

    /**
     * Finds a matching ESR person.
     * @param registry the registry which is being added.
     * @return matching person
     * @throws ServiceException
     */
    private Person findMatchingPerson(Registry registry) throws ServiceException {

        RegistryTrait trait = registry.getRegistryTrait();
        
        //Create a PersonIdentityTraits
        PersonIdentityTraits personIdentityTraits=new PersonIdentityTraits();
        if(trait.getSsn()!=null)
        {
	        SSN ssn=new SSN();
	        ssn.setSsnText(trait.getSsn());
	        personIdentityTraits.setSsn(ssn);
        }
        if(trait.getLastName()!=null || trait.getFirstName()!=null)
        {
	        Name name=new Name();
	        name.setFamilyName(trait.getLastName());
	        name.setGivenName(trait.getFirstName()); 
	        name.setType(this.getLookupService().getNameTypeByCode(NameType.LEGAL_NAME.getCode()));
	        personIdentityTraits.addName(name); 
        }
        
        try {
            //PS delegate to get identity traits.
           // Set identityTraits= this.getPsDelegateService().attendedSearch(personIdentityTraits,false);   
        	
        	 //CR 11776- Replacing EJB calls
        	 //PS delegate to get identity traits.
            Set identityTraits= this.getPsDelegateService().attendedSearch(personIdentityTraits,false);   //CCR 11776- non-composite
        
        	 
            if (identityTraits != null) {
               if (identityTraits.size() == 1) {
            	   //Call person service to get person.
            	   PersonIdentityTraits piTraits = (PersonIdentityTraits) identityTraits.iterator().next();
            	   Person person=this.getPersonService().find(piTraits);
            	   
	   		        //CCR 11403: for Vista inbound messages, check 200ESR Correlation, add 200ESRCorreltion if necessary
                   this.getPersonService().checkAndAddESRCorrelation(person);
	   		        
                   PersonLockedReason lockedReason = person.getPersonLockedReason();
                   if(person != null && (lockedReason == null || (lockedReason != null && 
                       !PersonLockedReason.PERSON_NO_ENROLLMENT_DETERMINATION.getReason().equals(lockedReason.getReason())))) {
                       return person;
                   }
               } else if (identityTraits.size() > 1) {
                   throw new MultipleRecordsFoundException("Multiple person records found for Registry criteria ");
               }
            }
        } catch(MaxRecordsExceededException maxEx) {
            throw new MultipleRecordsFoundException("Multiple person records found for Registry criteria",maxEx);
        }
       return null;
    }
    
    /**
     * Get ChangeTimes for the entity
     * 
     * @param entityKey
     * @return
     * @throws ServiceException
     */
    public Set getHistoryChangeTimes(EntityKey entityKey)
    throws ServiceException {
        try 
        {
            HistoryDAO historyDAO = getHistoryDAO(entityKey.getEntityClass().getName());
            return historyDAO.getHistoryChangeTimes(entityKey);
        } catch (DAOException e) {
                throw new ServiceException(e.getMessage(), e);
        }
    }

    /**
    * Get current and previous address by change time
    * 
    * @param event
    * @return
    * @throws ServiceException
    */
    public HistoricalInfo getHistoryByChangeTime(ChangeEvent event)
        throws ServiceException {
        try {
            HistoryDAO historyDAO = getHistoryDAO(event.getEntityKey().getEntityClass().getName());
            return historyDAO.getHistoryByChangeTime(event);
        } catch (DAOException e) {
            throw new ServiceException(e.getMessage(), e);
        }
    }
 
    
   /**
    * This method returns exact matching RegistryTrait if it is found.
    * @param registrySearchCriteria       
    * @return
    * @throws ServiceException
    */
    public Registry getMatchingRegistry(RegistrySearchCriteria registrySearchCriteria) throws ServiceException
    {
    	Registry registry=null;
    	List registryData = search(registrySearchCriteria);
    	if(registryData!=null && registryData.size() == 1) {
			RegistrySearchResultBean searchResult = (RegistrySearchResultBean) registryData.get(0);
			registry = getRegistryById(searchResult.getEntityKey(), searchResult.getRegistryType());			
    	}
    	else if(registryData!=null && registryData.size() >1)
    		throw new ServiceException("More than one matching record found for the given registry search creiteria.");
    	return registry;
    }
    

    /**
     * This method builds RegistrySearchCriteria from person for the given registryType.
     * @param person
     * @param registryType
     * @return
     */
    public RegistrySearchCriteria  getRegistrySearchCriteria(Person person, String registryType)
    throws ServiceException
    {    	
	    RegistrySearchCriteria query = new RegistrySearchCriteria();
	    SSN ssn = person.getOfficialSsn();
	    query.setSsn(ssn != null ? ssn.getFormattedSsnText() : null);
        Name name = person.getLegalName();
        if(name != null) 
        {
        	query.setLastName(name.getFamilyName());
        	query.setFirstName(name.getGivenName());
        }	  
		query.setRegistryType((RegistryType)getLookupService().getByCode(RegistryType.class,registryType));
		return query;
    }
    
    /**
     * This method links person with specific registry.
     * @param incoming
     * @param onfile
     * @throws ServiceException
     */
    public void linkPersonRegistry(Registry incoming, Registry onFile, Person incomingPerson, Person onFilePerson, RegistryType registryType, boolean isFromUI)  throws ServiceException
    {    	
    	this.getRegistryRuleService().linkPersonRegistry(incoming, onFile, incomingPerson, onFilePerson,registryType,isFromUI);
    }
    
    /**
     * This method links all three registries to person.
     * @param incoming
     * @param onFile
     * @throws ServiceException
     */
    public void linkPersonRegistries(Person incoming, Person onFile, boolean isFromUI) throws ServiceException
    {
        if(onFile == null) return;

        //Link PH Registry
        RegistryType phRegType = this.getRegistryType(RegistryType.CODE_PH_REGISTRY.getCode());	    
        this.linkPersonRegistry(incoming.getPurpleHeart(), onFile.getPurpleHeart(), incoming, onFile, phRegType, isFromUI);

	    //Link POW Registry
        RegistryType powRegType = this.getRegistryType(RegistryType.CODE_POW_REGISTRY.getCode());
        this.linkPersonRegistry(incoming.getPrisonerOfWar(), onFile.getPrisonerOfWar(), incoming, onFile, powRegType, isFromUI);
        
        //Link SHAD Registry, 
        RegistryType shadRegType = this.getRegistryType(RegistryType.CODE_SHAD_REGISTRY.getCode());
        this.linkPersonRegistry(incoming.getShad(), onFile.getShad(), incoming, onFile, shadRegType, isFromUI);
    }
    
    public EligibilityEnrollmentService getEligibilityEnrollmentService() throws ServiceException {
        if(eligibilityEnrollmentService == null) {
            eligibilityEnrollmentService = eligibilityEnrollmentServiceName != null ? (EligibilityEnrollmentService)this.getComponent(eligibilityEnrollmentServiceName) : null;
        }
        return eligibilityEnrollmentService;
    }
    
    public String getEligibilityEnrollmentServiceName() {
        return eligibilityEnrollmentServiceName;
    }

    public void setEligibilityEnrollmentServiceName(
            String eligibilityEnrollmentServiceName) {
        this.eligibilityEnrollmentServiceName = eligibilityEnrollmentServiceName;
    }

    public RegistryDAO getRegistryDAO() {
        return registryDAO;
    }

    public void setRegistryDAO(RegistryDAO registryDAO) {
        this.registryDAO = registryDAO;
    }

    public Map getHistoryDAOs() {
        return historyDAOs;
    }

    public void setHistoryDAOs(Map historyDAOs) {
        this.historyDAOs = historyDAOs;
    }
    
    private HistoryDAO getHistoryDAO(String entityClassName) {
        return (HistoryDAO) getHistoryDAOs().get(entityClassName);
    }
    
    private RegistryType getRegistryType(String registryType) throws ServiceException {
        return (RegistryType)this.getLookupService().getByCode(RegistryType.class,registryType);
    }
    
    private TriggerEvent getTriggerEvent(PersonEntityKey key)
	{
		PersonTriggerEvent triggerEvent = new PersonTriggerEvent(PersonTrigger.DestinationType.MESSAGING,
                PersonTrigger.TargetType.VISTA, PersonTrigger.DispatchType.NOTIFY,  PersonTrigger.DataType.ELIGIBILITY);
		triggerEvent.setPersonId(key);
		return triggerEvent;
	}

	public TriggerRouter getTriggerRouter() {
		return triggerRouter;
	}

	public void setTriggerRouter(TriggerRouter triggerRouter) {
		this.triggerRouter = triggerRouter;
	}
}
