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

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.Validate;

import gov.va.med.fw.io.RawFileDataList;
import gov.va.med.fw.model.lookup.AbstractCode;
import gov.va.med.fw.model.lookup.Lookup;
import gov.va.med.fw.service.AbstractComponent;
import gov.va.med.fw.util.StringUtils;

import gov.va.med.esr.common.infra.ImpreciseDate;
import gov.va.med.esr.common.infra.ImpreciseDateUtils;
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.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.MonetaryBenefitAward;
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.DependentFinancials;
import gov.va.med.esr.common.model.financials.Expense;
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.Hardship;
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.PatientVisitSummary;
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.insurance.InsurancePolicy;
import gov.va.med.esr.common.model.insurance.Medicare;
import gov.va.med.esr.common.model.insurance.PrivateInsurance;
import gov.va.med.esr.common.model.lookup.EligibilityType;
import gov.va.med.esr.common.model.lookup.ExpenseType;
import gov.va.med.esr.common.model.lookup.IncomeTestSource;
import gov.va.med.esr.common.model.lookup.IncomeType;
import gov.va.med.esr.common.model.lookup.PhoneType;
import gov.va.med.esr.common.model.lookup.SSAVerificationStatus;
import gov.va.med.esr.common.model.lookup.SignatureIndicator;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.messaging.SiteIdentity;
import gov.va.med.esr.common.model.party.Address;
import gov.va.med.esr.common.model.party.Phone;
import gov.va.med.esr.common.model.person.BirthRecord;
import gov.va.med.esr.common.model.person.DeathRecord;
import gov.va.med.esr.common.model.person.Dependent;
import gov.va.med.esr.common.model.person.Name;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.SSN;
import gov.va.med.esr.common.model.person.SignatureImage;
import gov.va.med.esr.common.model.person.Spouse;

/**
 * IVMDataSynchronizationProducer
 * @time   Apr 14, 2006 6:22:43 PM
 * @author DNS   MANSOG
 */
public class IVMDataSynchronizationProducer extends AbstractComponent {

    private static final String MONETARY_BENEFIT_TYPE_TA = "TA";
    private static final String END            = "END";
    private static final String EMPTY_STRING   = "";
    private static final String[] EXPENSE_TYPES = new String[] {
                                ExpenseType.EXPENSE_TYPE_ADJUSTED_MEDICAL.getCode(),
                                ExpenseType.EXPENSE_TYPE_FUNERAL_AND_BURIAL.getCode(), 
                                ExpenseType.EXPENSE_TYPE_COLLEGE_AND_VOCATION.getCode()};
    private static final String FIELD_COUNTY = "county";
    private static final String FIELD_CHILD_RELATIONSHIP = "childRelationship";
    private static final String FIELD_INCOME_TYPE = "incomeType";
    private static final String FIELD_EXPENSE_TYPE = "expenseType";
    private static final String FIELD_ADDRESS_TYPE = "addressType";
    private static final String FIELD_DETERMINED_STATUS= "determinedStatus";
    private static final String FIELD_MEANS_TEST_STATUS= "meansTestStatus";
    private static final String FIELD_SSA_VERIFICATION_STATUS= "ssaVerificationStatus";
    
    private String preciseDateTimeFormat = "MMM dd, yyyy HH:mm:ss";
    private String preciseDateFormat= "MMM dd, yyyy";
    private String impreciseDateFormat ="yyyyMMdd";
    
    /**
     * @see gov.va.med.fw.service.AbstractComponent#afterPropertiesSet()
     */
    public void afterPropertiesSet() throws Exception {
        super.afterPropertiesSet();
        Validate.notNull(preciseDateFormat, "preciseDateFormat is required.");
        Validate.notNull(impreciseDateFormat, "impreciseDateFormat is required.");
        Validate.notNull(preciseDateTimeFormat, "preciseDateTimeFormat is required.");
    }

