package gov.va.med.esr.service.impl;

import gov.va.med.esr.common.infra.ImpreciseDate;
import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.comms.CommsLogEntry;
import gov.va.med.esr.common.model.lookup.AddressType;
import gov.va.med.esr.common.model.lookup.BadAddressReason;
import gov.va.med.esr.common.model.lookup.Country;
import gov.va.med.esr.common.model.lookup.DeathDataSource;
import gov.va.med.esr.common.model.lookup.MaritalStatus;
import gov.va.med.esr.common.model.lookup.NameType;
import gov.va.med.esr.common.model.lookup.SSAVerificationStatus;
import gov.va.med.esr.common.model.lookup.SSNType;
import gov.va.med.esr.common.model.lookup.SelfIdentifiedGenderIdentity;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.lookup.DeathDataSource.Code;
import gov.va.med.esr.common.model.messaging.SiteIdentity;
import gov.va.med.esr.common.model.party.Address;
import gov.va.med.esr.common.model.party.Phone;
import gov.va.med.esr.common.model.person.BirthRecord;
import gov.va.med.esr.common.model.person.DeathRecord;
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.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.common.util.CommonDateUtils;

import gov.va.med.esr.service.EnvironmentParamService;
import gov.va.med.esr.service.IdmServiceVO;

import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.PersonIdentityTraits;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.persistent.DAOOperations;
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.ServiceException;
import gov.va.med.ps.model.PatientIdentifier;
import gov.va.oit.oed.DNS  MCCIIN000002UV01Document;
import gov.va.oit.oed.DNS  PRPAIN201301UV02Document;
import gov.va.oit.oed.DNS  PRPAIN201302UV02Document;
import gov.va.oit.oed.DNS  PRPAIN201305UV02Document;
import gov.va.oit.oed.DNS  PRPAIN201306UV02Document;
import gov.va.oit.oed.DNS  PRPAIN201309UV02Document;
import gov.va.oit.oed.DNS  PRPAIN201310UV02Document;
import gov.va.oit.oed.DNS  VAIdMStub;

import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.xmlbeans.XmlCursor;

import v3.hl7_org.AD;
import v3.hl7_org.ANY;
import v3.hl7_org.AdxpAdditionalLocator;
import v3.hl7_org.AdxpCity;
import v3.hl7_org.AdxpCountry;
import v3.hl7_org.AdxpPostalCode;
import v3.hl7_org.AdxpState;
import v3.hl7_org.AdxpStreetAddressLine;
import v3.hl7_org.BL;
import v3.hl7_org.CD;
import v3.hl7_org.CE;
import v3.hl7_org.COCTMT090003UV01AssignedEntity;
import v3.hl7_org.COCTMT090003UV01Organization;
import v3.hl7_org.COCTMT090100UV01AssignedPerson;
import v3.hl7_org.COCTMT090100UV01Person;
import v3.hl7_org.COCTMT150000UV02Organization;
import v3.hl7_org.COCTMT150002UV01Organization;
import v3.hl7_org.COCTMT150003UV03Organization;
import v3.hl7_org.CS;
import v3.hl7_org.EN;
import v3.hl7_org.EnFamily;
import v3.hl7_org.EnGiven;
import v3.hl7_org.EnPrefix;
import v3.hl7_org.EnSuffix;
import v3.hl7_org.II;
import v3.hl7_org.INT;
import v3.hl7_org.IVLTS;
import v3.hl7_org.MCCIMT000100UV01AttentionLine;
import v3.hl7_org.MCCIMT000100UV01Device;
import v3.hl7_org.MCCIMT000100UV01Receiver;
import v3.hl7_org.MCCIMT000100UV01Sender;
import v3.hl7_org.MCCIMT000200UV01Acknowledgement;
import v3.hl7_org.MCCIMT000300UV01Acknowledgement;
import v3.hl7_org.MCCIMT000300UV01AcknowledgementDetail;
import v3.hl7_org.MFMIMT700701UV01Custodian;
import v3.hl7_org.MFMIMT700701UV01DataEnterer;
import v3.hl7_org.MFMIMT700711UV01QueryAck;
import v3.hl7_org.PN;
import v3.hl7_org.PRPAIN201301UV02;
import v3.hl7_org.PRPAIN201301UV02MFMIMT700701UV01ControlActProcess;
import v3.hl7_org.PRPAIN201301UV02MFMIMT700701UV01RegistrationEvent;
import v3.hl7_org.PRPAIN201301UV02MFMIMT700701UV01Subject1;
import v3.hl7_org.PRPAIN201301UV02MFMIMT700701UV01Subject2;
import v3.hl7_org.PRPAIN201302UV02;
import v3.hl7_org.PRPAIN201302UV02MFMIMT700701UV01ControlActProcess;
import v3.hl7_org.PRPAIN201302UV02MFMIMT700701UV01RegistrationEvent;
import v3.hl7_org.PRPAIN201302UV02MFMIMT700701UV01Subject1;
import v3.hl7_org.PRPAIN201302UV02MFMIMT700701UV01Subject2;
import v3.hl7_org.PRPAIN201305UV02;
import v3.hl7_org.PRPAIN201305UV02QUQIMT021001UV01ControlActProcess;
import v3.hl7_org.PRPAIN201306UV02;
import v3.hl7_org.PRPAIN201306UV02MFMIMT700711UV01ControlActProcess;
import v3.hl7_org.PRPAIN201306UV02MFMIMT700711UV01Subject1;
import v3.hl7_org.PRPAIN201309UV02;
import v3.hl7_org.PRPAIN201309UV02QUQIMT021001UV01ControlActProcess;
import v3.hl7_org.PRPAIN201310UV02;
import v3.hl7_org.PRPAIN201310UV02MFMIMT700711UV01RegistrationEvent;
import v3.hl7_org.PRPAIN201310UV02MFMIMT700711UV01Subject1;
import v3.hl7_org.PRPAMT201301UV02BirthPlace;
import v3.hl7_org.PRPAMT201301UV02OtherIDs;
import v3.hl7_org.PRPAMT201301UV02Patient;
import v3.hl7_org.PRPAMT201301UV02Person;
import v3.hl7_org.PRPAMT201302UV02AdministrativeObservation;
import v3.hl7_org.PRPAMT201302UV02AdministrativeObservationId;
import v3.hl7_org.PRPAMT201302UV02BirthPlace;
import v3.hl7_org.PRPAMT201302UV02OtherIDs;
import v3.hl7_org.PRPAMT201302UV02Patient;
import v3.hl7_org.PRPAMT201302UV02Person;
import v3.hl7_org.PRPAMT201302UV02Subject4;
import v3.hl7_org.PRPAMT201304UV02Patient;
import v3.hl7_org.PRPAMT201306UV02LivingSubjectAdministrativeGender;
import v3.hl7_org.PRPAMT201306UV02LivingSubjectBirthTime;
import v3.hl7_org.PRPAMT201306UV02LivingSubjectId;
import v3.hl7_org.PRPAMT201306UV02LivingSubjectName;
import v3.hl7_org.PRPAMT201306UV02MatchCriterionList;
import v3.hl7_org.PRPAMT201306UV02MinimumDegreeMatch;
import v3.hl7_org.PRPAMT201306UV02OtherIDsScopingOrganization;
import v3.hl7_org.PRPAMT201306UV02ParameterList;
import v3.hl7_org.PRPAMT201306UV02PatientAddress;
import v3.hl7_org.PRPAMT201306UV02PatientTelecom;
import v3.hl7_org.PRPAMT201306UV02QueryByParameter;
import v3.hl7_org.PRPAMT201307UV02ParameterList;
import v3.hl7_org.PRPAMT201307UV02PatientIdentifier;
import v3.hl7_org.PRPAMT201307UV02QueryByParameter;
import v3.hl7_org.PRPAMT201310UV02BirthPlace;
import v3.hl7_org.PRPAMT201310UV02OtherIDs;
import v3.hl7_org.PRPAMT201310UV02Patient;
import v3.hl7_org.PRPAMT201310UV02Person;
import v3.hl7_org.PRPAMT201310UV02Subject;
import v3.hl7_org.ParticipationTargetSubject;
import v3.hl7_org.QUQIMT021001UV01AuthorOrPerformer;
import v3.hl7_org.QUQIMT021001UV01DataEnterer;
import v3.hl7_org.ST;
import v3.hl7_org.SXCMTS;
import v3.hl7_org.TEL;
import v3.hl7_org.TS;
import v3.hl7_org.XParticipationAuthorPerformer;


public class IdmWebServiceDelegateImpl extends AbstractComponent implements IdmWebServiceDelegate {


	private static final long serialVersionUID = 8534341805323475694L;


	//CCR 11402 - constants for BAD Address Reason
	private static Hashtable idmBadAddressTable= initIdmBadAddressTable();
	private static final String BAD_ADDRESS = "BAD";
	private static final String HOME_PERMANENT = "HP";
	private static final String PART_TYPE_ADL = "ADL";

	private static final String MOTHERS_MAIDEN_NAME_TYPE = "C";
	private static final String ALIAS_NAME_TYPE = "P";

	private static final String IDM_DOES_NOT_EXIST = "Does Not Exist";
	private static final String IDM_200ESR_NOT_FOUND = "No ACTIVE Correlation found";

	private static final String COMPOSITE_NO_HISTORY = "MVI.COMP1";
	private static final String ESR_CORRELATION = "200ESR";

	private static final String YES = "Y";
	private static final String NO = "N";
	private DAOOperations genericDAO;
	private static final String QUERY_GET_SSA_VER_DETAIL  = "idmWsDelegateQuery_GetSsaVerificationDetailByVpid";
	private static final String QRY_SSA_PARAM_NAME = "vpid";



	/**
	 * Time out for the web service call in milli seconds
	 */
	private long webServiceTimeout;
	/**
	 * Any environment specific variable go here.
	 */
	private String idmServiceEndPoint = null;


	private LookupService lookupService = null;

	private String BGS_ExternalKey = "VOA";
	private String BGS_applicationName = "MVI";
	private String BGS_CLIENT_MACHINE = null;


	/**
	 * Managed server name like MS1..It will be used in creating the unique
	 * message ID.
	 */
	private String msName = null;

	/*
	 * Development D SQA D PREPROD T (Training) Production P
	 */
	private String processingCode = null;

	private String esrStationNumber = null;



	/**
	 * There are the hard coded values for all the environments.
	 */
	public static final String ROOT_VA_OID = "2.16.840.1.113883.4.349";

	public static final String SSN_OID = "2.16.840.1.113883.4.1";

	public static final String VA_OID_EXT_PREFIX = "MCID";

	public static final String INTERACTION_ID_ROOT = "2.16.840.1.113883.1.6";

	public static final String SCOPING_ORG_OID = "1.2.840.114350.1.13.99997.2.3412";

	public static final String PROCESSING_MODE_CODE = "T";

	public static final String ACCEPT_ACK_CODE = "AL";

	public static final String APPLICATION_ACCEPT = "AA";

	// Receiver .
	public static final String REC_TYPE_CODE = "RCV";

	public static final String REC_CLASS_CODE = "DEV";

	public static final String REG_CLASS_CODE = "REG";

	public static final String REC_DETERMINER_CODE = "INSTANCE";

	public static final String REC_VA_OID = "2.16.840.113883.4.349";

	// Sender .
	public static final String SND_TYPE_CODE = "SND";

	public static final String SND_CLASS_CODE = "DEV";

	public static final String SND_DETERMINER_CODE = "INSTANCE";

	public static final String SND_VA_OID = "1.2.840.114350.1.13.99997.2.7788";

	// For controlActProcess
	public static final String CACT_CLASS_CODE = "CACT";

	public static final String CACT_MOOD_CODE = "EVN";

	public static final String CACT_CODE_SYSTEM = "2.16.840.1.113883.1.6";

	// queryByParameter
	public static final String QRY_ROOT = "1.2.840.114350.1.13.99999.4567.34";

	public static final String QRY_STATUS = "new";

	public static final String QRY_RES_PRIORITY_CD = "I";

	// patientIdentifier
	public static final String PATIENT_ID_ROOT = "2.16.840.1.113883.4.349";

	public static final String CORRELATION_VIEW = "COR";

	public static final String DoD_TEST_OID = "2.16.840.1.113883.3.364";

	public static final String DoD_PROD_OID = "2.16.840.1.113883.3.42.10001.100001.12";

	private static String[] BGSCredentialsArray = null;

	private static final String VERSION_CODE = "1.0";



	//CCR12665- added for verification of supported environments for site identities
	private EnvironmentParamService environmentParamService;

	public String getIdmServiceEndPoint() {
		return idmServiceEndPoint;
	}

	public void setIdmServiceEndPoint(String idmServiceEndPoint) {
		this.idmServiceEndPoint = idmServiceEndPoint;
	}

	public String getProcessingCode() {
		return processingCode;
	}

	public void setProcessingCode(String processingCode) {
		this.processingCode = processingCode;
	}

	public String getEsrStationNumber() {
		return esrStationNumber;
	}

	public void setEsrStationNumber(String esrStationNumber) {
		this.esrStationNumber = esrStationNumber;
	}

	/**
     * @return Returns the genericDAO.
     */
    public DAOOperations getGenericDAO()
    {
        return genericDAO;
    }

    /**
     * @param genericDAO The genericDAO to set.
     */
    public void setGenericDAO(DAOOperations genericDAO)
    {
        this.genericDAO = genericDAO;
    }

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

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


	public String getMsName() {
		if (msName == null) {
			msName = "JUnit";
		}
		return msName;
	}

	public void setMsName(String msName) {
		this.msName = msName;
	}

	public LookupService getLookupService() {
		return lookupService;
	}

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


	public long getWebServiceTimeout() {
		return webServiceTimeout;
	}

	public void setWebServiceTimeout(long webServiceTimeout) {
		this.webServiceTimeout = webServiceTimeout;
	}


	/**
	 * get the correlations with a Fully Qualified Identifier: "id^type^station number^assigning authority"
	 * e.g. "1008590379V913421^NI^200M^USVHA", "0000001008590379V913421000000^PI^200ESR^USVHA",
	 *   	"1606118356^NI^200DOD^USDOD", "186317^PI^556^USVHA"
	 * @param fullyQualifiedId
	 * @return List of correlations in PatientIdentifier objects
	 * @throws ServiceException
	 */
	public List getCorrelationsByFullyQualifiedIdentifier(String fullyQualifiedId) throws ServiceException
	{
		if (fullyQualifiedId.indexOf("200DOD")> 0)
			return getCorrelations(fullyQualifiedId, DoD_TEST_OID);

		return getCorrelations(fullyQualifiedId, ROOT_VA_OID);
	}

	public List getCorrelationsByVPID(VPIDEntityKey key)
			throws ServiceException {

		return getCorrelations(key.getVPID() + "^NI", ROOT_VA_OID);
	}


