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


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

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

import org.apache.commons.lang.Validate;

import gov.va.med.fw.batchprocess.DataQueryDetail;
import gov.va.med.fw.batchprocess.DataQueryProcessExecutionContext;
import gov.va.med.fw.batchprocess.ProcessStatistics;
import gov.va.med.fw.batchprocess.TargetDateQueryProcessStatistics;
import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.model.lookup.Lookup;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.DateUtils;

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.comms.MailingStatusLink;
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.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.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.ServiceConnectionAward;
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.Financial;
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.IncomeTest;
import gov.va.med.esr.common.model.financials.SpouseFinancials;
import gov.va.med.esr.common.model.lookup.AssetType;
import gov.va.med.esr.common.model.lookup.AssociationType;
import gov.va.med.esr.common.model.lookup.CombatPayType;
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.RegistryType;
import gov.va.med.esr.common.model.lookup.SSAVerificationStatus;
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.EmergencyResponseIndicator;
import gov.va.med.esr.common.model.person.Name;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.PersonChangeLogEntry;
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.registry.Registry;
import gov.va.med.esr.service.CommsLogService;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.PersonIdentityTraits;
import gov.va.med.esr.service.RegistryService;

/**
 * Producer process for HEC Legacy Data Sync.  Transforms Person and Registry data to
 * writable file data.
 * 
 * Created Apr 7, 2006 3:00:47 PM
 * @author DNS   BOHMEG
 */