    /**
     * This method generates a list of RawFileDataContainer Objects for the IVM
     * data sync process. Each element in a returned list will be a data line
     * row.
     * 
     * @param person -
     *            A Person Object
     * @param incomeYear -
     *            A income year
     * @param sites -
     *            Collection of sites visited by veteran
     * @return List of RawFileDataContainer Objects
     */
    public List getIVMRawData(Person person, Integer incomeYear, Set sites) {
        Validate.notNull(person, "A person can not be null.");
        Validate.notNull(incomeYear, "Income year can not be null.");

        List data = new ArrayList();

        data.add(getVeteranData(person));
        addPersonAddressData(data, person, incomeYear, false);
        data.add(getPatientData(person));

        EnrollmentDetermination ed = person.getEnrollmentDetermination();
        Eligibility primaryEligibility = ed == null ? null : ed.getPrimaryEligibility();
        Set secondaryEligibilities = ed == null ? new HashSet() : ed.getSecondaryEligibilities();
        IneligibilityFactor ieFactor = person.getIneligibilityFactor();

        data.add(getAwardData(person, primaryEligibility));
        data.add(getEligibilityData(primaryEligibility, ieFactor));
        for (Iterator iter = secondaryEligibilities.iterator(); iter.hasNext();) {
            data.add(getEligibilityData((Eligibility) iter.next(), null));
        }
        
        EligibilityType eligType = getEligibilityType(primaryEligibility);
        
        //DNS   doank
        //SCN and MON are included when ELI Code is 01 or 03
        //MON is included when ELI code is 02, 04 or 15
        if (EligibilityType.SERVICE_CONNECTED_50_TO_100_PERCENT.getCode().equals(getCode(eligType))
                || EligibilityType.SC_LESS_THAN_50_PERCENT.getCode().equals(getCode(eligType))) {
            data.add(getServiceConnectionData(person));
            data.add(getMonetaryBenefitData(person.getMonetaryBenefitAward()));
        }
        else if(EligibilityType.AID_AND_ATTENDANCE.getCode().equals(getCode(eligType))
        		|| EligibilityType.NSC_VA_PENSION.getCode().equals(getCode(eligType))
        		|| EligibilityType.HOUSEBOUND.getCode().equals(getCode(eligType))){
        	data.add(getMonetaryBenefitData(person.getMonetaryBenefitAward()));
        }
        		
        
        data.addAll(getAllVAMCIdsData(person, sites));
        data.add(getInsuranceData(person));
        Set insurances = person.getInsurances();
        for (Iterator iter = insurances.iterator(); iter.hasNext();) {
            InsurancePolicy insurance = (InsurancePolicy) iter.next();
            data.add(getPolicyData(insurance));
            data.add(getInsurancePlanData(insurance));
            
            Address address = insurance.getAddress();
            Phone phone = insurance.getBusinessPhone();
            if (address != null) {
                address.setStartDate(new ImpreciseDate(new Date()));
                data.add(getAddressData(address));
                data.add(getContactMethodData(address));
            }
            else if(phone != null)
            {
            	address = new Address();
            	data.add(getAddressData(address));
                data.add(getContactMethodData(address));
            }
            addPhone(data, insurance.getBusinessPhone());
        }

        IncomeTest incomeTest = person.getIncomeTest(incomeYear);
        FinancialStatement fStmt = person.getFinancialStatement(incomeYear);
        
        if (incomeTest != null) {
            if (Boolean.TRUE.equals(incomeTest.isPrimaryIncomeTest()))
                addConsentData(data, person.getSignatureImage(incomeYear));

            data.add(getIncomeTestData(incomeTest));
            data.add(getIncomeTestStatusData(incomeTest.getIncomeTestStatus()));
            data.add(getFinancialDataForVeteran(person, incomeTest, fStmt));
            if (fStmt != null) {
                data.addAll(getAllIncomeData(fStmt));
                data.addAll(getVeteranExpenseData(fStmt));
                data.addAll(getAllAssetData(fStmt));
            }
            addFinancialDataForNonPrimary(data, person, incomeTest);
            
            if (fStmt != null) {                
                for (Iterator iter = fStmt.getSpouseFinancials().iterator(); iter.hasNext();) {
                    SpouseFinancials spouseFinancials = (SpouseFinancials)iter.next();    
                    data.add(getSpouseData(spouseFinancials));
                    if (Boolean.TRUE.equals(spouseFinancials.getLivedWithPatient())) {
                        addPersonAddressData(data, person, incomeYear, true);
                    }
                    data.add(getFinancialDataForRelation(incomeTest, spouseFinancials));
                    data.addAll(getAllIncomeData(spouseFinancials));
                    data.addAll(getAllAssetData(spouseFinancials));
                }

                for (Iterator iter = fStmt.getDependentFinancials().iterator(); iter.hasNext();) {
                    DependentFinancials depFinancials = (DependentFinancials) iter.next();
                    data.add(getDependentData(depFinancials));
                    if (Boolean.TRUE.equals(depFinancials.getLivedWithPatient())) {
                        addPersonAddressData(data, person, incomeYear, true);
                    }
                    data.add(getFinancialDataForRelation(incomeTest, depFinancials));
                    data.addAll(getAllIncomeData(depFinancials));
                }
            }
        }
        else {
            // ChangeRequest:5895 
            // When a means test has been deleted in ESR and the vet was previously qualified 
            // for an ESR to IVM update, the ESR to IVM file must contain an indication that the MT has been deleted.  
            // This is to be accomplished by including a FIN line in the vet's record as shown below. 
            // Note that if vet was previously qualified for ESR to IVM update, the VET had a means test at that point
            // FIN^2006^
            List list = new RawFileDataList();
            list.add(IVMDataSynchronizationConstants.FINANCIAL_DATA_TYPE);
            list.add(incomeYear);
            list.add(EMPTY_STRING);
            data.add(list);
        }
        
        data.add(getEndData());
        return data;
    }