	private List getCorrelations(String fullyQualifiedId, String oid) throws ServiceException
	{
		List identities = new ArrayList();
		PRPAIN201309UV02Document pRPA_IN201309UV0212 = (PRPAIN201309UV02Document) getXmlObject(gov.va.oit.oed.DNS  PRPAIN201309UV02Document.class);
		PRPAIN201309UV02 pPRPAIN201309UV02 = pRPA_IN201309UV0212
				.addNewPRPAIN201309UV02();

		// <id root="2.16.840.1.113883.4.349" extension="MCID-12345"/>
		Date currentDate = Calendar.getInstance().getTime();
		String uniqueId = generateUniqueID(currentDate);

		pPRPAIN201309UV02.addNewVersionCode().setCode(VERSION_CODE);

		populateHeaderRoot(pPRPAIN201309UV02.addNewId(), uniqueId);
		populateCreationTime(pPRPAIN201309UV02.addNewCreationTime(),
				currentDate);

		// Interaction id.
		// <interactionId root="2.16.840.1.113883.1.6"
		// extension="PRPA_IN201309UV02" />
		populateInteractionId(pPRPAIN201309UV02.addNewInteractionId(),
				"PRPA_IN201309UV02");
		populateProcessingCode(pPRPAIN201309UV02.addNewProcessingCode());
		populateProcessingModeCode(pPRPAIN201309UV02.addNewProcessingModeCode());
		populateAcceptAckCode(pPRPAIN201309UV02.addNewAcceptAckCode());
		populateReceiver(pPRPAIN201309UV02.addNewReceiver());
		populateSender(pPRPAIN201309UV02.addNewSender());

		// <controlActProcess
		PRPAIN201309UV02QUQIMT021001UV01ControlActProcess controlActProcess = pPRPAIN201309UV02
				.addNewControlActProcess();
		controlActProcess.setClassCode(v3.hl7_org.ActClassControlAct.Enum
				.forString(CACT_CLASS_CODE));
		controlActProcess.setMoodCode(v3.hl7_org.XActMoodIntentEvent.Enum
				.forString(CACT_MOOD_CODE));
		CD code = controlActProcess.addNewCode();
		code.setCode("PRPA_TE201309UV02");
		code.setCodeSystem(CACT_CODE_SYSTEM);
		QUQIMT021001UV01AuthorOrPerformer authorOrPerformer = controlActProcess
				.addNewAuthorOrPerformer();
		authorOrPerformer.setTypeCode(XParticipationAuthorPerformer.Enum
				.forString("AUT"));

		PRPAMT201307UV02QueryByParameter queryByParameter = controlActProcess
				.addNewQueryByParameter();

		// These values will be echoed back to ESR, just send empty
		II queryId = queryByParameter.addNewQueryId();
		queryId.setRoot(getMsName());
		queryId.setExtension(uniqueId);

		CS statusCode = queryByParameter.addNewStatusCode();
		statusCode.setCode(QRY_STATUS);

		CS priorityCode = queryByParameter.addNewResponsePriorityCode();
		priorityCode.setCode(QRY_RES_PRIORITY_CD);

		PRPAMT201307UV02ParameterList paramList = queryByParameter
				.addNewParameterList();
		PRPAMT201307UV02PatientIdentifier pi = paramList
				.addNewPatientIdentifier();

		II pivalue = pi.addNewValue();

		pivalue.setRoot(oid);
		pivalue.setExtension(fullyQualifiedId);
		ST piSematicsText = pi.addNewSemanticsText();
		piSematicsText.newCursor().setTextValue("Patient.Id");
		PRPAIN201310UV02Document responseDocument = null;

        if (logger.isDebugEnabled())
            logger.debug("IdmServiceRequest: " + pRPA_IN201309UV0212);

		try {
			VAIdMStub stub = new VAIdMStub(getIdmServiceEndPoint());
			stub._getServiceClient().getOptions().setTimeOutInMilliSeconds(webServiceTimeout);
			responseDocument = stub.PRPA_IN201309UV02(pRPA_IN201309UV0212);
		} catch (Exception e) {
			throw new ServiceException(e);
		}
        if (logger.isDebugEnabled())
            logger.debug("IdmServiceResponse: " + responseDocument);

		PRPAIN201310UV02 response = responseDocument.getPRPAIN201310UV02();

		String responseCode = null;

		MCCIMT000300UV01Acknowledgement[] ack = response
				.getAcknowledgementArray();

		//get the return code from the server. If it is not AA then there is a problem.
		if (ack.length > 0) {
			responseCode = ack[0].getTypeCode().getCode();
		}

		if (!APPLICATION_ACCEPT.equals(responseCode)) {
			if (logger.isErrorEnabled()) {
				logger.error("getCorrelationsByVPID return error :"
						+ ack[0].getAcknowledgementDetailArray()[0].getText().newCursor().getTextValue());
				return null;
			}
		}

		PRPAIN201310UV02MFMIMT700711UV01Subject1[] subjects = response
				.getControlActProcess().getSubjectArray();

		for (int i = 0; subjects != null && i < subjects.length; i++) {

			for (int j = 0; j < subjects.length; j++) {
				PRPAIN201310UV02MFMIMT700711UV01Subject1 sub1 = subjects[j];

				//System.out.println("Subject1:" +sub1);
				PRPAIN201310UV02MFMIMT700711UV01RegistrationEvent regEvent = sub1
						.getRegistrationEvent();

				regEvent.getClassCode();

				//System.out.println("regEvent:" +regEvent);
				PRPAMT201304UV02Patient patient = regEvent.getSubject1()
						.getPatient();
				II[] ids = patient.getIdArray();
				for (int k = 0; k < ids.length; k++) {
					II currId = ids[k];
					String ext = currId.getExtension();
					//System.out.println(ext);
					PatientIdentifier identifier = new PatientIdentifier(ext);

					//ES 4.1.2_CodeCR13926 - Deprecated site DFN being treated as valid site
					//Filter out historical or deprecated (i.e. id state as H) DFNs
					//DFN means patient identifiers with identity type as PI
					//But allow active or deprecated PIs if 200ESR is station number.
					if ("PI".equalsIgnoreCase(identifier.getIdentityType())
							&& ("A".equalsIgnoreCase(identifier.getIdState()) || "200ESR".equalsIgnoreCase(identifier.getStationNumber()))) {
						identities.add(identifier);
					} else if (!"PI".equalsIgnoreCase(identifier.getIdentityType())) {
						identities.add(identifier);
					}

				}
			}
		}
		return identities;
	}

	//CCR 11758
	// If the primary view search does not return the SSN verification status code,
	//ESR will need to query for 200ESR correlation traits and retrieve the status.
	public Set<PersonIdentityTraits> searchWithCompositeCall(PersonIdentityTraits traits, IdMSearchInfo searchInfo) throws ServiceException {
		Set<PersonIdentityTraits> traitsSet = search(null, false, searchInfo, null, traits, COMPOSITE_NO_HISTORY, false);


		if (traitsSet != null)
			return overlaySSNVerificationStatus(traitsSet);  //CCR 11776 - overlay list of traits for SSNverifivcation status
		return null;

	}


	//CCR 11758
	// If the primary view search does not return the SSN verification status code,
	//ESR will need to query for 200ESR correlation traits and retrieve the status.
	public Set<PersonIdentityTraits> search(PersonIdentityTraits traits, boolean attendedSearch) throws ServiceException {
		Set<PersonIdentityTraits> traitsSet = search(null, false, null, null, traits, null, attendedSearch);
		if (traitsSet != null)
			return overlaySSNVerificationStatus(traitsSet);  //CCR 11776 - overlay list of traits for SSNverifivcation status
		return null;
	}


	public PersonIdentityTraits getESRCorrelation(VPIDEntityKey vpid)
		throws ServiceException {
		// get ESR correlation using vpid
		FullyQualifiedIdentity id = new FullyQualifiedIdentity(vpid.getVPID(), esrStationNumber);


		Set<PersonIdentityTraits> resultTraits = search(id, false, null, CORRELATION_VIEW, null, null, false);
		if (resultTraits == null || resultTraits.isEmpty()) {
			throw new ServiceException("Person Not Found: Search returned null for getESRCorrelation, vpid = " + vpid.getKeyValueAsString());
		}
		if (resultTraits.size() > 1) {
			throw new ServiceException("getESRCorrleation returned multiple for vpid = " + vpid.getKeyValueAsString());
		}
		return (PersonIdentityTraits) resultTraits.iterator().next();
	}

	//CCR 11495: equavalent to Legacy PSIM exceptions by keyword
	//•	“Does Not Exist” is equivalent to Legacy PSIM exception = NoRecordFoundException
	//•	“Invalid” and “format” is equivalent to Legacy PSIM exception = VPIDException
	private String getProperErrorMessage(MCCIMT000300UV01Acknowledgement ack)
	{
		String errorMessage = ack.getAcknowledgementDetailArray()[0].getText().newCursor().getTextValue();

		if (errorMessage.indexOf(IDM_DOES_NOT_EXIST)>=0 || errorMessage.indexOf(IDM_200ESR_NOT_FOUND)>=0)
			return "No Record Found: " + errorMessage;
		else if (errorMessage.indexOf("Invalid") >= 0 &&
				(errorMessage.indexOf("checkdigit") >= 0 || errorMessage.indexOf("format") >= 0) )
			return "VPID Exception: " + errorMessage;

		return errorMessage;

	}


	//CR11776- added a flag to execute unattended search
	//CCR12665 -isIVMdata -to get sites list...
	private Set<PersonIdentityTraits> search(FullyQualifiedIdentity id, boolean isSearchByDfn, IdMSearchInfo searchInfo, String responseGroupId, PersonIdentityTraits traits, String compositeCode, boolean attendedSearch) throws ServiceException {

		PRPAIN201305UV02Document pPRPAIN201305UV02Doc = createSearchRequest(id, responseGroupId, traits, compositeCode, searchInfo, attendedSearch);
		PRPAIN201306UV02Document response = null;
		Set<PersonIdentityTraits> resultTraits = null;

        if (logger.isDebugEnabled())
            logger.debug("IdmServiceRequest: " + pPRPAIN201305UV02Doc);


		try {
			VAIdMStub stub = new VAIdMStub(getIdmServiceEndPoint());
			stub._getServiceClient().getOptions().setTimeOutInMilliSeconds(webServiceTimeout);
			response = stub.PRPA_IN201305UV02(pPRPAIN201305UV02Doc);

	        if (logger.isDebugEnabled())
	            logger.debug("IdmServiceResponse: " + response);

			if (response == null)
				return null;

			//the value of SEARCH.TOKEN as in the id extension:
			//<id extension="WSDOC1405222227306941417197261" root="2.16.840.1.113883.4.349"/>
			if (searchInfo != null && response.getPRPAIN201306UV02().getId() != null) {
				searchInfo.setSearchToken(response.getPRPAIN201306UV02().getId().getExtension());
			}

			String responseCode = null;
			MCCIMT000300UV01Acknowledgement[] ack =  response.getPRPAIN201306UV02().getAcknowledgementArray();

			//get the return code from the server. If it is not AA then there is a problem.
			if (ack.length > 0) {
				responseCode = ack[0].getTypeCode().getCode();
			}

			//if not AA, log error code and throw exception
			if (!APPLICATION_ACCEPT.equals(responseCode)) {
				String errorMessage = getProperErrorMessage(ack[0]); //CCR 11495


				if (searchInfo != null && searchInfo.isExtendedSearch() && id != null ) {
					//must be level 1 retrieval with id, person not found
					//log it, and return null so that level 1 can continue search with traits
					logger.error("VOA Level 1 user retrieve by fully qualified identity= " + id + " returns error :" + errorMessage);
					return null;
				}

				if (logger.isErrorEnabled()) {
					logger.error("search by fully qualified identity returns error :" + errorMessage);
				}

				throw new ServiceException(errorMessage);
			}


			//this section was moved from within parseSearchResponse(response) here
			//this is not for retrieve; it's for search (match)to handle max records exceed, record not found, or unknown.
			//The Acknowledgement typeCode will remain an “AA” Application Accept.
			PRPAIN201306UV02MFMIMT700711UV01ControlActProcess controlActproess = response
					.getPRPAIN201306UV02().getControlActProcess();
			MFMIMT700711UV01QueryAck queryAck = controlActproess.getQueryAck();
			if (queryAck != null && queryAck.getQueryResponseCode() != null) {
				String queryAckResponseCode = queryAck.getQueryResponseCode()
						.getCode();
				// If your query results are above the maximum return records
				// defined by the business
				// a response code of “QE” (Query Error) will be returned.
				if (queryAckResponseCode != null
						&& "QE".equals(queryAckResponseCode)) {
					// Using -1 to indicate the total records that would have
					// been returned is not known.
					// throw new MaxRecordsExceededException(-1, -1);

					throw new MaxRecordsExceededException(-1, 10,
							"Search Idm Service exceeded allowable limit of 10 matched records.");

				} else if ("NF".equals(queryAckResponseCode)) {
					// Search returned no records.
					return null; //no match found
				} else if (!"OK".equals(queryAckResponseCode)) {
					throw new ServiceException(
							"Unknown response code for Search:"
									+ queryAckResponseCode);
				}
			}

			resultTraits = parseSearchResponse(response, isSearchByDfn, searchInfo);

		} catch(ServiceException se ) {
			throw se;
		} catch (Exception e) {
				throw new ServiceException(e);
		}

		if (resultTraits == null || resultTraits.size() == 0)
			return null;

		return resultTraits;
	}


	//CCR 10758: added search call with composite service

	public PersonIdentityTraits searchWithCompositeCall(FullyQualifiedIdentity id, boolean isSearchByDfn, IdMSearchInfo searchInfo) throws ServiceException
	{
		Set<PersonIdentityTraits> resultTraits = search(id, isSearchByDfn, searchInfo, null, null, COMPOSITE_NO_HISTORY, false);

		if (resultTraits == null || resultTraits.size() == 0)
			return null;

		//should just return one
		//CCR 11899 -add overlaySSNVerificationStatus
		return overlaySSNVerificationStatus((PersonIdentityTraits) resultTraits.toArray()[0]);

	}



	public PersonIdentityTraits search(FullyQualifiedIdentity id, boolean attendedSearch) throws ServiceException
	{
		Set<PersonIdentityTraits> resultTraits = search(id, false, null, null, null, null, attendedSearch);
		if (resultTraits == null || resultTraits.size() == 0)
			return null;

		//should just return one
		//CCR11770 - If the primary view search does not return the SSN verification status code,
		//ESR will need to query for 200ESR correlation traits and retrieve the status.
		return overlaySSNVerificationStatus((PersonIdentityTraits) resultTraits.toArray()[0]);

	}



	//CCR11770 - If the primary view search does not return the SSN verification status code,
	//ESR will need to query for 200ESR correlation traits and retrieve the status.
	private PersonIdentityTraits overlaySSNVerificationStatus(PersonIdentityTraits pvTraits) throws ServiceException
	{

		if (pvTraits != null && pvTraits.getSsn() != null && pvTraits.getSsn().getSsaVerificationStatus()== null )
		{
			//CCR13545
			//only need to attempt an overlay if we already have ssn verification detail record
			if (hasCompletedSsaVerification(pvTraits.getVpid())) {
				PersonIdentityTraits esrTraits = null;
				try {
					esrTraits = this.getESRCorrelation(pvTraits.getVpid());
				} catch (ServiceException se)
				{
					//CCR11847: for new person, 200ESR Correlation does not exist is normal, don't throw exception
					//	which prevents ESR to add 200ESR Correlation later. So just returns PV
					//	otherwise, jsut throw the exception
					if (se.getMessage().indexOf(IDM_DOES_NOT_EXIST) < 0 && se.getMessage().indexOf(IDM_200ESR_NOT_FOUND) < 0)
						throw se;
				}

				if (esrTraits != null && esrTraits.getSsn() != null && esrTraits.getSsn().getSsaVerificationStatus()!= null)
					pvTraits.getSsn().setSsaVerificationStatus(esrTraits.getSsn().getSsaVerificationStatus());
			}
		}
		return pvTraits;
	}

	//CCR13545
	private boolean hasCompletedSsaVerification(VPIDEntityKey vpid) throws ServiceException {

		if (vpid == null) return false;

		try {
			List list = this.getGenericDAO().findByNamedQueryAndNamedParam(
					QUERY_GET_SSA_VER_DETAIL, QRY_SSA_PARAM_NAME, vpid.getKeyValueAsString());

			if (list == null || list.isEmpty() || list.get(0) == null) {
				return false;
			}

		} catch (DAOException e) {
			throw new ServiceException(
					"DAO query failed to find ssa verification status by vpid", e);
		} catch (Exception ex) {
			throw new ServiceException(
					"Unknown IdmWsDelegate error in find ssa verification status by vpid", ex);
		}

		return true;
	}

	//CCR 11776 - overlaySSNVerificationStatus for a list of traits
	// If the primary view search does not return the SSN verification status code,
	//ESR will need to query for 200ESR correlation traits and retrieve the status.
	private Set<PersonIdentityTraits> overlaySSNVerificationStatus(Set<PersonIdentityTraits> mviTraitsSet) throws ServiceException {
		Set<PersonIdentityTraits> overlayedTraitsList = new HashSet<PersonIdentityTraits>();
		Iterator<PersonIdentityTraits> traitsIt = mviTraitsSet.iterator();
		while (traitsIt.hasNext()){
			PersonIdentityTraits traits = (PersonIdentityTraits)traitsIt.next();
			overlayedTraitsList.add(overlaySSNVerificationStatus(traits));
		}

		return overlayedTraitsList;
	}


