/********************************************************************
 * Copyright © 2006 VHA. All rights reserved
 ********************************************************************/

package gov.va.med.esr.common.batchprocess.datasync;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;

import gov.va.med.fw.model.AbstractKeyedEntity;
import gov.va.med.fw.service.AbstractComponent;
import gov.va.med.fw.util.Reflector;
import gov.va.med.fw.util.builder.Builder;
import gov.va.med.fw.util.builder.BuilderException;

import gov.va.med.esr.common.model.comms.CommsLogEntry;
import gov.va.med.esr.common.model.comms.CommsTemplate;
import gov.va.med.esr.common.model.ee.AgentOrangeExposure;
import gov.va.med.esr.common.model.ee.Application;
import gov.va.med.esr.common.model.ee.CancelDecline;
import gov.va.med.esr.common.model.ee.CombatEpisode;
import gov.va.med.esr.common.model.ee.EGTSetting;
import gov.va.med.esr.common.model.ee.Eligibility;
import gov.va.med.esr.common.model.ee.EligibilityVerification;
import gov.va.med.esr.common.model.ee.EnrollmentDetermination;
import gov.va.med.esr.common.model.ee.EnrollmentOverride;
import gov.va.med.esr.common.model.ee.EnvironmentalContaminationExposure;
import gov.va.med.esr.common.model.ee.IncompetenceRuling;
import gov.va.med.esr.common.model.ee.IneligibilityFactor;
import gov.va.med.esr.common.model.ee.MedicaidFactor;
import gov.va.med.esr.common.model.ee.MilitaryService;
import gov.va.med.esr.common.model.ee.MonetaryBenefit;
import gov.va.med.esr.common.model.ee.MonetaryBenefitAward;
import gov.va.med.esr.common.model.ee.POWEpisode;
import gov.va.med.esr.common.model.ee.PrisonerOfWar;
import gov.va.med.esr.common.model.ee.PurpleHeart;
import gov.va.med.esr.common.model.ee.PurpleHeartDocument;
import gov.va.med.esr.common.model.ee.RadiationExposure;
import gov.va.med.esr.common.model.ee.RatedDisability;
import gov.va.med.esr.common.model.ee.ServiceConnectionAward;
import gov.va.med.esr.common.model.financials.Asset;
import gov.va.med.esr.common.model.financials.Debt;
import gov.va.med.esr.common.model.financials.DependentFinancials;
import gov.va.med.esr.common.model.financials.Expense;
import gov.va.med.esr.common.model.financials.FinancialInformation;
import gov.va.med.esr.common.model.financials.FinancialStatement;
import gov.va.med.esr.common.model.financials.Income;
import gov.va.med.esr.common.model.financials.IncomeTest;
import gov.va.med.esr.common.model.financials.IncomeTestStatus;
import gov.va.med.esr.common.model.financials.RelationFinancials;
import gov.va.med.esr.common.model.financials.SpouseFinancials;
import gov.va.med.esr.common.model.lookup.AddressType;
import gov.va.med.esr.common.model.lookup.AssetType;
import gov.va.med.esr.common.model.lookup.ComLetterTemplateType;
import gov.va.med.esr.common.model.lookup.ComMailingTriggerType;
import gov.va.med.esr.common.model.lookup.CombatPayType;
import gov.va.med.esr.common.model.lookup.County;
import gov.va.med.esr.common.model.lookup.EligibilityStatus;
import gov.va.med.esr.common.model.lookup.EligibilityType;
import gov.va.med.esr.common.model.lookup.EligibilityVerificationSource;
import gov.va.med.esr.common.model.lookup.EnrollmentStatus;
import gov.va.med.esr.common.model.lookup.ExpenseType;
import gov.va.med.esr.common.model.lookup.IncomeTestType;
import gov.va.med.esr.common.model.lookup.IncomeType;
import gov.va.med.esr.common.model.lookup.Indicator;
import gov.va.med.esr.common.model.lookup.MonetaryBenefitType;
import gov.va.med.esr.common.model.lookup.PhoneType;
import gov.va.med.esr.common.model.lookup.Relationship;
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.party.Address;
import gov.va.med.esr.common.model.party.Phone;
import gov.va.med.esr.common.model.person.Association;
import gov.va.med.esr.common.model.person.DeathRecord;
import gov.va.med.esr.common.model.person.Dependent;
import gov.va.med.esr.common.model.person.EmergencyResponseIndicator;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.Relation;
import gov.va.med.esr.common.model.person.SSN;
import gov.va.med.esr.common.model.person.Spouse;
import gov.va.med.esr.common.model.registry.Registry;
import gov.va.med.esr.common.model.registry.RegistryTrait;
import gov.va.med.esr.common.model.registry.RegistryTraitDetail;
import gov.va.med.esr.common.persistent.comms.CommsTemplateDAO;
import gov.va.med.esr.service.CommsLogService;
import gov.va.med.esr.service.EGTService;
import gov.va.med.esr.service.LookupService;

/**
 * Resonsible for merging data from incoming file onto the on-file Person.
 * 
 * <p>Not so elegant code, but was deemed ok for this temporary process.
 * Another alternative for this could be to investigate Common builders that
 * HL7 Message processing uses.  At the time though, the current approach
 * seemed more straight forward and cohesive (less to remove when this
 * temporary process' lifespan ends).
 * 
 * <p>This builder can only do a "removeAll" while building if we are receiving all
 * the (relevant) data from HECLegacy.  If we are only receiving partial (eg, ESR only
 * receives the permanent address type), then we must preserve the others (eg, can not
 * remove all addresses).
 * 
 * Created Mar 20, 2006 10:17:22 AM
 * 
 * @author DNS   BOHMEG
 */