    private List getEndData() {
        List list = new RawFileDataList();
        list.add(END);
        list.add(EMPTY_STRING);
        return list;
    }

    private void addPersonAddressData(List data, Person person, Integer incomeYear, boolean isDependent) {
        Address permAddress = person.getPermanentAddress();
        if (permAddress == null) { //this should never happen
            permAddress = new Address(); 
        }
        permAddress.setStartDate(new ImpreciseDate(getIncomeYearStartDate(incomeYear)));
        data.add(getAddressData(permAddress));
        data.add(getContactMethodData(permAddress));        
        Phone homePhone = person.getHomePhone();
        Phone bizPhone = person.getBusinessPhone();
        addPhone(data, homePhone);
        if (!isDependent)
        	addPhone(data, bizPhone);
        Address tempAdd = person.getTemporaryCorrespondenceAddress();
        if (tempAdd != null) {
            data.add(getAddressData(tempAdd));
            data.add(getContactMethodData(tempAdd));
            if (tempAdd.getPhoneNumber() != null) {
                data.add(getTemporaryPhoneData(tempAdd));
            }
        }
    }

    private void addPhone(List data, Phone phone) {
        if (phone != null && phone.getPhoneNumber() != null) {
            data.add(getPhoneData(phone));
        }
    }

    private List getVeteranExpenseData(FinancialInformation finInfo) {
        List expenses = new RawFileDataList();
        for (Iterator iter = finInfo.getExpenses().values().iterator(); iter.hasNext();) {
            Expense exp = (Expense) iter.next();
            String expTypeCode = exp == null ? null : getCode(exp.getType());
            if (exp != null 
                    && expTypeCode!= null 
                    && StringUtils.contains(EXPENSE_TYPES, expTypeCode))
                expenses.add(getExpenseData(exp));
        }
        return expenses;
    }

    private List getAllAssetData(FinancialInformation finInfo) {
        List assets = new RawFileDataList();
        for (Iterator iter = finInfo.getAssets().values().iterator(); iter.hasNext();) {
            assets.add(getAssetData(finInfo, (Asset) iter.next()));
        }
        return assets;
    }

    private List getAllIncomeData(FinancialInformation finInfo) {
        List incomes = new RawFileDataList();
        for (Iterator iter = finInfo.getIncome().values().iterator(); iter.hasNext();) {
            incomes.add(getIncomeData((Income) iter.next()));
        }
        return incomes;
    }

    private List getVeteranData(Person person) {
        RawFileDataList list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.VETERAN_DATA_TYPE);
        list.add(EMPTY_STRING); // IEN
        list.add(EMPTY_STRING); // For CMOR

        addNameData(list, person.getLegalName());
        SSN ssn = person.getOfficialSsn();
        addSsnTextData(list, ssn);

        BirthRecord dob = person.getBirthRecord();
        DeathRecord dod = person.getDeathRecord();

        list.add(getNotNull(person.getGender()));
        list.add(toPreciseDateFormat(dob == null ? null : dob.getBirthDate()));
        list.add(toImpreciseDateFormat(dob == null ? null : dob.getBirthDate()));
        list.add(toPreciseDateFormat(dod == null ? null : dod.getDeathDate()));
        list.add(toImpreciseDateFormat(dod == null ? null : dod.getDeathDate()));

        addSsnStatusData(list, ssn);