	/*
	 public VPIDEntityKey addPerson(PersonIdentityTraits traits ,
	 boolean searchByPass) throws ServiceException{
	 return execute1310(traits, searchByPass, false, false, true);
	 }*/

	public VPIDEntityKey addPerson(IdmServiceVO idmServiceVO, IdMSearchInfo searchInfo)
			throws ServiceException {
		CE ce = execute1301(idmServiceVO, true, false, false, true, searchInfo);
		if (ce !=null && "ICN".equals(ce.getDisplayName())) {
			String longVPID = VPIDEntityKeyImpl.getLongVPID(ce.getCode());
			VPIDEntityKey key = CommonEntityKeyFactory
					.createVPIDEntityKey(longVPID);
			return key;
		}
		return null;
	}

	public VPIDEntityKey addCorrelation(IdmServiceVO idmServiceVO)
			throws ServiceException {
		CE ce = execute1301(idmServiceVO, true, true, false, false, null);

		if (ce !=null && ce.getCode() != null && ce.getCode().indexOf("200ESR") > 0) {
			String longVPID = VPIDEntityKeyImpl.getLongVPID(ce.getCode().split("\\^")[0]);
			VPIDEntityKey key = CommonEntityKeyFactory
					.createVPIDEntityKey(longVPID);
			return key;
		}

		return null;
	}

	public FullyQualifiedIdentity addPreferredFacility(IdmServiceVO idmServiceVO)
			throws ServiceException {

		CE ce = execute1301(idmServiceVO, true, false, true, false, null);

		if (ce !=null && "IEN".equals(ce.getDisplayName())) {
			//<code codeSystemName="MVI" code="100001994^PI^553^" displayName="IEN"/>
			String [] codes = ce.getCode().split("\\^");
			return new FullyQualifiedIdentity(codes[0], codes[2]); //(dfn, station number)
		}

		return null;
	}

	public void updatePerson(IdmServiceVO idmServiceVO) throws ServiceException {
		execute1302(idmServiceVO);
	}

	/**
	 * 1. searchByPass = true
	 * 		In this scenario, the sender is requesting that the Patient
	 * 		Identifier profile supplied be associated under a new ICN that
	 * 		is generated by the IDM service and indicates that the MVI
	 * 		system is responsible for determining whether a new ICN be
	 * 		created or the sent in Patient Identifier should be Linked
	 * 		to an existing ICN, based on an enterprise search and MVI
	 * 		business rules.
	 * 		in this case we will not return VPID
	 * 2.  searchByPass = false
	 * 		In this scenario, the sender is requesting that the Patient Identifier profile supplied
	 * 		be associated under a new ICN that is 	generated by the IDM service and indicates
	 * 		a sender already performed an enterprise search, but did not find the desired person.
	 * @param traits
	 * @param newICN
	 * @return
	 */
	private CE execute1301(IdmServiceVO idmServiceVO,
			boolean searchByPass, boolean isAddCorrelation,
			boolean isAddPreferredFacility, boolean isAddPerson, IdMSearchInfo searchInfo)
			throws ServiceException {
		//1. Create 1301 request.
		PRPAIN201301UV02Document pPRPAIN201301UV02Document = create1301Request();
		//2. Create 1301 headers.
		PRPAIN201301UV02 pPRPAIN201301UV02 = populate1301RequestHeaders(pPRPAIN201301UV02Document, searchInfo);
		PRPAIN201301UV02MFMIMT700701UV01ControlActProcess controlActProcess = add1301ControlActprocess(pPRPAIN201301UV02);

		PRPAIN201301UV02MFMIMT700701UV01RegistrationEvent regEvent = controlActProcess
				.getSubjectArray()[0].getRegistrationEvent();

		PRPAIN201301UV02MFMIMT700701UV01Subject2 sub1 = regEvent
				.addNewSubject1();
		sub1.setTypeCode(ParticipationTargetSubject.Enum.forString("SBJ"));
		PRPAMT201301UV02Patient patient = sub1.addNewPatient();
		patient.setClassCode("PAT");
		II patientId = patient.addNewId();
		patientId.setRoot(ROOT_VA_OID);

		/**
		 * For Add Correlation and Add Preferred Facility we need to send in ICN
		 */
		if (isAddCorrelation || isAddPreferredFacility) {
			if (idmServiceVO.getVpid() != null) {
				patientId.setExtension(idmServiceVO.getVpid().getVPID());
			}
		}

		if (isAddCorrelation || isAddPreferredFacility) {
			patientId.setNullFlavor("NA");
		} else {
			//must be add person.
			if (searchByPass) {
				patientId.setNullFlavor("ASKU");
			} else {
				patientId.setNullFlavor("UNK");
			}
		}

		patient.addNewStatusCode().setCode("active");

		COCTMT150003UV03Organization providerOrg = patient
				.addNewProviderOrganization();
		providerOrg.setClassCode("ORG");
		providerOrg.setDeterminerCode("INSTANCE");
		providerOrg.addNewId();
		providerOrg.addNewContactParty().setClassCode(
				v3.hl7_org.RoleClassContact.Enum.forString("CON"));

		PRPAMT201301UV02Person person = patient.addNewPatientPerson();
		populate1301Person(person, idmServiceVO, isAddCorrelation,
				isAddPreferredFacility);

		MFMIMT700701UV01Custodian custodian = regEvent.addNewCustodian();

		custodian.setTypeCode("CST");
		COCTMT090003UV01AssignedEntity assignedEntity = custodian
				.addNewAssignedEntity();
		assignedEntity.setClassCode("ASSIGNED");
		assignedEntity.addNewId().setRoot(ROOT_VA_OID);
		COCTMT090003UV01Organization assignedOrg = assignedEntity
				.addNewAssignedOrganization();
		assignedOrg.setClassCode("ORG");
		assignedOrg.setDeterminerCode("INSTANCE");
		EN name = assignedOrg.addNewName();
		name.newCursor().setTextValue(getEsrStationNumber());

		MCCIIN000002UV01Document responseDocument = null;

        if (logger.isDebugEnabled())
            logger.debug("IdmServiceRequest: " + pPRPAIN201301UV02Document);
		try {
			VAIdMStub stub = new VAIdMStub(getIdmServiceEndPoint());
			stub._getServiceClient().getOptions().setTimeOutInMilliSeconds(webServiceTimeout);
			responseDocument = stub
					.PRPA_IN201301UV02(pPRPAIN201301UV02Document);

	        if (logger.isDebugEnabled())
	            logger.debug("IdmServiceResponse: " + responseDocument);

		} catch (Exception e) {
			throw new ServiceException(e);
		}

		if (responseDocument != null) {
			//get the return code from the server. If it is not AA then there is a problem.
			MCCIMT000200UV01Acknowledgement ack = responseDocument
					.getMCCIIN000002UV01().getAcknowledgementArray()[0];

			String responseCode = ack.getTypeCode().getCode();
			//String responseCode = "AA";

			if (!APPLICATION_ACCEPT.equals(responseCode)) {
				String errorMessage = ack.getAcknowledgementDetailArray()[0]
						.getText().newCursor().getTextValue();
			/* CR13612 – complete registration - the transition on the ICN from temporary to permanent state-begin */
			/* Local Dev  ERROR Add Preferred Treating Facility MPI response=-1^COMMUNICATION Failure.   No handle after sending RPC to VistA */
			/* production ERROR Add Preferred Treating Facility MPI response=-1^The supplied ICN, 1024352589V489592, does NOT have an ID STATE of &apos;PERMANENT&apos;. */
				if ( errorMessage != null && errorMessage.indexOf("ERROR Add Preferred Treating Facility MPI response") != -1 )
				 {
				   if (logger.isErrorEnabled()) {
					   logger.error("Error communicating to VistA. Please re-submit the Complete Registration action; get the return code from the server:"+ errorMessage);
				   }
				   return null;
				}
			/*CR13612fix-end*/
				if (logger.isErrorEnabled()) {
					logger.error("parseSearchResponse return error :"
							+ errorMessage);
				}
				throw new ServiceException(errorMessage);
			} else {
				return ack.getAcknowledgementDetailArray()[0].getCode();


				//return ack.getAcknowledgementDetailArray()[0].getCode();
//				String icn = ack.getAcknowledgementDetailArray()[0].getCode()
//						.getCode();
//				String displayName = ack.getAcknowledgementDetailArray()[0]
//						.getCode().getDisplayName();
//				String codeSystemName = ack.getAcknowledgementDetailArray()[0]
//						.getCode().getCodeSystem();
//
//				if ("ICN".equals(displayName)) {
//					String longVPID = VPIDEntityKeyImpl.getLongVPID(icn);
//					VPIDEntityKey key = CommonEntityKeyFactory
//							.createVPIDEntityKey(longVPID);
//					return key;
//				}
			}
		}
		return null;
	}

	private PRPAIN201301UV02Document create1301Request()
			throws ServiceException {
		PRPAIN201301UV02Document pPRPAIN201301UV02Document = (PRPAIN201301UV02Document) getXmlObject(PRPAIN201301UV02Document.class);

		return pPRPAIN201301UV02Document;
	}

	private PRPAIN201302UV02Document create1302Request()
			throws ServiceException {
		PRPAIN201302UV02Document pPRPAIN201302UV02Document = (PRPAIN201302UV02Document) getXmlObject(PRPAIN201302UV02Document.class);

		return pPRPAIN201302UV02Document;
	}

	private PRPAIN201301UV02 populate1301RequestHeaders(
			PRPAIN201301UV02Document pPRPAIN201301UV02Document, IdMSearchInfo searchInfo) {

		PRPAIN201301UV02 pPRPAIN201301UV02 = pPRPAIN201301UV02Document
				.addNewPRPAIN201301UV02();
		// <id root="2.16.840.1.113883.4.349" extension="MCID-12345"/>
		Date currentDate = Calendar.getInstance().getTime();
		String uniqueId = generateUniqueID(currentDate);
		populateHeaderRoot(pPRPAIN201301UV02.addNewId(), uniqueId);
		populateCreationTime(pPRPAIN201301UV02.addNewCreationTime(),
				currentDate);


		if (searchInfo != null && searchInfo.isAddPersonExplicit()) {
			//CCR12852 - for extended search, add version 3.0
			pPRPAIN201301UV02.addNewVersionCode().setCode("3.0");

			//CCR 12852: add the attention line for Explicit Add
			MCCIMT000100UV01AttentionLine atten = pPRPAIN201301UV02.addNewAttentionLine();
			atten.addNewKeyWordText().newCursor().setTextValue("Search.Token");
			atten.addNewValue().newCursor().setTextValue(searchInfo.getSearchToken());
		} else {
			pPRPAIN201301UV02.addNewVersionCode().setCode(VERSION_CODE);
		}

		// Interaction id.
		// <interactionId root="2.16.840.1.113883.1.6"
		// extension="PRPA_IN201301UV02" />
		populateInteractionId(pPRPAIN201301UV02.addNewInteractionId(),
				"PRPA_IN201301UV02");
		populateProcessingCode(pPRPAIN201301UV02.addNewProcessingCode());
		populateProcessingModeCode(pPRPAIN201301UV02.addNewProcessingModeCode());
		populateAcceptAckCode(pPRPAIN201301UV02.addNewAcceptAckCode());
		populateReceiver(pPRPAIN201301UV02.addNewReceiver());
		populateSender(pPRPAIN201301UV02.addNewSender());
		return pPRPAIN201301UV02;
	}

	private PRPAIN201302UV02 populate1302RequestHeaders(
			PRPAIN201302UV02Document pPRPAIN201302UV02Document) {

		PRPAIN201302UV02 pPRPAIN201302UV02 = pPRPAIN201302UV02Document
				.addNewPRPAIN201302UV02();
		// <id root="2.16.840.1.113883.4.349" extension="MCID-12345"/>
		Date currentDate = Calendar.getInstance().getTime();
		String uniqueId = generateUniqueID(currentDate);
		populateHeaderRoot(pPRPAIN201302UV02.addNewId(), uniqueId);
		populateCreationTime(pPRPAIN201302UV02.addNewCreationTime(),
				currentDate);

		pPRPAIN201302UV02.addNewVersionCode().setCode(VERSION_CODE);

		// Interaction id.
		// <interactionId root="2.16.840.1.113883.1.6"
		// extension="PRPA_IN201301UV02" />
		populateInteractionId(pPRPAIN201302UV02.addNewInteractionId(),
				"PRPA_IN201302UV02");
		populateProcessingCode(pPRPAIN201302UV02.addNewProcessingCode());
		populateProcessingModeCode(pPRPAIN201302UV02.addNewProcessingModeCode());
		populateAcceptAckCode(pPRPAIN201302UV02.addNewAcceptAckCode());
		populateReceiver(pPRPAIN201302UV02.addNewReceiver());
		populateSender(pPRPAIN201302UV02.addNewSender());
		return pPRPAIN201302UV02;
	}

	/**
	 * Will add the basic stuff into the control act process.
	 * @param pPRPAIN201301UV02
	 */
	private PRPAIN201301UV02MFMIMT700701UV01ControlActProcess add1301ControlActprocess(
			PRPAIN201301UV02 pPRPAIN201301UV02) {
		PRPAIN201301UV02MFMIMT700701UV01ControlActProcess controlActProcess = pPRPAIN201301UV02
				.addNewControlActProcess();
		controlActProcess.setClassCode(v3.hl7_org.ActClassControlAct.Enum
				.forString(CACT_CLASS_CODE));
		controlActProcess.setMoodCode(v3.hl7_org.XActMoodIntentEvent.Enum
				.forString(CACT_MOOD_CODE));
		CD code = controlActProcess.addNewCode();
		code.setCode("PRPA_TE201301UV02");
		code.setCodeSystem(CACT_CODE_SYSTEM);
		PRPAIN201301UV02MFMIMT700701UV01Subject1 subject = controlActProcess
				.addNewSubject();

		MFMIMT700701UV01DataEnterer dataEnterer = controlActProcess
				.addNewDataEnterer();
		dataEnterer.setTypeCode("ENT");
		dataEnterer.setContextControlCode("AP");
		COCTMT090100UV01AssignedPerson assignedPerson = dataEnterer
				.addNewAssignedPerson();
		assignedPerson.setClassCode("ASSIGNED");
		COCTMT090100UV01Person assignedPerson1 = assignedPerson
				.addNewAssignedPerson();
		assignedPerson1.setClassCode("PSN");
		assignedPerson1.setDeterminerCode("INSTANCE");
		EN assignedPersonName = assignedPerson1.addNewName();

		/**
		 org.acegisecurity.context.SecurityContext sc = (org.acegisecurity.context.SecurityContext) SecurityContextHolder.getContext();
		 UserPrincipalImpl userPrincipalImpl = (UserPrincipalImpl)sc.getAuthentication().getPrincipal();
		 String userName = userPrincipalImpl.getName();
		 **/
		String auditUserId = SecurityContextHelper.getSecurityContext()
				.getUserName(true);

		//((UserPrincipalImpl)sc.getAuthentication().getPrincipal()).getName();

		assignedPersonName.newCursor().setTextValue(auditUserId);

		subject.setTypeCode("SUBJ");
		PRPAIN201301UV02MFMIMT700701UV01RegistrationEvent regEvent = subject
				.addNewRegistrationEvent();

		regEvent.setClassCode(REG_CLASS_CODE);
		regEvent.setMoodCode(CACT_MOOD_CODE);
		II regId = regEvent.addNewId();
		regId.setNullFlavor("NA");
		CS statusCode = regEvent.addNewStatusCode();
		statusCode.setCode("active");
		return controlActProcess;
	}