public class HECLegacyDataSynchronizationConsumerMergeBuilder extends
		AbstractComponent implements Builder {
	
    private static final long serialVersionUID = 9049687444104381827L;

    private static final String SPECIAL_CASE_INCOME_YEAR = HECLegacyDataSynchronizationConsumerMergeBuilder.class.getName()
	+ ".specialCaseIncomeYear";

	private static final String SPECIAL_CASE_ELIGIBILITY = HECLegacyDataSynchronizationConsumerMergeBuilder.class.getName()
	+ ".specialCaseEligibility";

	private static final String SPECIAL_CASE_NEW_COMMS_ENTRIES = HECLegacyDataSynchronizationConsumerMergeBuilder.class.getName()
	+ ".specialCaseNewCommsEntries";

	private static final String SPECIAL_CASE_HEC_ADDRESS = HECLegacyDataSynchronizationConsumerMergeBuilder.class.getName()
	+ ".specialCaseHECAddress";
	
	private LookupService lookupService;

	private CommsLogService commsLogService;	
	private CommsTemplateDAO commsTemplateDAO;  // ugh....per Comms design, this is needed
	
	private EGTService egtService;
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.fw.util.builder.Builder#build(java.lang.Object)
	 */
	public Object build(Object input) throws BuilderException {
		Object[] args = (Object[]) input;
		AbstractKeyedEntity onFileItem = (AbstractKeyedEntity) args[0];
		List acquiredData = (List) args[1];
		Boolean isCommsOnly = (Boolean) args[2];
		Iterator itr = acquiredData.iterator();
		Map specialCaseBookeeping = new HashMap();
		specialCaseBookeeping.put(SPECIAL_CASE_NEW_COMMS_ENTRIES, new ArrayList());
		try {
			while (itr.hasNext()) {
				Reflector.invoke(this, "overlayFileData", new Object[] { onFileItem, itr
						.next(), specialCaseBookeeping });
			}
			
			// now that all values in the file have been dealt with, now deal with cross fileData properties
			if(onFileItem instanceof Person) {
				if (isCommsOnly == null || !isCommsOnly.booleanValue()) {
					// No need if Comms Only
					setEligibilitiesOnEnrollmentDetermination((Person) onFileItem, specialCaseBookeeping);
				}
				createNewCommsLogEntriesWithDemographicAddress((Person) onFileItem, specialCaseBookeeping, isCommsOnly);
			}
			return onFileItem;
		} catch (Exception e) {
			throw new BuilderException(
					"Unable to overlay file data onto on-file Person", e);
		}
	}
	
	private void createNewCommsLogEntriesWithDemographicAddress(Person person, Map specialCaseBookeeping, Boolean isCommsOnly) throws Exception {		
		
		Address address = null;

		List newCommsEntries = (List) specialCaseBookeeping.get(SPECIAL_CASE_NEW_COMMS_ENTRIES);		

		if (newCommsEntries != null && !newCommsEntries.isEmpty()) {
			
			if (isCommsOnly != null && isCommsOnly.booleanValue()) {
				// If Letter change only, use on file person's address
				address = person.getPermanentAddress();
			} else {
				// If UI change, with full set of person data, use the file data
				address = (Address) specialCaseBookeeping.get(SPECIAL_CASE_HEC_ADDRESS);
				if(address == null)
					return;			
			}
			
			Iterator itr = newCommsEntries.iterator();
			CommsLogEntry cle = null;
			while(itr.hasNext()) {
				cle = (CommsLogEntry) itr.next();
				cle.setAddress(address);
				commsLogService.insert(cle);
			}
		}
	}

	/**
	 * This ensures that the EnrollmentDetermination has only the eligibilities that HEC used on it.
	 * @param person
	 * @param specialCaseBookeeping
	 */
	private void setEligibilitiesOnEnrollmentDetermination(Person person, Map specialCaseBookeeping) {
		EnrollmentDetermination ed = person.getEnrollmentDetermination();
		if(ed != null) {
			// remove existing entries (primary)
			Eligibility elig = ed.getPrimaryEligibility();
			if(elig != null) 
				elig.setEnrollmentDetermination(null);
			ed.setPrimaryEligiblity(null);
			// remove existing entries (secondary) - two steps to avoid ConcurrentModificationException
			Set removeMe = new HashSet();
			Iterator itr = ed.getSecondaryEligibilities().iterator();
			while(itr.hasNext()) {
				removeMe.add(itr.next());
			}
			itr = removeMe.iterator();
			while(itr.hasNext()) {
				elig = (Eligibility) itr.next();
				ed.removeSecondaryEligibility(elig);
			}
			// remove existing entries (other) - two steps to avoid ConcurrentModificationException
			removeMe.clear();
			itr = ed.getOtherEligibilities().iterator();
			while(itr.hasNext()) {
				removeMe.add(itr.next());
			}
			itr = removeMe.iterator();			
			while(itr.hasNext()) {
				elig = (Eligibility) itr.next();
				ed.removeOtherEligibility(elig);
			}			
			HECLegacyEligibilityFileData fileData = (HECLegacyEligibilityFileData) specialCaseBookeeping.get(SPECIAL_CASE_ELIGIBILITY);			
			if(fileData != null) {
				// set Primary on EnrollmentDetermination
				EligibilityType type = fileData.getEligibilityCode();
				if(type != null) {								
					elig = new Eligibility();
					elig.setPrimary();
					elig.setType(type);
					ed.setPrimaryEligiblity(elig);
				}
				// set Secondary on EnrollmentDetermination
				Set eligs = fileData.getOtherEligibilityCodes();
				itr = eligs != null ? eligs.iterator() : null;
				while(itr != null && itr.hasNext()) {
					type = (EligibilityType) itr.next();
					elig = new Eligibility();
					elig.setSecondary();
					elig.setType(type);
					ed.addSecondaryEligibility(elig);					
				}
			}
		}
	}
	
	/**
	 * Internal method that is set to public access so external Reflector can access it
	 */	
	public void overlayFileData(Registry registry,
			HECLegacyIdentityFileData fileData, Map specialCaseBookeeping) throws Exception {
	}

	/**
	 * Internal method that is set to public access so external Reflector can access it
	 */		
	public void overlayFileData(PrisonerOfWar registry,
			HECLegacyPOWFileData fileData, Map specialCaseBookeeping) throws Exception {
		RegistryTrait traits = registry.getRegistryTrait();
		if(traits == null) {
			traits = new RegistryTrait();
			registry.setRegistryTrait(traits);
		}
			
		traits.setFirstName(fileData.getLegalName().getGivenName());
		traits.setLastName(fileData.getLegalName().getFamilyName());
		traits.setMiddleName(fileData.getLegalName().getMiddleName());
		traits.setSuffix(fileData.getLegalName().getSuffix());
		traits.setSsn(fileData.getSSN());
				
		registry.removeAllEpisodes();  // TODO: this assumes only POWEpsiode could exist - is this really true?  I don't think so.
		Set episodes = registry.getEpisodes();		
		Iterator itr = episodes != null ? episodes.iterator() : null;
		POWEpisode ep = null;
		boolean found = false;
		while(itr != null && itr.hasNext()) {
			ep = (POWEpisode) itr.next();
			if(ep.getCaptureDate().equals(fileData.getCaptureDate()) && ep.getReleaseDate().equals(fileData.getReleaseDate()) &&
					ep.getConfinementLocation().equals(fileData.getConfinementLocation())) {
				found = true;
				ep.setCampLocation(fileData.getCasualtyPlace());
				ep.setDataSource(fileData.getSource());
				ep.setDateEntered(fileData.getEntered() != null ? fileData.getEntered().getDate() : null);		
				ep.setNumberOfDaysInCaptivity(fileData.getDaysIncarcirated());				
				break;
			}
		}
		if(!found) {
			ep = new POWEpisode();
			ep.setCaptureDate(fileData.getCaptureDate());
			ep.setReleaseDate(fileData.getReleaseDate());
			ep.setConfinementLocation(fileData.getConfinementLocation());
			ep.setCampLocation(fileData.getCasualtyPlace());
			ep.setDataSource(fileData.getSource());
			ep.setDateEntered(fileData.getEntered() != null ? fileData.getEntered().getDate() : null);		
			ep.setNumberOfDaysInCaptivity(fileData.getDaysIncarcirated());
			registry.addEpisode(ep);
		}
		
		registry.setUser(fileData.getUser());
		
		if(isTrue(fileData.getConfirmed())) {
			registry.setPowIndicator(getLookupService().getIndicatorByCode(Indicator.YES));
		} else if(isFalse(fileData.getConfirmed())) {
			registry.setPowIndicator(getLookupService().getIndicatorByCode(Indicator.NO));
		}		
		
		traits.removeAllRegistryTraitDetails(); // ok to do this since we get all the MilitaryServiceNumbers
		itr = fileData.getServiceNumbers().iterator();
		RegistryTraitDetail detail = null;
		while(itr.hasNext()) {
			detail = new RegistryTraitDetail();
			detail.setMilitaryServiceNumber((String) itr.next());
			traits.addRegistryTraitDetail(detail);
		}
	}

	/**
	 * Internal method that is set to public access so external Reflector can access it
	 */		
	public void overlayFileData(PurpleHeart registry,
			HECLegacyPurpleHeartFileData fileData, Map specialCaseBookeeping) throws Exception {
		RegistryTrait traits = registry.getRegistryTrait();
		if(traits == null) {
			traits = new RegistryTrait();
			registry.setRegistryTrait(traits);
		}
		
		traits.setFirstName(fileData.getLegalName().getGivenName());
		traits.setLastName(fileData.getLegalName().getFamilyName());
		traits.setMiddleName(fileData.getLegalName().getMiddleName());
		traits.setSuffix(fileData.getLegalName().getSuffix());
		traits.setPrefix(fileData.getLegalName().getPrefix());
		traits.setSsn(fileData.getSSN());		
		traits.setEducationalDegree(fileData.getDegree());
		registry.setStatus(fileData.getStatus());
		registry.setStatusLastEditDate(fileData.getStatusUpdateDate() != null ? fileData.getStatusUpdateDate().getDate() : null);
		registry.setRejectionRemark(fileData.getRejectedRemarks());
		registry.setStatusLastUpdateUser(fileData.getUpdateUser());
		registry.setFacility(fileData.getSite());
		if(isTrue(fileData.getIndicator()))
			registry.setPhIndicator(Boolean.TRUE);
		else if(isFalse(fileData.getIndicator()))
			registry.setPhIndicator(Boolean.FALSE);
		registry.setAssignedContactUser(fileData.getContactRepAssigned());
		
		// don't remove existing PurpleHeartDocument's since are a history of sorts...
		PurpleHeartDocument doc = new PurpleHeartDocument();
		doc.setDocumentType(fileData.getDocumentType());
		doc.setDataSource(fileData.getDataSource());
		doc.setReceivedDate(fileData.getDocReceivedDate() != null ? fileData.getDocReceivedDate().getDate() : null);
		doc.setLoginDate(fileData.getDocLoggedInDate() != null ? fileData.getDocLoggedInDate().getDate() : null);
		if(isTrue(fileData.getDocumentAcceptable()))
			doc.setAcceptable(Boolean.TRUE);
		else if(isFalse(fileData.getDocumentAcceptable()))
			doc.setAcceptable(Boolean.FALSE);
		doc.setUnacceptableRemarks(fileData.getUnacceptableRemarks());
		registry.addDocument(doc);
	}
	
	/**
	 * Internal method that is set to public access so external Reflector can access it
	 */		
	public void overlayFileData(Person person,
			HECLegacyIdentityFileData fileData, Map specialCaseBookeeping) throws Exception {
		// ignore any IDT data that is owned by PSIM

		// degree is NOT owned by PSIM
		person.setDegree(fileData.getDegree());
	}

	/**
	 * Internal method that is set to public access so external Reflector can access it
	 */		
	public void overlayFileData(Person person,
			HECLegacyCommunicationsFileData fileData, Map specialCaseBookeeping) throws Exception {
		// to be safe
		if(!fileData.getLetterForm().startsWith(ComLetterTemplateType.FORM_NUMBER_PREFIX))
			fileData.setLetterForm(ComLetterTemplateType.FORM_NUMBER_PREFIX + fileData.getLetterForm());
		
		List commsLogEntries = commsLogService.findLogEntriesByPersonId(person
				.getEntityKey().getKeyValueAsString());
		Iterator itr = commsLogEntries != null ? commsLogEntries.iterator() : null;
		CommsLogEntry logEntry = null;		
		
		/*
		 * per CCR7232, match on this logic (knowing it will never create a new letter if one
		 * existed already):
		 * 
		 * For each COM entry, 
 
			1) Get onfile letters for that Person
			2) Match those with same form number
			3) If there is only one, update it.
			4) If there is more than one, match on AACExtractId.  If one matches, update it.
			5) If none match based on AACExtractId, take most recently modified and update it.
		 */
		boolean found = false;
		CommsLogEntry latestOnFileForTheForm = null;
		while (itr != null && itr.hasNext()) {
			logEntry = (CommsLogEntry) itr.next();

			if (logEntry.getFormNumber().equals(fileData.getLetterForm())) {
				found = true;
				// ok, we found a matching CommsLogEntry by form number
				String extractId = logEntry.getAacExtractNumber();				
				if((commsLogEntries.size() == 1) ||					
					(StringUtils.isNotBlank(extractId) && extractId.equals(fileData.getFileName()))) {					
					updateCommsLogEntry(logEntry, fileData);
					latestOnFileForTheForm = null; // reset so later logic does not kick in
					break;
				}
				// if we get here, we know we have more than 1 on file with ESR for that form (use the latest)
				if(latestOnFileForTheForm == null ||
						logEntry.getModifiedOn().after(latestOnFileForTheForm.getModifiedOn())) {
					latestOnFileForTheForm = logEntry;
				}
			}
		}
		
		if(latestOnFileForTheForm != null) {
			updateCommsLogEntry(latestOnFileForTheForm, fileData);
		}
		
		//	unknown form number (or different file for known form number), so create?
		if(!found) {
			try {
				createNewCommsLogEntry(person, fileData, specialCaseBookeeping);
			} catch(Exception e) {
				if(logger.isWarnEnabled())
					logger.warn("Unable to create a new CommsLogEntry for letter form " + fileData.getLetterForm() + " - ignoring this entry", e);
			}
		}
	}
		
	private void updateCommsLogEntry(CommsLogEntry logEntry, HECLegacyCommunicationsFileData fileData)
		throws Exception {
		logEntry.addMailingStatus(lookupService
				.getComMailingStatusTypeByCode(fileData.getStatus()));
		logEntry.addActionComment(fileData.getActionMessage());
		
		if (StringUtils.isBlank(fileData.getRemail()))
			logEntry.setRemailIndicator(null);
		else if (CommsLogEntry.REMAIL_INDICATOR_REMAILED
				.equals(fileData.getRemail()))
			logEntry
					.setRemailIndicator(CommsLogEntry.REMAIL_INDICATOR_REMAILED);
		else if (CommsLogEntry.REMAIL_INDICATOR_RESEND.equals(fileData
				.getRemail()))
			logEntry
					.setRemailIndicator(CommsLogEntry.REMAIL_INDICATOR_RESEND);
		
		commsLogService.update(logEntry);		
	}
		
	private CommsLogEntry createNewCommsLogEntry(Person person, HECLegacyCommunicationsFileData fileData,
			Map specialCaseBookeeping) throws Exception {
		CommsLogEntry cle = new CommsLogEntry();
		cle.setPersonId(new BigDecimal(person.getPersonEntityKey().getKeyValueAsString()));
		cle.setActionComment(fileData.getActionMessage());
		cle.setAacExtractNumber(fileData.getFileName());
		ComLetterTemplateType formNumber = lookupService.getComLetterTemplateTypeByCode(fileData.getLetterForm());
		cle.applyTemplate((CommsTemplate) commsTemplateDAO.findTemplateListByFormNumber(formNumber.getCode()).get(0));
		cle.setName(person.getLegalName());
		
		// have to default this to something
		cle.setComMailingTriggerType(
				lookupService.getComMailingTriggerTypeByCode(ComMailingTriggerType.COM_MAILING_TRIGGER_AUTOMATIC.getCode()));
		
		if(CommsLogEntry.REMAIL_INDICATOR_REMAILED.equals(fileData.getRemail()))
			cle	.setRemailIndicator(CommsLogEntry.REMAIL_INDICATOR_REMAILED);
		else if(CommsLogEntry.REMAIL_INDICATOR_RESEND.equals(fileData.getRemail()))
				cle	.setRemailIndicator(CommsLogEntry.REMAIL_INDICATOR_RESEND);
		
		cle.addMailingStatus(lookupService
				.getComMailingStatusTypeByCode(fileData.getStatus()));
		
		List list = (List) specialCaseBookeeping.get(SPECIAL_CASE_NEW_COMMS_ENTRIES);
		list.add(cle);
		return cle;
	}
	
	/**
	 * Internal method that is set to public access so external Reflector can access it
	 */		
	public void overlayFileData(Person person,
			HECLegacyDemographicsFileData fileData, Map specialCaseBookeeping) throws Exception {
		person.setClaimFolderNumber(fileData.getClaimFolderNumber());
		person.setClaimFolderLocation(fileData.getClaimFolderLocation());
		
		DeathRecord dod = new DeathRecord();
		dod.setDeathDate(fileData.getDateOfDeath());
		dod.setFacilityReceived(fileData.getSiteReportingDeath());
		dod.setDataSource(fileData.getSourceOfDeathNotification());
		dod.setDeathReportDate(fileData.getDateOfDeathLastUpdated() != null ? fileData.getDateOfDeathLastUpdated().getDate() : null);
		person.setDeathRecord(dod);
		
		// for use later
		specialCaseBookeeping.put(SPECIAL_CASE_HEC_ADDRESS, fileData.getAddress());
		
		Address address = person.getPermanentAddress();
		if(address == null) {
			address = new Address();
			address.setType(lookupService.getAddressTypeByCode(AddressType.CODE_PERMANENT_ADDRESS.getCode()));			
			person.addAddress(address);
		}		
		address.setLine1(fileData.getAddress().getLine1());
		address.setLine2(fileData.getAddress().getLine2());
		address.setCity(fileData.getAddress().getCity());
		address.setState(fileData.getAddress().getState());
		address.setZipCode(fileData.getAddress().getZipCode());
		address.setZipPlus4(fileData.getAddress().getZipPlus4());
		setCountyName(fileData.getAddress(), address);
		address.setChangeDate(fileData.getAddress().getChangeDate());
		address.setChangeSource(fileData.getAddress().getChangeSource());
		address.setChangeSite(fileData.getAddress().getChangeSite());
		
		if(StringUtils.isNotBlank(fileData.getResidencePhone())) {
			Phone phone = person.getHomePhone();
			if(phone == null) {
				phone = new Phone();
				phone.setType(lookupService.getPhoneTypeByCode(PhoneType.CODE_HOME.getCode()));
				person.addPhone(phone);
			}
			phone.setPhoneNumber(fileData.getResidencePhone());
		}
		if(StringUtils.isNotBlank(fileData.getWorkPhone())) {
			Phone phone = person.getBusinessPhone();
			if(phone == null) {
				phone = new Phone();
				phone.setType(lookupService.getPhoneTypeByCode(PhoneType.CODE_BUSINESS.getCode()));
				person.addPhone(phone);
			}
			phone.setPhoneNumber(fileData.getWorkPhone());
		}
		
		// first need to determine if the incoming SSN is official/active or pseudo
        // CCR7638 no need to read the SSN information, lets just use the information onfile
//		SSN ssn = null;
//		boolean isPseudoSSN = isTrue(fileData.getPseudoSSN());
//		if(!isPseudoSSN) {
//			ssn = person.getOfficialSsn();
//		} else {
//			ssn = person.getPseudoSsn();
//		}
//		if(ssn == null) {
//			ssn = new SSN();
//			if(!isPseudoSSN)
//				ssn.setType(lookupService.getSSNTypeByCode(SSNType.CODE_ACTIVE.getCode()));
//			else 
//				ssn.setType(lookupService.getSSNTypeByCode(SSNType.CODE_PSEUDO.getCode()));
//			person.addSsn(ssn);
//		}
//		ssn.setSsnText(fileData.getMasterId());
//		ssn.setSsaVerificationDate(fileData.getSSNValidationDate() != null ? fileData.getSSNValidationDate().getDate() : null);
//		ssn.setSsaVerificationStatus(fileData.getSSNVerificationStatus());
		
        
		if(isTrue(fileData.getSensitivityLevel()))
			person.setSensitiveRecord(Boolean.TRUE);
		else if(isFalse(fileData.getSensitivityLevel()))
			person.setSensitiveRecord(Boolean.FALSE);
	}
	
	private void setCountyName(Address incoming) {
		setCountyName(incoming, incoming);
	}
	
	private void setCountyName(Address incoming, Address address) {
		if(StringUtils.isNotBlank(incoming.getCounty()) && StringUtils.isNotBlank(incoming.getState())) {
			String combinedCode = incoming.getCounty() + incoming.getState();
			try {
				County county = lookupService.getCountyByCode(combinedCode);
				address.setCounty(county.getName());
			} catch(Exception e) {
				if(logger.isErrorEnabled())
					logger.error("Unable to determine the county name from countyCode+stateCode combo [" +
							combinedCode + "], setting County to blank", e);
				address.setCounty(StringUtils.EMPTY);
			}
		}		
	}
	
	private boolean isTrue(String val) {
		return "1".equalsIgnoreCase(val) || "y".equalsIgnoreCase(val) || "true".equalsIgnoreCase(val) || "yes".equalsIgnoreCase(val);
	}

	private boolean isFalse(String val) {
		return "0".equalsIgnoreCase(val) || "n".equalsIgnoreCase(val) || "false".equalsIgnoreCase(val) || "no".equalsIgnoreCase(val);
	}
	
	/**
	 * Internal method that is set to public access so external Reflector can access it
	 */		
	public void overlayFileData(Person person,
			HECLegacyCombatFileData fileData, Map specialCaseBookeeping) throws Exception {
		if(fileData.getConflictLocation() == null || fileData.getCombatFromDate() == null || fileData.getCombatToDate() == null) // ignore
			return;

		// TODO: no place to store last modified date from HEC (will be updated with current time from ESR when committed)!		
		Set combats = person.getMilitaryService() != null ? person.getMilitaryService().getCombatEpisodes() : Collections.EMPTY_SET;
		Iterator itr = combats.iterator();
		CombatEpisode onFile = null;
		boolean found = false;
		while(itr.hasNext()) {
			onFile = (CombatEpisode) itr.next();
			// if matching 
			if(fileData.getConflictLocation().equals(onFile.getConflictLocation()) &&
					fileData.getCombatFromDate().equals(onFile.getStartDate()) &&
					fileData.getCombatToDate().equals(onFile.getEndDate())) {
				// match, so update
				found = true;
				if(isTrue(fileData.getHostileFireImminentDangerIndicator()))
					onFile.setCombatPayType(getLookupService().getCombatPayTypeByCode(CombatPayType.CODE_HOSTILE_FIRE_IMMINENT_DANGER));
				else if(isTrue(fileData.getCtzeIndicator()))
					onFile.setCombatPayType(getLookupService().getCombatPayTypeByCode(CombatPayType.CODE_COMBAT_ZONE_TAX_EXCLUSION));				
				onFile.setOEFOIFSource(fileData.getDataSource());
				onFile.setOEFOIFStationNumber(fileData.getFacility());
				break;
			}
		}
		
		// add new one
		if(!found) {
			CombatEpisode ep = new CombatEpisode();
			ep.setConflictLocation(fileData.getConflictLocation());
			ep.setStartDate(fileData.getCombatFromDate());
			ep.setEndDate(fileData.getCombatToDate());
			if(isTrue(fileData.getHostileFireImminentDangerIndicator()))
				ep.setCombatPayType(getLookupService().getCombatPayTypeByCode(CombatPayType.CODE_HOSTILE_FIRE_IMMINENT_DANGER));
			else if(isTrue(fileData.getCtzeIndicator()))
				ep.setCombatPayType(getLookupService().getCombatPayTypeByCode(CombatPayType.CODE_COMBAT_ZONE_TAX_EXCLUSION));				
			ep.setOEFOIFSource(fileData.getDataSource());
			ep.setOEFOIFStationNumber(fileData.getFacility());
			if(person.getMilitaryService() != null) {
				person.getMilitaryService().addCombatEpisode(ep);
			} else {
				MilitaryService ms = new MilitaryService();
				person.setMilitaryService(ms);
				ms.addCombatEpisode(ep);
			}
		}		
	}
	
	/**
	 * Internal method that is set to public access so external Reflector can access it
	 */		
	public void overlayFileData(Person person,
			HECLegacyEligibilityFileData fileData, Map specialCaseBookeeping) throws Exception {
		// save for future use with EnrollmentDetermation
		if(fileData != null)
			specialCaseBookeeping.put(SPECIAL_CASE_ELIGIBILITY, fileData);
		
		EligibilityVerification eligVerif = person.getEligibilityVerification();
		if(eligVerif == null) {
			eligVerif = new EligibilityVerification();
			person.setEligibilityVerification(eligVerif);
		}
		eligVerif.setEligibilityStatus(fileData.getEligibilityStatus());
		if(EligibilityStatus.CODE_VERIFIED.getCode().equals(fileData.getEligibilityStatus() != null ? fileData.getEligibilityStatus().getCode() : null))
			eligVerif.setVerificationSource(lookupService.getEligibilityVerificationSourceByCode(EligibilityVerificationSource.CODE_CEV.getCode()));
		eligVerif.setEligibilityStatusDate(fileData.getEligibilityStatusDate());
		eligVerif.setVerificationMethod(fileData.getEligibilityVerificationMethod());
		
		Iterator itr = null;
		
		ServiceConnectionAward sc = null;
		if(isTrue(fileData.getServiceConnected())) {
			sc = new ServiceConnectionAward();
			sc.setServiceConnectedIndicator(Boolean.TRUE);
			person.setServiceConnectionAward(sc);
		} else if(isFalse(fileData.getServiceConnected())) {
			sc = new ServiceConnectionAward();
			sc.setServiceConnectedIndicator(Boolean.FALSE);
			person.setServiceConnectionAward(sc);
		}
		if(sc != null) {
			sc.setServiceConnectedPercentage(fileData.getServiceConnectedPercentage() != null ?
					new Integer(fileData.getServiceConnectedPercentage().intValue()) : null);
			if(isTrue(fileData.getUnemployable()))
				sc.setUnemployable(Boolean.TRUE);
			else if(isFalse(fileData.getUnemployable()))
				sc.setUnemployable(Boolean.FALSE);
			if(isTrue(fileData.getPAndT()))
				sc.setPermanentAndTotal(Boolean.TRUE);
			else if(isFalse(fileData.getPAndT()))
				sc.setPermanentAndTotal(Boolean.FALSE);
			sc.setPermanentAndTotalEffectiveDate(fileData.getPAndTEffectiveDate() != null ? fileData.getPAndTEffectiveDate().getDate() : null);
			
			sc.removeAllRatedDisability(); // ok to remove all since we get all from HECLegacy
			itr = fileData.getRatedDisabilities().iterator();
			while(itr.hasNext())
				sc.addRatedDisability((RatedDisability) itr.next());
			sc.setCombinedServiceConnectedPercentageEffectiveDate(fileData.getCombinedSCPercentEffectiveDate());
		}
		
		MonetaryBenefitAward mba = new MonetaryBenefitAward();
		mba.setCheckAmount(fileData.getTotalAnnualVACheckAmount());
		person.setMonetaryBenefitAward(mba);
		MonetaryBenefit mb = null; 
		if(isTrue(fileData.getReceivingVAPension())) {
			mb = new MonetaryBenefit();
			mb.setType(lookupService.getMonetaryBenefitTypeByCode(MonetaryBenefitType.CODE_VA_PENSION.getCode()));			
			mb.setMonetaryBenefitIndicator(getLookupService().getIndicatorByCode(Indicator.YES));
			mba.addMonetaryBenefit(mb);
		} 
		if(isTrue(fileData.getReceivingAABenefits())) {
			mb = new MonetaryBenefit();
			mb.setType(lookupService.getMonetaryBenefitTypeByCode(MonetaryBenefitType.CODE_AID_AND_ATTENDANCE.getCode()));
			mb.setMonetaryBenefitIndicator(getLookupService().getIndicatorByCode(Indicator.YES));
			mba.addMonetaryBenefit(mb);
		}
		if(isTrue(fileData.getReceivingHouseholdBenefits())) {
			mb = new MonetaryBenefit();
			mb.setType(lookupService.getMonetaryBenefitTypeByCode(MonetaryBenefitType.CODE_HOUSEBOUND.getCode()));
			mb.setMonetaryBenefitIndicator(getLookupService().getIndicatorByCode(Indicator.YES));
			mba.addMonetaryBenefit(mb);
		}
		if(isTrue(fileData.getReceivingVADisability())) {
			mb = new MonetaryBenefit();
			mb.setType(lookupService.getMonetaryBenefitTypeByCode(MonetaryBenefitType.CODE_DISABILITY_COMPENSATION.getCode()));
			mb.setMonetaryBenefitIndicator(getLookupService().getIndicatorByCode(Indicator.YES));
			mba.addMonetaryBenefit(mb);
		}

		if(isTrue(fileData.getEligibleForMedicaid())) {
			MedicaidFactor mf = new MedicaidFactor();
			mf.setEligibleForMedicaid(Boolean.TRUE);			
			person.setMedicaidFactor(mf);
		} else if(isFalse(fileData.getEligibleForMedicaid())) {
			MedicaidFactor mf = new MedicaidFactor();
			mf.setEligibleForMedicaid(Boolean.FALSE);			
			person.setMedicaidFactor(mf);
		}
		
		MilitaryService ms = person.getMilitaryService();
		if(ms == null) {
			ms = new MilitaryService();
			person.setMilitaryService(ms);
		}
		if(isTrue(fileData.getDischargeDueToDisability())) {
			ms.setDischargeDueToDisability(Boolean.TRUE);			
		} else if(isFalse(fileData.getDischargeDueToDisability())) {
			ms.setDischargeDueToDisability(Boolean.FALSE);			
		}

		if(isTrue(fileData.getMilitaryDisabilityRetirement())) {
			ms.setDisabilityRetirementIndicator(Boolean.TRUE);			
		} else if(isFalse(fileData.getMilitaryDisabilityRetirement())) {
			ms.setDisabilityRetirementIndicator(Boolean.FALSE);			
		}
		
		if(isTrue(fileData.getPowStatusIndicated())) {
			PrisonerOfWar pow = new PrisonerOfWar();
			pow.setPowIndicator(getLookupService().getIndicatorByCode(Indicator.YES));
			person.setPrisonerOfWar(pow);
		} else if(isFalse(fileData.getPowStatusIndicated())) {
			PrisonerOfWar pow = new PrisonerOfWar();
			pow.setPowIndicator(getLookupService().getIndicatorByCode(Indicator.NO));
			person.setPrisonerOfWar(pow);
		}

		if(isTrue(fileData.getRatedIncompetent())) {
			IncompetenceRuling ir = new IncompetenceRuling();
			ir.setIncompetent(Boolean.TRUE);
			person.setIncompetenceRuling(ir);
		} else if(isFalse(fileData.getRatedIncompetent())) {
			IncompetenceRuling ir = new IncompetenceRuling();
			ir.setIncompetent(Boolean.FALSE);
			person.setIncompetenceRuling(ir);
		}		
		
		IneligibilityFactor ifact = new IneligibilityFactor(); 
		ifact.setIneligibleDate(fileData.getIneligibleDate() != null ? fileData.getIneligibleDate().getDate() : null);
		ifact.setReason(fileData.getIneligibleReason());
		ifact.setVaroDecision(fileData.getIneligibleVaroDecision());
		person.setIneligibilityFactor(ifact);
		
		AgentOrangeExposure factor1 = person.getAgentOrangeExposure();		
		if(isTrue(fileData.getExposedToAgentOrange())) {			
			if(factor1 == null) {
				factor1 = new AgentOrangeExposure();				
				person.addSpecialFactor(factor1);
			}
			factor1.setSpecialFactorIndicator(getLookupService().getIndicatorByCode(Indicator.YES));
			factor1.setLocation(fileData.getAgentOrangeExposureLocation());
		} else if(factor1 != null) {
			person.removeSpecialFactor(factor1);
		}
		
		RadiationExposure factor2 = person.getRadiationExposure();		
		if(isTrue(fileData.getRadiationExposureIndicator())) {					
			if(factor2 == null) {
				factor2 = new RadiationExposure();
				person.addSpecialFactor(factor2);
			}
			factor2.setSpecialFactorIndicator(getLookupService().getIndicatorByCode(Indicator.YES));
			factor2.setExposureMethod(fileData.getRadiationExposureMethod());
		} else if(factor2 != null) {
			person.removeSpecialFactor(factor2);
		}
		
		EnvironmentalContaminationExposure factor3 = new EnvironmentalContaminationExposure();		
		if(isTrue(fileData.getEnvironmentalContaminants()) && person.getEnvironmentalContaminationExposure() == null) {			
			factor3.setSpecialFactorIndicator(getLookupService().getIndicatorByCode(Indicator.YES));
			person.addSpecialFactor(factor3);
		} else if(factor3 != null) {
			person.removeSpecialFactor(factor3);
		}
		//Added to Fix CR_7565 to remove the emergency response when the HecLegacy data file has a null value
		Set onFiles = person.getEmergencyResponseIndicators();
        itr = onFiles != null ? onFiles.iterator() : null;
        EmergencyResponseIndicator emer = null;
		if(fileData.getEmergencyResponse() == null){
            while(itr != null && itr.hasNext()) {
                emer = (EmergencyResponseIndicator) itr.next();
                person.removeEmergencyResponseIndicator(emer);
            }
        }
		if(fileData.getEmergencyResponse() != null) {
			// see if have this one on ESR
			boolean found = false;
			while(itr != null && itr.hasNext()) {
				emer = (EmergencyResponseIndicator) itr.next();
				if(emer.getEmergencyResponse().equals(fileData.getEmergencyResponse())) {
					// nothing really to update....just make sure don't add again
					found = true;
					break;
				}
			}
			if(!found) {
				emer = new EmergencyResponseIndicator();
				emer.setEmergencyResponse(fileData.getEmergencyResponse());
				person.addEmergencyResponseIndicator(emer);				
			}
		}
		if(isTrue(fileData.getVeteranIndicator())) {
			person.setVeteran(Boolean.TRUE);
		} else if(isFalse(fileData.getVeteranIndicator())) {
			person.setVeteran(Boolean.FALSE);
		}
	}

	/**
	 * Internal method that is set to public access so external Reflector can access it
	 */		
	public void overlayFileData(Person person,
			HECLegacyEnrollmentFileData fileData, Map specialCaseBookeeping) throws Exception {
		EnrollmentDetermination ed = person.getEnrollmentDetermination();
		if(ed == null) {
			ed = new EnrollmentDetermination();
			person.setEnrollmentDetermination(ed);
		}
		Date effDateOfChange = fileData.getEffectiveDateOfChange() != null ? fileData.getEffectiveDateOfChange().getDate() : null;
		
		ed.setEffectiveDate(effDateOfChange);
		ed.setCalculationSource(fileData.getEnrollmentSource());
		if(fileData.getEnrollmentStatus() != null) {
			ed.setEnrollmentStatus(fileData.getEnrollmentStatus());
			ed.getEnrollmentStatus().setEnrollmentCategory(fileData.getEnrollmentCategory());
		}
		ed.setPriorityGroup(fileData.getEnrollmentPriorityGroup());
		ed.setPrioritySubGroup(fileData.getEnrollmentPrioritySubgroup());
		ed.setEnrollmentDate(fileData.getEnrollmentDate() != null ? fileData.getEnrollmentDate().getDate() : null);
		ed.setEndDate(fileData.getEnrollmentEndDate() != null ? fileData.getEnrollmentEndDate().getDate() : null);
		person.setMostRecentPreferredFacility(fileData.getPreferredFacility());
				
		// not all records will have a reference to an EGT, so this is a conditional check
		if(fileData.getEgtSetting().getEffectiveDate() != null) {			
			// can't assume HEC Legacy used the active one, this could be historical
			EGTSetting egt = egtService.getActiveEGTSetting(fileData.getEgtSetting().getEffectiveDate());
			if(egt != null)
				ed.setEgtSetting(egt);
			else
				throw new IllegalStateException("ESR is unable to find an EGT Setting that was valid for date: " +
						fileData.getEgtSetting().getEffectiveDate());
		} else {
			throw new IllegalStateException("ESR can not create an EnrollmentDetermination because the file does not contain " +
					"an EGTSetting effectiveDate");			
		}
		
		Application app = new Application();
		app.setApplicationDate(fileData.getApplicationDate() != null ? fileData.getApplicationDate().getDate() : null);
		person.setApplication(app);
		
		if(isTrue(fileData.getEnrollmentStatusOverride())) {
			EnrollmentOverride eo = new EnrollmentOverride();
			eo.setOverride(Boolean.TRUE);
			person.setEnrollmentOverride(eo);
		} else if(isFalse(fileData.getEnrollmentStatusOverride())) {
			EnrollmentOverride eo = new EnrollmentOverride();
			eo.setOverride(Boolean.FALSE);
			person.setEnrollmentOverride(eo);
		}
		
		CancelDecline cd = new CancelDecline();
		cd.setReason(fileData.getCancelDeclineReason());
		cd.setRemarks(fileData.getCancelDeclineRemarks());
		cd.setEffectiveDate(effDateOfChange);
		if(fileData.getCancelDeclineReason() != null &&
				EnrollmentStatus.CODE_CANCELLED_DECLINED.getCode().equals(
						fileData.getEnrollmentStatus() != null ? fileData.getEnrollmentStatus().getCode() : null)) {
			cd.setCancelDeclineIndicator(Boolean.TRUE);
		} else {
			cd.setCancelDeclineIndicator(Boolean.FALSE);
		}
		person.setCancelDecline(cd);
	}

	/**
	 * Internal method that is set to public access so external Reflector can access it
	 */		
	public void overlayFileData(Person person,
			HECLegacyDependentFinancialFileData fileData, Map specialCaseBookeeping) throws Exception {
		Integer incomeYear = (Integer) specialCaseBookeeping.get(SPECIAL_CASE_INCOME_YEAR);
		if(incomeYear == null)
			throw new IllegalStateException("Unable to determine IncomeYear for DependentFinancial data");
		FinancialStatement primary = person.getFinancialStatement(incomeYear);
		Relation human = null;
		RelationFinancials relationFin = null;
		if(Relationship.CODE_SPOUSE.getCode().equals(fileData.getRelationship() != null ? fileData.getRelationship().getCode() : null)) {
			SpouseFinancials spouse = new SpouseFinancials();
			relationFin = spouse;
			primary.addSpouseFinancials(spouse);
			Spouse humanSpouse = new Spouse();
			human = humanSpouse;
			spouse.setReportedOn(humanSpouse);
			humanSpouse.setMaidenName(fileData.getMaidenName());
			
			/* amount contributed to spouse can come in on either in FIN-21 or FND-29.
			 * it should be only be on one.....if it is on both, it is considered bad
			 * data and we will take the FND-29 (last one wins)
			 */
			if(fileData.getAmountContributedToSpouse() != null) { // check this first so we don't overwrite FIN-21 val
				// notice this is not an ExpenseType
				primary.setContributionToSpouse(fileData.getAmountContributedToSpouse());				
			}
		} else {
			// assume dependent
			DependentFinancials dep = new DependentFinancials();
			relationFin = dep;
			primary.addDependentFinancials(dep);
			Dependent humanDep = new Dependent();
			human = humanDep;
			dep.setReportedOn(humanDep);
			if(isTrue(fileData.getIncapableOfSelfSupport()))
				dep.setIncapableOfSelfSupport(Boolean.TRUE);
			else if(isFalse(fileData.getIncapableOfSelfSupport()))
				dep.setIncapableOfSelfSupport(Boolean.FALSE);
			if(isTrue(fileData.getContributedToSupport()))
				dep.setContributedToSupport(Boolean.TRUE);
			else if(isFalse(fileData.getContributedToSupport()))
				dep.setContributedToSupport(Boolean.FALSE);
			if(isTrue(fileData.getChildHadIncome()))
				dep.setHasIncome(Boolean.TRUE);
			else if(isFalse(fileData.getChildHadIncome()))
				dep.setHasIncome(Boolean.FALSE);
			if(isTrue(fileData.getIncomeAvailable()))
				dep.setIncomeAvailableToPatient(Boolean.TRUE);
			else if(isFalse(fileData.getIncomeAvailable()))
				dep.setIncomeAvailableToPatient(Boolean.FALSE);														
		}
		
		human.setName(fileData.getName());
		human.setGender(fileData.getGender());		
		human.setDob(fileData.getDateOfBirth());
		
		boolean incomingDependentSSNIsPseudo = fileData.getPseudoSSNReason() != null ? true : false;
		if(StringUtils.isNotBlank(fileData.getSSN())) {
			SSN ssn = new SSN();
			ssn.setSsnText(fileData.getSSN());
			if(!incomingDependentSSNIsPseudo) {
				ssn.setType(this.getLookupService().getSSNTypeByCode(SSNType.CODE_ACTIVE.getCode()));
			} else {
				ssn.setType(this.getLookupService().getSSNTypeByCode(SSNType.CODE_PSEUDO.getCode()));
				ssn.setPseudoSSNReason(fileData.getPseudoSSNReason());
			}
			ssn.setSsaVerificationStatus(this.getLookupService().getSSAVerificationStatusByCode(SSAVerificationStatus.NEW_RECORD.getCode()));
			human.addSsn(ssn);
		}
		
		human.setRelationship(fileData.getRelationship());
		human.setStartDate(fileData.getEffectiveDate());
		
		if(isTrue(fileData.getLivedWithPatient()))
			relationFin.setLivedWithPatient(Boolean.TRUE);
		else if(isFalse(fileData.getLivedWithPatient()))
			relationFin.setLivedWithPatient(Boolean.FALSE);
		
		updateFinancialAmounts(relationFin, fileData.getFinancialAmounts());
	}
	
	private void updateFinancialAmounts(FinancialInformation fin, HECLegacyFinancialAmountsFileData amounts) throws Exception {		
		Income income = null;
		if(amounts.getSocialSecurityNotSSIAmount() != null) {
			income = new Income();
			income.setAmount(amounts.getSocialSecurityNotSSIAmount());
			fin.setIncome(lookupService.getIncomeTypeByCode(IncomeType.INCOME_TYPE_SOCIAL_SECURITY.getCode()), income);
		}
		if(amounts.getUSCivilService() != null) {
			income = new Income();
			income.setAmount(amounts.getUSCivilService());
			fin.setIncome(lookupService.getIncomeTypeByCode(IncomeType.INCOME_TYPE_CIVIL_SERVICE.getCode()), income);
		}
		if(amounts.getUSRailroadRetirement() != null) {
			income = new Income();
			income.setAmount(amounts.getUSRailroadRetirement());
			fin.setIncome(lookupService.getIncomeTypeByCode(IncomeType.INCOME_TYPE_RAILROAD_RETIREMENT.getCode()), income);
		}
		if(amounts.getMilitaryRetirement() != null) {
			income = new Income();
			income.setAmount(amounts.getMilitaryRetirement());
			fin.setIncome(lookupService.getIncomeTypeByCode(IncomeType.INCOME_TYPE_MILITARY_RETIREMENT.getCode()), income);
		}
		if(amounts.getUnemploymentCompensation() != null) {
			income = new Income();
			income.setAmount(amounts.getUnemploymentCompensation());
			fin.setIncome(lookupService.getIncomeTypeByCode(IncomeType.INCOME_TYPE_UNEMPLOYMENT_COMPENSATION.getCode()), income);
		}
		if(amounts.getOtherRetirement() != null) {
			income = new Income();
			income.setAmount(amounts.getOtherRetirement());
			fin.setIncome(lookupService.getIncomeTypeByCode(IncomeType.INCOME_TYPE_OTHER_RETIREMENT.getCode()), income);
		}
		if(amounts.getTotalIncomeFromEmployment() != null) {
			income = new Income();
			income.setAmount(amounts.getTotalIncomeFromEmployment());
			fin.setIncome(lookupService.getIncomeTypeByCode(IncomeType.INCOME_TYPE_TOTAL_INCOME_FROM_EMPLOYMENT.getCode()), income);
		}
		if(amounts.getInterestDividendAnnuity() != null) {
			income = new Income();
			income.setAmount(amounts.getInterestDividendAnnuity());
			fin.setIncome(lookupService.getIncomeTypeByCode(IncomeType.INCOME_TYPE_INTEREST_DIVIDEND_ANNUITY.getCode()), income);
		}
		if(amounts.getWorkersCompBlackLung() != null) {
			income = new Income();
			income.setAmount(amounts.getWorkersCompBlackLung());
			fin.setIncome(lookupService.getIncomeTypeByCode(IncomeType.INCOME_TYPE_WORKERS_COMP_BLACK_LUNG.getCode()), income);
		}
		if(amounts.getAllOtherIncome() != null) {
			income = new Income();
			income.setAmount(amounts.getAllOtherIncome());
			fin.setIncome(lookupService.getIncomeTypeByCode(IncomeType.INCOME_TYPE_TOTAL_ALL_OTHER_INCOME.getCode()), income);
		}

		
		Expense expense = null;
		if(amounts.getMedicalExpenses() != null) {
			expense = new Expense();
			expense.setAmount(amounts.getMedicalExpenses());
			fin.setExpense(lookupService.getExpenseTypeByCode(ExpenseType.EXPENSE_TYPE_ADJUSTED_MEDICAL.getCode()), expense);
		}
		if(amounts.getFuneralAndBurialExpenses() != null) {
			expense = new Expense();
			expense.setAmount(amounts.getFuneralAndBurialExpenses());
			fin.setExpense(lookupService.getExpenseTypeByCode(ExpenseType.EXPENSE_TYPE_FUNERAL_AND_BURIAL.getCode()), expense);
		}
		if(amounts.getEducationExpenses() != null) {
			expense = new Expense();
			expense.setAmount(amounts.getEducationExpenses());
			String educationCode = ExpenseType.EXPENSE_TYPE_COLLEGE_AND_VOCATION.getCode();
			if(fin instanceof RelationFinancials) {
				educationCode = ExpenseType.EXPENSE_TYPE_EDUCATIONAL_EXPENSES_BY_DEPENDENT.getCode();
			}
			fin.setExpense(lookupService.getExpenseTypeByCode(educationCode), expense);
		}
				
		Asset asset = null;
		if(amounts.getCashInBank() != null) {
			asset = new Asset();
			asset.setAmount(amounts.getCashInBank());
			fin.setAsset(lookupService.getAssetTypeByCode(AssetType.CODE_CASH.getCode()), asset);
		}
		if(amounts.getStocksAndBonds() != null) {
			asset = new Asset();
			asset.setAmount(amounts.getStocksAndBonds());
			fin.setAsset(lookupService.getAssetTypeByCode(AssetType.CODE_STOCKS_AND_BONDS.getCode()), asset);
		}
		if(amounts.getRealProperty() != null) {
			asset = new Asset();
			asset.setAmount(amounts.getRealProperty());
			fin.setAsset(lookupService.getAssetTypeByCode(AssetType.CODE_REAL_ESTATE.getCode()), asset);
		}
		if(amounts.getOtherPropertyAndAssets() != null) {
			asset = new Asset();
			asset.setAmount(amounts.getOtherPropertyAndAssets());
			fin.setAsset(lookupService.getAssetTypeByCode(AssetType.CODE_OTHER.getCode()), asset);
		}
		
		Debt debt = null;
		if(amounts.getDebts() != null) {
			debt = new Debt();
			debt.setAmount(amounts.getDebts());
			fin.setDebt(debt);
		}		
	}
	
	
	/**
	 * Internal method that is set to public access so external Reflector can access it
	 */		
	public void overlayFileData(Person person,
			HECLegacyFinancialFileData fileData, Map specialCaseBookeeping) throws Exception {
		specialCaseBookeeping.put(SPECIAL_CASE_INCOME_YEAR, fileData.getIncomeYear());
		FinancialStatement fin = new FinancialStatement();
		fin.setIsPost2005Format(Boolean.FALSE); // HEC does not track this so ESR assumes this value
		fin.setIncomeYear(fileData.getIncomeYear());		
		person.setFinancialStatement(fileData.getIncomeYear(), fin);		
		
		updateFinancialAmounts(fin, fileData.getFinancialAmounts());
		if(isTrue(fileData.getRanchFarmOwner())) {
			Income income = new Income();
			// notice no amount sent from HEC Legacy!
			fin.setIncome(lookupService.getIncomeTypeByCode(IncomeType.INCOME_TYPE_FARM_RANCH_PROPERTY_OR_BUSINESS_INCOME.getCode()), income);
		}
		
		// notice this is not an ExpenseType
		fin.setContributionToSpouse(fileData.getAmountContributedToSpouse());		
		
		if(isTrue(fileData.getMarriedLastCalendarYear()))
			fin.setMarriedLastCalendarYear(Boolean.TRUE);
		else if(isFalse(fileData.getMarriedLastCalendarYear()))
			fin.setMarriedLastCalendarYear(Boolean.FALSE);
		fin.setNumberOfDependentChildren(fileData.getNumberOfDependentChildren());
		
		// can have more than one IncomeTest type in file from HECLegacy
		IncomeTest meansTest = null;
		IncomeTest copayTest = null;
		if(fileData.getMeansTestDate() != null || 
				fileData.getMeansTestStatus() != null ||
				fileData.getMeansTestDeterminedStatus() != null ||
				fileData.getMeansTestAdjudicationDate() != null ||
				fileData.getMeansTestCompletedDate() != null ||
				fileData.getSiteConductingMeansTest() != null) {
			meansTest = new IncomeTest();
			meansTest.setType(lookupService.getIncomeTestTypeByCode(IncomeTestType.CODE_MEANS_TEST.getCode()));
			meansTest.setEffectiveDate(fileData.getMeansTestDate() != null ? fileData.getMeansTestDate().getDate() : null);
			meansTest.setAdjudicationDate(fileData.getMeansTestAdjudicationDate() != null ? fileData.getMeansTestAdjudicationDate().getDate() : null);
			meansTest.setSiteConductingTest(fileData.getSiteConductingMeansTest()); // same for both IncomeTests			
			meansTest.setIncomeTestStatus(lookupService.getIncomeTestTypeByCode(IncomeTestType.CODE_MEANS_TEST.getCode()),
					fileData.getMeansTestStatus(), fileData.getMeansTestDeterminedStatus());
			meansTest.setCompletedDate(fileData.getMeansTestCompletedDate() != null ? fileData.getMeansTestCompletedDate().getDate() : null);
			meansTest.setSource(fileData.getSourceOfPrimaryIncomeTest()); // applies to all types of IncomeTests being sync'ed
			meansTest.setLastEditedDate(fileData.getTestLastEditedDate() != null ? fileData.getTestLastEditedDate().getDate() : null);
		}
		
		if(fileData.getCopayTestDate() != null || 
				fileData.getCopayTestStatus() != null ||
				fileData.getCopayTestDeterminedStatus() != null ||
				fileData.getCopayTestAdjudicationDate() != null ||
				fileData.getCopayTestCompletedDate() != null) {
			copayTest = new IncomeTest();
			copayTest.setType(lookupService.getIncomeTestTypeByCode(IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST.getCode()));
			copayTest.setEffectiveDate(fileData.getCopayTestDate() != null ? fileData.getCopayTestDate().getDate() : null);
			copayTest.setAdjudicationDate(fileData.getCopayTestAdjudicationDate() != null ? fileData.getCopayTestAdjudicationDate().getDate() : null);
			copayTest.setSiteConductingTest(fileData.getSiteConductingMeansTest()); // same for both IncomeTests
			copayTest.setIncomeTestStatus(lookupService.getIncomeTestTypeByCode(IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST.getCode()),
					fileData.getCopayTestStatus(), fileData.getCopayTestDeterminedStatus());
			copayTest.setCompletedDate(fileData.getCopayTestCompletedDate() != null ? fileData.getCopayTestCompletedDate().getDate() : null);
			copayTest.setSource(fileData.getSourceOfPrimaryIncomeTest()); // applies to all types of IncomeTests being sync'ed
			copayTest.setLastEditedDate(fileData.getTestLastEditedDate() != null ? fileData.getTestLastEditedDate().getDate() : null);
			
			if(meansTest != null) {
				// an IncomeTestStatus can only have one non-null "owner" (see IncomeTestStatus.setIncomeTest(...))
				IncomeTestStatus its = copayTest.getIncomeTestStatus(); // set from up above
				its.setIncomeTest(null); // disassociate from CopayTest...
				meansTest.addIncomeTestStatus(its); // ...hook up to MeansTest
			}
		}
				
		if(meansTest != null)  // if have both, only set the other IncomeTest data on meansTest
			populateIncomeTest(meansTest, person, fileData);
		else
			populateIncomeTest(copayTest, person, fileData);
		
		// now set primary income test (should be in file from HECLegacy but is not since no real concept on HECLegacy)
		if(meansTest != null)
			meansTest.setPrimaryIncomeTest(Boolean.TRUE);
		else if(copayTest != null) // else if construct - implies that meansTest is null
			copayTest.setPrimaryIncomeTest(Boolean.TRUE);
			
		// Guardian
		if(fileData.getGuardianType() != null) {
			Association assoc = Association.getAssociationOfType(person.getAssociations(), fileData.getGuardianType().getCode());
			if(assoc == null) {
				assoc = new Association();
				assoc.setType(fileData.getGuardianType());
				person.addAssociation(assoc);
			}
			assoc.setRepresentativeName(fileData.getGuardianName());
			assoc.setOrganizationName(fileData.getGuardianInstitution());
			setCountyName(fileData.getGuardianAddress()); // reset County name
			assoc.setAddress(fileData.getGuardianAddress());
			assoc.setPrimaryPhone(fileData.getGuardianPhone());
			assoc.setRelationship(fileData.getGuardianRelationship());			
		}
		
		if(fileData.getIncompetentCivilRulingDate() != null || fileData.getIncompetentVARulingDate() != null) {
			IncompetenceRuling ruling = new IncompetenceRuling();
			ruling.setCivilRulingDate(fileData.getIncompetentCivilRulingDate());
			ruling.setVaRulingDate(fileData.getIncompetentVARulingDate());
			person.setIncompetenceRuling(ruling);			
		}
	}
	
	private void populateIncomeTest(IncomeTest test, Person person, HECLegacyFinancialFileData fileData) {
		if(test != null) {			
			test.setNetWorth(fileData.getNetWorth());			
			test.setTotalIncome(fileData.getTotalIncome());			
			test.setThresholdA(fileData.getThresholdA());
			test.setTotalNumberOfDependents(fileData.getTotalDependents());			
			
			if(isTrue(fileData.getAgreedToPayDeductible()))
				test.setAgreesToPayDeductible(Boolean.TRUE);
			else if(isFalse(fileData.getAgreedToPayDeductible()))
				test.setAgreesToPayDeductible(Boolean.FALSE);
			
			if(isTrue(fileData.getDeclinesToGiveIncomeInfo()))
				test.setDiscloseFinancialInformation(Boolean.TRUE);
			else if(isFalse(fileData.getDeclinesToGiveIncomeInfo()))
				test.setDiscloseFinancialInformation(Boolean.FALSE);			
			test.setGmtThresholdAmount(fileData.getGMTThreshold());
			setCountyName(fileData.getGMTAddress()); // reset County name
			test.setGmtAddress(fileData.getGMTAddress());
			test.setIncomeYear(fileData.getIncomeYear());
			person.setIncomeTest(fileData.getIncomeYear(), test);			
		}		
	}

	/**
	 * Internal method that is set to public access so external Reflector can access it
	 */		
	public void overlayFileData(Person person, HECLegacyPOWFileData fileData, Map specialCaseBookeeping) throws Exception {
		PrisonerOfWar pow = person.getPrisonerOfWar();
		if(pow == null) {
			pow = new PrisonerOfWar();
			person.setPrisonerOfWar(pow);
		}
		overlayFileData(pow, fileData, specialCaseBookeeping);
	}

	/**
	 * Internal method that is set to public access so external Reflector can access it
	 */		
	public void overlayFileData(Person person,
			HECLegacyPurpleHeartFileData fileData, Map specialCaseBookeeping) throws Exception {
		PurpleHeart ph = person.getPurpleHeart();
		if(ph == null) {
			ph = new PurpleHeart();
			person.addDecoration(ph);
		}
		overlayFileData(ph, fileData, specialCaseBookeeping);
	}

	public void afterPropertiesSet() {
		Validate.notNull(lookupService);
		Validate.notNull(commsLogService);
		Validate.notNull(egtService);
		Validate.notNull(commsTemplateDAO);
	}

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

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

	/**
	 * @return Returns the commsLogService.
	 */
	public CommsLogService getCommsLogService() {
		return commsLogService;
	}

	/**
	 * @param commsLogService The commsLogService to set.
	 */
	public void setCommsLogService(CommsLogService commsLogService) {
		this.commsLogService = commsLogService;
	}

	/**
	 * @return Returns the egtService.
	 */
	public EGTService getEgtService() {
		return egtService;
	}

	/**
	 * @param egtService The egtService to set.
	 */
	public void setEgtService(EGTService egtService) {
		this.egtService = egtService;
	}

	/**
	 * @return Returns the commsTemplateDAO.
	 */
	public CommsTemplateDAO getCommsTemplateDAO() {
		return commsTemplateDAO;
	}

	/**
	 * @param commsTemplateDAO The commsTemplateDAO to set.
	 */
	public void setCommsTemplateDAO(CommsTemplateDAO commsTemplateDAO) {
		this.commsTemplateDAO = commsTemplateDAO;
	}
}