        list.add(getNotNull(person.getClaimFolderLocation()));
        list.add(getNotNull(person.getClaimFolderNumber()));
        list.add(to2DigitCode(person.getEmployment() == null ? null: person.getEmployment().getEmploymentStatus()));
        return list;
    }

    private List getAddressData(Address address) {
        RawFileDataList list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.ADDRESS_DATA_TYPE);
        list.add(getNotNull(address.getLine1()));
        list.add(getNotNull(address.getLine2()));
        list.add(getNotNull(address.getLine3()));
        list.add(getNotNull(address.getCity()));
        list.add(getNotNull(address.getState()));
        list.add(getNotNull(address.getZipCode()));
        list.addValueToBeTransformed(FIELD_COUNTY, address);
        if (address.getType() == null || address.getType().getCode() == null) {
            list.add(EMPTY_STRING);
        }
        else {
            list.addValueToBeTransformed(FIELD_ADDRESS_TYPE, address.getType().getCode());
        }
        list.add(toPreciseDateTimeFormat(address.getModifiedOn()));
        list.add(toPreciseDateFormat(address.getStartDate()));
        list.add(toImpreciseDateFormat(address.getStartDate()));
        list.add(toPreciseDateFormat(address.getEndDate()));
        list.add(toImpreciseDateFormat(address.getEndDate()));
        return list;
    }

    private List getContactMethodData(Address address) {
        if (address == null)
            address = new Address();

        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.CONTACT_MECHANISM_DATA_TYPE);
        list.add(IVMDataSynchronizationConstants.CONTACT_METHOD_TYPE_CODE);
        list.add(toPreciseDateTimeFormat(address.getModifiedOn()));
        return list;
    }

    private List getPhoneData(Phone phone) {
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.TELECOM_DATA_TYPE);
        list.add(to2DigitCode(phone.getType()));
        list.add(getNotNull(phone.getPhoneNumber()));
        list.add(toPreciseDateTimeFormat(phone.getModifiedOn()));
        return list;
    }

    private List getTemporaryPhoneData(Address address) {
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.TELECOM_DATA_TYPE);
        list.add(to2DigitCode(PhoneType.CODE_HOME));
        list.add(getNotNull(address.getPhoneNumber()));
        list.add(toPreciseDateTimeFormat(address.getModifiedOn()));
        return list;
    }

    private List getPatientData(Person person) {
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.PATIENT_DATA_TYPE);        
        String vpid = getNotNull(person.getVPIDEntityKey().getShortVPID());
        list.add(vpid.length() >= 10 ? vpid.substring(0, 10) : vpid);
        list.add(getIcnCheckSum(vpid));
        return list;
    }

    private String getIcnCheckSum(String vpid) {
        int indexOfV = vpid.indexOf("V");
        String icnCheckSum = "";
        if (indexOfV > -1) {
            icnCheckSum = vpid.length() >= indexOfV + 7 ? vpid.substring(indexOfV+1, indexOfV + 7) : vpid
                    .substring(indexOfV);
        }
        return icnCheckSum;
    }

    private List getAwardData(Person person, Eligibility primaryEligibility) {
        IncompetenceRuling ruling = person.getIncompetenceRuling();
        ServiceConnectionAward scAward = person.getServiceConnectionAward();
        EligibilityVerification ev = person.getEligibilityVerification();

        Date reportDate = primaryEligibility == null ? null : primaryEligibility.getModifiedOn();
        
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.AWARD_DATA_TYPE);
        list.add(getNotNull(ev == null ? null : ev.getEligibilityStatus()));
        list.add(toPreciseDateFormat(reportDate));
        list.add(getIndicator(ruling == null ? null : ruling.getIncompetent()));
        list.add(getIndicator(scAward == null ? null : scAward.getServiceConnectedIndicator()));
        return list;
    }

    private List getEligibilityData(Eligibility eligibility, IneligibilityFactor ieFactor) {
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.ELIGIBILITY_DATA_TYPE);
        list.add(to2DigitCode(getEligibilityType(eligibility)));
        list.add(toPreciseDateTimeFormat(eligibility == null ? null : eligibility.getModifiedOn()));
        list.add(getNotNull(eligibility == null ? null : eligibility.getIndicator()));
        Date ineligibleDate = ieFactor == null ? null : ieFactor.getIneligibleDate();
        list.add(toPreciseDateFormat(ineligibleDate));
        list.add(toImpreciseDateFormat(ineligibleDate));
        list.add(getNotNull(ieFactor == null ? null : ieFactor.getReason()));
        return list;
    }

    private EligibilityType getEligibilityType(Eligibility eligibility) {
        return  eligibility == null ? null : eligibility.getType();
    }

    private List getServiceConnectionData(Person person) {
        ServiceConnectionAward scAward = person.getServiceConnectionAward();
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.SERVICE_CONNECTION_DATA_TYPE);
        list.add(getNotNull(scAward == null ? null : scAward.getServiceConnectedPercentage()));
        list.add(toPreciseDateTimeFormat(scAward == null ? null : scAward.getModifiedOn()));
        return list;

    }

    private List getMonetaryBenefitData(MonetaryBenefitAward award) {
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.MONETARY_BENEFIT_DATA_TYPE);
        list.add(toPreciseDateTimeFormat(award == null ? null : award.getModifiedOn()));
        list.add(MONETARY_BENEFIT_TYPE_TA);
        list.add(getNotNull(award == null ? null : award.getCheckAmount()));
        return list;
    }

    private List getAllVAMCIdsData(Person person, Set sites) {
        List vamcList = new RawFileDataList();
        for (Iterator iter = sites.iterator(); iter.hasNext();) {
            vamcList.add(getVAMCIdData(person, (SiteIdentity) iter.next()));
        }
        return vamcList;
    }

    private List getVAMCIdData(Person person, SiteIdentity siteIdentity) {
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.VAMC_ID_DATA_TYPE);
        VAFacility facility = siteIdentity.getVaFacility();
        list.add(getNotNull(facility.getStationNumber()));
        list.add(getNotNull(siteIdentity.getDfn()));
        PatientVisitSummary summary = PatientVisitSummary.getLastestVisitedSummary(person
                .getPatientVisitSummaries(facility));
        list.add(toPreciseDateFormat(summary == null ? null : summary.getLastVisitDate()));
        list.add(toPreciseDateTimeFormat(summary == null ? null : summary.getModifiedOn()));
        return list;
    }

    private List getInsuranceData(Person person) {
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.INSURANCE_DATA_TYPE);

        InsurancePolicy insurance = getActiveInsurance(person.getInsurances());
        list.add(getIndicator(insurance != null));
        
        MedicaidFactor medicaidFactor = person.getMedicaidFactor();
        list.add(getIndicator(medicaidFactor == null ? null : medicaidFactor.getEligibleForMedicaid()));
        
        Date reportDate = insurance != null 
                          ? insurance.getModifiedOn() 
                          : (medicaidFactor != null 
                                  ? medicaidFactor.getModifiedOn() 
                                  : new Date());
        list.add(toPreciseDateTimeFormat(reportDate));
        return list;
    }
    
    private InsurancePolicy getActiveInsurance(Set insurances) {
        if (insurances == null || insurances.size() == 0) {
            return null;
        }
        for (Iterator iter = insurances.iterator(); iter.hasNext();) {
            InsurancePolicy insurance = (InsurancePolicy) iter.next();
            if (insurance instanceof Medicare) {
                return insurance;
            }
            if (insurance instanceof PrivateInsurance) {
                PrivateInsurance pi = (PrivateInsurance)insurance;
                Date effectiveDate = ImpreciseDateUtils.getDateWithDefault(pi.getPolicyEffectiveDate());                
                Date expiryDate = ImpreciseDateUtils.getDateWithDefault(pi.getPolicyExpirationDate());
                Date now = new Date();
                if (effectiveDate != null 
                        && effectiveDate.compareTo(now) <=0 
                        && expiryDate != null 
                        && expiryDate.compareTo(now) > 0) {
                    
                    return insurance;
                }
            }            
        }
        return null;
    }

    private List getPolicyData(InsurancePolicy insurance) {
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.POLICY_DATA_TYPE);
        
        ImpreciseDate effDate = null;
        ImpreciseDate expDate = null;
        
        if (insurance instanceof PrivateInsurance) {
        	PrivateInsurance pi = (PrivateInsurance) insurance;
        	effDate = pi == null ? null : pi.getPolicyEffectiveDate();
        	expDate = pi == null ? null : pi.getPolicyExpirationDate();
        }
        else if(insurance instanceof Medicare){
        	Medicare me = (Medicare) insurance;
        	//Use PartA effective date, otherwise use PartB effective date, otherwise use empty value
        	expDate = null;
        	effDate = null;
        	
        	// if (me.getPartAEffectiveDate() != null)
        	//	effDate = new ImpreciseDate(me.getPartAEffectiveDate());
        	// else if (me.getPartBEffectiveDate() != null)
        	// 	effDate = new ImpreciseDate(me.getPartBEffectiveDate());
        	
        	if (me!=null){
        		if (me.getPartAEffectiveDate()!=null)
        			effDate = me.getPartAEffectiveDate();
        		else if (me.getPartBEffectiveDate()!=null)
        			effDate = me.getPartBEffectiveDate();
        	}
        }
        
        list.add(getNotNull(insurance.getCompanyName()));
        list.add(toPreciseDateTimeFormat(insurance.getModifiedOn()));
        list.add(toPreciseDateFormat(effDate));
        list.add(toImpreciseDateFormat(effDate));
        list.add(toPreciseDateFormat(expDate));
        list.add(toImpreciseDateFormat(expDate));
        return list;
    }

    private List getInsurancePlanData(InsurancePolicy insurance) {
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.INSURE_PLAN_DATA_TYPE);
        list.add(getNotNull(insurance.getGroupName()));
        list.add(getNotNull(insurance.getGroupNumber()));
        list.add(toPreciseDateTimeFormat(insurance.getModifiedOn()));

        return list;
    }

    private void addConsentData(List data, SignatureImage sigImage) {
        if (sigImage == null) {
            return;
        }
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.CONSENT_DATA_TYPE);
        list.add(getNotNull(sigImage.getSiteSubmittingImage()));
        
        // start REEG_00006730
        SignatureIndicator indicator = sigImage.getSignatureIndicator();
        String indicatorVal = getNotNull( indicator );
        String translation = indicatorVal.equals( "0" ) ? indicatorVal : to2DigitCode( indicator );
        list.add( translation );
        // end REEG_00006730
        
        list.add(getNotNull(sigImage.getImageIdNumber()));
        list.add(toPreciseDateTimeFormat(sigImage.getModifiedOn()));
        list.add(toPreciseDateFormat(sigImage.getDateIndex()));

        data.add(list);
    }

    private List getIncomeTestData(IncomeTest incomeTest) {
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.INCOME_TEST_DATA_TYPE);
        list.add(toPreciseDateFormat(incomeTest.getEffectiveDate()));
        list.add(toPreciseDateTimeFormat(incomeTest.getCompletedDate()));
        IncomeTestSource source = incomeTest.getSource();
        list.add(to2DigitCode(source));
        list.add(getNotNull(incomeTest.getTotalNumberOfDependents()));
        list.add(to2DigitCode(incomeTest.getType()));
        list.add(getNotNull(incomeTest.getThresholdA()));

        Hardship hardship = incomeTest.getHardship();
        if (hardship == null) {
            hardship = new Hardship();
        }
        list.add(toPreciseDateFormat(hardship.getReviewDate()));
        list.add(getNotNull(incomeTest.getNetWorth()));
        list.add(toPreciseDateFormat(hardship.getEffectiveDate()));
        list.add(getNotNull(getSiteConductingTest(incomeTest)));
        list.add(getNotNull(hardship.getSiteGrantingHardship()));
        list.add(toPreciseDateTimeFormat(incomeTest.getLastEditedDate()));
        list.add(getNotNull(incomeTest.getDeductibleExpenses()));
        return list;
    }

    private List getIncomeTestStatusData(IncomeTestStatus status) {
        if (status == null ) { //should never happen, but just precautionary
            status = new IncomeTestStatus();
        }
        RawFileDataList list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.INCOME_TEST_STATUS_DATA_TYPE);        
        list.addValueToBeTransformed(FIELD_DETERMINED_STATUS, getCode(status.getDeterminedStatus()));
        list.add(to2DigitCode(status.getType()));
        list.addValueToBeTransformed(FIELD_MEANS_TEST_STATUS, getCode(status.getStatus()));
        list.add(toPreciseDateTimeFormat(status.getModifiedOn()));
        return list;
    }

    private List getFinancialDataForVeteran(Person person, IncomeTest incomeTest, FinancialStatement fStmt) {
        String farmRanchIncomeInd = exists(Income.getIncomeOfType(fStmt.getIncome().values(),
                IncomeType.INCOME_TYPE_FARM_RANCH_PROPERTY_OR_BUSINESS_INCOME));
        String testSiteCode = getSiteConductingTest(incomeTest);
        PatientVisitSummary summary = person.getPatientVisitSummary(incomeTest.getIncomeYear(), incomeTest.getSiteConductingTest());
        Boolean marriedLastCalendarYear = fStmt == null ? null : fStmt.getMarriedLastCalendarYear();
        Date modifiedOn = summary == null ? incomeTest.getModifiedOn() : summary.getModifiedOn();
        return getFinancialsData(incomeTest.getIncomeYear(), modifiedOn, testSiteCode, null, marriedLastCalendarYear, farmRanchIncomeInd, summary);
    }

    private void addFinancialDataForNonPrimary(List list, Person person, IncomeTest incomeTest) {
        Set summaries = person.getPatientVisitSummaries(incomeTest.getIncomeYear());
        String testSiteCode = getSiteConductingTest(incomeTest);
        
        for (Iterator iter = summaries.iterator(); iter.hasNext();) {
            PatientVisitSummary summary = (PatientVisitSummary) iter.next();
            VAFacility facilityVisited = summary.getFacilityVisited();
            String facilityVisitedCode =  facilityVisited == null ? "" : facilityVisited.getCode();
            if (!facilityVisitedCode.equals(testSiteCode)) {
                list.add(getFinancialsData(incomeTest.getIncomeYear(), summary.getModifiedOn(), facilityVisitedCode, null, null, null, summary));
            }
        }
    }

    private List getFinancialDataForRelation(IncomeTest incomeTest, RelationFinancials relationFin) {
        String site = getSiteConductingTest(incomeTest);  
        return getFinancialsData(incomeTest.getIncomeYear(), relationFin.getModifiedOn(), site, 
                relationFin.getLivedWithPatient(), null, null, null);
    }

    private List getFinancialsData(Integer incomeYear, Date reportDate, String siteCode,
            Boolean livedWithPatient, Boolean marriedLastYear, String farmRanchIncomeInd,
            PatientVisitSummary visitSummary) {
        List list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.FINANCIAL_DATA_TYPE);
        list.add(getNotNull(incomeYear));
        list.add(toPreciseDateTimeFormat(reportDate));
        list.add(getNotNull(siteCode));
        list.add(getIndicator(livedWithPatient));
        list.add(getIndicator(marriedLastYear));
        list.add(getNotNull(farmRanchIncomeInd));
        list.add(getNotNull(visitSummary == null ? null : visitSummary.getInpatientDays()));
        list.add(getNotNull(visitSummary == null ? null : visitSummary.getOutpatientDays()));
        list.add(toPreciseDateFormat(visitSummary == null ? null : visitSummary.getLastVisitDate()));

        return list;
    }

    private List getIncomeData(Income income) {
        RawFileDataList list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.INCOME_DATA_TYPE);
        addFinancialData(list, income, FIELD_INCOME_TYPE, income == null ? null : getCode(income.getType()));
        return list;
    }

    private List getExpenseData(Expense expense) {
        RawFileDataList list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.EXPENSE_DATA_TYPE);
        addFinancialData(list, expense, FIELD_EXPENSE_TYPE, expense == null ? null : getCode(expense.getType()));
        return list;
    }

    private List getAssetData(FinancialInformation finInfo, Asset asset) {
        RawFileDataList list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.ASSET_DATA_TYPE);
        addFinancialData(list, asset, null, asset == null ? null : getCode(asset.getType()));
        list.add(getNotNull(finInfo.getDebt()));
        return list;
    }

    private void addFinancialData(RawFileDataList list, Financial financial, String typeName, String typeCode) {
        list.add(toPreciseDateTimeFormat(financial == null ? null : financial.getModifiedOn()));
        if (typeName == null) {
            list.add(getNotNull(typeCode));
        }
        else {
            list.addValueToBeTransformed(typeName, typeCode);
        }
        list.add(getNotNull(financial == null ? null : financial.getAmount()));
    }

    private List getSpouseData(SpouseFinancials spouseFinancials) {
        RawFileDataList list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.SPOUSE_DATA_TYPE);
        Spouse spouse = spouseFinancials == null ? new Spouse() : spouseFinancials.getReportedOn();
        Name name = spouse.getName();
        addNameData(list, name);
        SSN officialSsn = spouse.getOfficialSsn();
        addSsnTextData(list, officialSsn);
        addSsnTextData(list, spouse.getPseudoSsn());
        list.add(getNotNull(spouse.getGender()));
        list.add(toPreciseDateFormat(spouse.getDob()));
        list.add(toImpreciseDateFormat(spouse.getDob()));
        list.add(toPreciseDateFormat(spouse.getMarriageDate()));
        list.add(toImpreciseDateFormat(spouse.getMarriageDate()));

        addSsnStatusData(list, officialSsn);

        list.add(to2DigitCode(spouse.getEmployment() == null? null : spouse.getEmployment().getEmploymentStatus()));

        return list;
    }

    private List getDependentData(DependentFinancials depFinancials) {
        RawFileDataList list = new RawFileDataList();
        list.add(IVMDataSynchronizationConstants.DEPENDENT_DATA_TYPE);
        Dependent dep = depFinancials == null ? new Dependent() : depFinancials.getReportedOn();
        Name name = dep.getName();
        addNameData(list, name);
        list.addValueToBeTransformed(FIELD_CHILD_RELATIONSHIP, dep.getRelationship());
        list.add(getNotNull(dep.getGender()));

        addSsnTextData(list, dep.getOfficialSsn());
        list.add(toPreciseDateFormat(dep.getDob()));
        list.add(toImpreciseDateFormat(dep.getDob()));
        list.add(toPreciseDateFormat(dep.getStartDate()));
        list.add(toImpreciseDateFormat(dep.getStartDate()));
        list.add(getIndicator(depFinancials.getIncapableOfSelfSupport()));

        return list;
    }

    private void addNameData(List list, Name name) {
        list.add(getNotNull(name == null ? null : name.getFamilyName()));
        list.add(getNotNull(name == null ? null : name.getGivenName()));
        list.add(getNotNull(name == null ? null : name.getMiddleName()));
        list.add(getNotNull(name == null ? null : name.getPrefix()));
        list.add(getNotNull(name == null ? null : name.getSuffix()));
    }

    private void addSsnTextData(List list, SSN ssn) {
        String ssnText = ssn == null ? null : ssn.getSsnText();
        ssnText = ssnText == null ? EMPTY_STRING : ssnText.replaceAll("-", "");
        list.add(ssnText);
    }

    private void addSsnStatusData(RawFileDataList list, SSN ssn) {
        SSAVerificationStatus ssaVerStatus = ssn == null ? null : ssn.getSsaVerificationStatus();
        list.addValueToBeTransformed(FIELD_SSA_VERIFICATION_STATUS, ssaVerStatus == null ? null : ssaVerStatus.getCode());
        list.add(toPreciseDateFormat(ssn == null ? null : ssn.getSsaVerificationDate()));
        list.add(toImpreciseDateFormat(ssn == null ? null : ssn.getSsaVerificationDate()));
    }

    private String toPreciseDateTimeFormat(Date date) {
        if (date != null) {
            SimpleDateFormat format = new SimpleDateFormat(preciseDateTimeFormat);
            return format.format(date);        
        }
        return EMPTY_STRING;
    }
    
    private String toPreciseDateFormat(Date date) {
        if (date != null) {
            SimpleDateFormat format = new SimpleDateFormat(preciseDateFormat);
            return format.format(date);        
        }
        return EMPTY_STRING;
    }
    
    private String toImpreciseDateFormat(Date date) {
        if (date != null) {
            SimpleDateFormat format = new SimpleDateFormat(impreciseDateFormat);
            return format.format(date);        
        }
        return EMPTY_STRING;
    }
    
    private String toPreciseDateFormat(ImpreciseDate date) {
        return toPreciseDateFormat(ImpreciseDateUtils.getDateWithDefault(date));
    }
    
    private String toImpreciseDateFormat(ImpreciseDate date) {
        return toImpreciseDateFormat(ImpreciseDateUtils.getDateWithDefault(date));
    }
    
    private String getIndicator(Boolean ind) {
        return ind == null ? EMPTY_STRING
                : Boolean.TRUE.equals(ind) ? IVMDataSynchronizationConstants.INDICATOR_Y
                        : IVMDataSynchronizationConstants.INDICATOR_N;
    }

    private String getIndicator(boolean ind) {
        return ind ? IVMDataSynchronizationConstants.INDICATOR_Y
                : IVMDataSynchronizationConstants.INDICATOR_N;
    }

    private String exists(Financial financial) {
        return financial == null 
                || financial.getAmount() == null 
                || financial.getAmount().intValue() == 0 
                    ? IVMDataSynchronizationConstants.INDICATOR_N
                    : IVMDataSynchronizationConstants.INDICATOR_Y;
    }

    private String getNotNull(String str) {
        return str == null ? EMPTY_STRING : str;
    }

    private String getNotNull(Integer numbr) {
        return numbr == null ? EMPTY_STRING : numbr.toString();
    }

    private String getNotNull(BigDecimal numbr) {
        return numbr == null ? EMPTY_STRING : numbr.toString();
    }

    private String getNotNull(Financial financial) {
        return financial == null ? EMPTY_STRING : getNotNull(financial.getAmount());
    }

    private String getNotNull(Lookup lookup) {
        String code = lookup == null ? EMPTY_STRING : lookup.getCode();
        return code == null ? EMPTY_STRING : code;
    }
    
    private String getNotNull(Date date) {
        return date == null ? EMPTY_STRING : date.toString();
    }

    private String getNotNull(ImpreciseDate date) {
        return date == null ? EMPTY_STRING : date.toStandardFormat();
    }

    private String to2DigitCode(Lookup lookup) {
        return leftPad(lookup, 2, '0');
    }
    
    private String to2DigitCode(AbstractCode lookup) {
        return leftPad(lookup, 2, '0');
    }
    
    private String leftPad(Lookup lookup, int size, char charToPad) {
        String result = StringUtils.leftPad(getCode(lookup), size, charToPad);
        return result == null ? EMPTY_STRING : result;
    }
    
    private String leftPad(AbstractCode code, int size, char charToPad) {
        String result = StringUtils.leftPad(getCode(code), size, charToPad);
        return result == null ? EMPTY_STRING : result;
    }
    
    private String getCode(Lookup lookup) {
        return lookup == null ? null : lookup.getCode();
    }
    
    private String getCode(AbstractCode code) {
        return code != null ? code.getCode() : null;
    }

    private String getSiteConductingTest(IncomeTest test)  {
        String source = getCode(test.getSource());
        String siteCondTestCode = getCode(test.getSiteConductingTest());
        if (siteCondTestCode == null && IncomeTestSource.CODE_HEC.getCode().equals(source))
            return VAFacility.CODE_HEC.getCode();
        return siteCondTestCode;
    }
    
    private Date getIncomeYearStartDate(Integer incomeYear) {
        Calendar cal = Calendar.getInstance();
        cal.set(incomeYear.intValue(), Calendar.JANUARY, 01, 00, 00, 00);
        return cal.getTime();
    }
    
    /**
     * @return Returns the impreciseDateFormat.
     */
    public String getImpreciseDateFormat() {
        return impreciseDateFormat;
    }

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

    
    /**
     * @return Returns the preciseDateFormat.
     */
    public String getPreciseDateFormat() {
        return preciseDateFormat;
    }

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

    
    /**
     * @return Returns the preciseDateTimeFormat.
     */
    public String getPreciseDateTimeFormat() {
        return preciseDateTimeFormat;
    }

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