	/**
	 * Will add the basic stuff into the control act process.
	 * @param pPRPAIN201301UV02
	 */
	private PRPAIN201302UV02MFMIMT700701UV01ControlActProcess add1302ControlActprocess(
			PRPAIN201302UV02 pPRPAIN201302UV02) {
		PRPAIN201302UV02MFMIMT700701UV01ControlActProcess controlActProcess = pPRPAIN201302UV02
				.addNewControlActProcess();
		controlActProcess.setClassCode(v3.hl7_org.ActClassControlAct.Enum
				.forString(CACT_CLASS_CODE));
		controlActProcess.setMoodCode(v3.hl7_org.XActMoodIntentEvent.Enum
				.forString(CACT_MOOD_CODE));
		CD code = controlActProcess.addNewCode();
		code.setCode("PRPA_TE201302UV02");
		code.setCodeSystem(CACT_CODE_SYSTEM);
		PRPAIN201302UV02MFMIMT700701UV01Subject1 subject = controlActProcess
				.addNewSubject();

		MFMIMT700701UV01DataEnterer dataEnterer = controlActProcess
				.addNewDataEnterer();
		dataEnterer.setTypeCode("ENT");
		dataEnterer.setContextControlCode("AP");
		COCTMT090100UV01AssignedPerson assignedPerson = dataEnterer
				.addNewAssignedPerson();
		assignedPerson.setClassCode("ASSIGNED");
		COCTMT090100UV01Person assignedPerson1 = assignedPerson
				.addNewAssignedPerson();
		assignedPerson1.setClassCode("PSN");
		assignedPerson1.setDeterminerCode("INSTANCE");
		EN assignedPersonName = assignedPerson1.addNewName();

		String auditUserId = SecurityContextHelper.getSecurityContext()
				.getUserName(true);
		assignedPersonName.newCursor().setTextValue(auditUserId);

		subject.setTypeCode("SUBJ");
		PRPAIN201302UV02MFMIMT700701UV01RegistrationEvent regEvent = subject
				.addNewRegistrationEvent();

		regEvent.setClassCode(REG_CLASS_CODE);
		regEvent.setMoodCode(CACT_MOOD_CODE);
		II regId = regEvent.addNewId();
		regId.setNullFlavor("NA");
		CS statusCode = regEvent.addNewStatusCode();
		statusCode.setCode("active");
		return controlActProcess;
	}

	private void populate1301Person(PRPAMT201301UV02Person person,
			IdmServiceVO idmServiceVO, boolean isAddCorrelation,
			boolean isAddPreferredFacility) throws ServiceException {

		//1. populate Name.
		Set names = idmServiceVO.getNames();
		Name legalName = null;
		Name aliasName = null;

		/**
		 * There must be only one name here.
		 */
		if (names != null && names.size() > 0) {
			/*
			Iterator nameItr = names.iterator();
			while (nameItr.hasNext()) {
				Name currName = (Name) nameItr.next();
				//if ( NameType.LEGAL_NAME.equals(currName.getType())){
				//legalName = (Name)nameItr.next();
				legalName = currName;
				//}
			}
			*/
			// CCR 11486 - intermittantly using mother's maiden name
			// so be specific about type
			legalName = this.getLegalNameFromSet(names);
			aliasName = this.getAliasNameFromSet(names);
		}
		if (legalName != null) {
			String givenName = legalName.getGivenName();
			String lastName = legalName.getFamilyName();
			String middleName = legalName.getMiddleName();
			String prefix = legalName.getPrefix();
			String suffix = legalName.getSuffix();

			PN personName = person.addNewName();
			ArrayList useList = new ArrayList();
			useList.add(v3.hl7_org.PersonNameUseLegalByBOT.Enum.forString("L"));
			personName.setUse(useList);

			if (givenName != null) {
				EnGiven given = personName.addNewGiven();
				given.newCursor().setTextValue(givenName);
				//given.set
			}
			if (middleName != null) {
				EnGiven given = personName.addNewGiven();
				given.newCursor().setTextValue(middleName);
			}

			if (lastName != null) {
				EnFamily family = personName.addNewFamily();
				family.newCursor().setTextValue(lastName);
			}

			if ( prefix != null && prefix.length() > 0 ){
				EnPrefix enPrefix = personName.addNewPrefix();
				enPrefix.newCursor().setTextValue(prefix);
			}

			if (suffix != null && suffix.length() > 0 ) {
				EnSuffix enSuffix = personName.addNewSuffix();
				enSuffix.newCursor().setTextValue(suffix);
			}

		}

          // Alias name CCR 11648

		if (aliasName != null) {
			String givenName = aliasName.getGivenName();
			String lastName = aliasName.getFamilyName();
			String middleName = aliasName.getMiddleName();
			String prefix = aliasName.getPrefix();
			String suffix = aliasName.getSuffix();

			PN personName = person.addNewName();
			ArrayList useList = new ArrayList();
			useList.add(ALIAS_NAME_TYPE);
			personName.setUse(useList);

			if (givenName != null) {
				EnGiven given = personName.addNewGiven();
				given.newCursor().setTextValue(givenName);
				// given.set
			}
			if (middleName != null) {
				EnGiven given = personName.addNewGiven();
				given.newCursor().setTextValue(middleName);
			}

			if (lastName != null) {
				EnFamily family = personName.addNewFamily();
				family.newCursor().setTextValue(lastName);
			}
		}

		String mothersMaidenName = idmServiceVO.getMothersMaidenName();
		if (mothersMaidenName != null) {
			PN personName = person.addNewName();
			ArrayList useList = new ArrayList();
			useList.add(MOTHERS_MAIDEN_NAME_TYPE);
			personName.setUse(useList);

			EnFamily family = personName.addNewFamily();
			family.newCursor().setTextValue(mothersMaidenName);
		}

		if (idmServiceVO.getGender() != null)
			person.addNewAdministrativeGenderCode().setCode(
					idmServiceVO.getGender().getCode());

		//WI 305033 Backout - sigi not ready yet on MVI side
		/*
		if (idmServiceVO.getSigi() != null && idmServiceVO.getSigi().getCode() != null) {
			if (person.getAdministrativeGenderCode() != null) {
				person.getAdministrativeGenderCode().addNewTranslation().setCode(idmServiceVO.getSigi().getCode());
			}
		}*/


		if (idmServiceVO.getBirthRecord() != null) {
			if (idmServiceVO.getBirthRecord().getMultipleBirth() != null) {
				BL bl = person.addNewMultipleBirthInd();
				bl.setValue(idmServiceVO.getBirthRecord().getMultipleBirth()
						.booleanValue());
			}

			if (idmServiceVO.getBirthRecord().getBirthDate() != null) {
				person.addNewBirthTime().setValue(
						idmServiceVO.getBirthRecord().getBirthDate()
								.getyyyyMMddFormat()); //CCR 12101

			}

			PRPAMT201301UV02BirthPlace birthPlace = person.addNewBirthPlace();

			if (birthPlace != null) {
				AD birthAdd = birthPlace.addNewAddr();
				BirthRecord bRecord = idmServiceVO.getBirthRecord();

				String city = bRecord.getCity();
				if (city != null) {
					AdxpCity pcity = birthAdd.addNewCity();
					pcity.newCursor().setTextValue(city);
				}

				String stateCode = bRecord.getState();
				if (stateCode != null) {
					AdxpState pstate = birthAdd.addNewState();
					pstate.newCursor().setTextValue(stateCode);
				}

				String countryCode = bRecord.getCountry();
				if (countryCode != null) {
					AdxpCountry pcountry = birthAdd.addNewCountry();
					pcountry.newCursor().setTextValue(countryCode);
				}
			}
		}

		if (idmServiceVO.getAddressByType(AddressType.CODE_PERMANENT_ADDRESS
				.getCode()) != null) {
			Address address = idmServiceVO
					.getAddressByType(AddressType.CODE_PERMANENT_ADDRESS
							.getName());
			populateAddress(person.addNewAddr(), address);
		}

		if (idmServiceVO.getHomePhone() != null) {
			populatePhone(idmServiceVO.getHomePhone(), person.addNewTelecom());
		}

		if (idmServiceVO.getSsn() != null) {
			PRPAMT201301UV02OtherIDs otherIds = person.addNewAsOtherIDs();
			otherIds.setClassCode("SSN");
			II ssnId = otherIds.addNewId();
			//CCR 12147 - if ssn verification status not null, set it so that it will not null out the existing value
			if (idmServiceVO.getSsn().getSsaVerificationStatus() != null) {
				String ssaVerificationStatusCode = idmServiceVO.getSsn().getSsaVerificationStatus().getCode();
				otherIds.addNewStatusCode().setCode(ssaVerificationStatusCode);
			}

			ssnId.setExtension(idmServiceVO.getSsn().getFormattedSsnText());
			ssnId.setRoot(SSN_OID);
			populateScopingOrganization(otherIds.addNewScopingOrganization(),
					SSN_OID);
		}

		// This must be a addCorrelation and add preffered facility.
		PRPAMT201301UV02OtherIDs icnOtherIds = person.addNewAsOtherIDs();
		icnOtherIds.setClassCode("PAT");
		II icnid = icnOtherIds.addNewId();
		icnid.setRoot(ROOT_VA_OID);
		populateScopingOrganization(icnOtherIds.addNewScopingOrganization(),
				ROOT_VA_OID);
		if (isAddCorrelation) {
			if (idmServiceVO.getVpid() != null) {
				icnid.setExtension(idmServiceVO.getVpid().getVPID() + "^PI^"
						+ getEsrStationNumber() + "^USVHA");
			}
		} else if (isAddPreferredFacility) {
			icnid.setExtension(idmServiceVO.getVpid().getVPID() + "^PI^"
					+ getEsrStationNumber() + "^USVHA");
			String prefFacilityStNum = null;
			gov.va.med.esr.common.model.lookup.VAFacility prefFacility = idmServiceVO
					.getPreferredFacilty();
			if (prefFacility != null) {
				prefFacilityStNum = prefFacility.getStationNumber();
			}

			icnid
					.setExtension("PROXY_VISTA^PI^" + prefFacilityStNum
							+ "^USVHA");

		} else { //add Person.
			icnid.setExtension("USE_ICN^PI^200ESR^USVHA");
		}

		if (isAddPreferredFacility) {

			PRPAMT201301UV02OtherIDs patientType = person.addNewAsOtherIDs();
			patientType.setClassCode("patientServiceConnected");
			II serviceConnectcedId = patientType.addNewId();
			serviceConnectcedId.setExtension(getYesNoValue(idmServiceVO
					.isServiceConnected()));
			serviceConnectcedId.setRoot(ROOT_VA_OID);
			//Add scopingOrganization
			populateScopingOrganization(
					patientType.addNewScopingOrganization(), ROOT_VA_OID);
			PRPAMT201301UV02OtherIDs veteran = person.addNewAsOtherIDs();
			veteran.setClassCode("patientType");
			II veteranId = veteran.addNewId();

			veteranId.setExtension(idmServiceVO.getPatientType());
			veteranId.setRoot(ROOT_VA_OID);
			populateScopingOrganization(veteran.addNewScopingOrganization(),
					ROOT_VA_OID);

		}

		PRPAMT201301UV02OtherIDs veteran = person.addNewAsOtherIDs();
		veteran.setClassCode("patientVeteran");
		II veteranId = veteran.addNewId();
		veteranId.setExtension(getYesNoValue(idmServiceVO.isVeteran()));
		veteranId.setRoot(ROOT_VA_OID);
		populateScopingOrganization(veteran.addNewScopingOrganization(),
				ROOT_VA_OID);
	}

	private Name getLegalNameFromSet(Set names){
		Iterator nameItr = names.iterator();
		while (nameItr.hasNext()) {
			Name currName = (Name) nameItr.next();
			// CCR 11486 enforce the legal name restriction
			if ( currName.getType() != null &&
					NameType.LEGAL_NAME.getCode().equals(currName.getType().getCode())){
				return currName;
			}
		}
		return null;
	}

	private Name getAliasNameFromSet(Set names){
		Iterator nameItr = names.iterator();
		while (nameItr.hasNext()) {
			Name currName = (Name) nameItr.next();
			//
			if ( currName.getType() != null &&
					NameType.ALIAS_NAME.getCode().equals(currName.getType().getCode())){
				return currName;
			}
		}
		return null;
	}

