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

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.OrderedMap;
import org.apache.commons.collections.map.ListOrderedMap;
import org.apache.commons.lang.Validate;

import gov.va.med.esr.common.infra.ImpreciseDate;
import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.lookup.Capability;
import gov.va.med.esr.common.model.lookup.NameType;
import gov.va.med.esr.common.model.lookup.SSNType;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.messaging.SiteIdentity;
import gov.va.med.esr.common.model.messaging.SiteIdentityGroup;
import gov.va.med.esr.common.model.person.BirthRecord;
import gov.va.med.esr.common.model.person.FullyQualifiedIdentity;
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.SSN;
import gov.va.med.esr.common.model.person.id.VPIDEntityKey;
import gov.va.med.esr.common.model.person.id.VPIDEntityKeyImpl;
import gov.va.med.esr.service.EnvironmentParamService;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.PSDelegateService;
import gov.va.med.esr.service.PersonIdentityTraits;
import gov.va.med.esr.service.UnknownLookupCodeException;
import gov.va.med.esr.service.UnknownLookupTypeException;
import gov.va.med.fw.model.AuditInfo;
import gov.va.med.fw.persistent.MaxRecordsExceededException;
import gov.va.med.fw.security.SecurityContextHelper;
import gov.va.med.fw.service.AbstractComponent;
import gov.va.med.fw.service.MissingCapabilityException;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.TimeoutServiceException;

import gov.va.med.fw.util.StopWatchLogger;
import gov.va.med.fw.util.TimeoutException;
import gov.va.med.person.exceptions.PSException;

import gov.va.med.person.idmgmt.IPersonIdentity;
import gov.va.med.person.idmgmt.IQualifiedIdentifier;
import gov.va.med.person.idmgmt.common.ISSN;
import gov.va.med.person.idmgmt.exceptions.NPIException;
import gov.va.med.person.idmgmt.exceptions.PSIMException;
import gov.va.med.person.idmgmt.exceptions.VPIDException;
import gov.va.med.person.idmgmt.factory.QualifiedIdentifierFactory;
import gov.va.med.person.idmgmt.types.MultipleBirthIndicator;
import gov.va.med.person.service.IPersonService;
import gov.va.med.ps.model.PatientIdentifier;
import gov.va.med.esr.common.model.lookup.EnrollmentStatus;

/**
 * Contains the integration of VHA systems ESR and PS (Person Services)
 * <p/>
 * Created Aug 23, 2005 9:53:26 PM
 *
 * @author DNS   BOHMEG
 */