public class HECLegacyDataSynchronizationProducerProcess extends
	AbstractDataSynchronizationProducerProcess {
	public static String TRUE = "1";
	public static String FALSE = "0";
	
	private CommsLogService commsLogService;
	private RegistryService registryService;
	private LookupService lookupService;
	
	protected ProcessStatistics createProcessStatistics() {
		return new TargetDateQueryProcessStatistics();
	}
	
    protected void prepareDataQueryDetail(DataQueryProcessExecutionContext context, DataQueryDetail query) {
		// single target date applies to all queries first parameter
		TargetDateQueryProcessStatistics stats = (TargetDateQueryProcessStatistics) context.getProcessStatistics();
		Date targetDate = stats.getTargetDate();
		if(targetDate == null) {
			if(context.getExecutionArguments() != null) {
				Object arg = context.getExecutionArguments();
				// allow for Date or String to be passed
				if(arg instanceof Date)
					targetDate  = (Date) arg;
				else
					targetDate = new ImpreciseDate((String) arg).getDate();
			} else {
				targetDate = DateUtils.getYesterdayDate();
			}
			
			stats.setTargetDate(targetDate);
		}
		Object[] paramValues = query.getQuery().getParamValues();
		paramValues[0] = targetDate;
		query.getQuery().setParamValues(paramValues);
		
		if(query.getQuery().getQuery().indexOf("registryQuery.POW") > -1)
			context.getContextData().put("queryRegistryMode", "POW");
		else if(query.getQuery().getQuery().indexOf("registryQuery.purpleHeart") > -1)
			context.getContextData().put("queryRegistryMode", "PH");
    }
	
	
	protected Object getProcessedObject(DataQueryProcessExecutionContext context, Object acquiredObject) throws ServiceException {
		Object processedObject = null;
		if(acquiredObject instanceof PersonChangeLogEntry) {
			// retrieve Person
			processedObject = super.getProcessedObject(context, acquiredObject);
		} else {
			// assume Registry data ids (see doAcquireData)
			String registryId = (String) acquiredObject;
			RegistryType type = context.getContextData().containsValue("PH") ?
					lookupService.getRegistryTypeByCode(RegistryType.CODE_PH_REGISTRY.getCode()) :
						lookupService.getRegistryTypeByCode(RegistryType.CODE_POW_REGISTRY.getCode());
			EntityKey key = CommonEntityKeyFactory.createRegistryEntityKey(registryId, type);
			processedObject = registryService.getRegistryById(key, type);
		}
		return processedObject;
	}	
	
	protected boolean shouldProcessAcquiredObject(DataQueryProcessExecutionContext context, Object acquiredObject) {
		boolean shouldProcessAcquiredObject = true;
		if(acquiredObject instanceof PersonChangeLogEntry) {
			shouldProcessAcquiredObject = super.shouldProcessAcquiredObject(context, acquiredObject);
		} else {
			// assume Registry data is always ok to process
		}
		return shouldProcessAcquiredObject;
	}
	
	protected Object transformObject(DataQueryProcessExecutionContext context, Object processedObject, Object acquiredObject) throws ServiceException {
		List fileData = new ArrayList();
		if(processedObject instanceof Person) {
			Person person = (Person) processedObject;
			add(fileData, extractIdentityData(person));
			add(fileData, extractCommunicationsData(person));
			add(fileData, extractDemographicsData(person));
			add(fileData, extractEligibilityData(person));
			add(fileData, extractCombatData(person));
			add(fileData, extractEnrollmentData(person));
			add(fileData, extractFinancialData(person));
			add(fileData, extractPOWData(person));
			add(fileData, extractPurpleHeartData(person));			
		} else {
			// assume Registry data
			Registry reg = (Registry) processedObject;
			if(reg.getRegistryTrait() == null)
				throw new IllegalStateException("Unable to transform Registry object since it lacks RegistryTrait's and a RegistryType");
			
			if(RegistryType.CODE_PH_REGISTRY.getCode().equals(reg.getRegistryTrait().getRegistryType().getCode())) {
				add(fileData, extractIdentityData(reg));			
				add(fileData, extractPurpleHeartData((PurpleHeart) reg, null));					
			} else if(RegistryType.CODE_POW_REGISTRY.getCode().equals(reg.getRegistryTrait().getRegistryType().getCode())) {
				add(fileData, extractIdentityData(reg));			
				add(fileData, extractPOWData((PrisonerOfWar) reg, null));										
			}				
		}
		return fileData;
	}
	
	private void add(List list, Object data) {
		if(data != null) {
			if(data instanceof Collection)
				list.addAll((Collection) data);
			else
				list.add(data);
		}
	}
	
	private List extractCommunicationsData(Person person) throws ServiceException {
		List data = new ArrayList();
		List commsLogEntries = commsLogService.findLogEntriesByPersonId(person.getPersonEntityKey().getKeyValueAsString());
		Iterator itr = commsLogEntries != null ? commsLogEntries.iterator() : null;
		CommsLogEntry logEntry = null;
		HECLegacyCommunicationsFileData item = null;
		
		/* since there can be more than one CommsLogEntry of each form type for a Person,
		 * per agreement with HECLegacy and the limitation that they can only have one,
		 * preprocess to find the "latest" (using the mailing status modified date)
		 */
		Map uniqueCommsLogEntries = new HashMap();
		CommsLogEntry best = null;
		while (itr != null && itr.hasNext()) {
			logEntry = (CommsLogEntry) itr.next();
			if(logEntry.getFormNumber() == null)
				continue; // can't use one that has no form number
			
			// get the best and compare creation date 
			best = (CommsLogEntry) uniqueCommsLogEntries.get(logEntry.getFormNumber());
			if(best != null) {
				MailingStatusLink bestStatus = best.getLatestMailingStatus();
				Date bestDateForComparison = bestStatus != null ? bestStatus.getModifiedOn() :
					best.getModifiedOn();
				
				MailingStatusLink targetStatus = logEntry.getLatestMailingStatus();
				Date targetDateForComparison = targetStatus != null ? targetStatus.getModifiedOn() :
					logEntry.getModifiedOn();
				
				if(targetDateForComparison.after(bestDateForComparison))
					uniqueCommsLogEntries.put(logEntry.getFormNumber(), logEntry);
			} else {
				uniqueCommsLogEntries.put(logEntry.getFormNumber(), logEntry);
			}			
		}
		
		itr = uniqueCommsLogEntries.values().iterator();
		while (itr != null && itr.hasNext()) {
			logEntry = (CommsLogEntry) itr.next();
			
			item = new HECLegacyCommunicationsFileData();
			// since HECLegacy can only handle one ActionComment, use the latest
			item.setActionMessage(logEntry.getLatestActionComment());
			item.setLetterForm(logEntry.getFormNumber());
			item.setRemail(logEntry.getRemailIndicator());
			item.setFileName(logEntry.getAacExtractNumber());
			MailingStatusLink status = logEntry.getLatestMailingStatus();
			if(status != null)
				item.setStatus(status.getMailingStatus().getCode());
			data.add(item);
		}
		return data;
	}

	private List extractFinancialData(Person person) {
		List data = new ArrayList();
		Map stmts = person.getFinancialStatements();
		Integer incomeYear = person.getLatestIncomeYear();
		HECLegacyFinancialFileData item = new HECLegacyFinancialFileData();
		// add financial data
		data.add(item);
						
		if(stmts != null && !stmts.isEmpty()) {
			FinancialStatement fs = (FinancialStatement) stmts.get(incomeYear);
			
			item.setIncomeYear(incomeYear);
			item.setFinancialAmounts(extractFinancialInformation(item, fs));
			item.setAmountContributedToSpouse(fs.getContributionToSpouse());
			if(isTrue(fs.getMarriedLastCalendarYear()))			
				item.setMarriedLastCalendarYear(TRUE);
			else if(isFalse(fs.getMarriedLastCalendarYear()))			
				item.setMarriedLastCalendarYear(FALSE);
			
			item.setNumberOfDependentChildren(fs.getNumberOfDependentChildren());
			
			// add dependents financial data
			data.addAll(extractDependentFinancialData(fs, incomeYear));			
		}
		
		IncomeTest test = person.getIncomeTest(incomeYear);
		if(test != null) {
			if(Boolean.TRUE.equals(test.isPrimaryIncomeTest()))
				item.setSourceOfPrimaryIncomeTest(test.getSource());
			
			if(IncomeTestType.CODE_MEANS_TEST.getCode().equals(test.getType().getCode())) {
				item.setMeansTestDate(getImpreciseDate(test.getEffectiveDate()));
				item.setMeansTestStatus(test.getStatus());
				item.setMeansTestAdjudicationDate(getImpreciseDate(test.getAdjudicationDate()));
				item.setMeansTestCompletedDate(getImpreciseDate(test.getCompletedDate()));
				item.setMeansTestDeterminedStatus(test.getDeterminedStatus());
			}
			else if(IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST.getCode().equals(test.getType().getCode())) {
				item.setCopayTestDate(getImpreciseDate(test.getEffectiveDate()));
				item.setCopayTestStatus(test.getStatus());
				item.setCopayTestAdjudicationDate(getImpreciseDate(test.getAdjudicationDate()));
				item.setCopayTestCompletedDate(getImpreciseDate(test.getCompletedDate()));
				item.setCopayTestDeterminedStatus(test.getDeterminedStatus());
			}
			
			item.setTotalIncome(test.getTotalIncome());
			item.setNetWorth(test.getNetWorth());
			if(isTrue(test.getAgreesToPayDeductible()))			
				item.setAgreedToPayDeductible(TRUE);
			else if(isFalse(test.getAgreesToPayDeductible()))			
				item.setAgreedToPayDeductible(FALSE);
			
			item.setThresholdA(test.getThresholdA());
			item.setTotalDependents(test.getTotalNumberOfDependents());
			if(isTrue(test.getDiscloseFinancialInformation()))			
				item.setDeclinesToGiveIncomeInfo(TRUE);
			else if(isFalse(test.getDiscloseFinancialInformation()))			
				item.setDeclinesToGiveIncomeInfo(FALSE);
			
			item.setSiteConductingMeansTest(test.getSiteConductingTest());
			item.setTestLastEditedDate(getImpreciseDate(test.getLastEditedDate()));
			item.setGMTThreshold(test.getGmtThresholdAmount());
			Address a = test.getGmtAddress();
			if(a != null && a.isUSAddress())
				item.setGMTAddress(a);
		}			
		
		IncompetenceRuling ir = person.getIncompetenceRuling();
		if(ir != null) {
			item.setIncompetentCivilRulingDate(ir.getCivilRulingDate());
			item.setIncompetentVARulingDate(ir.getCivilRulingDate());
		}
		
		Set set = person.getAssociations();
		Iterator itr2 = set != null ? set.iterator() : null;
		Association ass = null;
		while(itr2 != null && itr2.hasNext()) {
			ass = (Association) itr2.next();
			if(AssociationType.CODE_GUARDIAN_VA.getCode().equals(ass.getType().getCode()) ||
					AssociationType.CODE_GUARDIAN_CIVIL.getCode().equals(ass.getType().getCode()) ) {
				// TODO: just use first one found since HEC can only handle one
				item.setGuardianType(ass.getType());
				Name repName = ass.getRepresentativeName();
				if(repName != null)
					item.setGuardianName(repName);
				item.setGuardianInstitution(ass.getOrganizationName());
				Address a = ass.getAddress();
				if(a != null && a.isUSAddress())
					item.setGuardianAddress(a);
				item.setGuardianPhone(ass.getPrimaryPhone());
				item.setGuardianRelationship(ass.getRelationship());
				break;
			}
		}
		
		return data;
	}
		
	/**
	 * @param info
	 * @return
	 */
	private HECLegacyFinancialAmountsFileData extractFinancialInformation(HECLegacyFinancialFileData item, FinancialInformation info) {
		HECLegacyFinancialAmountsFileData data = new HECLegacyFinancialAmountsFileData();
		if(info == null)
			return data;
		
		Map items = info.getAssets();
		Iterator itr = items != null ? items.keySet().iterator() : null;
		Lookup type = null;
		while(itr != null && itr.hasNext()) {
			type = (Lookup) itr.next();
			if(AssetType.CODE_CASH.getCode().equals(type.getCode()))
				data.setCashInBank(((Financial) items.get(type)).getAmount());
			else if(AssetType.CODE_STOCKS_AND_BONDS.getCode().equals(type.getCode()))
				data.setStocksAndBonds(((Financial) items.get(type)).getAmount());
			else if(AssetType.CODE_REAL_ESTATE.getCode().equals(type.getCode()))
				data.setRealProperty(((Financial) items.get(type)).getAmount());
			else if(AssetType.CODE_OTHER.getCode().equals(type.getCode()))
				data.setOtherPropertyAndAssets(((Financial) items.get(type)).getAmount());						
		}
		
		items = info.getIncome();
		itr = items != null ? items.keySet().iterator() : null;
		while(itr != null && itr.hasNext()) {
			type = (Lookup) itr.next();
			if(IncomeType.INCOME_TYPE_SOCIAL_SECURITY.getCode().equals(type.getCode()))
				data.setSocialSecurityNotSSIAmount(((Financial) items.get(type)).getAmount());
			else if(IncomeType.INCOME_TYPE_CIVIL_SERVICE.getCode().equals(type.getCode()))
				data.setUSCivilService(((Financial) items.get(type)).getAmount());
			else if(IncomeType.INCOME_TYPE_RAILROAD_RETIREMENT.getCode().equals(type.getCode()))
				data.setUSRailroadRetirement(((Financial) items.get(type)).getAmount());
			else if(IncomeType.INCOME_TYPE_INTEREST_DIVIDEND_ANNUITY.getCode().equals(type.getCode()))
				data.setInterestDividendAnnuity(((Financial) items.get(type)).getAmount());
			else if(IncomeType.INCOME_TYPE_MILITARY_RETIREMENT.getCode().equals(type.getCode()))
				data.setMilitaryRetirement(((Financial) items.get(type)).getAmount());
			else if(IncomeType.INCOME_TYPE_OTHER_RETIREMENT.getCode().equals(type.getCode()))
				data.setOtherRetirement(((Financial) items.get(type)).getAmount());
			else if(IncomeType.INCOME_TYPE_TOTAL_INCOME_FROM_EMPLOYMENT.getCode().equals(type.getCode()))
				data.setTotalIncomeFromEmployment(((Financial) items.get(type)).getAmount());
			else if(IncomeType.INCOME_TYPE_UNEMPLOYMENT_COMPENSATION.getCode().equals(type.getCode()))
				data.setUnemploymentCompensation(((Financial) items.get(type)).getAmount());
			else if(IncomeType.INCOME_TYPE_WORKERS_COMP_BLACK_LUNG.getCode().equals(type.getCode()))
				data.setWorkersCompBlackLung(((Financial) items.get(type)).getAmount());
			else if(IncomeType.INCOME_TYPE_TOTAL_ALL_OTHER_INCOME.getCode().equals(type.getCode()))
				data.setAllOtherIncome(((Financial) items.get(type)).getAmount());
			else if(IncomeType.INCOME_TYPE_FARM_RANCH_PROPERTY_OR_BUSINESS_INCOME.getCode().equals(type.getCode()) &&
					item != null)
				item.setRanchFarmOwner(TRUE); // notice no amount is transmitted and only synced at Person level			
		}
		
		items = info.getExpenses();
		itr = items != null ? items.keySet().iterator() : null;
		while(itr != null && itr.hasNext()) {
			type = (Lookup) itr.next();
			if(ExpenseType.EXPENSE_TYPE_COLLEGE_AND_VOCATION.getCode().equals(type.getCode()))
				data.setEducationExpenses(((Financial) items.get(type)).getAmount());
		}		

		Debt debt = info.getDebt();
		data.setDebts(debt != null ? debt.getAmount() : null);
		
		return data;
	}

	private List extractDependentFinancialData(FinancialStatement fs, Integer incomeYear) {
		List data = new ArrayList();
        SSN ssn = null;
        HECLegacyDependentFinancialFileData item = null;

        Set sfs = fs.getSpouseFinancials();
        Iterator itr = sfs != null ? sfs.iterator() : null;
        SpouseFinancials sf = null;
        for (; itr != null && itr.hasNext();) {
		    sf = (SpouseFinancials)itr.next();
			item = new HECLegacyDependentFinancialFileData();
			item.setIncomeYear(incomeYear);
			item.setFinancialAmounts(extractFinancialInformation(null, sf));
			if(sf.getReportedOn().getName() != null)
				item.setName(sf.getReportedOn().getName());
			item.setGender(sf.getReportedOn().getGender());
			item.setDateOfBirth(sf.getReportedOn().getDob());
			ssn = getTargetSSN(sf.getReportedOn());
			item.setSSN(ssn != null ? ssn.getFormattedSsnText() : null);
			item.setPseudoSSNReason(ssn != null ? ssn.getPseudoSSNReason() : null);
			item.setRelationship(sf.getReportedOn().getRelationship());
			item.setMaidenName(sf.getReportedOn().getMaidenName());
			item.setEffectiveDate(sf.getReportedOn().getStartDate());
			if(isTrue(sf.getLivedWithPatient()))
				item.setLivedWithPatient(TRUE);
			else if(isFalse(sf.getLivedWithPatient()))
				item.setLivedWithPatient(FALSE);			
			data.add(item);
		}
		Set set = fs.getDependentFinancials();
		itr = set != null ? set.iterator() : null;
		DependentFinancials df = null;        
		for (; itr != null && itr.hasNext();) {
			df = (DependentFinancials) itr.next();
			item = new HECLegacyDependentFinancialFileData();
			item.setIncomeYear(incomeYear);
			item.setFinancialAmounts(extractFinancialInformation(null, df));
			if(df.getReportedOn().getName() != null)
				item.setName(df.getReportedOn().getName());
			item.setGender(df.getReportedOn().getGender());
			item.setDateOfBirth(df.getReportedOn().getDob());
			ssn = getTargetSSN(df.getReportedOn());
			item.setSSN(ssn != null ? ssn.getFormattedSsnText() : null);
			item.setPseudoSSNReason(ssn != null ? ssn.getPseudoSSNReason() : null);			
			item.setRelationship(df.getReportedOn().getRelationship());
			item.setEffectiveDate(df.getReportedOn().getStartDate());
			if(isTrue(df.getLivedWithPatient()))
				item.setLivedWithPatient(TRUE);
			else if(isFalse(df.getLivedWithPatient()))
				item.setLivedWithPatient(FALSE);

			if(isTrue(df.getIncapableOfSelfSupport()))
				item.setIncapableOfSelfSupport(TRUE);
			else if(isFalse(df.getIncapableOfSelfSupport()))
				item.setIncapableOfSelfSupport(FALSE);

			if(isTrue(df.getContributedToSupport()))
				item.setContributedToSupport(TRUE);
			else if(isFalse(df.getContributedToSupport()))
				item.setContributedToSupport(FALSE);

			if(isTrue(df.getHasIncome()))
				item.setChildHadIncome(TRUE);
			else if(isFalse(df.getHasIncome()))
				item.setChildHadIncome(FALSE);
			
			if(isTrue(df.getIncomeAvailableToPatient()))
				item.setIncomeAvailable(TRUE);
			else if(isFalse(df.getIncomeAvailableToPatient()))
				item.setIncomeAvailable(FALSE);

			data.add(item);			
		}
		return data;
	}

	private HECLegacyIdentityFileData extractIdentityData(Person person) {
		HECLegacyIdentityFileData data = new HECLegacyIdentityFileData();
		data.setICN(person.getVPIDEntityKey().getShortVPID());
		data.setGender(person.getGender());
		data.setDateOfBirth(person.getBirthRecord() != null ?
				person.getBirthRecord().getBirthDate() : null);
		data.setLegalName(person.getLegalName());
		SSN ssn = getTargetSSN(person);
		data.setSSN(ssn != null ? ssn.getFormattedSsnText() : null);
		return data;
	}
	
	private SSN getTargetSSN(Person person) {
		if(person == null)
			return null;
	
		SSN ssn = person.getOfficialSsn();
		if(ssn == null) {
			ssn = person.getPseudoSsn();
		}
		return ssn;			
	}

	private SSN getTargetSSN(Relation relation) {
		if(relation == null)
			return null;
	
		SSN ssn = relation.getOfficialSsn();
		if(ssn == null) {
			ssn = relation.getPseudoSsn();
		}
		return ssn;			
	}
	
	private HECLegacyIdentityFileData extractIdentityData(Registry reg) {
		HECLegacyIdentityFileData data = new HECLegacyIdentityFileData();
		Name name = new Name();
		name.setFamilyName(reg.getRegistryTrait().getLastName());
		name.setGivenName(reg.getRegistryTrait().getFirstName());
		data.setLegalName(name);
		data.setSSN(reg.getRegistryTrait().getSsn());
		data.setRegistryType(reg.getRegistryTrait().getRegistryType());
		return data;
	}
	
	private HECLegacyDemographicsFileData extractDemographicsData(Person person) {
		HECLegacyDemographicsFileData data = new HECLegacyDemographicsFileData();
		Address homeAddress = person.getPermanentAddress();
		if(homeAddress != null && homeAddress.isUSAddress()) {
			data.setAddress(homeAddress);
			data.setAddressChangedBy(homeAddress.getModifiedBy().getName());
		}
		data.setClaimFolderNumber(person.getClaimFolderNumber());
		data.setClaimFolderLocation(person.getClaimFolderLocation());
		SSN ssn = getTargetSSN(person);
		data.setMasterId(ssn != null ?
				ssn.getFormattedSsnText() : null);
		data.setSSNValidationDate(ssn != null ? getImpreciseDate(ssn.getSsaVerificationDate()) : null);
		SSAVerificationStatus ssaStatus = ssn != null ? ssn.getSsaVerificationStatus() : null;
		data.setSSNVerificationStatus(ssaStatus);
		
		if ((ssn != null) && (ssn.getPseudoSSNReason() != null))
			data.setPseudoSSN(TRUE);
		else
			data.setPseudoSSN(FALSE);
		
		Phone phone = person.getHomePhone();
		data.setResidencePhone(phone != null ? phone.getPhoneNumber() : null);
		phone = person.getBusinessPhone();
		data.setWorkPhone(phone != null ? phone.getPhoneNumber() : null);
		if(isTrue(person.getSensitiveRecord()))			
			data.setSensitivityLevel(TRUE);
		else if(isFalse(person.getSensitiveRecord()))			
			data.setSensitivityLevel(FALSE);
		
		if(person.getDeathRecord() != null) {
			data.setDateOfDeath(person.getDeathRecord().getDeathDate());
			data.setDateOfDeathLastUpdated(getImpreciseDate(person.getDeathRecord().getDeathReportDate()));		
			data.setSiteReportingDeath(person.getDeathRecord().getFacilityReceived());
			data.setSourceOfDeathNotification(person.getDeathRecord().getDataSource());
		}
		return data;
	}

	private List extractCombatData(Person person) {
		List fileData = new ArrayList();
		
		MilitaryService ms = person.getMilitaryService();
		Set combats = ms != null ? ms.getCombatEpisodes() : Collections.EMPTY_SET;
		CombatEpisode combat = null;
		Iterator itr = combats.iterator();
		while(itr.hasNext()) {
			combat = (CombatEpisode) itr.next();
			HECLegacyCombatFileData data = new HECLegacyCombatFileData();
			data.setConflictLocation(combat.getConflictLocation());
			data.setCombatFromDate(combat.getStartDate());
			data.setCombatToDate(combat.getEndDate());
			data.setDataSource(combat.getOEFOIFSource());
			data.setFacility(combat.getOEFOIFStationNumber());
			data.setLastUpdated(combat.getModifiedOn());
 
			if(combat.getCombatPayType() != null && CombatPayType.CODE_HOSTILE_FIRE_IMMINENT_DANGER.getCode().equals(
					combat.getCombatPayType().getCode()))
				data.setHostileFireImminentDangerIndicator(TRUE);
			else
				data.setHostileFireImminentDangerIndicator(FALSE);
			
			if(combat.getCombatPayType() != null && CombatPayType.CODE_COMBAT_ZONE_TAX_EXCLUSION.getCode().equals(
					combat.getCombatPayType().getCode()))
				data.setCtzeIndicator(TRUE);
			else
				data.setCtzeIndicator(FALSE);
			fileData.add(data);
		}
		return fileData;
	}
	
	private HECLegacyEligibilityFileData extractEligibilityData(Person person) {
		HECLegacyEligibilityFileData data = new HECLegacyEligibilityFileData();
		Boolean veteranIndicator = person.getVeteran();
		if(veteranIndicator != null) {
			if(isTrue(veteranIndicator))
				data.setVeteranIndicator(TRUE);
			else
				data.setVeteranIndicator(FALSE);
		}
		
		EligibilityVerification eligVerif = person.getEligibilityVerification();
		if(eligVerif != null) {
			data.setEligibilityVerificationMethod(eligVerif.getVerificationMethod());
			data.setEligibilityStatusDate(eligVerif.getEligibilityStatusDate());
			data.setEligibilityStatus(eligVerif.getEligibilityStatus());
		}
		AgentOrangeExposure aoe = (AgentOrangeExposure) person.getSpecialFactorByType(AgentOrangeExposure.class);
		data.setExposedToAgentOrange(aoe != null && isTrue(aoe.getAgentOrangeExposureIndicator())? TRUE : FALSE);
		data.setAgentOrangeExposureLocation(aoe != null ? aoe.getLocation() : null);
		RadiationExposure re = (RadiationExposure) person.getSpecialFactorByType(RadiationExposure.class);
		data.setRadiationExposureIndicator(re != null && isTrue(re.getRadiationExposureIndicator())? TRUE : FALSE);
		data.setRadiationExposureMethod(re != null ? re.getExposureMethod() : null);
		EnvironmentalContaminationExposure ece = (EnvironmentalContaminationExposure) person.getSpecialFactorByType(EnvironmentalContaminationExposure.class);
		data.setEnvironmentalContaminants(ece != null && isTrue(ece.getEnvironmentalContaminationExposureIndicator())? TRUE : FALSE);
		Set ers = person.getEmergencyResponseIndicators();
		// TODO: assume only one?
		data.setEmergencyResponse(ers != null && !ers.isEmpty() ?
				((EmergencyResponseIndicator) ers.iterator().next()).getEmergencyResponse() : null);
		IneligibilityFactor iFact = person.getIneligibilityFactor();
		if(iFact != null) {
			data.setIneligibleDate(getImpreciseDate(iFact.getIneligibleDate()));
			data.setIneligibleReason(iFact.getReason());
			data.setIneligibleVaroDecision(iFact.getVaroDecision());
		}
		IncompetenceRuling ir = person.getIncompetenceRuling();
		if(ir != null) {
			if(isTrue(ir.getIncompetent()))
				data.setRatedIncompetent(TRUE);
			else if(isFalse(ir.getIncompetent()))
				data.setRatedIncompetent(FALSE);			
		}
		PrisonerOfWar pow = person.getPrisonerOfWar();
		if(pow != null) {
			if(isTrue(pow.getPowIndicator()))
				data.setPowStatusIndicated(TRUE);
			else if(isFalse(pow.getPowIndicator()))
				data.setPowStatusIndicated(FALSE);			
		}
		MonetaryBenefitAward mba = person.getMonetaryBenefitAward();
		data.setTotalAnnualVACheckAmount(mba != null ? mba.getCheckAmount() : null);
		Set set = mba != null ? mba.getMonetaryBenefits() : null;
		Iterator itr = set != null ? set.iterator() : null;
		MonetaryBenefitType mbType = null;
		MonetaryBenefit mb = null;
		while(itr != null && itr.hasNext()) {
			mb = (MonetaryBenefit) itr.next();
			mbType = mb.getType();
			if(mbType == null)
				continue;
			if(MonetaryBenefitType.CODE_DISABILITY_COMPENSATION.getCode().equals(mbType.getCode())){
				if (isTrue(mb.getMonetaryBenefitIndicator()))
					data.setReceivingVADisability(TRUE);
				else
					data.setReceivingVADisability(FALSE);
			}
			else if(MonetaryBenefitType.CODE_AID_AND_ATTENDANCE.getCode().equals(mbType.getCode())){
				if (isTrue(mb.getMonetaryBenefitIndicator()))
					data.setReceivingAABenefits(TRUE);
				else
					data.setReceivingAABenefits(FALSE);				
			}
			else if(MonetaryBenefitType.CODE_HOUSEBOUND.getCode().equals(mbType.getCode())){
				if (isTrue(mb.getMonetaryBenefitIndicator()))
					data.setReceivingHouseholdBenefits(TRUE);
				else {
					data.setReceivingHouseholdBenefits(FALSE);
				}
			}
			else if(MonetaryBenefitType.CODE_VA_PENSION.getCode().equals(mbType.getCode())){
				if (isTrue(mb.getMonetaryBenefitIndicator()))
					data.setReceivingVAPension(TRUE);
				else
					data.setReceivingVAPension(FALSE);
			}
		}
		MilitaryService ms = person.getMilitaryService();
		if(ms != null) {
			if(isTrue(ms.getDisabilityRetirementIndicator()))
				data.setMilitaryDisabilityRetirement(TRUE);
			else if(isFalse(ms.getDisabilityRetirementIndicator()))
				data.setMilitaryDisabilityRetirement(FALSE);
			
			if(isTrue(ms.getDischargeDueToDisability()))
				data.setDischargeDueToDisability(TRUE);
			else if(isFalse(ms.getDischargeDueToDisability()))
				data.setDischargeDueToDisability(FALSE);									
		}
		MedicaidFactor mf = person.getMedicaidFactor();
		if(mf != null) {
			if(isTrue(mf.getEligibleForMedicaid()))
				data.setEligibleForMedicaid(TRUE);
			else if(isFalse(mf.getEligibleForMedicaid()))
				data.setEligibleForMedicaid(FALSE);												
		}
		ServiceConnectionAward sc = person.getServiceConnectionAward();
		if(sc != null) {
			data.setCombinedSCPercentEffectiveDate(sc.getCombinedServiceConnectedPercentageEffectiveDate());
			data.setPAndTEffectiveDate(getImpreciseDate(sc.getPermanentAndTotalEffectiveDate()));
			if(isTrue(sc.getPermanentAndTotal()))
				data.setPAndT(TRUE);
			else if(isFalse(sc.getPermanentAndTotal()))
				data.setPAndT(FALSE);
			
			if(isTrue(sc.getUnemployable()))
				data.setUnemployable(TRUE);
			else if(isFalse(sc.getUnemployable()))
				data.setUnemployable(FALSE);			
			data.setServiceConnectedPercentage(sc.getServiceConnectedPercentage());
			data.setRatedDisabilities(sc.getRatedDisabilities());
			
			if (isTrue(sc.getServiceConnectedIndicator()))
				data.setServiceConnected(TRUE);
			else 
				data.setServiceConnected(FALSE);			
			
		} else {
			data.setServiceConnected(FALSE);
		}
		
		// get eligibility codes from current EnrollmentDetermination
		EnrollmentDetermination ed = person.getEnrollmentDetermination();
		if(ed != null) {
			Eligibility e = ed.getPrimaryEligibility();
			data.setEligibilityCode(e != null ? e.getType() : null);
			
			set = ed.getSecondaryEligibilities();
			itr = set != null ? set.iterator() : null;
			while(itr != null && itr.hasNext()) {
				e = (Eligibility) itr.next();
				data.addOtherEligibilityCode(e.getType());
			}			
		}
		return data;
	}

	private HECLegacyEnrollmentFileData extractEnrollmentData(Person person) {
		HECLegacyEnrollmentFileData data = new HECLegacyEnrollmentFileData();
		EnrollmentDetermination ed = person.getEnrollmentDetermination();
		if(ed != null) {
			data.setEffectiveDateOfChange(getImpreciseDate(ed.getEffectiveDate()));
			data.setEnrollmentSource(ed.getCalculationSource());
			data.setEnrollmentStatus(ed.getEnrollmentStatus());
			if(isTrue(ed.getOverridden()))
				data.setEnrollmentStatusOverride(TRUE);
			else if(isFalse(ed.getOverridden()))
				data.setEnrollmentStatusOverride(FALSE);
			data.setEnrollmentPriorityGroup(ed.getPriorityGroup());
			data.setEnrollmentPrioritySubgroup(ed.getPrioritySubGroup());
			data.setEnrollmentDate(getImpreciseDate(ed.getEnrollmentDate()));
			data.setEnrollmentEndDate(getImpreciseDate(ed.getEndDate()));
			data.setEnrollmentCategory(ed.getEnrollmentStatus() != null ?
					ed.getEnrollmentStatus().getEnrollmentCategory() : null);
			data.setEgtSetting(ed.getEgtSetting());
		}
		Application app = person.getApplication();
		if(app != null)
			data.setApplicationDate(getImpreciseDate(app.getApplicationDate()));
		CancelDecline cd = person.getCancelDecline();
		if(cd != null) {
			data.setCancelDeclineReason(cd.getReason());
			data.setCancelDeclineRemarks(cd.getRemarks());
		}
		data.setPreferredFacility(person.getMostRecentPreferredFacility());
		
		return data;
	}

	private HECLegacyPOWFileData extractPOWData(PrisonerOfWar reg, Person person) {
		HECLegacyPOWFileData data = new HECLegacyPOWFileData();
		Name name = new Name();
		// traits on Person take precedence
		if(person != null) {
			PersonIdentityTraits traits = person.getIdentityTraits();
			data.setLegalName(traits.getLegalName());
			data.setSSN(traits.getSsnText());
			data.setServiceNumbers(person.getMilitaryServiceNumbers());
		} else if(reg.getRegistryTrait() != null) {
			name.setFamilyName(reg.getRegistryTrait().getLastName());
			name.setGivenName(reg.getRegistryTrait().getFirstName());
			name.setMiddleName(reg.getRegistryTrait().getMiddleName());
			name.setSuffix(reg.getRegistryTrait().getSuffix());
			data.setLegalName(name);		
			data.setSSN(reg.getRegistryTrait().getSsn());
			data.setServiceNumbers(reg.getRegistryTrait().getMilitaryServiceNumbersAsSet());
		}		
		if(isTrue(reg.getPowIndicator()))
			data.setConfirmed(TRUE);
		else if(isFalse(reg.getPowIndicator()))
			data.setConfirmed(FALSE);
		data.setEntered(reg.getDateEntered() != null ? new ImpreciseDate(reg.getDateEntered()) : null);
		data.setUser(reg.getUser());		
		
		Set set = reg.getEpisodes();
		// assume only one episode for Registry data
		POWEpisode ep = set != null && !set.isEmpty() ? (POWEpisode) set.iterator().next() : null;
		if(ep != null) {
			data.setCaptureDate(ep.getCaptureDate());
			data.setReleaseDate(ep.getReleaseDate());
			data.setConfinementLocation(ep.getConfinementLocation());
			data.setCasualtyPlace(ep.getCampLocation());
			data.setSource(ep.getDataSource());
			data.setDaysIncarcirated(ep.getNumberOfDaysInCaptivity());
		}
		return data;
	}
		
	private HECLegacyPOWFileData extractPOWData(Person person) {
		PrisonerOfWar pow = person.getPrisonerOfWar();
		// use name and SSN from POW info
		return pow != null ? extractPOWData(pow, person) : null;
	}

	private HECLegacyPurpleHeartFileData extractPurpleHeartData(PurpleHeart reg, PersonIdentityTraits traits) {		
		HECLegacyPurpleHeartFileData data = new HECLegacyPurpleHeartFileData();
			data.setContactRepAssigned(reg.getAssignedContactUser());
		Set docs = reg.getDocuments();
		// assume only one document for Registry data
		PurpleHeartDocument doc = (docs != null && !docs.isEmpty()) ?
				(PurpleHeartDocument) docs.iterator().next() : null;
		if(doc != null) {
			data.setDataSource(doc.getDataSource());
			data.setDocLoggedInDate(getImpreciseDate(doc.getLoginDate()));
			data.setDocReceivedDate(getImpreciseDate(doc.getReceivedDate()));
			if(isTrue(doc.getAcceptable()))
				data.setDocumentAcceptable(TRUE);
			else if(isFalse(doc.getAcceptable()))
				data.setDocumentAcceptable(FALSE);
			data.setDocumentType(doc.getDocumentType());		
			data.setUnacceptableRemarks(doc.getUnacceptableRemarks());
		}
		if(isTrue(reg.getPhIndicator()))
			data.setIndicator(TRUE);
		else if(isFalse(reg.getPhIndicator()))
			data.setIndicator(FALSE);
		// traits on Person take precendence
		if(traits != null) {
			data.setLegalName(traits.getLegalName());
			data.setSSN(traits.getSsnText());
		} else if(reg.getRegistryTrait() != null) {
			Name name = new Name();
			name.setFamilyName(reg.getRegistryTrait().getLastName());
			name.setGivenName(reg.getRegistryTrait().getFirstName());
			name.setMiddleName(reg.getRegistryTrait().getMiddleName());
			name.setSuffix(reg.getRegistryTrait().getSuffix());
			name.setPrefix(reg.getRegistryTrait().getPrefix());
			//name.setDegree(reg.getRegistryTrait().getEducationalDegree());
			data.setLegalName(name);
			data.setDegree(reg.getRegistryTrait().getEducationalDegree());
			data.setSSN(reg.getRegistryTrait().getSsn());
		}
		data.setRejectedRemarks(reg.getRejectionRemark());
		data.setSite(reg.getFacility());		
		data.setStatus(reg.getStatus());
		data.setStatusUpdateDate(getImpreciseDate(reg.getStatusLastEditDate()));			
		data.setUpdateUser(reg.getStatusLastUpdateUser());
		data.setUpdatedDate(getImpreciseDate(reg.getModifiedOn()));
		return data;
	}
	
	private HECLegacyPurpleHeartFileData extractPurpleHeartData(Person person) {
		PurpleHeart ph = person.getPurpleHeart();
		return ph != null ? extractPurpleHeartData(ph, person.getIdentityTraits()) : null;
	}
	
	/**
	 * @return Returns the commsLogService.
	 */
	public CommsLogService getCommsLogService() {
		return commsLogService;
	}

	/**
	 * @param commsLogService The commsLogService to set.
	 */
	public void setCommsLogService(CommsLogService commsLogService) {
		this.commsLogService = commsLogService;
	}	
			
	public void afterPropertiesSet() {
		super.afterPropertiesSet();
		Validate.notNull(commsLogService, "CommsLogService is required");
		Validate.notNull(registryService, "registryService is required");
		Validate.notNull(lookupService, "lookupService is required");
	}


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

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

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

	/**
	 * @param lookupService The lookupService to set.
	 */
	public void setLookupService(LookupService lookupService) {
		this.lookupService = lookupService;
	}
	
	/* These are helper methods that probably can be moved out to some place common */
	private Date getDate(ImpreciseDate date) {
		return date != null ? date.getDate() : null;
	}	
	
	private ImpreciseDate getImpreciseDate(Date date) {
		return date != null ? new ImpreciseDate(date) : null;
	}
	
	private boolean isTrue(Boolean bool) {
		return bool != null && bool.booleanValue();
	}

	private boolean isTrue(Indicator ind) {
		return ind != null && ind.getCode().equals(Indicator.YES.getCode());
	}
	
	private boolean isFalse(Boolean bool) {
		return bool != null && !bool.booleanValue();
	}
	
	private boolean isFalse(Indicator ind) {
		return ind != null && ind.getCode().equals(Indicator.NO.getCode());
	}

	private boolean isUnknown(Boolean bool) {
		return bool == null;
	}
	
	private boolean isUnknown(Indicator ind) {
		return ind != null && ind.getCode().equals(Indicator.UNKNOWN.getCode());
	}		
}