	private void populate1302Person(PRPAMT201302UV02Person person,
			IdmServiceVO idmServiceVO) throws ServiceException {

		// 1. populate Name.
		Set names = idmServiceVO.getNames();
		Name legalName = null;
		Name aliasName = null;

		/**
		 * There must be only one name here.
		 */
		if (names != null && names.size() > 0) {
			/*
			Iterator nameItr = names.iterator();
			while (nameItr.hasNext()) {
				Name currName = (Name) nameItr.next();
				// CCR 11486 enforce the legal name restriction
				if ( currName.getType() != null &&
						NameType.LEGAL_NAME.getCode().equals(currName.getType().getCode())){
					legalName = currName;
					break;
				}
			}
			*/
			// CCR 11486 - intermittantly using mother's maiden name
			// so be specific about type. Switched to common method.
			legalName = this.getLegalNameFromSet(names);
			aliasName = this.getAliasNameFromSet(names);
		}
		if (legalName != null) {
			String givenName = legalName.getGivenName();
			String lastName = legalName.getFamilyName();
			String middleName = legalName.getMiddleName();
			String prefix = legalName.getPrefix();
			String suffix = legalName.getSuffix();

			PN personName = person.addNewName();
			ArrayList useList = new ArrayList();
			useList.add(v3.hl7_org.PersonNameUseLegalByBOT.Enum.forString("L"));
			personName.setUse(useList);

			if (givenName != null) {
				EnGiven given = personName.addNewGiven();
				given.newCursor().setTextValue(givenName);
				// given.set
			}
			if (middleName != null) {
				EnGiven given = personName.addNewGiven();
				given.newCursor().setTextValue(middleName);
			}

			if (lastName != null) {
				EnFamily family = personName.addNewFamily();
				family.newCursor().setTextValue(lastName);
			}


			if ( prefix != null && prefix.length() > 0 ){
				EnPrefix enPrefix = personName.addNewPrefix();
				enPrefix.newCursor().setTextValue(prefix);
			}

			if (suffix != null && suffix.length() > 0 ) {
				EnSuffix enSuffix = personName.addNewSuffix();
				enSuffix.newCursor().setTextValue(suffix);
			}
		}
		// Alias name CCR 11648

		if (aliasName != null) {
			String givenName = aliasName.getGivenName();
			String lastName = aliasName.getFamilyName();
			String middleName = aliasName.getMiddleName();
			String prefix = aliasName.getPrefix();
			String suffix = aliasName.getSuffix();

			PN personName = person.addNewName();
			ArrayList useList = new ArrayList();
			useList.add(ALIAS_NAME_TYPE);
			personName.setUse(useList);

			if (givenName != null) {
				EnGiven given = personName.addNewGiven();
				given.newCursor().setTextValue(givenName);
				// given.set
			}
			if (middleName != null) {
				EnGiven given = personName.addNewGiven();
				given.newCursor().setTextValue(middleName);
			}

			if (lastName != null) {
				EnFamily family = personName.addNewFamily();
				family.newCursor().setTextValue(lastName);
			}

		}


		String mothersMaidenName = idmServiceVO.getMothersMaidenName();
		if (mothersMaidenName != null) {
			PN personName = person.addNewName();
			ArrayList useList = new ArrayList();
			useList.add("C");
			personName.setUse(useList);
			if (mothersMaidenName != null) {
				EnFamily family = personName.addNewFamily();
				family.newCursor().setTextValue(mothersMaidenName);
			}
		}

		if (idmServiceVO.getGender() != null)
			person.addNewAdministrativeGenderCode().setCode(
					idmServiceVO.getGender().getCode());

		//WI 305033 backout
		/*if (idmServiceVO.getSigi() != null && idmServiceVO.getSigi().getCode() != null) {
			if (person.getAdministrativeGenderCode() != null) {
				person.getAdministrativeGenderCode().addNewTranslation().setCode(idmServiceVO.getSigi().getCode());
			}
		}*/

		if (idmServiceVO.getBirthRecord() != null) {
			if (idmServiceVO.getBirthRecord().getMultipleBirth() != null) {
				BL bl = person.addNewMultipleBirthInd();
				bl.setValue(idmServiceVO.getBirthRecord().getMultipleBirth()
						.booleanValue());
			}

			if (idmServiceVO.getBirthRecord().getBirthDate() != null) {
				person.addNewBirthTime().setValue(
						idmServiceVO.getBirthRecord().getBirthDate()
								.getyyyyMMddFormat()); //CCR 12101
			}

			PRPAMT201302UV02BirthPlace birthPlace = person.addNewBirthPlace();

			if (birthPlace != null) {
				AD birthAdd = birthPlace.addNewAddr();
				BirthRecord bRecord = idmServiceVO.getBirthRecord();

				String city = bRecord.getCity();
				if (city != null) {
					AdxpCity pcity = birthAdd.addNewCity();
					pcity.newCursor().setTextValue(city);
				}

				String stateCode = bRecord.getState();
				if (stateCode != null) {
					AdxpState pstate = birthAdd.addNewState();
					pstate.newCursor().setTextValue(stateCode);
				}

				String countryCode = bRecord.getCountry();
				if (countryCode != null) {
					AdxpCountry pcountry = birthAdd.addNewCountry();
					pcountry.newCursor().setTextValue(countryCode);
				}

			}

		}

		if (idmServiceVO.getAddressByType(AddressType.CODE_PERMANENT_ADDRESS
				.getCode()) != null) {
			Address address = idmServiceVO
					.getAddressByType(AddressType.CODE_PERMANENT_ADDRESS
							.getName());
			populateAddress(person.addNewAddr(), address);
		}

		if (idmServiceVO.getHomePhone() != null) {
			populatePhone(idmServiceVO.getHomePhone(), person.addNewTelecom());
		}
/*
    <!-- SSN -->
    <asOtherIDs classCode="SSN">
        <id extension="111223333" root="2.16.840.1.113883.4.1" />
		<!-- SSN Verification Status -->
  		<statusCode code="0" />
      	<!-- SSA Verification Date [DRAFT]-->
  		<effectiveTime value="20111012" />
        <scopingOrganization classCode="ORG" determinerCode="INSTANCE">
        	<id root="2.16.840.1.113883.4.1" />
  			<!-- Values from STD_SSNChangeSource can be used. [DRAFT] -->
      		<code code="02" displayName="SSN Source of Change">
				<!-- Optional SSA Display Text  [DRAFT] -->
	      		<originalText>SSA display message</originalText>
			</code>
        </scopingOrganization>
     </asOtherIDs>

     <!-- PseudoSSN -->
     	<asOtherIDs classCode="SSN" nullFlavor="OTH">
	       <id root="2.16.840.1.113883.4.349" extension="111223333"/>
	      <statusCode code="R"/>
	      <scopingOrganization classCode="ORG" determinerCode="INSTANCE">
	      <id root="1.2.840.114350.1.13.99997.2.3412"/>
	   </scopingOrganization>
	</asOtherIDs>

*/
		if (idmServiceVO.getSsn() != null) {
			SSN ssn = idmServiceVO.getSsn();
			PRPAMT201302UV02OtherIDs otherIds = person.addNewAsOtherIDs();
			otherIds.setClassCode("SSN");
			II ssnId = otherIds.addNewId();
			ssnId.setExtension(ssn.getFormattedSsnText());
			ssnId.setRoot(SSN_OID);

			//CCR11403: added for SSN attributes (from UI)
			if (ssn.getSsaVerificationStatus() != null) {
				String ssaVerificationStatusCode = ssn.getSsaVerificationStatus().getCode();
				otherIds.addNewStatusCode().setCode(ssaVerificationStatusCode); //<- UI

				/* CCR11777 add SSA Verification Date
				 *  if the SSN verification status is 1 (in_process), send SSA_SENT_DATE;
				 *  if the SSN verification status is 2 (invalid pers SSA), send SSA_RECEIVED_DATE
				 *  if the SSN verification status is 4 (verified), send SSA_VERIFIED_DATE
				 *  If ESR data doesn’t exist, then do not send effectiveTime element
				 */
				if (SSAVerificationStatus.IN_PROCESS.getCode().equals(ssaVerificationStatusCode) && ssn.getSsaSentDate() != null) {
					otherIds.addNewEffectiveTime().setValue(new SimpleDateFormat("yyyyMMdd").format(ssn.getSsaSentDate()));
				} else if (SSAVerificationStatus.INVALID_PER_SSA.getCode().equals(ssaVerificationStatusCode) && ssn.getSsaReceivedDate() != null) {
					otherIds.addNewEffectiveTime().setValue(new SimpleDateFormat("yyyyMMdd").format(ssn.getSsaReceivedDate()));
				} else if (SSAVerificationStatus.VERIFIED.getCode().equals(ssaVerificationStatusCode) && ssn.getSsaVerificationDate() != null) {
					otherIds.addNewEffectiveTime().setValue(new SimpleDateFormat("yyyyMMdd").format(ssn.getSsaVerificationDate()));
				}
			}
			// CCR 11699 add PseudoSSN:
			// NOTE: existing ESR code store pesudo ssn reason under official SSN
			// according to Cory email 7/5/2012:
          	// Q. Can ESR always determine a pseudo SSN by the existence of pseudo ssn reason (status = R, S, N)?
			// A. Yes, a reason code is required to be a “pseudo”
			if (//ssn.getType()!= null &&
				//	SSNType.CODE_PSEUDO.getCode().equals(ssn.getType().getCode()) &&
					ssn.getPseudoSSNReason() != null) {
				otherIds.setNullFlavor("OTH");
				otherIds.addNewStatusCode().setCode(ssn.getPseudoSSNReason().getCode());
				ssnId.setRoot(ROOT_VA_OID);
			}

			COCTMT150002UV01Organization scopingorganization = otherIds.addNewScopingOrganization();
			populateScopingOrganization(scopingorganization,SSN_OID);

			if(ssn.getSourceOfChange() != null) {
				scopingorganization.addNewCode().setCode(ssn.getSourceOfChange().getCode()); //<- UI
				scopingorganization.getCode().setDisplayName(ssn.getSourceOfChange().getDescription()); //<- UI
				if (ssn.getSsaMessage() != null)
					scopingorganization.getCode().addNewOriginalText().newCursor().setTextValue(ssn.getSsaMessage().getDescription());
			}
		}

		// This must be a addCorrelation and add preffered facility.
		PRPAMT201302UV02OtherIDs icnOtherIds = person.addNewAsOtherIDs();
		icnOtherIds.setClassCode("PAT");
		II icnid = icnOtherIds.addNewId();
		icnid.setRoot(ROOT_VA_OID);
		populateScopingOrganization(icnOtherIds.addNewScopingOrganization(),
				ROOT_VA_OID);

		if (idmServiceVO.getVpid() != null) {
			icnid.setExtension(idmServiceVO.getVpid().getVPID() + "^PI^"
					+ getEsrStationNumber() + "^USVHA");
		}

		//according to Cory email on 7/6/2012, these 3 sections are not needed for 1302

/*		PRPAMT201302UV02OtherIDs serviceConnected = person.addNewAsOtherIDs();
		serviceConnected.setClassCode("patientServiceConnected");
		II serviceConnectcedId = serviceConnected.addNewId();
		serviceConnectcedId.setExtension(getYesNoValue(idmServiceVO
				.isServiceConnected()));
		serviceConnectcedId.setRoot(ROOT_VA_OID);
		// Add scopingOrganization
		populateScopingOrganization(serviceConnected
				.addNewScopingOrganization(), ROOT_VA_OID);

		PRPAMT201302UV02OtherIDs patientType = person.addNewAsOtherIDs();
		patientType.setClassCode("patientType");
		II patientTypeId = patientType.addNewId();
		patientTypeId.setExtension(idmServiceVO.getPatientType());
		patientTypeId.setRoot(ROOT_VA_OID);
		populateScopingOrganization(patientType.addNewScopingOrganization(),
				ROOT_VA_OID);

		PRPAMT201302UV02OtherIDs veteran = person.addNewAsOtherIDs();
		veteran.setClassCode("patientVeteran");
		II veteranId = veteran.addNewId();
		veteranId.setExtension(getYesNoValue(idmServiceVO.isVeteran()));
		veteranId.setRoot(ROOT_VA_OID);
		populateScopingOrganization(veteran.addNewScopingOrganization(),
				ROOT_VA_OID);*/
	}


	private void populateAddress(AD addressTag, Address address) {
		if (address != null) {
			ArrayList useList = new ArrayList();
			if (address.getBadAddressReason() != null)
			{
				//CCR 11402: bad address
				useList.add(v3.hl7_org.AddressUse.Member.Enum
						.forString(BAD_ADDRESS)); //!!!!not getting it ""
//				useList.add(BAD_ADDRESS);
				addressTag.setUse(useList);

				//<additionalLocator partType=”ADL">VAB3^[NOT A RESIDENTIAL ADDRESS]</additionalLocator>
				//Attribute  partType=”ADL" should always be specified in this case for <additionalLocator>.
				AdxpAdditionalLocator additionalLocator = addressTag.addNewAdditionalLocator();
				additionalLocator.setPartType(PART_TYPE_ADL);
				additionalLocator.newCursor().setTextValue((String)idmBadAddressTable.get(address.getBadAddressReason().getCode())); //!!!!not getting it

			} else {
				useList.add(v3.hl7_org.HomeAddressUse.Enum
						.forString(HOME_PERMANENT)); //!!!!not getting it, enum type doesn't exist
//				useList.add(HOME_PERMANENT); //add the string directly
				addressTag.setUse(useList);
			}

			String addressLine1 = address.getLine1();
			String addressLine2 = address.getLine2();
			String addressLine3 = address.getLine3();
			List addressLineList = new ArrayList();
			if (addressLine1 != null && addressLine1.length() > 0) {
				addressLineList.add(addressLine1);
			}
			if (addressLine2 != null && addressLine2.length() > 0) {
				addressLineList.add(addressLine2);
			}
			if (addressLine3 != null && addressLine3.length() > 0) {
				addressLineList.add(addressLine3);
			}
			for (int i = 0; i < addressLineList.size(); i++) {
				String addressLine = (String) addressLineList.get(i);
				AdxpStreetAddressLine line = addressTag
						.addNewStreetAddressLine();
				line.newCursor().setTextValue(addressLine);
			}

			String city = address.getCity();
			if (city != null) {
				AdxpCity pcity = addressTag.addNewCity();
				pcity.newCursor().setTextValue(city);
			}

			String countryCode = address.getCountry();
			if (countryCode != null) {
				AdxpCountry pcountry = addressTag.addNewCountry();
				pcountry.newCursor().setTextValue(countryCode);
			}

			// CCR11654 When a Foreign address exists for the Veteran (not country = USA),
			// all Add Person and Update Person web service messages to MVI must send
			// Province in the State location and Postal Code in place of and in the same location as Zip Code.
			String stateCode, zipCode;

			if (Country.isUSAddress(countryCode)) {
				stateCode = address.getState();
				zipCode = address.getZipCode();
			} else {
				stateCode = address.getProvince();
				zipCode = address.getPostalCode();
			}

			if (stateCode != null) {
				AdxpState pstate = addressTag.addNewState();
				pstate.newCursor().setTextValue(stateCode);
			}


			if (zipCode != null) {
				String zipPlus4 = address.getZipPlus4();

				if (zipPlus4 != null && zipPlus4.length() > 0) {
					zipCode = zipCode + "-" + zipPlus4;
				}
				AdxpPostalCode postalCode = addressTag.addNewPostalCode();
				postalCode.newCursor().setTextValue(zipCode);
			}
		}

	}

	private void populatePhone(Phone phone, TEL tel) {
		tel.setValue(phone.getPhoneNumber());
		ArrayList useList = new ArrayList();
		useList.add(v3.hl7_org.HomeAddressUse.Enum.forString(HOME_PERMANENT));
		tel.setUse(useList);
	}


	private PRPAIN201305UV02Document createSearchRequest(FullyQualifiedIdentity id, String responseGroupId, PersonIdentityTraits traits, String compositeCode, IdMSearchInfo searchInfo,
			boolean attendedSearch) throws ServiceException {

		PRPAIN201305UV02Document pPRPAIN201305UV02Doc = (PRPAIN201305UV02Document) getXmlObject(PRPAIN201305UV02Document.class);
		PRPAIN201305UV02 pPRPAIN201305UV02 = pPRPAIN201305UV02Doc
				.addNewPRPAIN201305UV02();

		// <id root="2.16.840.1.113883.4.349" extension="MCID-12345"/>
		Date currentDate = Calendar.getInstance().getTime();
		String uniqueId = generateUniqueID(currentDate);
		populateHeaderRoot(pPRPAIN201305UV02.addNewId(), uniqueId);
		populateCreationTime(pPRPAIN201305UV02.addNewCreationTime(),
				currentDate);

		//CCR12852 - for extended search, add version 3.0
		if (searchInfo != null && searchInfo.isExtendedSearch()) {
			pPRPAIN201305UV02.addNewVersionCode().setCode("3.0");
		} else {
			pPRPAIN201305UV02.addNewVersionCode().setCode(VERSION_CODE);
		}


		// Interaction id.
		// <interactionId root="2.16.840.1.113883.1.6"
		// extension="PRPA_IN201309UV02" />
		populateInteractionId(pPRPAIN201305UV02.addNewInteractionId(),
				"PRPA_IN201305UV02");
		populateProcessingCode(pPRPAIN201305UV02.addNewProcessingCode());
		populateProcessingModeCode(pPRPAIN201305UV02.addNewProcessingModeCode());
		populateAcceptAckCode(pPRPAIN201305UV02.addNewAcceptAckCode());
		populateReceiver(pPRPAIN201305UV02.addNewReceiver());
		populateSender(pPRPAIN201305UV02.addNewSender());

		PRPAIN201305UV02QUQIMT021001UV01ControlActProcess controlActProcess = pPRPAIN201305UV02
				.addNewControlActProcess();
		controlActProcess.setClassCode(v3.hl7_org.ActClassControlAct.Enum
				.forString(CACT_CLASS_CODE));
		controlActProcess.setMoodCode(v3.hl7_org.XActMoodIntentEvent.Enum
				.forString(CACT_MOOD_CODE));
		CD code = controlActProcess.addNewCode();
		code.setCode("PRPA_TE201305UV02");
		code.setCodeSystem(CACT_CODE_SYSTEM);

		//CCR12852 - for extended search, add version 3.0
		if (searchInfo != null && searchInfo.isExtendedSearch()) {

			populateExtendedSearchDataEnterer(controlActProcess, searchInfo);

		} else {
			QUQIMT021001UV01AuthorOrPerformer authorOrPerformer = controlActProcess.addNewAuthorOrPerformer();
			authorOrPerformer.setTypeCode(XParticipationAuthorPerformer.Enum.forString("AUT"));
		}
		PRPAMT201306UV02QueryByParameter queryByParameter = controlActProcess
				.addNewQueryByParameter();

		// These values will be echoed back to ESR, just send empty
		II queryId = queryByParameter.addNewQueryId();
		queryId.setRoot(getMsName());
		queryId.setExtension(uniqueId);

		CS statusCode = queryByParameter.addNewStatusCode();
		statusCode.setCode(QRY_STATUS);

		/*if (compositeSearch) //CCR 11758
			queryByParameter.addNewModifyCode().setCode(COMPOSITE_NO_HISTORY);*/
		//queryByParameter.addNewModifyCode().setCode(COMPOSITE_WITH_HISTORY);

		//CR11776-
		/*compositeCode can have three values:
			1.	null - regular no composite call
			2.	COMPOSITE_WITH_HISTORY (COM2)
			3.	COMPOSITE_NO_HISTORY (COMP1) */

		if (compositeCode != null){
			queryByParameter.addNewModifyCode().setCode(compositeCode);
			if (logger.isDebugEnabled())
		            logger.debug("composite search flag: " + compositeCode);
		}

		CS priorityCode = queryByParameter.addNewResponsePriorityCode();
		priorityCode.setCode(QRY_RES_PRIORITY_CD);


		//CR11776- added a flag to execute unattended search
		//composite search always is unattended and initial quantity must be 1
		//init quantity is always 1 for search by fully qualified identifier

		INT initQuantity = queryByParameter.addNewInitialQuantity();
		if (id != null || compositeCode != null || !attendedSearch){
			initQuantity.setValue(new BigInteger("1"));
		}
		else {
			//CCR 11776 - init quantity = 10 for attended search
			initQuantity.setValue(new BigInteger("10"));
		}

		// Get ESR Correlation or get Primary view, default PV
		if (responseGroupId != null)
		{
			II responseElementGroupId = queryByParameter.addNewResponseElementGroupId();
			responseElementGroupId.setExtension(responseGroupId);
			responseElementGroupId.setRoot(ROOT_VA_OID);
		}

		/*	if (id != null) {
			if (isUnAttended){
				initQuantity.setValue(new BigInteger("1"));   //dc-added for unattended search
			}
			else {
				initQuantity.setValue(new BigInteger("2"));
			}

			// Get ESR Correlation or get Primary view, default PV
			if (responseGroupId != null)
			{
				II responseElementGroupId = queryByParameter.addNewResponseElementGroupId();
				responseElementGroupId.setExtension(responseGroupId);
				responseElementGroupId.setRoot(ROOT_VA_OID);
			}

		} else {
			//CR11776- added a flag to execute unattended search
			if (isUnAttended){
				initQuantity.setValue(new BigInteger("1"));   //dc-added for unattended search
			}
			else {
				initQuantity.setValue(new BigInteger("10"));  //attended search criteria
			}
		}*/

		PRPAMT201306UV02MatchCriterionList matchCriterionList = queryByParameter
				.addNewMatchCriterionList();

		PRPAMT201306UV02MinimumDegreeMatch minimumDegreeMatch = matchCriterionList
				.addNewMinimumDegreeMatch();
		ANY value = minimumDegreeMatch.addNewValue();
		//value.newCursor().
		//value.

		PRPAMT201306UV02ParameterList parameterList = queryByParameter
		.addNewParameterList();

		populateSearchParameterList(parameterList, id, traits, searchInfo);

		return pPRPAIN201305UV02Doc;
	}