public class PSDelegateServiceImpl extends AbstractComponent implements
		PSDelegateService {
	private static final String PSIM_UPDATE_STATUS_ACCEPTED = "A";
	private static final String PSIM_UPDATE_STATUS_PENDING = "P";

	private static final int DEFAULT_BATCH_SIZE = 25;

	private String esrStationNumber;
	private boolean isDataClean = true;
	private String processingId;
	private IPersonService PSIMSOADelegateService;

	// CCR11403, CCR11776 replace obtain call
	private IdmWebServiceDelegate idmServiceDelegate = null;

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

	/**
	 * An instance of lookupService
	 */
	private LookupService lookupService;

	/**
	 * An instance of environmentParamService
	 */
	private EnvironmentParamService environmentParamService;

	/**
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		Validate.notNull(lookupService);
		Validate.notNull(esrStationNumber);
		Validate.notNull(processingId);
		Validate.notNull(PSIMSOADelegateService);
		Validate.notNull(environmentParamService);
	}

	/**
	 * @see gov.va.med.esr.service.PSDelegateService#getVPID(String,String)
	 */
	@Override
	public VPIDEntityKey getVPID(String dfn, String stationNumber)
			throws ServiceException {
		try {
			// IQualifiedIdentifier identifier =
			// getDfnQualifiedIdentifierWithStationNumber(dfn, stationNumber);
			// IQualifiedIdentifier vpid =
			// getPSIMSOADelegateService().obtainVPID(identifier);

			// CR 11776- Replacing legacy EJBs
			FullyQualifiedIdentity fqi = new FullyQualifiedIdentity(dfn,
					stationNumber);
			PersonIdentityTraits pit = idmServiceDelegate.search(fqi, false);
			VPIDEntityKey vpid = pit.getVpid();

			if (logger.isDebugEnabled()) {
				logger.debug("Idm Web Service returned: " + vpid);
			}

			return (vpid != null && vpid.getKeyValue() != null) ? CommonEntityKeyFactory
					.createVPIDEntityKey(vpid.getVPID()) : null;
		} catch (TimeoutException ex) {
			throw new TimeoutServiceException(ex.getMessage(), ex);
		} catch (Exception e) {
			throw new ServiceException("Unable to get VPID", e);
		}
	}

	@Override
	public PersonIdentityTraits getIdentityTraitsWithCompositeCall(String dfn,
			String stationNumber) throws ServiceException {
		try {
			// CCR11403 replace obtain call
			FullyQualifiedIdentity id = new FullyQualifiedIdentity(dfn,
					stationNumber);
			PersonIdentityTraits identityTraits = getIdmServiceDelegate()
					.searchWithCompositeCall(id, true, null);
			if (logger.isDebugEnabled())
				logger.debug("IdM Web Service returned: " + identityTraits);

			// CR 11776 - overlayMissingData is now handled by 1306
			// verified with ESR team if removal was OK. 1306 now includes the
			// data that were missing
			// CCR 11482: missing data element in 1306, get the ejb call and
			// overlay the missing data elements
			// IQualifiedIdentifier identifier =
			// getDfnQualifiedIdentifierWithStationNumber(dfn, stationNumber);
			// overlayMissing1306Data(identityTraits, identifier);

			return identityTraits;
		} catch (TimeoutException ex) {
			throw new TimeoutServiceException(ex.getMessage(), ex);
		} catch (Exception e) {
			throw new ServiceException(
					"Unable to get IdentityTraits from Idm WebService for IQI with station: "
							+ stationNumber + " and dfn:" + dfn
							+ " calling searchWithCompositeCall() with "
							+ esrStationNumber + " station number.", e);
		}
	}

	/**
	 * CCR 11758
	 */
	@Override
	public PersonIdentityTraits getIdentityTraitsWithCompositeCall(
			VPIDEntityKey key) throws ServiceException {
		// No traits for a null key
		if (key == null) {
			return null;
		}
		return doGetIdentityTraits(key, true);
	}

	/**
	 * @see gov.va.med.esr.service.PSDelegateService#getIdentityTraits(gov.va.med.esr.common.model.person.id.VPIDEntityKey)
	 */
	@Override
	public PersonIdentityTraits getIdentityTraits(VPIDEntityKey key)
			throws ServiceException {
		// No traits for a null key
		if (key == null) {
			return null;
		}
		return doGetIdentityTraits(key, false);
	}

	private PersonIdentityTraits doGetIdentityTraits(VPIDEntityKey key,
			boolean compositeCall) throws ServiceException {
		// Get the traits for the VPID
		PersonIdentityTraits traits = null;
		try {
			// CCR11403 replace obtain call
			FullyQualifiedIdentity id = new FullyQualifiedIdentity(
					key.getShortVPID());

			// CCR 11758
			if (compositeCall) {
				traits = getIdmServiceDelegate().searchWithCompositeCall(id, false, null);
			} else
				traits = getIdmServiceDelegate().search(id, false);

			if (logger.isDebugEnabled())
				logger.debug("IdM Web Service returned: " + traits);

			// CR 11776 - overlayMissingData is now handled by 1306
			// 1306 now includes the data that were missing

			// CCR 11482: missing data element in 1306, get the ejb call and
			// overlay the missing data elements
			// IQualifiedIdentifier identifier =
			// getVpidQualifiedIdentifier(key.getVPID());
			// this.overlayMissing1306Data(traits, identifier);

			/*
			 * if (traits.isDeprecated()) { if (logger.isWarnEnabled())
			 * logger.warn(
			 * "IdM Web Service returned a deprecated search (getIdentityTraits) result for VPID: "
			 * + traits.getVpid().getShortVPID()); }
			 */
		} catch (TimeoutException ex) {
			throw new TimeoutServiceException(ex.getMessage(), ex);
		} catch (Exception e) {
			// A VPIDException will be thrown if the VPID format is invalid (not
			// if the obtain method doesn't find traits)
			if (e instanceof VPIDException) {
				if (!isDataClean) {
					traits = createdCannedTraitsWhenDataIsNotClean(key
							.getKeyValueAsString());
				} else {
					// When the data is not clean, treat this as no records
					// found.
					// This is needed when searching by VPID from the UI and bad
					// data is entered.
					e = new gov.va.med.person.exceptions.NoRecordFoundException(
							"Unable to get IdentityTraits due to invalid VPID: "
									+ key.getKeyValueAsString(), e);
				}
			}

			// Throw a Service Exception
			throw new ServiceException(
					"Unable to get IdentityTraits from Idm Web Service for VPID: "
							+ key.getKeyValueAsString(), e);
		}

		// Return the traits for normal flow
		return traits;
	}

	/**
	 * @see gov.va.med.esr.service.PSDelegateService#getIdentityTraits(java.util.Collection)
	 */
	@Override
	public Map getIdentityTraits(Collection vpidEntityKeys)
			throws ServiceException {
		// throttle the calls to x at a time....this is arbitrary and needs to
		// be tuned
		int batchSize = vpidEntityKeys != null ? vpidEntityKeys.size() : 0;
		Iterator itr = vpidEntityKeys != null ? vpidEntityKeys.iterator()
				: null;
		IQualifiedIdentifier identifier = null;
		IQualifiedIdentifier[] identifiers = null;
		String vpid = null;
		int totalCount = 0;
		int currentBatchCount = 0;
		int currentBatchSize = 0;
		Object input = null;
		Map result = new HashMap();
		StopWatchLogger watch = new StopWatchLogger("PSIM obtain");
		while (itr != null && itr.hasNext()) {
			if (identifiers == null) {
				currentBatchSize = batchSize - totalCount > DEFAULT_BATCH_SIZE ? DEFAULT_BATCH_SIZE
						: batchSize - totalCount;
				identifiers = new IQualifiedIdentifier[currentBatchSize];
			}

			try {
				input = itr.next();
				if (input instanceof VPIDEntityKey)
					vpid = ((VPIDEntityKey) input).getVPID();
				else
					// assume String
					vpid = (String) input;

				identifier = getVpidQualifiedIdentifier(vpid);
				identifiers[currentBatchCount++] = identifier;
				totalCount++;
				if (currentBatchCount == currentBatchSize) {
					watch.start();
					Map psData = getPSIMSOADelegateService()
							.obtain(identifiers);
					if (psData != null) {
						watch.stopAndLog("returned a collection of "
								+ psData.size() + " person trait info");
						for (Iterator i = psData.entrySet().iterator(); i
								.hasNext();) {
							Map.Entry entry = (Map.Entry) i.next();

							Object key = entry.getKey();
							Object value = entry.getValue();

							if (key instanceof IQualifiedIdentifier
									&& value instanceof IPersonIdentity) {
								IQualifiedIdentifier id = (IQualifiedIdentifier) key;
								IPersonIdentity personInfo = (IPersonIdentity) value;
								result.put(
										id.getIdentifier(),
										this.convertTraits(
												personInfo,
												CommonEntityKeyFactory.createVPIDEntityKey(id
														.getIdentifier())));
							} else {
								// This means that PSIM failed to retrieve a
								// person for the specific VPID
								// and a PSException is returned in a map, log
								// errors here
								if (logger.isErrorEnabled()) {
									logger.error("Failed to obtain a person identity with VPID "
											+ (key instanceof IQualifiedIdentifier ? ((IQualifiedIdentifier) key)
													.getIdentifier() : null));
									logger.error("Exception message "
											+ (value instanceof Exception ? ((Exception) value)
													.getMessage() : null));
								}
							}
						}
					}
					identifiers = null;
					currentBatchCount = 0;
				}
			} catch (TimeoutException ex) {
				throw new TimeoutServiceException(ex.getMessage(), ex);
			} catch (Exception e) {
				throw new ServiceException(
						"Unable to get multiple Identity Trait's from PSIM", e);
			} finally {
				watch.reset();
			}
		}
		if (logger.isInfoEnabled()) {
			logger.info("Number of VPID's input: " + batchSize);
			logger.info("Number of VPID's processed: " + totalCount);
		}
		return result;
	}

	/**
	 * @see gov.va.med.esr.service.PSDelegateService#getSubmittedIdentityTraits(java.util.Collection)
	 */
	@Override
	public Map getSubmittedIdentityTraits(Collection vpidEntityKeys)
			throws ServiceException {
		// Throttle the calls to x at a time....this is arbitrary and needs to
		// be tuned
		int batchSize = vpidEntityKeys != null ? vpidEntityKeys.size() : 0;
		Iterator itr = vpidEntityKeys != null ? vpidEntityKeys.iterator()
				: null;
		IQualifiedIdentifier identifier = null;
		IQualifiedIdentifier[] identifiers = null;
		String vpid = null;
		int totalCount = 0;
		int currentBatchCount = 0;
		int currentBatchSize = 0;
		Object input = null;
		Map result = new HashMap();
		StopWatchLogger watch = new StopWatchLogger("PSIM obtain");
		while (itr != null && itr.hasNext()) {
			if (identifiers == null) {
				currentBatchSize = batchSize - totalCount > DEFAULT_BATCH_SIZE ? DEFAULT_BATCH_SIZE
						: batchSize - totalCount;
				identifiers = new IQualifiedIdentifier[currentBatchSize];
			}

			try {
				input = itr.next();
				if (input instanceof VPIDEntityKey)
					vpid = ((VPIDEntityKey) input).getVPID();
				else
					// assume String
					vpid = (String) input;

				identifier = getVpidQualifiedIdentifierWithStationNumber(vpid);
				identifiers[currentBatchCount++] = identifier;
				totalCount++;
				if (currentBatchCount == currentBatchSize) {
					watch.start();

					Map psData = getPSIMSOADelegateService().obtainCorrelation(
							identifiers); // notice no history is needed here

					if (psData != null) {
						watch.stopAndLog("returned a collection of "
								+ psData.size() + " person trait info");
						for (Iterator i = psData.entrySet().iterator(); i
								.hasNext();) {
							Map.Entry entry = (Map.Entry) i.next();

							Object key = entry.getKey();
							Object value = entry.getValue();

							if (key instanceof IQualifiedIdentifier
									&& value instanceof IPersonIdentity) {
								IQualifiedIdentifier id = (IQualifiedIdentifier) key;
								IPersonIdentity personInfo = (IPersonIdentity) value;
								result.put(
										id.getIdentifier(),
										this.convertTraits(
												personInfo,
												CommonEntityKeyFactory.createVPIDEntityKey(id
														.getIdentifier())));
							} else {
								// This means that PSIM failed to retrieve a
								// person for the specific VPID
								// and a PSException is returned in a map, log
								// errors here
								if (logger.isErrorEnabled()) {
									logger.error("Failed to obtain a person identity with VPID "
											+ (key instanceof IQualifiedIdentifier ? ((IQualifiedIdentifier) key)
													.getIdentifier() : null));
									logger.error("Exception message "
											+ (value instanceof Exception ? ((Exception) value)
													.getMessage() : null));
								}
							}
						}
					}
					identifiers = null;
					currentBatchCount = 0;
				}
			} catch (TimeoutException ex) {
				throw new TimeoutServiceException(ex.getMessage(), ex);
			} catch (Exception e) {
				throw new ServiceException(
						"Unable to get multiple Identity Trait's from PSIM", e);
			} finally {
				watch.reset();
			}
		}
		if (logger.isInfoEnabled()) {
			logger.info("Number of VPID's input: " + batchSize);
			logger.info("Number of VPID's processed: " + totalCount);
		}
		return result;
	}

	/**
	 * General purpose search using identity traits from an attended process
	 * (eg, UI)
	 *
	 * @param traits
	 *            the identity traits
	 * @param enforceESRSecurity
	 *            Whether the search should be restricted by an ESR role or not
	 *
	 * @return Set<PersonIdentityTrait>
	 */
	// CR 11776 - Replacing EJBs
	// attendedSearch that utilizes IdmWSDelegateImpl
	@Override
	public Set attendedSearch(PersonIdentityTraits traits,
			boolean enforceESRSecurity) throws ServiceException {
		/*
		 * String searchTypeForPSIM =
		 * IPersonIdentityMatchTypes.MATCH_TYPE_ATTENDED; There are two concepts
		 * here: -Certain search combinations must be annotated when calling
		 * PSIM -Certain (not necessarily same as above) search combinations are
		 * restricted by ESR role
		 *
		 * //TODO verify if this code block is needed // PSIM Restriction: check
		 * search combination to see if caller is requesting a "special"
		 * combination.... if (traits.containsAtLeastFullName() ||
		 * traits.containsAtLeastLastNameAndDOB() || traits.containsOnlySSN() ||
		 * traits.containsSsnDOBGender()) searchTypeForPSIM =
		 * "attended superuser"; // special undocumented agreement between HEC
		 * and IMDQ
		 */

		// ESR Restriction: ESR role check
		if (enforceESRSecurity) {
			String specialCapability = Capability.LIMITED_SEARCH.getCode(); // note:
																			// includes
																			// LName+DOB
																			// only
			if (traits.containsOnlyFullName())
				specialCapability = Capability.SEARCH_NAME_ONLY.getCode();
			else if (traits.containsOnlySSN())
				specialCapability = Capability.SEARCH_SSN_ONLY.getCode();

			if (!SecurityContextHelper.getSecurityContext().getUserPrincipal()
					.isPermissionGranted(specialCapability))
				throw new MissingCapabilityException(
						lookupService.getCapabilityByCode(specialCapability));
		}
		return search(traits, false, true); // CR 11776 - Replacing EJBs
	}

	/**
	 * General purpose search using identity traits from an unattended process
	 * (eg, Messaging)
	 *
	 * @param traits
	 *            the identity traits
	 *
	 * @return Set<PersonIdentityTrait>
	 */
	// CR 11776 - Replacing EJBs
	// unattendedSearch that utilizes IdmWSDelegateImpl
	@Override
	public Set unattendedSearch(PersonIdentityTraits traits)
			throws ServiceException {
		// Check if user is trying to search on only SSN in unattended search.
		// PSIM is logging an error, but not throwing an exception so we will do
		// it here.
		if (traits.containsOnlySSN()) {
			throw new UnsupportedOperationException(
					"Unattended Search with only SSN is not supported.  "
							+ "ESR is throwing this exception because PSIM is only logging an error and not throwing an exception.");
		}
		return search(traits, false, false); // CR 11776
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * gov.va.med.esr.service.PSDelegateService#getSites(gov.va.med.esr.common
	 * .model.person.id.VPIDEntityKey)
	 */
	// CR 11776 - Replacing EJBs
	// revised method that now utilizes the IdmWebServiceImpl class instead of
	// legacy EJBs
	@Override
	public Set getSites(VPIDEntityKey key) throws ServiceException {
		Set siteIdentities = new HashSet();
		if (key == null) {
			return siteIdentities;
		}

		try {
			SiteIdentity site = null;
			VAFacility facility = null;

			// create one instance for the SiteIdentityGroup
			SiteIdentityGroup group = new SiteIdentityGroup();
			group.setPersonKey(key);

			// get a list of correlations/patient identifiers by 1309 call
			List sites = idmServiceDelegate.getCorrelationsByVPID(key);

			Iterator sitesIt = sites.iterator();

			while (sitesIt.hasNext()) {
				// PSIM(obtainSOI())returns two types of sites. One of type 'NI'
				// and one of type "PI".
				// "NI" means the Identifier in the object is either a VPID or
				// ICN. If the Identifier Type is 'PI', the Identifier in
				// the object will be the IEN (in this case DFN)from the
				// Patient file (2) in VistA.
				// IEN = Internal Entry Number. This is basically the primary
				// key in a VistA file. The DFN is the IEN from the Patient
				// file.
				// So list of sites should be only of type "PI"
				PatientIdentifier pid = (PatientIdentifier) sitesIt.next();
				if ("PI".equalsIgnoreCase(pid.getIdentityType())
						&& "USVHA"
								.equalsIgnoreCase(pid.getAssigningAuthority())
						&& (!"200ESR".equalsIgnoreCase(pid.getStationNumber()))) { // dc
					// one last check...ESR only cares about medical treating
					// facilities
					facility = lookupService.getVaFacilityByCode(pid
							.getStationNumber());

					if (logger.isDebugEnabled()) {
						logger.debug("[ facility Identifier : "
								+ facility.getIdentifier() + ", "
								+ "Station #: " + facility.getStationNumber()
								+ "]");
					}
					// Only include the site if it's supported by the
					// environment
					if (isSupportedInEnvironment(facility)) {
						if (facility.getType().getIsMedicalTreating()) {
							site = new SiteIdentity();
							group.addSiteIdentity(site);
							site.setDfn(pid.getIdentity());
							site.setVaFacility(facility);
							if (logger.isDebugEnabled()) {
								logger.debug("[Site dfn: " + pid.getIdentity()
										+ ", " + "facility Identifier : "
										+ facility.getIdentifier() + ", "
										+ "Station #: "
										+ pid.getStationNumber() + "]");
							}
							siteIdentities.add(site);
						}
					} else {
						if (logger.isDebugEnabled())
							logger.debug("Site for facility with station #: "
									+ facility.getStationNumber()
									+ " excluded from list for VPID: "
									+ key.getKeyValueAsString()
									+ " because not supported by environment.");
					}

				}
			}
			return siteIdentities;
		} catch (TimeoutException ex) {
			throw new TimeoutServiceException(ex.getMessage(), ex);
		} catch (UnknownLookupTypeException tex) {
			logger.error("Type exception Error during lookup " + tex);
			throw new ServiceException(
					"Unable to look up station number: ", tex);
		} catch (UnknownLookupCodeException  lex) {
			logger.error("Code exception Error during lookup " + lex);
			throw new ServiceException(
					"Unable to look up station number: ", lex);
		}catch (Exception e) {
			throw new ServiceException(
					"Unable to get sites from Idm Web Service for VPID: "
							+ key.getKeyValueAsString(), e);
		}
	}

	@Override
	public boolean isSupportedInEnvironment(VAFacility facility)
			throws ServiceException {
		return environmentParamService.isSupportedFacility(facility);
	}

	// CR 11776 - Replacing EJBs
	// private class that utilizs by attendedSearch/unattendedSearch
	private Set search(PersonIdentityTraits criteria, boolean compositeCall, boolean attendedSearch)
			throws ServiceException {
		return doSearch(criteria, compositeCall, attendedSearch);
	}

	// CR 11776 - Replacing EJBs
	// private class utilized by attendedSearch/unattendedSearch
	protected Set doSearch(PersonIdentityTraits traits, boolean compositeCall, boolean attendedSearch)
			throws ServiceException {

		try {
			if (traits == null)
				return null;

			if (compositeCall) {
				return getIdmServiceDelegate().searchWithCompositeCall(traits, null);
			} else
				return getIdmServiceDelegate().search(traits, attendedSearch);
		} catch (TimeoutException ex) {
			throw new TimeoutServiceException(ex.getMessage(), ex);
		}
		//CCR12266 - UI search for more than 10 records displays stack trace
		//we don't want to catch generic exception here
		//it eats up the MaxRecordsExceededException as well as the specific ServiceExceptions from IDM
		/*} catch (Exception e) {
			throw new ServiceException(
					"Unable to search for identity traits against PSIM", e);
		}*/

	}

	/**
	 * Converts PSIM object to ESR object. TODO: move this to ConversionService
	 *
	 * @param source
	 *            the PSIM traits
	 * @param passedInVPID
	 *            the VPID
	 *
	 * @return the ESR traits
	 * @throws ServiceException
	 *             if any problems were encountered.
	 */
	private PersonIdentityTraits convertTraits(IPersonIdentity source,
			VPIDEntityKey passedInVPID) throws ServiceException {
		PersonIdentityTraits target = new PersonIdentityTraits();
		if (source == null)
			return target;

		// Set the audit info
		AuditInfo auditInfo = new AuditInfo();
		auditInfo.setChangeDate(source.getAuditEventTimestamp());
		auditInfo.setChangeUser(source.getAuditUserId());
		if (PSIM_UPDATE_STATUS_ACCEPTED.equalsIgnoreCase(source
				.getUpdateStatus()))
			auditInfo.setStatus(PersonIdentityTraits.UPDATE_STATUS_ACCEPTED);
		else if (PSIM_UPDATE_STATUS_PENDING.equalsIgnoreCase(source
				.getUpdateStatus()))
			auditInfo.setStatus(PersonIdentityTraits.UPDATE_STATUS_PENDING);
		else if (org.apache.commons.lang.StringUtils.isNotBlank(source
				.getUpdateStatus()))
			auditInfo.setStatus(PersonIdentityTraits.UPDATE_STATUS_REJECTED
					+ "-" + source.getUpdateStatus());
		target.setAuditInfo(auditInfo);

		int numNames = source.getNumberOfNames();
		Name name = null;
		for (int i = 0; i < numNames; i++) {
			name = new Name();
			name.setGivenName(source.getFirstName(i));
			name.setMiddleName(source.getMiddleName(i));
			name.setFamilyName(source.getLastName(i));

			name.setType(lookupService.getNameTypeByCode(source.getNameType(i)));

			name.setPrefix(source.getPrefix(i));
			name.setSuffix(source.getSuffix(i));
			target.addName(name);
		}
		target.setMothersMaidenName(source.getMothersMaidenName());

		String pobCity = source.getPlaceOfBirthCity(); // text
		String pobState = source.getPlaceOfBirthState(); // code for
		// POSTALNAME
		String pobCountry = source.getPlaceOfBirthCountry(); // code for
		// ALPHA3CODE
		MultipleBirthIndicator isMultipleBirth = source.getMultiBirth();
		// ccr10145 test only
		// MultipleBirthIndicator isMultipleBirth = new
		// MultipleBirthIndicator(true);

		// TODO: we are living with an ImpreciseDate mismatch between PSIM
		// and ESR!?
		gov.va.med.person.idmgmt.types.ImpreciseDate psimImpreciseBirthDate = source
				.getDateOfBirth();
		if (psimImpreciseBirthDate != null || pobCity != null
				|| pobState != null || pobCountry != null
				|| isMultipleBirth != null) {
			BirthRecord dob = new BirthRecord();
			if (psimImpreciseBirthDate != null
					&& !org.apache.commons.lang.StringUtils
							.isBlank(psimImpreciseBirthDate.getDate()))
				dob.setBirthDate(new ImpreciseDate(psimImpreciseBirthDate
						.getDate()));
			dob.setCity(pobCity);
			dob.setState(pobState);
			// for Country, get the name based on the code
			if (pobCountry != null) {
				try {
					dob.setCountry(lookupService.getCountryByCode(pobCountry)
							.getShortName());
				} catch (ServiceException e) {
					// for now, just ignore and set in dob
					dob.setCountry(pobCountry);
				}
			}
			// CCR10145
			// isMultipleBirth.getValue() returning Y, N or blank
			if (isMultipleBirth != null) {
				// dob.setMultipleBirth(new
				// Boolean(isMultipleBirth.getValue()));
				if ("Y".equalsIgnoreCase(isMultipleBirth.getValue()))
					dob.setMultipleBirth(new Boolean("TRUE"));
				else if ("N".equalsIgnoreCase(isMultipleBirth.getValue()))
					dob.setMultipleBirth(new Boolean("FALSE"));
			}
			target.setBirthRecord(dob);
		}

		gov.va.med.person.idmgmt.common.ISSN psimSSN = source.getSSN();
		IQualifiedIdentifier ssn = psimSSN != null ? psimSSN.getSSN() : null;

		if (ssn != null) {
			// if blank, assume official SSN
			String psimSSNTypeCode = org.apache.commons.lang.StringUtils
					.isNotBlank(psimSSN.getSsnType()) ? psimSSN.getSsnType()
					: SSNType.CODE_ACTIVE.getCode();
			String psimPseudoSSNReasonCode = psimSSN.getPseudoSSNReason();
			SSN traitSsn = new SSN();
			traitSsn.setType(getLookupService().getSSNTypeByCode(
					psimSSNTypeCode));
			traitSsn.setSsnText(ssn.getIdentifier());
			traitSsn.setSsaSentDate(psimSSN.getSentToSSA());
			traitSsn.setSsaReceivedDate(psimSSN.getReceivedFromSSA());
			traitSsn.setSsaVerificationDate(psimSSN.getVerifiedBySSA());
			if (org.apache.commons.lang.StringUtils
					.isNotBlank(psimPseudoSSNReasonCode))
				traitSsn.setPseudoSSNReason(getLookupService()
						.getPseudoSSNReasonByCode(psimPseudoSSNReasonCode));

			if (org.apache.commons.lang.StringUtils.isNotBlank(psimSSN
					.getSsnVerificationStatus()))
				traitSsn.setSsaVerificationStatus(getLookupService()
						.getSSAVerificationStatusByCode(
								psimSSN.getSsnVerificationStatus()));
			if (org.apache.commons.lang.StringUtils.isNotBlank(psimSSN
					.getPseudoSSNReason()))
				traitSsn.setPseudoSSNReason(getLookupService()
						.getPseudoSSNReasonByCode(psimSSN.getPseudoSSNReason()));
			if (org.apache.commons.lang.StringUtils.isNotBlank(psimSSN
					.getSsnChangeSource()))
				traitSsn.setSourceOfChange(getLookupService()
						.getSSNChangeSourceByCode(psimSSN.getSsnChangeSource()));
			if (org.apache.commons.lang.StringUtils.isNotBlank(psimSSN
					.getSsaVerificationCode()))
				traitSsn.setSsaMessage(getLookupService().getSSAMessageByCode(
						psimSSN.getSsaVerificationCode()));

			target.setSsn(traitSsn);
		}

		String gender = source.getGender();
		target.setGender(gender != null ? lookupService.getGenderByCode(gender)
				: null);

		// CCR 11403: safeguard to always convert to long vpid
		target.setVpid(CommonEntityKeyFactory
				.createVPIDEntityKey(VPIDEntityKeyImpl.getLongVPID(source
						.getVPID().getIdentifier())));

		// ensure comparing short VPID to short VPID
		if (passedInVPID != null
				&& !target.getVpid().getShortVPID()
						.equals(passedInVPID.getShortVPID()))
			target.setRetrievedWithDeprecatedVPID(true);

		target.setHasPendingUpdates(source.isUpdatePending());

		target.setDeprecated(source.isDeprecated());

		// CCR 10471 ADD A PERSON
		// TBD; preferred faclity is sending facility??
		// target.setPreferredFacilty(source.getSendingFacility());

		return target;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * gov.va.med.esr.service.PSDelegateService#extractIdentityTraits(gov.va
	 * .med.esr.common.model.person.Person)
	 */
	@Override
	public PersonIdentityTraits extractIdentityTraits(Person person) {
		return person.getIdentityTraits();
	}

	/**
	 * Gets the history of updates to identity traits. Any records that have no
	 * audit timestamp are filtered out. Duplicate timestamp entries are also
	 * filtered out. An empty list is returned if no items are found.
	 *
	 * @param key
	 *            the VPID to get the history for
	 *
	 * @return the list of PersonIdentityTrait objects
	 * @throws ServiceException
	 *             if any problems were encountered
	 */
	@Override
	public List getIdentityTraitsUpdateHistory(VPIDEntityKey key)
			throws ServiceException {
		// Get the unfiltered PSIM traits
		IPersonIdentity[] items = doGetIdentityTraitsUpdateHistory(key);

		// Iterate through the PSIM traits and convert them to ESR traits
		OrderedMap traitMap = new ListOrderedMap();
		if (items != null && items.length > 0) {
			for (int i = 0; i < items.length; i++) {
				IPersonIdentity psimTraits = items[i];
				if (psimTraits.getAuditEventTimestamp() != null) {
					// Place this entry in the map. If an entry with this
					// timestamp already exists,
					// this will replace it with the newer update.
					traitMap.put(psimTraits.getAuditEventTimestamp(),
							convertTraits(psimTraits, null));
				}
			}
		}

		// Return the filtered list of ESR traits
		return new ArrayList(traitMap.values());
	}

	/**
	 * Gets the unfiltered history of updates to identity traits. The order
	 * appears to be the order the updates were requested to PSIM (i.e. oldest
	 * to newest).
	 *
	 * @param key
	 *            the VPID to get the history for
	 *
	 * @return the list of IPersonIdentity objects
	 * @throws ServiceException
	 *             if any problems were encountered
	 */
	private IPersonIdentity[] doGetIdentityTraitsUpdateHistory(VPIDEntityKey key)
			throws ServiceException {
		// Return null if no VPID was passed in
		if (key == null) {
			return null;
		}

		// Return the list of unfiltered traits
		try {
			IQualifiedIdentifier vpid = getVpidQualifiedIdentifierWithoutStationNumber(key
					.getVPID());
			return getPSIMSOADelegateService().obtainHistory(vpid);
		} catch (TimeoutException ex) {
			throw new TimeoutServiceException(ex.getMessage(), ex);
		} catch (Exception e) {
			throw new ServiceException(
					"Unable to retrieve history of identity traits updates for VPID: "
							+ key.getVPID(), e);
		}
	}

	/**
	 * @return Returns the lookupService.
	 */
	public LookupService getLookupService() {
		return lookupService;
	}

	/**
	 * @param lookupService
	 *            The lookupService to set.
	 */
	public void setLookupService(LookupService lookupService) {
		this.lookupService = lookupService;
	}

	/**
	 * @return Returns the environmentParamService.
	 */
	public EnvironmentParamService getEnvironmentParamService() {
		return environmentParamService;
	}

	/**
	 * @param environmentParamService
	 *            The environmentParamService to set.
	 */
	public void setEnvironmentParamService(
			EnvironmentParamService environmentParamService) {
		this.environmentParamService = environmentParamService;
	}

	/**
	 * @return Returns the esrStationNumber.
	 */
	public String getEsrStationNumber() {
		return esrStationNumber;
	}

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

	/**
	 * @return Returns the isDataClean.
	 */
	public boolean isDataClean() {
		return isDataClean;
	}

	/**
	 * @param isDataClean
	 *            The isDataClean to set.
	 */
	public void setIsDataClean(boolean isDataClean) {
		this.isDataClean = isDataClean;
	}

	/**
	 * @return Returns the processingId.
	 */
	public String getProcessingId() {
		return processingId;
	}

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

	public IPersonService getPSIMSOADelegateService() {
		return PSIMSOADelegateService;
	}

	public void setPSIMSOADelegateService(IPersonService delegateService) {
		PSIMSOADelegateService = delegateService;
	}

	/**
	 * Retrieves VPIDs for Persons whose SSA Verification status matches one of
	 * those passed in.
	 *
	 * @param SSAVerificationStatuses
	 *            - Set of String SSAVerificationStatus codes (eg, the String
	 *            "VERIFIED")
	 *
	 * @return Set<VPIDEntityKey>
	 * @throws ServiceException
	 */
	public Set getVPIDsBySSAVerificationStatuses(Set SSAVerificationStatuses)
			throws ServiceException {
		Set results = new HashSet();
		if (SSAVerificationStatuses == null
				|| SSAVerificationStatuses.isEmpty())
			return results;
		ISSN[] criteria = new gov.va.med.person.idmgmt.db.hb.SSN[SSAVerificationStatuses
				.size()];
		gov.va.med.person.idmgmt.db.hb.SSN item = null;
		Iterator itr = SSAVerificationStatuses.iterator();
		int i = 0;
		String oneCriteria = null;
		while (itr.hasNext()) {
			oneCriteria = (String) itr.next();
			item = new gov.va.med.person.idmgmt.db.hb.SSN();
			item.setSsnVerificationStatus(oneCriteria);
			item.setSsnType(SSNType.CODE_ACTIVE.getCode());
			criteria[i++] = item;
		}
		try {
			IQualifiedIdentifier[] vpids = getPSIMSOADelegateService()
					.obtainVpidsBySsnVerificationStatus(criteria,
							esrStationNumber); // TODO dc

			if (vpids != null && vpids.length > 0) {
				for (i = 0; i < vpids.length; i++) {
					results.add(CommonEntityKeyFactory
							.createVPIDEntityKey(vpids[i].getIdentifier()));
				}
			}
		} catch (TimeoutException ex) {
			throw new TimeoutServiceException(ex.getMessage(), ex);
		} catch (PSException e) {
			throw new ServiceException(
					"Unable to get VPIDs by SSA Verification Status", e);
		}
		return results;
	}

	protected IQualifiedIdentifier getVpidQualifiedIdentifier(String vpid)
			throws PSIMException, VPIDException, NPIException {
		return QualifiedIdentifierFactory.create(vpid, "NI");
	}

	protected IQualifiedIdentifier getVpidQualifiedIdentifierWithStationNumber(
			String vpid) throws PSIMException, VPIDException, NPIException {
		// CCR8045: change from NI to PI for PSIM 2.2.1
		// PSIM Version 2.2.1 will no longer support retrieval of an ESR
		// Correlation
		// using an identifier type of NI (national identifier).
		// obtainCorrelation calls
		// and update calls need to change to use identifier type PI (patient
		// identifier) rather than NI.
		return QualifiedIdentifierFactory.create(vpid, "PI", "USVHA",
				esrStationNumber);
	}

	protected IQualifiedIdentifier getVpidQualifiedIdentifierWithoutStationNumber(
			String vpid) throws PSIMException, VPIDException, NPIException {
		return QualifiedIdentifierFactory.create(vpid, "NI", "USVHA", null);
	}

	protected IQualifiedIdentifier getSsnQualifiedIdentifier(String ssn)
			throws PSIMException, VPIDException, NPIException {
		return QualifiedIdentifierFactory.create(ssn, "SS");
	}

	protected IQualifiedIdentifier getDfnQualifiedIdentifierWithStationNumber(
			String dfn, String stationNumber) throws PSIMException,
			VPIDException, NPIException {
		return QualifiedIdentifierFactory.create(dfn, "PI", "USVHA",
				stationNumber);
	}

	// CR 11776 - replacing legacy EJB
	@Override
	public PersonIdentityTraits getSubmittedIdentityTraits(VPIDEntityKey key)
			throws ServiceException {
		PersonIdentityTraits submittedTraits = doGetSubmittedIdentityTraitsByIdmWS(
				key, false);
		return submittedTraits;
	}

	// CR 11776 - replacing legacy EJB
	// replacing obtainCorrelation call
	private PersonIdentityTraits doGetSubmittedIdentityTraitsByIdmWS(
			VPIDEntityKey key, boolean returnHistory) throws ServiceException {
		// No traits for a null key
		if (key == null) {
			return null;
		}
		PersonIdentityTraits identityTraits = null;
		try {
			// IQualifiedIdentifier identifier =
			// getVpidQualifiedIdentifierWithStationNumber(key.getVPID());
			// identityTraits =
			// getPSIMSOADelegateService().obtainCorrelation(identifier,
			// returnHistory);

			// CR 11776 - replacing legacy EJB
			// replacing obtainCorrelation call
			identityTraits = idmServiceDelegate.getESRCorrelation(key);
			if (logger.isDebugEnabled()) {
				logger.debug("Idm WebService returned: " + identityTraits);
			}

			// getESRCorrelation will will return surviving traits, so no need
			// to check for deprecates
			/*
			 * if (identityTraits.isDeprecated()) { if (logger.isWarnEnabled())
			 * logger.warn(
			 * "PSIM returned a deprecated obtainCorrelation (getIdentityTraits) result for VPID: "
			 * + identityTraits.getVpid().getKeyValue()); //
			 * identityTraits.getVPID().getIdentifier()); }
			 */
		} catch (TimeoutException ex) {
			throw new TimeoutServiceException(ex.getMessage(), ex);
		} catch (Exception e) {
			// Throw a Service Exception
			throw new ServiceException(
					"Unable to get submitted IdentityTraits from PSIM for key: "
							+ key.getKeyValueAsString(), e);
		}

		return identityTraits;
	}

	/**
	 * TODO - Use doGetSubmittedIdentityTraitsByIdmWS method instead, once the
	 * decoupling is done for the
	 * getSubmittedIdentityTraitsUpdateHistory(VPIDEntityKey key) method.
	 *
	 * This method will be removed when decoupling of EJBs can be removed from
	 * the getSubmittedIdentityTraitsUpdateHistory(VPIDEntityKey key). The
	 * getSubmittedIdentityTraitsUpdateHistory(VPIDEntityKey key) method invokes
	 * this method. Use doGetSubmittedIdentityTraitsByIdmWS method instead once
	 * decoupling is done for the
	 * getSubmittedIdentityTraitsUpdateHistory(VPIDEntityKey key) method.
	 *
	 * @param key
	 * @param returnHistory
	 * @return
	 * @throws ServiceException
	 */
	// This method has an equivalent version using Idm Web Service. However, it
	// will still be here as it is used by
	// getSubmittedIdentityTraitsUpdateHistory(), which needs to be
	// rewritten once MVI has a solution for that.
	private IPersonIdentity doGetSubmittedIdentityTraits(VPIDEntityKey key,
			boolean returnHistory) throws ServiceException {
		// No traits for a null key
		if (key == null) {
			return null;
		}

		IPersonIdentity identityTraits = null;
		try {
			IQualifiedIdentifier identifier = getVpidQualifiedIdentifierWithStationNumber(key
					.getVPID());
			identityTraits = getPSIMSOADelegateService().obtainCorrelation(
					identifier, returnHistory);

			if (logger.isDebugEnabled()) {
				logger.debug("PSIM returned: " + identityTraits);
			}

			if (identityTraits.isDeprecated()) {
				if (logger.isWarnEnabled())
					logger.warn("PSIM returned a deprecated obtainCorrelation (getIdentityTraits) result for VPID: "
							+ identityTraits.getVPID().getIdentifier());
			}
		} catch (TimeoutException ex) {
			throw new TimeoutServiceException(ex.getMessage(), ex);
		} catch (Exception e) {
			// Throw a Service Exception
			throw new ServiceException(
					"Unable to get submitted IdentityTraits from PSIM for key: "
							+ key.getKeyValueAsString(), e);
		}

		return identityTraits;
	}

	@Override
	public List getSubmittedIdentityTraitsUpdateHistory(VPIDEntityKey key)
			throws ServiceException {
		OrderedMap traitMap = new ListOrderedMap();
		IPersonIdentity submittedTraits = doGetSubmittedIdentityTraits(key,
				true);
		if (submittedTraits != null) {
			if (submittedTraits.getAuditEventTimestamp() != null)
				traitMap.put(submittedTraits.getAuditEventTimestamp(),
						convertTraits(submittedTraits, key));

			try {
				// since this needs history, loop here
				IPersonIdentity[] history = submittedTraits.getHistory();
				if (history != null) {
					IPersonIdentity historyTraits = null;
					for (int i = 0; i < history.length; i++) {
						historyTraits = history[i];
						if (historyTraits.getAuditEventTimestamp() != null) {
							// Place this entry in the map. If an entry with
							// this timestamp already exists,
							// this will replace it with the newer update.
							traitMap.put(
									historyTraits.getAuditEventTimestamp(),
									convertTraits(historyTraits, null));
						}
					}
				}
			} catch (PSIMException e) {
				throw new ServiceException(
						"Unable to navigate through submitted identitityTraits history",
						e);
			}
		}
		return new ArrayList(traitMap.values());
	}

	/*
	 * has proven helpful when using faked out VPIDs in conjunction with
	 * development testing
	 */
	private PersonIdentityTraits createdCannedTraitsWhenDataIsNotClean(
			String invalidVPID) throws ServiceException {
		if (isDataClean)
			return null;

		// Use canned traits when data is not clean and when the VPID isn't
		// valid
		logger.error("VPIDException (invalid VPID) received from PSIM...but since data is not clean, returning canned traits for VPID: "
				+ invalidVPID);

		PersonIdentityTraits traits = new PersonIdentityTraits();
		Name name = new Name();
		name.setFamilyName("Invalid VPID");
		name.setType(lookupService.getNameTypeByCode(NameType.LEGAL_NAME
				.getCode()));
		traits.addName(name);
		BirthRecord record = new BirthRecord();
		record.setBirthDate(new ImpreciseDate("194712"));
		record.setCity("Orlando");
		record.setState("FL");
		record.setMultipleBirth(Boolean.TRUE);
		traits.setBirthRecord(record);
		SSN traitSsn = new SSN();
		traitSsn.setType(lookupService.getSSNTypeByCode(SSNType.CODE_ACTIVE
				.getCode()));
		traitSsn.setSsnText("999999999");
		traits.setSsn(traitSsn);
		traits.setVpid(CommonEntityKeyFactory
				.createVPIDEntityKey("Invalid_VPID"));

		// Return the canned traits
		return traits;
	}

	/**
	 * get the patient's current Master Patient Index Identity State
	 *
	 * @param VPIDEntity
	 *            key
	 * @return String IDState
	 * @throws ServiceException
	 */
	@Override
	public String getIDState(VPIDEntityKey key) throws ServiceException {
		// No traits for a null key
		if (key == null) {
			return null;
		}
		// Get the traits for the VPID
		PersonIdentityTraits traits = null;
		try {
			// CCR11403 replace obtain call
			FullyQualifiedIdentity id = new FullyQualifiedIdentity(
					key.getShortVPID());
			traits = getIdmServiceDelegate().search(id, false);

			if (traits != null) {
				return traits.getIDState();
			}
		} catch (TimeoutException ex) {
			throw new TimeoutServiceException(ex.getMessage(), ex);
		} catch (Exception e) {
			// A VPIDException will be thrown if the VPID format is invalid (not
			// if the obtain method doesn't find traits)
			if (e instanceof VPIDException) {

				// When the data is not clean, treat this as no records found.
				// This is needed when searching by VPID from the UI and bad
				// data is entered.
				e = new gov.va.med.person.exceptions.NoRecordFoundException(
						"Unable to get IdentityTraits due to invalid VPID: "
								+ key.getKeyValueAsString(), e);
			}

			// Throw a Service Exception
			throw new ServiceException(
					"Unable to get IdentityTraits from PSIM for VPID: "
							+ key.getKeyValueAsString(), e);
		}

		return null;
	}

	/**
	 * request PSIM for the IDState change
	 *
	 * @param VPIDEntity
	 *            key
	 * @param EnrollmentStatus
	 *            enrollment status
	 * @return String resulting IDState
	 * @throws ServiceException
	 */
	@Override
	public String requestIDStateChange(VPIDEntityKey key,
			EnrollmentStatus status) throws ServiceException {
		/*
		 * The following attributes are required for this call 1. Full ICN 2.
		 * Station Number - Input format: "Station Number" from the
		 * "Institution List" table in the VHA Standard Data Service. 3.
		 * Enrollment status - Input format: "Code" from the "Enrollment Status"
		 * table in the VHA Standard Data Service.
		 */
		try {
			// IQualifiedIdentifier identifier =
			// getVpidQualifiedIdentifier(key.getVPID());
			//
			// PSIMIDStateChangeRequest request =
			// (PSIMIDStateChangeRequest)PSIMRequestFactory.createIDStateChangeRequest(identifier,
			// esrStationNumber, status.getCode());
			//
			// PSIMIDStateChangeResponse response =
			// getPSIMSOADelegateService().changeIDState(request);
			//
			// return response.getIdState();

			// IQualifiedIdentifier identifier =
			// getVpidQualifiedIdentifier(key.getVPID());
			// String requestXML = generateRequestXML(person);
			// PSIMIDStateChangeRequest request =
			// (PSIMIDStateChangeRequest)PSIMRequestFactory.createIDStateChangeRequest(identifier,
			// esrStationNumber, status.getCode());
			// PSIMIDStateChangeResponse response =
			// getPSIMSOADelegateService().changeIDState(request);
			PersonIdentityTraits traits = getIdentityTraits(key);
			String requestXML = requestXMLByTraits(traits);
			String responseXML = this.getPSIMSOADelegateService().execute(
					requestXML);
			return getIdStateFromResponseXML(responseXML);

		} catch (Exception e) {
			throw new ServiceException("Error request IDState change VPID = "
					+ key.getKeyValueAsString() + ": " + e.getMessage(), e);
		}

	}

	private String requestXMLByTraits(PersonIdentityTraits traits) {

		String XMLString = "<PSIM_REQUEST type='ADDPREFTF'> <ARGUMENT name='profile'> "
				+ "<PROFILE type=''> <ATTRIBUTE type='MMN'>  <VALUE>"
				+ traits.getMothersMaidenName()
				+ "</VALUE>"
				+ "</ATTRIBUTE> <ATTRIBUTE type='GENDER'> <VALUE>"
				+ traits.getGender()
				+ "</VALUE> "
				+ "</ATTRIBUTE> <ATTRIBUTE type='DOB'> <VALUE>"
				+ traits.getBirthRecord().getBirthDate()
				+ "</VALUE> "
				+ "</ATTRIBUTE> <NAME type='L'> <LASTNAME>"
				+ traits.getLegalName().getFamilyName()
				+ "</LASTNAME> "
				+ "<FIRSTNAME>"
				+ traits.getLegalName().getGivenName()
				+ "</FIRSTNAME> "
				+ "<MIDDLENAME>"
				+ traits.getLegalName().getMiddleName()
				+ "</MIDDLENAME> "
				+ "<SUFFIX>"
				+ traits.getLegalName().getSuffix()
				+ "</SUFFIX> </NAME> <NAME type='A'> "
				+ "<IDENTIFIER type='SS' subtype='ALIAS'> <ID> ??????????? </ID> <SOURCE>SSA</SOURCE> "
				+ "<ISSUER>USSSA</ISSUER> </IDENTIFIER> <LASTNAME>PSIMTESTALIAS</LASTNAME> "
				+ "<FIRSTNAME>FOURALIAS</FIRSTNAME> <MIDDLENAME></MIDDLENAME> <SUFFIX></SUFFIX> "
				+ "</NAME> <ADDRESS type='POB'> <CITY>PROVIDENCE</CITY> <STATE>VA</STATE> </ADDRESS> "
				+ "<IDENTIFIER type='SS' subtype='LEGAL'> <ID>"
				+ traits.getSsnText()
				+ "</ID> <ISSUER>USSSA</ISSUER> </IDENTIFIER> "
				+ "<IDENTIFIER type='NI'> <ID>1008522837V021968</ID> </IDENTIFIER> </PROFILE> </ARGUMENT> "
				+ "<ARGUMENT name='preferredFacilityNumber'> <VALUE>500</VALUE> </ARGUMENT> "
				+ "<ARGUMENT name='patientVeteran'> <VALUE>Y</VALUE> </ARGUMENT> "
				+ "<ARGUMENT name='patientServiceConnected'> <VALUE>Y</VALUE> </ARGUMENT> "
				+ "<ARGUMENT name='patientType'> <VALUE>1</VALUE> </ARGUMENT> </PSIM_REQUEST>";

		return XMLString;
	}

	private String getIdStateFromResponseXML(String responseXML)
			throws Exception {

		String idState = null;

		if (!(responseXML.indexOf("type=\'ERROR\'") == -1)) {
			String errorText = responseXML.substring(
					responseXML.indexOf("<TEXT>") + "<TEXT>".length(),
					responseXML.indexOf("</TEXT>")).trim();
			String onlyErrText = errorText.substring(
					errorText.indexOf("<![CDATA[") + "<![CDATA[".length(),
					errorText.indexOf("]]>"));
			throw new Exception(onlyErrText);
		} else {
			idState = responseXML.substring(
					responseXML.indexOf("<STATUS>") + 8,
					responseXML.indexOf("</STATUS>"));
		}

		return idState;
	}

	public IdmWebServiceDelegate getIdmServiceDelegate() {
		return idmServiceDelegate;
	}

	public void setIdmServiceDelegate(IdmWebServiceDelegate idmServiceDelegate) {
		this.idmServiceDelegate = idmServiceDelegate;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * gov.va.med.esr.service.PSDelegateService#getVhicId(gov.va.med.esr.common
	 * .model.person.id.VPIDEntityKey)
	 */

	@Override
	public String getVhicId(VPIDEntityKey key) throws ServiceException {

		if (key == null) {
			return null;
		}
		String vhicId = null;
		try {

			// get a list of correlations/patient identifiers by 1309 call
			List ids = idmServiceDelegate.getCorrelationsByVPID(key);
			Iterator idsIt = ids.iterator();

			while (idsIt.hasNext()) {
				// PSIM(obtainSOI())returns two types of sites. One of type 'NI'
				// and one of type "PI".
				// "NI" means the Identifier in the object is either a VPID or
				// ICN. If the Identifier Type is 'PI', the Identifier in
				// the object will be the IEN (in this case DFN)from the
				// Patient file (2) in VistA.
				// IEN = Internal Entry Number. This is basically the primary
				// key in a VistA file. The DFN is the IEN from the Patient
				// file.
				// So list of sites should be only of type "PI"
				PatientIdentifier pid = (PatientIdentifier) idsIt.next();
				if ("PI".equalsIgnoreCase(pid.getIdentityType())
						&& "USVHA"
								.equalsIgnoreCase(pid.getAssigningAuthority())
						&& ("742V1".equalsIgnoreCase(pid.getStationNumber()))) // 742V1 is for VHIC site
				 {

					vhicId = pid.getIdentity();
					if(vhicId != null && vhicId.length() >0 )
						return vhicId;
				 }

			}
		 } catch (TimeoutException ex) {
			throw new TimeoutServiceException(ex.getMessage(), ex);
		 } catch (Exception e) {
			throw new ServiceException(
					"Unable to get sites from Idm Web Service for VPID: "
							+ key.getKeyValueAsString(), e);
		}
		return vhicId;
	}

	// Not used by ESR but can be reused in the future

	/*
	 * private void convertName(PersonIdentityImpl impl, Name name, int index) {
	 * impl.setFirstName(index, name.getGivenName()); impl.setMiddleName(index,
	 * name.getMiddleName()); impl.setLastName(index, name.getFamilyName()); if
	 * (name.getType() != null) impl.setNameType(index,
	 * name.getType().getCode()); impl.setPrefix(index, name.getPrefix());
	 * impl.setSuffix(index, name.getSuffix()); }
	 */

	/**
	 * Converts ESR object to PSIM object. TODO: move this to ConversionService
	 *
	 * @param source
	 *            the ESR traits
	 *
	 * @return the PSIM traits
	 * @throws ConversionServiceException
	 *             if the traits couldn't be converted.
	 */

	/*
	 * private IPersonIdentity convertTraits(PersonIdentityTraits source) throws
	 * ConversionServiceException { try { PersonIdentityImpl target = new
	 * PersonIdentityImpl(); if (source == null) return target;
	 *
	 * if (source.getVpid() != null) { IQualifiedIdentifier vpid =
	 * getVpidQualifiedIdentifier(source.getVpid().getVPID());
	 * target.setVPID(vpid); }
	 *
	 * since this conversion can also apply for updating, make sure to include
	 * all names. PSIM will perform searching (if that is the context) on just
	 * the first name in this list (so guarantee legal name is first)
	 *
	 * int i = 0; Name legalName = source.getLegalName(); if (legalName != null)
	 * { convertName(target, legalName, i++); } Set names = source.getNames();
	 * Iterator itr = names != null ? names.iterator() : null; Name name = null;
	 * while (itr != null && itr.hasNext()) { name = (Name)itr.next(); if
	 * (name.equals(legalName)) continue; // ignore this one
	 *
	 * convertName(target, name, i++); }
	 * target.setMothersMaidenName(source.getMothersMaidenName());
	 *
	 * if (source.getGender() != null)
	 * target.setGender(source.getGender().getCode()); SSN ssnBom =
	 * source.getSsn(); if (ssnBom != null &&
	 * !org.apache.commons.lang.StringUtils.isEmpty(ssnBom.getSsnText())) {
	 * IQualifiedIdentifier ssn =
	 * getSsnQualifiedIdentifier(ssnBom.getSsnText());
	 * gov.va.med.person.idmgmt.db.hb.SSN ssnObj = new
	 * gov.va.med.person.idmgmt.db.hb.SSN(); // make sure to set all the SSN
	 * fields ssnObj.setSSN(ssn); // if no SSNType, assume that is CODE_OFFICIAL
	 * String ssnTypeCode = ssnBom.getType() != null ?
	 * ssnBom.getType().getCode() : SSNType.CODE_ACTIVE.getCode();
	 * ssnObj.setSsnType(ssnTypeCode);
	 * ssnObj.setSentToSSA(ssnBom.getSsaSentDate());
	 * ssnObj.setReceivedFromSSA(ssnBom.getSsaReceivedDate());
	 * ssnObj.setVerifiedBySSA(ssnBom.getSsaVerificationDate());
	 *
	 * if (ssnBom.getSsaVerificationStatus() != null)
	 * ssnObj.setSsnVerificationStatus
	 * (ssnBom.getSsaVerificationStatus().getCode());
	 *
	 * if (ssnBom.getPseudoSSNReason() != null)
	 * ssnObj.setPseudoSSNReason(ssnBom.getPseudoSSNReason().getCode());
	 *
	 * if (ssnBom.getSourceOfChange() != null)
	 * ssnObj.setSsnChangeSource(ssnBom.getSourceOfChange().getCode());
	 *
	 * if (ssnBom.getSsaMessage() != null)
	 * ssnObj.setSsaVerificationCode(ssnBom.getSsaMessage().getCode());
	 *
	 * target.setSSN(ssnObj); } if (source.getBirthRecord() != null) {
	 * target.setPlaceOfBirthCity(source.getBirthRecord().getCity());
	 * target.setPlaceOfBirthState(source.getBirthRecord().getState());
	 * target.setPlaceOfBirthCountry(source.getBirthRecord().getCountry());
	 *
	 * Boolean isMultipleBirth = source.getBirthRecord().getMultipleBirth(); if
	 * (isMultipleBirth==null)
	 * target.setMultiBirth(MultipleBirthIndicator.MULTI_BIRTH_DELETE); else
	 * target.setMultiBirth(isMultipleBirth.booleanValue());
	 *
	 * // TODO: we are living with an ImpreciseDate mismatch between // PSIM //
	 * and ESR!? ImpreciseDate esrImpreciseBirthDate = source.getBirthRecord()
	 * .getBirthDate(); if (esrImpreciseBirthDate != null) target
	 * .setDateOfBirth(new gov.va.med.person.idmgmt.types.ImpreciseDate(
	 * esrImpreciseBirthDate.toStandardFormat())); }
	 *
	 * // CCR 10471 ADD A PERSON //if (source.isAddAPerson()) { // TBD;
	 * preferred faclity is not in the identity traits. // Is preferred facility
	 * the sending facility?
	 *
	 * return target; } catch (Exception e) { throw new
	 * ConversionServiceException("Unable to convert ESR traits to PSIM traits",
	 * e); } }
	 */

	/**
	 * @see gov.va.med.esr.service.PSDelegateService#notifyPsdOfDemographicChange(gov.va.med.esr.common.model.person.id.VPIDEntityKey)
	 */
	/*
	 * public void notifyPsdOfDemographicChange(VPIDEntityKey key) throws
	 * ServiceException { //CCR11876 return; if (logger.isInfoEnabled()) {
	 * logger.info("Notifying PSD of a demographics change for VPID: " +
	 * key.getVPID()); }
	 *
	 * try { getPSIMSOADelegateService().notifyPsdOfAdrUpdate(key.getVPID()); }
	 * catch (TimeoutException ex) { throw new
	 * TimeoutServiceException(ex.getMessage(), ex); } catch (PSException e) {
	 * throw new ServiceException("Unable to communicate Person change to PSD",
	 * e); } }
	 */

	/**
	 * add preferred "proxy" treating facility
	 *
	 * @param Person
	 *            person
	 * @param String
	 *            IDState to be changed to
	 * @throws ServiceException
	 */
	// CR 11776 - replacing legacy EJB
	// This method is not used by ESR anymore, so commented out.
	// utilizes the IdmWebService.addPreferredFacility
	/*
	 * @Override public void addPreferredTreatingFacility(Person person) throws
	 * ServiceException { Validate.notNull(person, "person must be non-null");
	 * IdmServiceVO idmServiceVO =
	 * personService.convertPersonToIdmServiceVO(person);
	 * idmServiceDelegate.addPreferredFacility(idmServiceVO); }
	 */

}