	private void populateExtendedSearchDataEnterer(PRPAIN201305UV02QUQIMT021001UV01ControlActProcess controlActProcess,
			IdMSearchInfo searchInfo)
	{
		QUQIMT021001UV01DataEnterer enterer = controlActProcess.addNewDataEnterer();
		enterer.setContextControlCode("AP");
		enterer.setTypeCode("ENT");

		COCTMT090100UV01AssignedPerson assPerson = enterer.addNewAssignedPerson();
		assPerson.setClassCode("ASSIGNED");
		II idSec = assPerson.addNewId();
		idSec.setRoot("2.16.840.1.113883.3.2017");
		idSec.setExtension(searchInfo.getDataEntererExtension());

		COCTMT090100UV01Person assPerson2 = assPerson.addNewAssignedPerson();
		assPerson2.setDeterminerCode("INSTANCE");
		assPerson2.setClassCode("PSN");

		EN name = assPerson2.addNewName();
		name.addNewGiven().newCursor().setTextValue(searchInfo.getDataEntererGivenName());
		name.addNewFamily().newCursor().setTextValue(searchInfo.getDataEntererFamilyName());


		COCTMT150000UV02Organization repOrg = assPerson.addNewRepresentedOrganization();
		repOrg.setClassCode("ORG");
		repOrg.setDeterminerCode("INSTANCE");

		II idSec2 = repOrg.addNewId();
		idSec2.setRoot("2.16.840.1.113883.4.349");
		//ExternalUid = Vet Last Name, First Name
		idSec2.setExtension(searchInfo.getDataEntererFamilyName()+ ", " + searchInfo.getDataEntererGivenName());

		repOrg.addNewCode().setCode(BGS_ExternalKey); //ExternalKey
		repOrg.addNewDesc().newCursor().setTextValue(BGS_applicationName); //applicationName
		repOrg.addNewTelecom().setValue(getHostName());//CLIENT_MACHINE
	}

	private String getHostName()
	{
		if (BGS_CLIENT_MACHINE != null)
			return BGS_CLIENT_MACHINE;

		//try InetAddress.LocalHost first;
		//NOTE -- InetAddress.getLocalHost().getHostName() will not work in certain environments.
		try {
		    BGS_CLIENT_MACHINE = InetAddress.getLocalHost().getHostName();
		    if (StringUtils.isNotEmpty(BGS_CLIENT_MACHINE))
		        return BGS_CLIENT_MACHINE;
		} catch (UnknownHostException e) {
		    // failed;  try alternate means.
		}

		// try environment properties.
		BGS_CLIENT_MACHINE = System.getenv("COMPUTERNAME"); //Windows
		if (BGS_CLIENT_MACHINE != null)
		    return BGS_CLIENT_MACHINE;

		BGS_CLIENT_MACHINE = System.getenv("HOSTNAME"); //UNIX
		if (BGS_CLIENT_MACHINE != null)
		    return BGS_CLIENT_MACHINE;

		BGS_CLIENT_MACHINE = this.getMsName(); //weblogic cluster MS[n] name

		return BGS_CLIENT_MACHINE;
	}




	private void populateSearchParameterList(PRPAMT201306UV02ParameterList parameterList,
			FullyQualifiedIdentity id, PersonIdentityTraits traits,IdMSearchInfo searchInfo)
	{
		/**
		 * There are 2 different types of 1305 Request that can be submitted for this function:

			1.	Match criteria in queryByParameter, with person trait data to be searched for in parameterList.
			2.	Correlation identifier in parameterList – either ICN or IEN (one or the other, but not both).  No person trait data is supplied for this type of request.
		**/

		if (id != null) {
			// search by identifier
			// set the IEN or ICN in the PRPAMT201306UV02ParameterList
			II uniqueId = parameterList.addNewId();
			uniqueId.setExtension(id.getIdmExtension());
			uniqueId.setRoot(ROOT_VA_OID);

			if(searchInfo != null && searchInfo.isExtendedSearch()){
				setMVIOrchestration(parameterList);
			}
			return;
		}

		// Else no vpid available, search with person traits

		//Core Logic to set the search fields.
		//-	Last Name                                   (required)
		//-	Middle Name
		//-	First Name
		//-	Social Security Number            (required)
		//-	Date of Birth                                (required)
		//-	Gender                                         (required)
		//-	Address (Street and Number)
		//-	Address City
		//-	Address State
		//-	Address Zip Code
		//-	Postal Code
		//-	Country Code
		//-	Home Phone Number
		Set names = traits.getNames();
		Name legalName = null;

		/**
		 * There must be only one name here.
		 */
		if (names != null && names.size() > 0) {
			/*
			Iterator nameItr = names.iterator();
			while (nameItr.hasNext()) {
				Name currName = (Name) nameItr.next();
				//if ( NameType.LEGAL_NAME.equals(currName.getType())){
				//legalName = (Name)nameItr.next();
				legalName = currName;
				//}
			}
			*/
			// CCR 11486 - intermittantly using mother's maiden name
			// so be specific about type. Traits can contain multiple names.
			legalName = this.getLegalNameFromSet(names);
		}
		if (legalName != null) {
			String givenName = legalName.getGivenName();

			String lastName = legalName.getFamilyName();
			String middleName = legalName.getMiddleName();

			PRPAMT201306UV02LivingSubjectName livingSubjectName = parameterList
					.addNewLivingSubjectName();
			EN name = livingSubjectName.addNewValue();
			ArrayList useList = new ArrayList();
			useList.add(v3.hl7_org.PersonNameUseLegalByBOT.Enum.forString("L"));
			name.setUse(useList);

			if (givenName != null) {
				EnGiven given = name.addNewGiven();
				given.newCursor().setTextValue(givenName);
				//given.set
			}
			if (middleName != null) {
				EnGiven given = name.addNewGiven();
				given.newCursor().setTextValue(middleName);
			}

			if (lastName != null) {
				EnFamily family = name.addNewFamily();
				family.newCursor().setTextValue(lastName);
			}
			ST livingSubST = livingSubjectName.addNewSemanticsText();
			livingSubST.newCursor().setTextValue("LivingSubject.name");

		}

		//set SSN
		String ssnText = traits.getSsnText();
		if (ssnText != null && ssnText.length() > 0) {
			PRPAMT201306UV02LivingSubjectId livingSubId = parameterList
					.addNewLivingSubjectId();
			II subSSN = livingSubId.addNewValue();
			subSSN.setRoot("2.16.840.1.113883.4.1");
			subSSN.setExtension(traits.getSsn().getSsnText());
			ST semantics = livingSubId.addNewSemanticsText();
			semantics.newCursor().setTextValue(
					"OtherIDs.scopingOrganization.id");
		}

		//Set DOB
		String dobText = null;
		BirthRecord birthRecord = traits.getBirthRecord();
		if (birthRecord != null) {
			ImpreciseDate birthDate = birthRecord.getBirthDate();
			if (birthDate != null) {
				//CCR13436 change dob to short format, e.g. from 19400205000000 to 19400205
				dobText = birthDate.getyyyyMMddFormat();

			}
		}
		if (dobText != null) {
			PRPAMT201306UV02LivingSubjectBirthTime birthTime = parameterList
					.addNewLivingSubjectBirthTime();
			IVLTS birthTimeValue = birthTime.addNewValue();
			birthTimeValue.setValue(dobText);
			ST semantics = birthTime.addNewSemanticsText();
			semantics.newCursor().setTextValue("LivingSubject.birthTime");

		}
		//Set Gender
		String genderCode = null;
		if (traits.getGender() != null) {
			genderCode = traits.getGender().getCode();
		}
		if (genderCode != null) {
			PRPAMT201306UV02LivingSubjectAdministrativeGender livingSubGender = parameterList
					.addNewLivingSubjectAdministrativeGender();

			CE genderCodeValue = livingSubGender.addNewValue();
			genderCodeValue.setCode(genderCode);
			ST semantics = livingSubGender.addNewSemanticsText();
			semantics.newCursor().setTextValue(
					"LivingSubject.administrativeGender");

			/* WI 305033 backout
			String sigiCode = null;
			if (traits.getSigi() != null && traits.getSigi().getCode() != null) {
				sigiCode = traits.getSigi().getCode();
			}
			if (sigiCode != null) {

				genderCodeValue.addNewTranslation().setCode(sigiCode);

			}*/
		}



		Address address = traits
				.getAddressByType(AddressType.CODE_PERMANENT_ADDRESS.getName());
		if (address != null) {
			PRPAMT201306UV02PatientAddress pAddress = parameterList
					.addNewPatientAddress();
			AD addressValue = pAddress.addNewValue();
			populateAddress(addressValue, address);
		}

		String phone = address != null ? address.getPhoneNumber() : null;
		;
		if (phone != null) {
			PRPAMT201306UV02PatientTelecom teleCom = parameterList
					.addNewPatientTelecom();
			TEL tel = teleCom.addNewValue();
			tel.setValue(phone);

			ArrayList useList = new ArrayList();
			useList.add(v3.hl7_org.HomeAddressUse.Enum.forString(HOME_PERMANENT));
			tel.setUse(useList);

		}

		//CCR 12852, for extended search, add
//		<otherIDsScopingOrganization>
//        <value root="2.16.840.1.113883.4.349" extension="VBA" />
//        <semanticsText>MVI.ORCHESTRATION</semanticsText>
//      </otherIDsScopingOrganization>"


		if(searchInfo != null && searchInfo.isExtendedSearch()){
			setMVIOrchestration(parameterList);
		}
	}

	private void setMVIOrchestration(PRPAMT201306UV02ParameterList parameterList){
		PRPAMT201306UV02OtherIDsScopingOrganization scopingOrg = parameterList.addNewOtherIDsScopingOrganization();
		II val = scopingOrg.addNewValue();
		val.setRoot("2.16.840.1.113883.4.349");
		val.setExtension("VBA");

		ST semantics = scopingOrg.addNewSemanticsText();
		semantics.newCursor().setTextValue("MVI.ORCHESTRATION");
	}

	private Set parseSearchResponse(PRPAIN201306UV02Document responseDocument, boolean isSearchByDfn, IdMSearchInfo searchInfo)
			throws Exception {

		if (responseDocument == null
				|| responseDocument.getPRPAIN201306UV02() == null) {
			return null;
		}
		PRPAIN201306UV02 response = responseDocument.getPRPAIN201306UV02();

		MCCIMT000300UV01Acknowledgement[] ack = response
				.getAcknowledgementArray();


		int matchThreshhold = -1;
		int potentialThreshhold = -1;

		MCCIMT000300UV01AcknowledgementDetail[] ackDetails = ack[0].getAcknowledgementDetailArray();

		for (int i = 0; ackDetails != null && i < ackDetails.length; i++) {
			MCCIMT000300UV01AcknowledgementDetail ackDetail = ackDetails[i];
			if ("MVI".equals(ackDetail.getCode().getCodeSystem())) {
				String disName = ackDetail.getCode().getDisplayName();
				String code = ackDetail.getCode().getCode();
				int codeInt = -1;
				if (code != null) {
					codeInt = Integer.parseInt(code);
				}

				if ("IMT".equals(disName)) {
					matchThreshhold = codeInt;
				} else if ("PDT".equals(disName)) {
					potentialThreshhold = codeInt;
				}
			} else {
				matchThreshhold = -1;
				potentialThreshhold = -1;
			}
		}

		PRPAIN201306UV02MFMIMT700711UV01ControlActProcess controlActprocess = response.getControlActProcess();
		MFMIMT700711UV01QueryAck queryAck = controlActprocess.getQueryAck();

		if (queryAck != null && queryAck.getQueryResponseCode() != null) {
			String queryAckResponseCode = queryAck.getQueryResponseCode().getCode();
			//If your query results are above the maximum return records defined by the business
			//a response code of “QE” (Query Error) will be returned.
			if (queryAckResponseCode != null
					&& "QE".equals(queryAckResponseCode)) {
				// Using -1 to indicate the total records that would have been returned is not known.
				//throw new MaxRecordsExceededException(-1, -1);


				throw new MaxRecordsExceededException(-1, 10,
		                "Search Idm Service exceeded allowable limit of 10 matched records.");


			} else if ( "NF".equals(queryAckResponseCode)) {
				// Search returned no records.
				return null;
			} else if (!"OK".equals(queryAckResponseCode) ) {
				throw new ServiceException("Unknown response code for Search:" + queryAckResponseCode);
			}
		}

		//CCR11776 - if a traits is searched by a dfn that is deprecated, the traits is set to deprecated.
		//As a result, this will prevent sending messages for the deprecated dfn.
		/*<parameterList>
	    <id extension="100002638^PI^553^USVHA" root="2.16.840.1.113883.4.349"/>
	    </parameterList>*/
		//parse dfn passed in from the response
		String dfn = null;
		if (isSearchByDfn){
			if (controlActprocess.getQueryByParameter() != null) {
				PRPAMT201306UV02ParameterList qryList = controlActprocess.getQueryByParameter().getParameterList();
				PatientIdentifier identifier = null;
				if (qryList != null){
					II id = qryList.getId();
					String idValue = id.getExtension();
					identifier = new PatientIdentifier(idValue);
					dfn = identifier.getIdentity();

				}
			}
		}

		PRPAIN201306UV02MFMIMT700711UV01Subject1[] subjects = response.getControlActProcess().getSubjectArray();
		Set responseTraits = new HashSet();
		for (int i = 0; subjects != null && i < subjects.length; i++) {
			PRPAIN201306UV02MFMIMT700711UV01Subject1 subject = subjects[i];
			PRPAMT201310UV02Patient patient = subject.getRegistrationEvent().getSubject1().getPatient();
			PersonIdentityTraits trait = parsePatientTraits(patient, dfn, matchThreshhold, potentialThreshhold);

			responseTraits.add(trait);
		}

		return responseTraits;
	}



	private PersonIdentityTraits parsePatientTraits(PRPAMT201310UV02Patient patient, String dfnParam, int matchThreshhold, int potentialThreshhold) throws Exception {

		PersonIdentityTraits trait = new PersonIdentityTraits();
		II[] ids = patient.getIdArray();

		//CCR12665- site identities will be stored here for all the sites a patient is associated with
		//This way, a separate 1309 WS call to MVI will be avoided.
		Set<SiteIdentity> siteIdentities = new HashSet<SiteIdentity>();


		//there is no list of deprecated traits
        //surviving trait is returned

		//CCR 12789: need a list of all correlations for the person identity traits
		ArrayList<PatientIdentifier> identifierLst = new ArrayList();

		for (int i = 0; ids != null && i < ids.length; i++) {
			II id = ids[i];
			String idValue = id.getExtension();

			PatientIdentifier identifier = new PatientIdentifier(idValue);
			identifierLst.add(identifier); //CCR 12789

			/* CCR11699 -- SSN and SSN Verification Status has been moved to the new asOtherIDs section
			 * if ("SS".equals(identifier.getIdentityType())) {
				SSN ssn = new SSN();
				ssn.setSsnText(SSN.formatSSN(identifier.getIdentity()));
				ssn.setType(getLookupService().getSSNTypeByCode(SSNType.CODE_ACTIVE.getCode()));
				trait.setSsn(ssn);
			} else*/
			//11899- added station number chk because EDIPI is also NI

			if ("NI".equals(identifier.getIdentityType()) && "200M".equals(identifier.getStationNumber())){
				//e.g. <id root="2.16.840.1.113883.4.349" extension="1001169928V721950^NI^200M^USVHA^T"/>
				//this must be VPID. ALWAYS convert to long vpid
				//System.out.println(VPIDEntityKeyImpl.getLongVPID(identifier.getIdentity()));

				String vpidInCorrelation = identifier.getIdentity();
				VPIDEntityKey longVPID = CommonEntityKeyFactory.createVPIDEntityKey(VPIDEntityKeyImpl.getLongVPID(vpidInCorrelation));
				trait.setVpid(longVPID);

				// CCR 11403: added for idState
				String idState = identifier.getIdState();
				trait.setIDState(idState);

				//CR11776- pending updates
				if ("PCE".equalsIgnoreCase(idState)){   //if idState = PCE, set pending updates to true
					trait.setHasPendingUpdates(true);
				}

				if (logger.isDebugEnabled()){
					logger.debug("has Pending Updates: " + trait.isHasPendingUpdates());
					logger.debug("vpid status: " + trait.getIDState());
				}

			} else if ("PI".equals(identifier.getIdentityType()) && ESR_CORRELATION.equals(identifier.getStationNumber())){
				//CCR 11758, id array for composite array results. If 200ESR is listed, it has 200ESR Correlation
				// SOAP msg example:
				//<subject1 typeCode="SBJ">
		        //  <patient classCode="PAT">
		        //   <id extension="1008590759V367283^NI^200M^USVHA^P" root="2.16.840.1.113883.4.349"/>
		        //   <id extension="0000001008590759V367283000000^PI^200ESR^USVHA^A" root="2.16.840.1.113883.4.349"/>
		        //   <id extension="100001982^PI^553^USVHA^A" root="2.16.840.1.113883.4.349"/>
		        //   <id extension="1207251139285861224530362^PI^200SSA^USSSA^A" root="2.16.840.1.113883.4.1"/>
		        //   <id extension="672839102^SS" root="2.16.840.1.113883.4.1"/>
				//...
				trait.set200ESRCorrelation(YES); //"Y", "N", null (unknown - default)
			}
			else if ("PI".equalsIgnoreCase(identifier.getIdentityType()) && "USVHA".equalsIgnoreCase(identifier.getAssigningAuthority()) && (!"200ESR".equalsIgnoreCase(identifier.getStationNumber()))){

				//CCR11776 - if passed in dfn is deprecated, then set the traits as deprecated
				if (dfnParam != null){
				     String idState = identifier.getIdState();
				     trait.setIDState(idState);
				     String dfnValue = identifier.getIdentity();
					//CR11776- deprecated dfn
					if ((dfnValue.equalsIgnoreCase(dfnParam) && "H".equalsIgnoreCase(idState))){   //if idState = H, set traits to be deprecated
						trait.setDeprecated(true);
					}
				}

				//CCR12665 - sites info is being included which helps avoid a separate 1309 call
				SiteIdentity siteIdentity = getPatientSiteIdentity(identifier);
				if (siteIdentity != null){
					siteIdentities.add(siteIdentity);
					trait.setSiteIdentities(siteIdentities);  //set this property in the PersonIdentityTraits
				}

			}
		}

		trait.setAllCorrelations (identifierLst);//CCR 12789

		//CCR 11758, if 200ESR Correlation not found, set to "N";
		if (trait.has200ESRCorrelation() == null)
			trait.set200ESRCorrelation(NO);

		PRPAMT201310UV02Person pPerson = patient.getPatientPerson();

		Set names = new HashSet();
		PN[] pns = pPerson.getNameArray();

		for (int j = 0; pns != null & j < pns.length; j++) {
			PN pn = pns[j];
			String nameUse = null;

			List useList = pn.getUse();
			if (useList != null && useList.size() > 0) {
				nameUse = useList.get(0).toString();
				if (MOTHERS_MAIDEN_NAME_TYPE.equals(nameUse))
				{
					//mothers maiden name
					EnFamily[] familyNames = pn.getFamilyArray();
					if (familyNames != null && familyNames.length > 0) {
						trait.setMothersMaidenName(familyNames[0].newCursor().getTextValue());
					}
					continue; //skip to next
				}
			}


			Name name = new Name();

			EnFamily[] familyNames = pn.getFamilyArray();
			if (familyNames != null && familyNames.length > 0) {
				name.setFamilyName(familyNames[0].newCursor().getTextValue());
			}
			EnGiven[] givenNames = pn.getGivenArray();
			if (givenNames != null && givenNames.length > 0) {
				name.setGivenName(givenNames[0].newCursor().getTextValue());
			}
			if (givenNames != null && givenNames.length > 1) {
				name.setMiddleName(givenNames[1].newCursor().getTextValue());
			}
			EnPrefix[] prefixArr = pn.getPrefixArray();
			if (prefixArr != null && prefixArr.length > 0) {
				name.setPrefix(prefixArr[0].newCursor().getTextValue());
			}
			EnSuffix[] suffixArr = pn.getSuffixArray();
			if (suffixArr != null && suffixArr.length > 0) {
				name.setSuffix(suffixArr[0].newCursor().getTextValue());
			}

			//Set the name type here.
			if (nameUse != null ) {
				name.setType(getLookupService().getNameTypeByCode(nameUse));
			}

			names.add(name);
		}


		trait.setNames(names);


		CE ce = pPerson.getAdministrativeGenderCode();
		if (ce != null) {
			//CCR 13415 - DOD adminGenderCode coming in as real null or string "null" in some cases
			//need to check it before set to avoid lookup exception
			if (ce.getCode() != null && !ce.getCode().equalsIgnoreCase("null")) {
				trait.setGender(getLookupService().getGenderByCode(ce.getCode()));

				//WI 305033 Backout - SIGI not ready yet from MVI side
				/*
				if (ce.getTranslationArray() != null && ce.getTranslationArray().length > 0 && ce.getTranslationArray(0) != null) {

					trait.setSigi((SelfIdentifiedGenderIdentity)getLookupService().getByCode(SelfIdentifiedGenderIdentity.class, ce.getTranslationArray(0).getCode()));
				}*/
			}
		}
		BirthRecord brecord = new BirthRecord();
		TS ts = pPerson.getBirthTime();
		if (ts != null) {
			brecord.setBirthDate(new ImpreciseDate(ts.getValue()));

		}


		PRPAMT201310UV02BirthPlace bPlace = pPerson.getBirthPlace();
		if (bPlace != null) {

			AD bPlaceAddress = bPlace.getAddr();
			Address bAddress = parseAddress(bPlaceAddress);
			brecord.setCity(bAddress.getCity());
			brecord.setState(bAddress.getState());
			brecord.setCountry(bAddress.getCountry());
			//brecord.set)
		}
		if (pPerson.getMultipleBirthInd() != null) {
			brecord.setMultipleBirth(new Boolean(pPerson.getMultipleBirthInd()
					.getValue()));
		}
		//CCR 11403 set Date of Death
		TS dodTs = pPerson.getDeceasedTime() ;
		if (dodTs != null) {
			trait.setDateOfDeathText(dodTs.getValue()); //yyyyMMdd
		}

		if (pPerson.getMaritalStatusCode() != null) {
			String maritalStatusCode = pPerson.getMaritalStatusCode().getCode();
			MaritalStatus mstatus = getLookupService().getMaritalStatusByCode(
					maritalStatusCode);


			//trait.setM  TODO
		}



		trait.setBirthRecord(brecord);

		PRPAMT201310UV02Subject[] subjectOf1 = patient.getSubjectOf1Array();
		if (subjectOf1 != null && subjectOf1.length > 0) {
			ANY val = subjectOf1[0].getQueryMatchObservation().getValue();
			XmlCursor valCursor = val.newCursor();

			while (valCursor.toNextToken() != null
					&& valCursor.getName() != null) {
				String attributeName = valCursor.getName().getLocalPart();
				String attributeValue = valCursor.getTextValue();

				if ("value".equals(attributeName)) {
					float threshHoldValue = Float.parseFloat(attributeValue);
					//System.out.println("threshHoldValue: " + threshHoldValue);
					if (threshHoldValue >= matchThreshhold) {
						// set the boolean flag for match to true.
						trait.setIdmMatchType(PersonIdentityTraits.EXACT_MATCH);
					} else if (potentialThreshhold >= threshHoldValue) {
						trait
								.setIdmMatchType(PersonIdentityTraits.POTENTIAL_MATCH);
					}
				}
			}
		}

        //CCR 11699: add asOtherIDs section, get the ssn and the verification status
        //<urn:asOtherIDs classCode="SSN">
		//  <urn:id extension="212212121" root="2.16.840.1.113883.4.1"/>
		//  <urn:statusCode code="1"/>
		//  <urn:scopingOrganization classCode="ORG" determinerCode="INSTANCE">
		//        <urn:id root="2.16.840.1.113883.4.1"/>
		//  </urn:scopingOrganization>
        //</urn:asOtherIDs>
        //
        // Email answer from Cory Chin on 6/29/2012:
        // Q. if the code is 0-4 (as this could contain other values such as “S” or “R”), ESR will get the SSN from the extension of the first id segment and the verification status code from the code of statusCode segment. Is that correct?
        // A. Yes and No I wouldn’t say “first” id element.  Technically there should would only be “one”, but really the SSN from the ID element is a combination of:
        //      1. <id> element child of “asOtherIDs” with classCode="SSN"
        //      2. Root attribute = to the SSA OID of "2.16.840.1.113883.4.1"
        //      3. Verification status is determined by:
        //          1.  “<statusCode>” element, child of “asOtherIDs” with classCode=”SSN”
        //          2. And the code attribute

        PRPAMT201310UV02OtherIDs[] otherIds = pPerson.getAsOtherIDsArray();
        SSN ssn = new SSN();
        boolean ssnSet = false;

        for (int i = 0; otherIds != null && i < otherIds.length && !ssnSet; i++)
        {
              if ("SSN".equals(otherIds[i].getClassCode()) )
              {
                    II[] idArray = otherIds[i].getIdArray();

                    for (int j = 0; idArray != null && j < idArray.length; j++)
                    {
                          if (SSN_OID.equals(idArray[j].getRoot())) //root attribute must equal to SSA OID
                          {
                                ssn.setSsnText(SSN.formatSSN(idArray[j].getExtension()));
                                ssn.setType(getLookupService().getSSNTypeByCode(SSNType.CODE_ACTIVE.getCode()));
                                if (otherIds[i].getStatusCode() != null && otherIds[i].getStatusCode().getCode() != null &&
                                            isSsnVerificationCode(otherIds[i].getStatusCode().getCode())) //valid code 0-4
                                {
                                      ssn.setSsaVerificationStatus(getLookupService().getSSAVerificationStatusByCode(otherIds[i].getStatusCode().getCode()));

                                      /* CCR11777 -- parse the effectiveTime value:
                                       * <!—SSN Status Date [DRAFT]-->
                                       * <effectiveTime value="20111012" />
                                       *  if the SSN verification status is 1 (in_process), set SSA_SENT_DATE;
                                       *  if the SSN verification status is 2 (invalid per SSA), set SSA_RECEIVED_DATE
                                       *  if the SSN verification status is 4 (verified), set SSA_VERIFIED_DATE
                                       *  if the status is 0 (New) or 3 (Rsend to SSA), date is current date, do not set any dates (no specific ESR storage
                                       */
                                      TS effectiveTimeTs = otherIds[i].getEffectiveTime();
                                      Date effectiveDate = null;
                                      if (effectiveTimeTs != null && !StringUtils.isBlank(effectiveTimeTs.getValue())) {
                                     	  effectiveDate = new SimpleDateFormat("yyyyMMdd").parse(effectiveTimeTs.getValue());
                                      }
                                	  if (SSAVerificationStatus.IN_PROCESS.getCode().equals(ssn.getSsaVerificationStatus().getCode())) {
                                		  ssn.setSsaSentDate(effectiveDate);
                                	  } else if (SSAVerificationStatus.INVALID_PER_SSA.getCode().equals(ssn.getSsaVerificationStatus().getCode())) {
                                		  ssn.setSsaReceivedDate(effectiveDate);
                                  	  } else if (SSAVerificationStatus.VERIFIED.getCode().equals(ssn.getSsaVerificationStatus().getCode())) {
                                		  ssn.setSsaVerificationDate(effectiveDate);
                                      }
                                }
                                trait.setSsn(ssn);
                                ssnSet = true;
                                break;
                          }

                          /*  pseudo SSN, nullFlavor="OTH", root ID is VA OID, and statusCode is either "R", "S", or "N".
                          	<asOtherIDs classCode="SSN" nullFlavor="OTH">
                          	       <id root="2.16.840.1.113883.4.349" extension="111223333"/>
                          	      <statusCode code="R"/>
                          	      <scopingOrganization classCode="ORG" determinerCode="INSTANCE">
                          	      <id root="1.2.840.114350.1.13.99997.2.3412"/>
                          	   </scopingOrganization>
                          	</asOtherIDs>
                          	according to Cory email 7/5/2012:
                          	Q. Can ESR always determine a pseudo SSN by the existence of pseudo ssn reason (status = R, S, N)?
							A. Yes, a reason code is required to be a “pseudo”

                          	NOTE: in ESR PSIM ejb calls, the pesudo ssn is passed in as ACTIVE ssn, that's why
                          	 	  in ESR code, the pseudo ssn reason is stored/retrieved from official (active) ssn
                          	 	  This may need to be addressed in the future. But for now, follow the existing ESR logic
						  */
                          else if (ROOT_VA_OID.equals(idArray[j].getRoot()) &&
                        		  "OTH".equals(otherIds[i].getNullFlavor())
                        		  )
                          {
                              ssn.setSsnText(SSN.formatSSN(idArray[j].getExtension()));
                              //ssn.setType(getLookupService().getSSNTypeByCode(SSNType.CODE_PSEUDO.getCode()));
                              ssn.setType(getLookupService().getSSNTypeByCode(SSNType.CODE_ACTIVE.getCode())); //set as ACTIVE ssn according to existing ESR logic
                              if (otherIds[i].getStatusCode() != null && otherIds[i].getStatusCode().getCode() != null &&
                                          isPsuedoSsnReasonCode(otherIds[i].getStatusCode().getCode())) //valid code R,S,N
                              {
                                    ssn.setPseudoSSNReason(getLookupService().getPseudoSSNReasonByCode(otherIds[i].getStatusCode().getCode()));
                              }
                              trait.setSsn(ssn);
                              ssnSet = true;
                              break;
                          }
                    }
              }
        }

		return trait;
	}



	//CCR12665- added to verify supporting environments
	public boolean isSupportedInEnvironment(VAFacility facility) throws ServiceException {
		return environmentParamService.isSupportedFacility(facility);
	}

	//CCR12665- return a siteIdentity object for a given patient
	private SiteIdentity getPatientSiteIdentity(PatientIdentifier identifier) throws ServiceException{

		VAFacility facility = lookupService.getVaFacilityByCode(identifier.getStationNumber()); //ESR only cares about medical treating facilities -VAMC
		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()){
				SiteIdentity site = new SiteIdentity();
				site.setDfn(identifier.getIdentity());
				site.setVaFacility(facility);

				if (logger.isDebugEnabled()) {
					logger.debug("[Site dfn: " + identifier.getIdentity() + ", " + "facility Identifier : " + facility.getIdentifier() + ", " + "Station #: " + identifier.getStationNumber() + "]");
				}
				return site;
			}
		}
		else {
			if (logger.isDebugEnabled())
				logger.debug("Site for facility with station #: " + facility.getStationNumber() + " excluded from the list because it is not supported by the environment.");
		}

		return null;
	}


	//the valid SSN verification status codes (0-4)
	private boolean isSsnVerificationCode(String code)
	{
		if ("0".equals(code) ||"1".equals(code) || "2".equals(code) || "3".equals(code) || "4".equals(code))
			return true;

       return false;
	}


	//the valid Pseudo SSN reason codes (R, S, N)
	private boolean isPsuedoSsnReasonCode(String code)
	{
		if ("R".equals(code) || "S".equals(code) || "N".equals(code))
			return true;

       return false;
	}

	private Address parseAddress(AD ad) throws ServiceException {

		Address address = new Address();
		AdxpStreetAddressLine[] addressLines = ad.getStreetAddressLineArray();
		for (int k = 0; addressLines != null && k < addressLines.length; k++) {
			AdxpStreetAddressLine line = addressLines[k];
			if (k == 0)
				address.setLine1(line.newCursor().getTextValue());
			if (k == 1)
				address.setLine2(line.newCursor().getTextValue());
			if (k == 2)
				address.setLine3(line.newCursor().getTextValue());
		}

		AdxpCity[] cities = ad.getCityArray();
		if (cities != null && cities.length > 0) {
			address.setCity(cities[0].newCursor().getTextValue());
		}
		AdxpState[] states = ad.getStateArray();

		if (states != null && states.length > 0) {
			address.setState(states[0].newCursor().getTextValue());
		}

		AdxpCountry[] countries = ad.getCountryArray();
		if (countries != null && countries.length > 0) {
			address.setCountry(countries[0].newCursor().getTextValue());
		}
		AdxpPostalCode[] postalCodes = ad.getPostalCodeArray();
		if (postalCodes != null && postalCodes.length > 0) {
			address.setPostalCode(postalCodes[0].newCursor().getTextValue());
		}
		List useList = ad.getUse();
		if (useList != null && useList.size() > 0) {
			String addressType = useList.get(0).toString();
			AddressType sdsAddressType = getLookupService()
					.getAddressTypeByCode(addressType);
			address.setType(sdsAddressType);
		}

		return address;
	}

	//CCR 13856
	private XmlObject createDeathSourceFragment(DeathRecord record) throws XmlException {

		/*
		 * <!-- The Source of Notification for the Date of Death information  -->
    	<value xsi:type="CD" code="3" displayName="DEATH CERTIFICATE ON FILE"/>
		 */

		 XmlObject xo = XmlObject.Factory.newInstance();
		 XmlCursor xc = xo.newCursor();
		 xc.toNextToken();
		 xc.toNextToken();
		 xc.insertAttributeWithValue("type", "CD");
		 xc.insertAttributeWithValue("code", record.getDataSource().getCode());
		 xc.insertAttributeWithValue("displayName", record.getDataSource().getDescription().toUpperCase());
		 xc.dispose();

		return xo;
	}

	private void execute1302(IdmServiceVO idmServiceVO) throws ServiceException {

		//1. Create 1302 request.
		PRPAIN201302UV02Document pPRPAIN201302UV02Document = create1302Request();

		//2. Create 1302 headers.
		PRPAIN201302UV02 pPRPAIN201302UV02 = populate1302RequestHeaders(pPRPAIN201302UV02Document);

		PRPAIN201302UV02MFMIMT700701UV01ControlActProcess controlActProcess = add1302ControlActprocess(pPRPAIN201302UV02);

		PRPAIN201302UV02MFMIMT700701UV01RegistrationEvent regEvent = controlActProcess
				.getSubjectArray()[0].getRegistrationEvent();

		PRPAIN201302UV02MFMIMT700701UV01Subject2 sub1 = regEvent
				.addNewSubject1();
		sub1.setTypeCode(ParticipationTargetSubject.Enum.forString("SBJ"));
		PRPAMT201302UV02Patient patient = sub1.addNewPatient();
		patient.setClassCode("PAT");
		II patientId = patient.addNewId();
		patientId.setRoot(ROOT_VA_OID);

		if (idmServiceVO.getVpid() != null) {
			patientId.setExtension(idmServiceVO.getVpid().getVPID());
		}

		patient.addNewStatusCode().setCode("active");

		COCTMT150003UV03Organization providerOrg = patient
				.addNewProviderOrganization();
		providerOrg.setClassCode("ORG");
		providerOrg.setDeterminerCode("INSTANCE");
		providerOrg.addNewId();
		providerOrg.addNewContactParty().setClassCode(
				v3.hl7_org.RoleClassContact.Enum.forString("CON"));

		PRPAMT201302UV02Person person = patient.addNewPatientPerson();
		populate1302Person(person, idmServiceVO);

		boolean sendDeathFields = true;
		boolean isSourceNCA = false;

		if (idmServiceVO.getDeathRecord() != null && idmServiceVO.getDeathRecord().getDataSource() != null &&
				idmServiceVO.getDeathRecord().getDataSource().getCode() != null &&
					idmServiceVO.getDeathRecord().getDataSource().getCode().equals(DeathDataSource.CODE_NCA.getCode())) {
			isSourceNCA = true;
		}


		if (!this.getLookupService().getDODSharingInd() && idmServiceVO.getDeathEvent()) {
			logger.info("Not sending MVI death notification due to sharing indicator disabled");
			return;

		}

		//CCR 13856  - update is death event, and source is NCA, do nothing
		if (idmServiceVO.getDeathEvent() && isSourceNCA) {

			logger.info("Not sending MVI death notification due to source NCA on person: " + idmServiceVO.getVpid().getKeyValueAsString());
			return;

		} else {
			//not a death event, person deceased and source is NCA, send everything but death fields
			if (!idmServiceVO.getDeathEvent() && isSourceNCA)
				 sendDeathFields = false;
		}

		// *** CCR 13856 date of death fields to MVI
		if (this.getLookupService().getDODSharingInd() && sendDeathFields &&
				idmServiceVO.getDeathRecord() != null) {

			String auditUserId = SecurityContextHelper.getSecurityContext()
					.getUserName(true);

			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");

			//lazarus must be handled different as most of the record will be null
			//per irsd, send only hl7v3 null for deletes
			if (idmServiceVO.getDeathRecord().getDeathDate() == null &&
					idmServiceVO.getDeathRecord().getLazarusDate() != null) {

				TS deathTime = person.addNewDeceasedTime();
				deathTime.setNullFlavor("NA");

			} else {
				//it's death add or modify, send it all
				PRPAMT201302UV02Subject4 subjectOf= patient.addNewSubjectOf();
				PRPAMT201302UV02AdministrativeObservation deathObservation = subjectOf.addNewAdministrativeObservation();
				deathObservation.setClassCode("VERIF");

				//ID, per iRSD, send 'VAMC* users as hl7v3 null, only populate actual users
				PRPAMT201302UV02AdministrativeObservationId id = deathObservation.addNewId();
				if (auditUserId.startsWith("VAMC")) {
					id.setNullFlavor("NA");
				} else {
					id.setExtension(auditUserId);
				}

				id.setRoot(ROOT_VA_OID);

				//type of observation
				CD deathCode = deathObservation.addNewCode();
				deathCode.setCode("DOD_SOURCE");
				deathCode.setDisplayName("SOURCE_OF_NOTIFICATION");
				deathCode.setCodeSystem(ROOT_VA_OID);

				ANY notSource = deathObservation.addNewValue();

				if (idmServiceVO.getDeathRecord().getDeathDate() != null) {
					TS deathTime = person.addNewDeceasedTime();
					deathTime.setValue(CommonDateUtils.formatWithTime(idmServiceVO.getDeathRecord().getDeathDate(),"yyyyMMdd"));
					person.setDeceasedTime(deathTime);
				}

				//late update date
				SXCMTS modDate = deathObservation.addNewEffectiveTime();
				modDate.setValue(dateFormat.format(idmServiceVO.getDeathRecord().getDeathReportDate()));

				//notification source is type ANY, must be valid xml obj
				try {
					notSource.set(createDeathSourceFragment(idmServiceVO.getDeathRecord()));
				} catch (XmlException e) {
					throw new ServiceException(e);
				}

			}
		}
		// *** end CCR 13856

		MFMIMT700701UV01Custodian custodian = regEvent.addNewCustodian();

		custodian.setTypeCode("CST");
		COCTMT090003UV01AssignedEntity assignedEntity = custodian
				.addNewAssignedEntity();
		assignedEntity.setClassCode("ASSIGNED");
		assignedEntity.addNewId().setRoot(ROOT_VA_OID);
		COCTMT090003UV01Organization assignedOrg = assignedEntity
				.addNewAssignedOrganization();
		assignedOrg.setClassCode("ORG");
		assignedOrg.setDeterminerCode("INSTANCE");
		EN name = assignedOrg.addNewName();
		name.newCursor().setTextValue(getEsrStationNumber());

		MCCIIN000002UV01Document responseDocument = null;

        if (logger.isDebugEnabled())
            logger.debug("IdmServiceRequest: " + pPRPAIN201302UV02Document);



		try {
			VAIdMStub stub = new VAIdMStub(getIdmServiceEndPoint());
			stub._getServiceClient().getOptions().setTimeOutInMilliSeconds(webServiceTimeout);
			responseDocument = stub
					.PRPA_IN201302UV02(pPRPAIN201302UV02Document);
		} catch (Exception e) {
			throw new ServiceException(e);
		}

        if (logger.isDebugEnabled())
            logger.debug("IdmServiceResponse: " + responseDocument);

		if (responseDocument != null) {
			//get the return code from the server. If it is not AA then there is a problem.
			MCCIMT000200UV01Acknowledgement ack = responseDocument
					.getMCCIIN000002UV01().getAcknowledgementArray()[0];

			String responseCode = ack.getTypeCode().getCode();

			if (!APPLICATION_ACCEPT.equals(responseCode)) {
				String errorMessage = ack.getAcknowledgementDetailArray()[0]
						.getText().newCursor().getTextValue();

				if (logger.isErrorEnabled()) {
					logger.error("parse1302UpdateResponse returns error :"
							+ errorMessage);
				}
				throw new ServiceException(errorMessage);
			}
		}
		return;
	}

	// Create the desired XmlObject and provide it as the test object
	public org.apache.xmlbeans.XmlObject getXmlObject(java.lang.Class type)
			throws ServiceException {

		java.lang.reflect.Method creatorMethod = null;
		try {
			if (org.apache.xmlbeans.XmlObject.class.isAssignableFrom(type)) {
				Class[] declaredClasses = type.getDeclaredClasses();
				for (int i = 0; i < declaredClasses.length; i++) {
					Class declaredClass = declaredClasses[i];
					if (declaredClass.getName().endsWith("$Factory")) {
						creatorMethod = declaredClass.getMethod("newInstance",
								null);
						break;
					}

				}
			}
			if (creatorMethod != null) {
				return (org.apache.xmlbeans.XmlObject) creatorMethod.invoke(
						null, null);
			} else {
				throw new Exception("Creator not found!");
			}
		} catch (Exception e) {
			Throwable t = e.getCause();
			e.printStackTrace();
			while (t.getCause() != null) {
				t = t.getCause();
				t.printStackTrace();
			}
			throw new ServiceException(e);
		}
	}

	/**
	 * <id root="2.16.840.1.113883.4.349" extension="MCID-12345"/>
	 *
	 * @param id
	 */
	private void populateHeaderRoot(II id, String uniqueId) {
		id.setRoot(PATIENT_ID_ROOT);

		id.setExtension("MCID_" + uniqueId);
	}

	/**
	 *
	 * @return unique String in this format
	 * MS[n]_ThreadNumber_YYYYMMDDHHMMSS
	 */
	private String generateUniqueID(Date currentDate) {
		SimpleDateFormat dateFommat = new SimpleDateFormat("yyyyMMddHHmmss");
		String currDateStr = dateFommat.format(currentDate);
		String threadName = Thread.currentThread().getName();
		String serverName = getMsName();

		return serverName + "_" + threadName + "_" + currDateStr;
	}

	private void populateInteractionId(II id, String currentAction) {
		id.setRoot(INTERACTION_ID_ROOT);
		id.setExtension(currentAction);
	}

	private void populateProcessingCode(CS cs) {
		cs.setCode(getProcessingCode());
	}

	private void populateProcessingModeCode(CS cs) {
		cs.setCode(PROCESSING_MODE_CODE);
	}

	private void populateAcceptAckCode(CS cs) {
		cs.setCode(ACCEPT_ACK_CODE);
	}

	private MCCIMT000100UV01Receiver populateReceiver(
			MCCIMT000100UV01Receiver receiver) {
		receiver.setTypeCode(v3.hl7_org.CommunicationFunctionType.Enum
				.forString(REC_TYPE_CODE));
		MCCIMT000100UV01Device device = receiver.addNewDevice();
		device.setClassCode(v3.hl7_org.EntityClassDevice.Enum
				.forString(REC_CLASS_CODE));
		device.setDeterminerCode(REC_DETERMINER_CODE);
		II deviceId = device.addNewId();
		deviceId.setRoot(ROOT_VA_OID);
		return receiver;
	}

	private MCCIMT000100UV01Sender populateSender(MCCIMT000100UV01Sender sender) {
		sender.setTypeCode(v3.hl7_org.CommunicationFunctionType.Enum
				.forString(SND_TYPE_CODE));
		MCCIMT000100UV01Device device = sender.addNewDevice();
		device.setClassCode(v3.hl7_org.EntityClassDevice.Enum
				.forString(SND_CLASS_CODE));
		device.setDeterminerCode(SND_DETERMINER_CODE);
		sender.setDevice(device);
		II deviceId = device.addNewId();
		deviceId.setRoot(ROOT_VA_OID);
		deviceId.setExtension(getEsrStationNumber());
		return sender;
	}

	private void populateScopingOrganization(
			COCTMT150002UV01Organization scopingorganization, String oid) {
		scopingorganization.setClassCode("ORG");
		scopingorganization.setDeterminerCode("INSTANCE");
		II id = scopingorganization.addNewId();
		id.setRoot(oid);
	}

	private void populateCreationTime(TS creationTime, Date currentDate) {
		SimpleDateFormat dateFommat = new SimpleDateFormat("yyyyMMddHHmmss");
		String currDateStr = dateFommat.format(currentDate);
		creationTime.setValue(currDateStr);
	}

	private String getYesNoValue(boolean flag) {
		if (flag) {
			return "Y";
		} else {
			return "N";
		}
	}

	private String getIdMBadAdressString(String code)
	{
		return (String) idmBadAddressTable.get(code);
	}

	/*
	 * initialize idm bad address code and text
	 */
	private static Hashtable initIdmBadAddressTable()
	{
		//A bad address code and descriptive text can be specified in an optional <additionalLocator> element, separated by a caret (^) where bad address code is one of:
		//	VAB1 (bad address - undeliverable)
		//	VAB2 (bad address - homeless)
		//	VAB3 (bad address - other)
		//	VAB4 (bad address - not found)
		//	Descriptive text can be an alphanumeric description that provides further information relating to the bad address code.
		Hashtable tab = new Hashtable();
		tab.put(BadAddressReason.CODE_UNDELIVERABLE.getCode(), "VAB1^[UNDELIVERABLE: Mail was returned or is otherwise known to be undeliverable]");
		tab.put(BadAddressReason.CODE_HOMELESS.getCode(), "VAB2^[HOMELESS: Veteran has no known address]");
		tab.put(BadAddressReason.CODE_OTHER.getCode(), "VAB3^[OTHER: Address could not be shared for some reason other than HOMELESS or UNDELIVERABLE]");
		tab.put(BadAddressReason.CODE_ADDRESS_NOT_FOUND.getCode(), "VAB4^[ADDRESS NOT FOUND: For use by HEC only]");

		return tab;
	}

}
