package gov.va.med.esr.common.rule.parameter;

import gov.va.med.esr.UseCaseName;
import gov.va.med.esr.common.model.financials.BeneficiaryTravel;
import gov.va.med.esr.common.model.financials.DependentFinancials;
import gov.va.med.esr.common.model.person.Dependent;
import gov.va.med.esr.common.model.financials.Expense;
import gov.va.med.esr.common.model.financials.FinancialStatement;
import gov.va.med.esr.common.model.financials.GMTThreshold;
import gov.va.med.esr.common.model.financials.Hardship;
import gov.va.med.esr.common.model.financials.InProcessFinancialInfo;
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.IncomeThreshold;
import gov.va.med.esr.common.model.financials.PatientVisitSummary;
import gov.va.med.esr.common.model.financials.RelaxationPercentage;
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.ExpenseType;
import gov.va.med.esr.common.model.lookup.IncomeTestSource;
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.MeansTestStatus;
import gov.va.med.esr.common.model.lookup.VAFacility;
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.Person;
import gov.va.med.esr.common.model.person.Spouse;
import gov.va.med.esr.common.model.person.Employment;
import gov.va.med.esr.common.rule.FinancialInput;
import gov.va.med.esr.common.rule.data.EEInputData;
import gov.va.med.esr.common.rule.data.FinancialInputData;
import gov.va.med.esr.common.ui.form.FinancialDetailsForm;
import gov.va.med.esr.service.IVMFinancialInfo;
import gov.va.med.esr.service.MessagingService;
import gov.va.med.esr.service.UnknownLookupCodeException;
import gov.va.med.esr.service.UnknownLookupTypeException;
import gov.va.med.fw.conversion.ConversionService;
import gov.va.med.fw.model.lookup.AbstractLookup;
import gov.va.med.fw.rule.RuleException;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.DateUtils;
import gov.va.med.fw.util.SerializationUtils;
import gov.va.med.fw.util.StringUtils;
import gov.va.med.esr.common.util.CommonDateUtils;
import gov.va.med.fw.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.Validate;

import sun.misc.BASE64Encoder;


/**
 *
 * @author Carlos Ruiz
 * @author Ghazenfer Mansoor
 * @version 1.0
 */
public class FinancialInputParameter extends AbstractFinancialInputParameter
        implements FinancialInput {
    /**
     * An instance of serialVersionUID
     */
    private static final long serialVersionUID = 901893310343112107L;
    private static Integer YEAR_2008= new Integer("2008");
    public static final String[] incompleteMTStatuses = {
            MeansTestStatus.MT_STATUS_MT_COPAY_EXEMPT.getName(),
            MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName(),
            MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName(),
            MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION.getName() };

    public static final String[] incompleteCopayTestStatuses = {
            MeansTestStatus.MT_STATUS_NON_EXEMPT.getName(),
            MeansTestStatus.MT_STATUS_EXEMPT.getName() };

    public static final String MESSAGING_SERVICE_BEAN_NAME = "messagingService";

    private MessagingService messagingSevice = null;

    private boolean isDataFromZ07 = false;

    private Boolean determinedPrimaryFlag = null;

    private Integer calculatedGMTThreshold = null;

    private Address calculatedGMTAddress = null;

    private Boolean meansTestPermitted = null;

    private Boolean subjectToMeansTest = null;

    private Boolean primaryCopayApplicable = null;

    private BigDecimal calculatedMTThreshold = null;

    private String calculatedMeansTestStatusCode = null;

    private String calculatedRxCopayStatusCode = null;

    private boolean financialStatementCopied = false;

    private boolean incomeTestCopied = false;

    private BigDecimal calculatedNetworthThreshold = null;

    private IncomeThreshold incomeThresholdForComms = null;

    private IncomeTest priorIncomeTest = null;

    private IncomeTest conversionBaseTest = null;

    private ConversionService conversionService;

    public FinancialInputParameter() {
        super();
    }

    // VFA-SP1 TODO
    // removed NW discontinuation /
    // adding back for 9803 related CCR10111

    protected boolean isIncomeYearLaterThan2008() {
          Integer incomeYear = getIncomingIncomeYear();
          return CommonDateUtils.isIncomeYear2009OrLatter(incomeYear);
    }



    public Integer getCurrentIncomeTestYear(Person person)
            throws ServiceException {
        return (getCurrentIncomeTest() != null) ? getCurrentIncomeTest()
                .getIncomeYear() : null;
    }

    //added for CR9803
    public float getRelaxPercentage(Integer incomeYear) throws ServiceException {
        Validate.notNull(incomeYear, "incomeYear cannot be null");
        RelaxationPercentage relaxationPercentage = getRelaxationPercentageService()
                .getRelaxationPercentageByIncomeYear(incomeYear.intValue());
        return relaxationPercentage.getValue().floatValue();
    }

    public float getRelaxPercentage() throws ServiceException {
    	return getRelaxPercentage(getIncomingIncomeYear());
    }

    public IncomeTest getCurrentIncomeTest() throws ServiceException {
        return getCurrentIncomeTest(getPristinePerson());
    }

    public IncomeTest getCurrentIncomeTest(Person person)
            throws ServiceException {
        return getHelperService().getCurrentIncomeTest(person);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getCurrentIncomeTestForEE(gov.va.med.esr.common.model.person.Person)
     */
    public IncomeTest getCurrentIncomeTestForEE(Person person)
            throws ServiceException {
        return getHelperService().getCurrentIncomeTestForEE(person);
    }

    public IncomeTest getMostRecentIncomeTest() throws ServiceException {
        return getCurrentIncomeTest();
    }

    public BigDecimal getAssetAmount(AssetType.Code assetType) {

        BigDecimal amount = getAssetAmount(getIncomingFinancialStatement(),
                assetType);
        return amount == null ? new BigDecimal(0d) : amount;
    }

    public BigDecimal getIncomeAmount(IncomeType.Code incomeType) {
        BigDecimal amount = getIncomeAmount(getIncomingFinancialStatement(),
                incomeType);
        return amount == null ? new BigDecimal(0d) : amount;
    }

    public ConversionService getConversionService() {
        return conversionService;
    }

    public void setConversionService(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    /**
     * Get eleigible total income amount from Veteran/Spouse/Dependent CCR 6636
     *
     * @param incomeType
     * @return
     */
    public BigDecimal getTotalIncomeAmount(IncomeType.Code incomeType)
            throws ServiceException {
        BigDecimal amount = getTotalIncome(getIncomingFinancialStatement(),
                incomeType);
        return amount == null ? new BigDecimal(0d) : amount;
    }

    public BigDecimal getExpenseAmount(ExpenseType.Code expenseType) {
        BigDecimal amount = getExpenseAmount(getIncomingFinancialStatement(),
                expenseType);
        return amount == null ? new BigDecimal(0d) : amount;
    }

    public BigDecimal getDebtAmount() {
        FinancialStatement stmt = getIncomingFinancialStatement();
        return (stmt != null && stmt.getDebt() != null) ? stmt.getDebt()
                .getAmount() : new BigDecimal(0d);
    }

    public boolean isIncomeInRange(double lower, double upper) {
        FinancialStatement stmt = getIncomingFinancialStatement();
        if (stmt != null && stmt.getIncome() != null) {
            return this
                    .isIncomeInRange(stmt.getIncome().values(), lower, upper);
        }
        return true;
    }

    public boolean isExpenseInRange(double lower, double upper) {
        FinancialStatement stmt = getIncomingFinancialStatement();
        if (stmt != null && stmt.getExpenses() != null) {
            return this.isExpenseInRange(stmt.getExpenses().values(), lower,
                    upper);
        }
        return true;
    }

    public boolean isAssetInRange(double lower, double upper) {
        FinancialStatement stmt = getIncomingFinancialStatement();
        if (stmt != null && stmt.getAssets() != null) {
            return this.isAssetInRange(stmt.getAssets().values(), lower, upper);
        }
        return true;
    }

    public boolean isDebtInRange(double lower, double upper) {
        return this.isInRange(this.getDebtAmount(), lower, upper);
    }

    public void setIncomeTestType(String typeCode)
            throws UnknownLookupTypeException, UnknownLookupCodeException {
        setIncomeTestType(getResultIncomeTest(), typeCode);
    }

    public void setIncomeTestType(IncomeTest test, String typeCode)
            throws UnknownLookupTypeException, UnknownLookupCodeException {
        IncomeTestType type = getLookupService().getIncomeTestTypeByCode(
                typeCode);
        test.setType(type);
    }

    public void setSiteConductingTest(VAFacility facility) {
        getResultIncomeTest().setSiteConductingTest(facility);
    }

    /**
     * @throws UnknownLookupCodeException
     * @throws UnknownLookupTypeException
   */
  private void setMeansTestStatus(IncomeTest incomeTest, String status)
          throws UnknownLookupTypeException, UnknownLookupCodeException {

      MeansTestStatus mtStatus = getLookupService().getMeansTestStatusByCode(
              status);
      IncomeTestType type = getLookupService().getIncomeTestTypeByCode(
              IncomeTestType.CODE_MEANS_TEST.getName());
      if (incomeTest != null && type != null && mtStatus != null) {
          incomeTest.setIncomeTestStatus(type, mtStatus);
      }
  }

  /**
     * @throws UnknownLookupCodeException
     * @throws UnknownLookupTypeException
     * @see gov.va.med.esr.common.rule.FinancialInput#setMeansTestStatus(java.lang.String)
     */
    public void setMeansTestStatus(String status)
            throws UnknownLookupTypeException, UnknownLookupCodeException {
    //rewrite this code so if IncomeTest is null, it will be created
    // NOTE: this code may confuse ClearCase merge process. May need to look at before manually merge.
    if (this.getRuleDataAware() instanceof EEInputData) {
          // This is for EE context only.
          IncomeTest it = this.getHelperService().getCurrentIncomeTest(this.getResultPerson());
          setMeansTestStatus(it, status);
      }
      else {
          setMeansTestStatus(getOrCreateIncomeTest(), status);
        }
    }

    public void setMeansTestStatus(String mtStatusCode,
            String determinedStatusCode) throws UnknownLookupTypeException,
            UnknownLookupCodeException {
        setMeansTestStatus(getResultIncomeTest(), mtStatusCode,
                determinedStatusCode);
    }

    public void setMeansTestStatus(IncomeTest test, String mtStatusCode,
            String determinedStatusCode) throws UnknownLookupTypeException,
            UnknownLookupCodeException {
        IncomeTestType type = getLookupService().getIncomeTestTypeByCode(
                IncomeTestType.CODE_MEANS_TEST.getName());
        MeansTestStatus mtStatus = getLookupService().getMeansTestStatusByCode(
                mtStatusCode);
        MeansTestStatus determinedStatus = getLookupService()
                .getMeansTestStatusByCode(determinedStatusCode);
        test.setIncomeTestStatus(type, mtStatus, determinedStatus);
    }

    public void setPharmacyTestStatus(String status)
            throws UnknownLookupTypeException, UnknownLookupCodeException {
        MeansTestStatus mtStatus = getLookupService().getMeansTestStatusByCode(
                status);
        IncomeTestType type = getLookupService().getIncomeTestTypeByCode(
                IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST.getName());
        if (this.getRuleDataAware() instanceof EEInputData) {
            // This is for EE context only.
            IncomeTest it = this.getHelperService().getCurrentIncomeTest(
                    this.getResultPerson());
            if (it != null && type != null && mtStatus != null) {
                it.setIncomeTestStatus(type, mtStatus);
            }
        } else {
            getResultIncomeTest().setIncomeTestStatus(type, mtStatus);
        }
    }

    public void setPharmacyTestStatus(String mtStatusCode,
            String determinedStatusCode) throws UnknownLookupTypeException,
            UnknownLookupCodeException {
        setPharmacyTestStatus(getResultIncomeTest(), mtStatusCode,
                determinedStatusCode);
    }

    public void setPharmacyTestStatus(IncomeTest test, String mtStatusCode,
            String determinedStatusCode) throws UnknownLookupTypeException,
            UnknownLookupCodeException {
        IncomeTestType type = getLookupService().getIncomeTestTypeByCode(
                IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST.getName());
        MeansTestStatus mtStatus = getLookupService().getMeansTestStatusByCode(
                mtStatusCode);
        MeansTestStatus determinedStatus = getLookupService()
                .getMeansTestStatusByCode(determinedStatusCode);
        test.setIncomeTestStatus(type, mtStatus, determinedStatus);
    }

    public BigDecimal getCalculatedMTThreshold() throws ServiceException {

        if (calculatedMTThreshold == null) {
            IncomeTest test = getIncomingIncomeTest();
            calculatedMTThreshold = determineMTThreshold(test.getIncomeYear(),
                    getCalculatedNumberOfDependents());
        }
        logger.debug(calculatedMTThreshold);
        return calculatedMTThreshold;
    }

    private BigDecimal determineMTThreshold(Integer incomeYear,
            Integer dependentCount) throws ServiceException {
        BigDecimal mtThreshold = new BigDecimal(0);
        if (incomeYear == null) {
            return mtThreshold;
        }
        IncomeThreshold it = getLookupService().getIncomeThreshold(incomeYear);
        if (it != null && dependentCount != null) {
            int count = dependentCount.intValue();
            if (count == 0) {
                mtThreshold = getNotNull(it.getExemptAmount());
            } else if (count > 0) { // for 1st dependent
                mtThreshold = getNotNull(it.getDependent());
            }
            // for each extra dependent in addition to 1, add to MT threshold
            // for 1 dep
            for (int i = 1; i < count; i++) {
                mtThreshold = mtThreshold.add(getNotNull(it
                        .getAddDependentThreshold()));
            }
        }
        return mtThreshold;

    }

    private BigDecimal determineNetworthThreshold(Integer incomeYear)
            throws ServiceException {
        BigDecimal propertyThreshold = new BigDecimal(0);
        if (incomeYear == null) {
            return propertyThreshold;
        }
        IncomeThreshold it = getLookupService().getIncomeThreshold(incomeYear);
        if (it != null) {
            propertyThreshold = getNotNull(it.getPropertyThreshold());
        }
        return propertyThreshold;

    }

    private void determineGMTInformation() throws ServiceException {
    // CCR9660 VOA -- added isMessageFromVOA()
    if (isUpdateFromGUI() || isMessageFromVOA()) {
            manageGMTThreshold(getCalculatedNumberOfDependents());
        } else {
            processGMTThreshold(getIncomeTest().getTotalNumberOfDependents());
        }
    }

    private void processGMTThreshold(Integer totalNumberOfDependents)
            throws ServiceException {

        Integer incomeTestGMTThreshold = getGMTThreshold(getIncomingIncomeTest());
        //in case of process, keep the gmt threshold coming from message.
        if (incomeTestGMTThreshold != null) {
            calculatedGMTThreshold = incomeTestGMTThreshold;
        }

        if (calculatedGMTAddress == null) {
            Address pristineGmtAddress = getGMTAddress(getPristineIncomeTest());
            // Return the existing GMT address if already exists
            if (pristineGmtAddress != null ) {
                calculatedGMTAddress = pristineGmtAddress;
            } else {
                calculatedGMTAddress = processGMTAddress(
                        totalNumberOfDependents, incomeTestGMTThreshold);
            }
        }
    }

    private Address processGMTAddress(Integer totalNumberOfDependents,
            Integer incomeTestGMTThreshold) throws ServiceException {
        Address permAddress = getIncomingPerson().getPermanentAddress();

        Integer newGMTThreshold = calculateGMTThresholdAmount(permAddress,
                totalNumberOfDependents);

        // Compare the amounts and select the address
        if (isEqual(incomeTestGMTThreshold, newGMTThreshold)) {
            // Choose the Permanent address
            if (permAddress != null) {
                Address gmtAddress = new Address();
                gmtAddress.setLine1(permAddress.getLine1());
                gmtAddress.setLine2(permAddress.getLine2());
                gmtAddress.setLine3(permAddress.getLine3());
                gmtAddress.setCity(permAddress.getCity());
                gmtAddress.setState(permAddress.getState());
                gmtAddress.setZipCode(permAddress.getZipCode());

                return gmtAddress;
            }
        } else {
            // choose the facility address
            VAFacility.VAFacilityAddress sendingFacilityAddr = getSourceFacility()
                    .getStreetAddress();
            if (sendingFacilityAddr != null) {
                Address gmtAddress =  new Address();
                gmtAddress.setLine1(sendingFacilityAddr.getLine1());
                gmtAddress.setLine2(sendingFacilityAddr.getLine2());
                gmtAddress.setLine3(sendingFacilityAddr.getLine3());
                gmtAddress.setCity(sendingFacilityAddr.getCity());
                gmtAddress
                        .setState(sendingFacilityAddr.getState() == null ? null
                                : sendingFacilityAddr.getState().getCode());
                gmtAddress.setZipCode(sendingFacilityAddr.getZipCode());

                return gmtAddress;
            }
        }
        return null;
    }

    /**
     * Set the GMT address on the result Income test.
     *
     * @param address
     */
    public void setGMTAddress(Address address) {
        getResultIncomeTest().setGmtAddress(address);
    }

    public Address getGMTAddress(IncomeTest test) {
        if (test == null) {
            return null;
        }
        return test.getGmtAddress();
    }

    public Integer getCalculatedGMTThreshold() throws ServiceException {
        if (calculatedGMTThreshold == null) {
            determineGMTInformation();
        }
        logger.debug(calculatedGMTThreshold);
        return calculatedGMTThreshold;
    }

    /*
     * (non-Javadoc)
     *
     * @see gov.va.med.esr.common.rule.FinancialInput#getCalculatedGMTThresholdAsBigDecimal()
     */
    public BigDecimal getCalculatedGMTThresholdAsBigDecimal()
            throws ServiceException {
        Integer gmtThreshold= getCalculatedGMTThreshold();
        BigDecimal gmtThresBD=null;

        if(gmtThreshold != null){
            gmtThresBD = new BigDecimal(gmtThreshold.doubleValue());
        }
        return gmtThresBD;
    }

    public Address getCalculatedGMTAddress() throws ServiceException {
        if (calculatedGMTAddress == null) {
            determineGMTInformation();
        }
        return calculatedGMTAddress;
    }

    public void setGMTThreshold(Integer threshold) {
        BigDecimal th = threshold != null ? new BigDecimal(threshold.intValue())
                : null;
        getResultIncomeTest().setGmtThresholdAmount(th);
    }

    public void setMTThreshold(BigDecimal threshold) {
        getResultIncomeTest().setThresholdA(threshold);
    }

    public void saveInProcessInfo(Person person, Integer incomeYear ) throws RuleException, ServiceException
    {
    	//validate to make sure the financials of the onFile person can be updated
    	this.getFinancialsService().validateFinancialsUpdate(incomeYear, this.getPristinePerson()); //CCR12575

        FinancialDetailsForm detailsForm = new FinancialDetailsForm();
        detailsForm.setIncomeYear(incomeYear.toString());

        //get the finaicial data from the incoming person
        conversionService.convert(person,detailsForm);

        //need to store the in-process financial info in the result person
        Person resultPerson = this.getResultPerson(); //12575

        byte[] data = SerializationUtils.serialize(detailsForm.getFinancialsData());
        InProcessFinancialInfo inProcessFinancial = resultPerson.getInProcessFinancial(incomeYear);
        if (inProcessFinancial == null) {
            inProcessFinancial = new InProcessFinancialInfo();
            inProcessFinancial.setIncomeYear(incomeYear);
            resultPerson.setInProcessFinancial(incomeYear, inProcessFinancial);
        }

        inProcessFinancial.setInProcessData(new BASE64Encoder().encode(data));
        //CCR12575: removed the save here because the result person will be saved after all the rules is processed
        //getFinancialsService().updateInProcess(incomeYear, person, true);

    }



    @SuppressWarnings("rawtypes")
	public Integer calculateGMTThresholdAmount(Address address,
            Integer totalNumberOfDependents) throws ServiceException {
        Integer thresholdAmount = null;

        if (address == null)
            return thresholdAmount;

        // Get ZIP from Address object
        String zipCode = address.getZipCode();
        if (zipCode == null)
            return thresholdAmount;

        String fipsCode = null;
        // TODO do we need throw runtime exception or ignore ??
        try {
            fipsCode = getLookupService().getFipsCode(zipCode);
        } catch (Exception e) {
        }
        if (fipsCode == null)
            return thresholdAmount;

        String msaCode = null;
        //ccr 8862
        try {
            msaCode = getLookupService().getMsaCode(zipCode);
            if(msaCode !=null && msaCode.equals("0")){
            	msaCode="9999";  // from 2007 onwards they changed the msa code from 0 to 9999.
            }
        } catch (Exception e) {
        }
        // Assuming income year always exist - it shouldn't come here if not
        Integer incomeYear = getIncomingIncomeYear();

        List gmtThresholds = null;

        if (StringUtils.isEmpty(msaCode)) {
            // No MSA Code
            gmtThresholds = getLookupService().getGMTThreshold(incomeYear,
                    fipsCode);
        } else // with MSA Code
        {
            gmtThresholds = getLookupService().getGMTThreshold(incomeYear,
                    fipsCode, msaCode);
        }

        // Get the first one from the list
        if (gmtThresholds != null && gmtThresholds.size() > 0) {
            GMTThreshold gmtThreshold = (GMTThreshold) gmtThresholds.get(0);

            int dependents = (totalNumberOfDependents == null) ? 0
                    : totalNumberOfDependents.intValue();
            // If spouse exists and is a valid dependent add 1
            // Not required total is set on IncomeTest

            return calculateThresholdAmount(gmtThreshold, dependents);
        }
        // return default amount 0
        return thresholdAmount;
    }

    /**
     * Get GMT address if not already determined, determine and return
     */
    private void manageGMTThreshold(Integer totalNumberOfDependents)
            throws ServiceException {

        IncomeTest test = getResultIncomeTest();
        // Return the existing GMT address if already exists
        if (test != null && test.getGmtAddress() != null) {
            calculatedGMTAddress = test.getGmtAddress();
            calculatedGMTThreshold = getGMTThreshold(test);
            return;
        }

        // Check whether GMT address can be determined for the permanent address
        Address permAddress = getIncomingPerson().getPermanentAddress();
        calculatedGMTThreshold = calculateGMTThresholdAmount(permAddress,
                totalNumberOfDependents);
        if (calculatedGMTThreshold != null
                && calculatedGMTThreshold.intValue() > 0) {
            // We have valid gmt address
            calculatedGMTAddress = permAddress;
            return;
        }

        // Find the most recent Means Test
        // Helper services checks the expiration date
        IncomeTest mostRecentTest = getCurrentIncomeTest();
        if (mostRecentTest != null
                && IncomeTestType.CODE_MEANS_TEST.getName().equals(
                        getIncomeTestTypeCode(mostRecentTest))
                && mostRecentTest.getSource() != null
                && IncomeTestSource.CODE_VAMC.getCode().equals(
                        mostRecentTest.getSource().getCode()) // source VAMC
                && isNoOfDaysBetweenDates(mostRecentTest.getEffectiveDate(),
                        getCurrentDate(), 365)) {
            Address siteAddress = getFacilityAddress(mostRecentTest
                    .getSiteConductingTest());

            // Calculate GMT address for the site address
            calculatedGMTThreshold = calculateGMTThresholdAmount(siteAddress,
                    totalNumberOfDependents);
            if (calculatedGMTThreshold != null
                    && calculatedGMTThreshold.intValue() > 0) {
                // We have valid gmt address
                calculatedGMTAddress = siteAddress;
                return;
            }
        }

        // Find the site that last transmitted the message
        Address siteAddress = getFacilityAddress(getRecentMsgTransmittedFacility());
        calculatedGMTThreshold = calculateGMTThresholdAmount(siteAddress,
                totalNumberOfDependents);
        if (calculatedGMTThreshold != null
                && calculatedGMTThreshold.intValue() > 0) {
            // We have valid gmt address
            calculatedGMTAddress = siteAddress;
            return;
        }
        // GMT Address could not be determined
    }

    private boolean isNoOfDaysBetweenDates(Date date1, Date date2, int noOfDays) {
        GregorianCalendar newDate1 = null;
        if (date1 != null) {
            // The new date is noOfDays after the date1
            newDate1 = new GregorianCalendar();
            newDate1.setTime(date1);
            newDate1.add(Calendar.DATE, noOfDays);
        }
        return (newDate1 != null && (newDate1.getTime().getTime() > (date2
                .getTime())));
    }

    private Integer calculateThresholdAmount(GMTThreshold gmtThreshold,
            int totalDependents) {
        BigDecimal totalAmount = null;
        if (totalDependents > 7) {
            BigDecimal amountFor4 = gmtThreshold.getThresholdValue(3);
            BigDecimal amountFor8 = gmtThreshold.getThresholdValue(7);
            // 8% of 4 person base is added for each additional person over 8
            double excessAmount = (totalDependents - 7)
                    * amountFor4.doubleValue() * 0.08;
            totalAmount = amountFor8.add(new BigDecimal(excessAmount));
        } else {
            totalAmount = gmtThreshold.getThresholdValue(totalDependents);
            // TODO need to change
        }
        // Round to the nearest up $50
        double thresholdAmount = totalAmount.doubleValue();
        int lowerTotalAmont = ((int)thresholdAmount/50) * 50 ;
        return new Integer(
                (thresholdAmount - lowerTotalAmont > 0) ? (int) (lowerTotalAmont + 50)
                        : lowerTotalAmont);
    }

    public void setNumberOfDependentChildren(Integer numberOfDependentChildren) {
        FinancialStatement resultStmt = getResultFinancialStatement();
        resultStmt.setNumberOfDependentChildren(numberOfDependentChildren);
    }

    public void setNumberOfDependents(Integer totalNumberOfDependents) {
        IncomeTest resultTest = getResultIncomeTest();
        resultTest.setTotalNumberOfDependents(totalNumberOfDependents);
    }

    @SuppressWarnings("rawtypes")
	public void setValidDependent(FinancialStatement stmt) {
        if (stmt == null) {
            return;
        }
        Set dfSet = stmt.getDependentFinancials();
        for (Iterator iter = dfSet.iterator(); iter.hasNext();) {
            DependentFinancials df = (DependentFinancials) iter.next();
            if (isDependentValidDependent(df)) {
                df.setValidDependent(Boolean.TRUE);
            } else {
                df.setValidDependent(Boolean.FALSE);
            }
        }
        for (Iterator i=stmt.getSpouseFinancials().iterator(); i.hasNext();) {
            SpouseFinancials sf = (SpouseFinancials) i.next();
            if (isSpouseValidDependent(sf)) {
                sf.setValidDependent(Boolean.TRUE);
            } else {
                sf.setValidDependent(Boolean.FALSE);
            }
        }
    }

    public void setNetworth(BigDecimal networth) {
        getResultIncomeTest().setNetWorth(networth);
    }

    public void setTotalIncome(BigDecimal income) throws ServiceException {
        getResultIncomeTest().setTotalIncome(income);
    }

    public void setNetIncome(BigDecimal income) throws ServiceException {
        getResultIncomeTest().setNetIncome(income);
    }

    public void setPensionThreshold(BigDecimal threshold) {
        getResultIncomeTest().setPensionThreshold(threshold);
    }

    public BigDecimal getCalculatedNetworthThreshold() throws ServiceException {
        if (calculatedNetworthThreshold == null) {
            calculatedNetworthThreshold = determineNetworthThreshold(getIncomingIncomeYear());
        }
        return calculatedNetworthThreshold;
    }

    public void setNetworthThreshold(BigDecimal threshold) {
        getResultIncomeTest().setNetworthThreshold(threshold);
    }

    public void setIncomeExclusionThreshold(BigDecimal threshold) {
        getResultIncomeTest().setChildIncomeExclusionThreshold(threshold);
    }

    public void setDeductibleExpense(BigDecimal expense) {
        getResultIncomeTest().setDeductibleExpenses(expense);
    }

    public void setAdjustableMedicalExpense(BigDecimal expense) {
        setExpenseAmount(getResultFinancialStatement(),
                ExpenseType.EXPENSE_TYPE_ADJUSTED_MEDICAL, expense);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#setNonReimbursedMedicalExpense(java.math.BigDecimal)
     */
    public void setNonReimbursedMedicalExpense(BigDecimal expense) {
        setExpenseAmount(getResultFinancialStatement(),
                ExpenseType.EXPENSE_TYPE_NON_REIMBURSED_MEDICAL, expense);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#deletePrimaryIncomeTest()
     */
    public void deletePrimaryIncomeTest() {
        getResultPerson().setIncomeTest(getIncomingIncomeYear(), null);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#isIncomeTestDeleted()
     */
    public boolean isIncomeTestDeleted() {
        return (getIncomingIncomeTest() == null)
                && (getPristineIncomeTest() != null);
    }

    public Date getLastEditDate(IncomeTest test) {
        return test == null ? null : test.getLastEditedDate();
    }

    public Date getIncomeTestLastEditedDate() {
        return getLastEditDate(getIncomeTest());
    }

    public Date getPristineIncomeTestLastEditedDate() {
        return getLastEditDate(getPristineIncomeTest());
    }

    public void setLastEditDate(Date date) {
        if (this.getRuleDataAware() instanceof EEInputData) {
            // This is for EE context only.
            IncomeTest incomeTest = this.getHelperService()
                    .getCurrentIncomeTest(this.getResultPerson());
            if (incomeTest != null) {
                setLastEditDate(incomeTest, this.getCurrentDate());
            }
        } else {
            // For financials context
            setLastEditDate(getOrCreateIncomeTest(), date);
        }
    }

    public void setLastEditDate(IncomeTest incomeTest, Date date) {
        incomeTest.setLastEditedDate(date);
    }

 // ES 4.0.3_CodeCR13722
    public Date getCompletedDate(IncomeTest test) {
 //       return test == null ? null : test.getCompletedDate();
    	 return test == null ? new Date() : test.getCompletedDate();
    }

    public Date getCompletedDate() {
        return getCompletedDate(getIncomeTest());
    }

    public void setCompletedDate(Date date) {
        setCompletedDate(getOrCreateIncomeTest(), date);
    }

    public void setCompletedDate(IncomeTest incomeTest, Date date) {
        incomeTest.setCompletedDate(date);
    }

    public Date determineCompletedDate() {
        // UC 33.6
        IncomeTest onFile = getPristineIncomeTest();
        IncomeTest incoming = getIncomeTest();
        String onFileSiteCode = getSiteConductingTestFacilityCode(onFile);
        String incomingSiteCode = getSiteConductingTestFacilityCode(incoming);

        // 4656[UC46.6.1]
        if (isPrimary(onFile) && isTestTypeMeansTest(onFile)
                && isTestTypeMeansTest(incoming)
                && StringUtils.equals(incomingSiteCode, onFileSiteCode)
                && !isCADAdjusted(onFile)) {

            // save the earliest completed
            return getEarliest(onFile.getCompletedDate(), incoming
                    .getCompletedDate());
        }

        String[] statuses = {
                MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName(),
                MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName(),
                MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION.getName() };

        // 4657[UC46.6.2]
        if (isPrimary(onFile)
                && isTestTypeMeansTest(onFile)
                && (StringUtils.contains(statuses, getDeterminedStatus(onFile)))
                && !isCADAdjusted(onFile)
                && StringUtils
                        .contains(statuses, getDeterminedStatus(incoming))
                && Boolean.TRUE.equals(getAgreeToPayIndicator(incoming))) {

            return getEarliest(onFile.getCompletedDate(), incoming
                    .getCompletedDate());
        }

        // 4659[UC46.6.3]
        if (isTestTypeMeansTest(incoming) && getDeterminedPrimaryFlag()) {
            return getCompletedDate(incoming);
        }

        // 5202[UC46.6.4]
        if (isPrimary(onFile) && isTestTypePharmacyCopayTest(onFile)
                && isCADAdjusted(onFile)
                && getCompletedDate(incoming).after(getCompletedDate(onFile))) {
            return getCompletedDate(incoming);
        }

        // 5203[UC46.6.5]
        if (isPrimary(onFile) && isTestTypePharmacyCopayTest(onFile)
                && !isCADAdjusted(onFile)) {
            return getCompletedDate(incoming);
        }

        // 4660[UC46.6.6]
        if (isTestTypePharmacyCopayTest(incoming) && getDeterminedPrimaryFlag()) {
            return getCompletedDate(incoming);
        }

        // 33.6.7 (No Change)
        return getCompletedDate(onFile);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getEffectiveTestDate()
     */
    public Date getEffectiveTestDate() {
        return getEffectiveTestDate(getIncomingIncomeTest());
    }

    public Date determineEffectiveDate() {
        // UC 33.5.1
        IncomeTest onFile = getPristineIncomeTest();
        IncomeTest incoming = getIncomeTest();
        if (isPrimary(onFile)
                && !isIncomingTestDeterminedPrimary()
                && isEarlier(getEffectiveTestDate(incoming),
                        getEffectiveTestDate(onFile))) {
            return getEffectiveTestDate(incoming);
        }
        return getEffectiveTestDate(onFile);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#setEffectiveTestDate(java.util.Date)
     */
    public void setEffectiveTestDate(Date date) {
        Date testEffectiveDate = date;
        if (date != null) {
            Calendar calendar = DateUtils.createCalendar(date);
            int year = calendar.get(Calendar.YEAR);
            int month = calendar.get(Calendar.MONTH);
            int day = calendar.get(Calendar.DATE);
            calendar.clear();
            calendar.set(year,month,day);
            testEffectiveDate = calendar.getTime();
        }
        setEffectiveTestDate(getOrCreateIncomeTest(), testEffectiveDate);
    }

    public void setEffectiveTestDate(IncomeTest incomeTest, Date date) {
        incomeTest.setEffectiveDate(date);
        checkSetFutureTestFlag(incomeTest, date);
    }

    public void setCADAdjusted(Boolean cadAdjusted) {
        getOrCreateIncomeTest().setCadAdjusted(cadAdjusted);
    }

    public Boolean getCADAdjusted(IncomeTest test) {
        if (test == null) {
            return null;
        }
        return test.getCadAdjusted();
    }

    public void setAgreeToPayIndicator(Boolean atpd) {
        getOrCreateIncomeTest().setAgreesToPayDeductible(atpd);
    }

    public String getDisclosure() {
        Boolean disclosure = getDisclosure(getIncomeTest());
        if (disclosure != null) {
            return disclosure.booleanValue() ? YES_INDICATOR : NO_INDICATOR;
        }
        return null;
    }

    public void setDisclosure(Boolean disclosure) {
        getOrCreateIncomeTest().setDiscloseFinancialInformation(disclosure);
    }

    public String getDeductibleStatus() {
        Boolean deductible = getAgreeToPayIndicator(getIncomeTest());
        if (deductible != null) {
            return deductible.booleanValue() ? YES_INDICATOR : NO_INDICATOR;
        }
        return null;
    }

    /**
     * (non-Javadoc)
     *
     * @see gov.va.med.esr.common.rule.FinancialInput#getGMTThreshold()
     */
    public Integer getGMTThreshold() {
        return this.getGMTThreshold(this.getIncomingIncomeTest());
    }

    /**
     * (non-Javadoc)
     *
     * @see gov.va.med.esr.common.rule.FinancialInput#getMTThreshold()
     */
    public Integer getMTThreshold() {
        return this.getMTThreshold(this.getIncomingIncomeTest());
    }

    public String getMeansTestStatusCode() {
        return getMeansTestStatusCode(getIncomingIncomeTest());
    }

    public String getPharmacyCopayStatusCode() {
    	IncomeTest incomeTest=getIncomingIncomeTest();
    	if(incomeTest !=null){
        return getPharmacyCopayStatusCode(getIncomingIncomeTest());
    	}
    	return null;
    }

    public String getMeansTestStatusCode(IncomeTest incomeTest) {
        return getMeansTestStatusCode(incomeTest,
                IncomeTestType.CODE_MEANS_TEST);
    }

    public String getPharmacyCopayStatusCode(IncomeTest incomeTest) {
        // Corrected this on 11/23 - was using getIncomingTest()
        return getMeansTestStatusCode(incomeTest,
                IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST);
    }

    public String getMeansTestDeterminedStatusCode(IncomeTest incomeTest) {
        return getDeterminedStatusCode(incomeTest,
                IncomeTestType.CODE_MEANS_TEST);
    }

    public String getPharmacyCopayDeterminedStatusCode(IncomeTest incomeTest) {
        return getDeterminedStatusCode(incomeTest,
                IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getIncomeTest()
     */
    public IncomeTest getIncomeTest() {
        return this.getIncomingIncomeTest();
    }

    public IncomeTest getResultIncomeTest() {
        Integer incomeYear = getIncomingIncomeYear();
        return getResultPerson().getIncomeTest(incomeYear);
    }

    public FinancialStatement getResultFinancialStatement() {
        Integer incomeYear = getIncomingIncomeYear();
        return getResultPerson().getFinancialStatement(incomeYear);
    }

    public IncomeTestStatus getResultIncomeTestStatus() {
        return getOrCreateIncomeTestStatus();
    }

    protected IncomeTestStatus getOrCreateIncomeTestStatus() {
        IncomeTestStatus rStatus = getOrCreateIncomeTest()
                .getIncomeTestStatus();
        if (rStatus == null) {
            rStatus = new IncomeTestStatus();
        }
        return rStatus;
    }

    public String getSiteConductingTestFacilityCode(IncomeTest iTest) {
        VAFacility site = getSiteConductingTest(iTest);
        return site == null ? null : site.getCode();
    }

    public VAFacility getSiteConductingTest(IncomeTest iTest) {
        if (iTest == null)
            return null;
        return iTest.getSiteConductingTest();
    }

    public String getReceivingFacilityCode() {
        return getLookupCode(getSourceFacility());
    }

    public VAFacility getReceivingFacility() {
        return getSourceFacility();
    }

    /**
     * (non-Javadoc)
     *
     * @see gov.va.med.esr.common.rule.FinancialInput#getSpousalSupportAmount()
     */
    public BigDecimal getSpousalSupportAmount() {
        FinancialStatement stmt = getIncomingFinancialStatement();
        if (stmt != null) {
            return stmt.getContributionToSpouse();
        } else {
            return null;
        }
    }

    /**
     * (non-Javadoc)
     *
     * @see gov.va.med.esr.common.rule.FinancialInput#hasMarriedLastCalendarYear()
     */
    public Boolean hasMarriedLastCalendarYear() {
        FinancialStatement stmt = getIncomingFinancialStatement();
        if (stmt != null) {
            return stmt.getMarriedLastCalendarYear();
        } else {
            return null;
        }
    }

    /**
     * TODO check which spouse needs to be set
     */
    public void setLivedWithPatient(Boolean flag) {
        FinancialStatement fstmt = getResultFinancialStatement();
        if (fstmt != null) {
            SpouseFinancials sf = fstmt.getRecentSpouseFinancials();
            if (sf != null){
                sf.setLivedWithPatient(flag);
            }
        }
    }

    /**
     * (non-Javadoc) TODO check which spouse needs to be set
     *
     * @see gov.va.med.esr.common.rule.FinancialInput#getSpouseLivesWithVeteran()
     */
    public Boolean spouseLivesWithVeteran() {
        FinancialStatement fstmt = getIncomingFinancialStatement();
        if (fstmt != null) {
            SpouseFinancials sf = fstmt.getRecentSpouseFinancials();
            if (sf != null) {
                return sf.getLivedWithPatient();
            }
        }
        return null;
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#setIncomeTest(gov.va.med.esr.common.model.financials.IncomeTest)
     */
    public void setIncomeTest(IncomeTest incomeTest) throws ServiceException {
        if (incomeTest == null) {
            getResultPerson().setIncomeTest(getIncomingIncomeYear(), null);
        } else {
            IncomeTest rsTest = getOrCreateIncomeTest();
            // Code CR 8672 - need to check if the test has FDT flag already. If
            // so, restore the effective
            // date after the merge.
            Date effDate = null;
            boolean isFuture = (rsTest.isFutureTest() != null) ? rsTest
                    .isFutureTest().booleanValue() : false;
            if (isFuture) {
                effDate = rsTest.getEffectiveDate();
            }
            getMergeRuleService().mergeIncomeTest(incomeTest, rsTest);
            if (isFuture) {
                rsTest.setEffectiveDate(effDate);
            }
            checkSetFutureTestFlag(rsTest, rsTest.getEffectiveDate());
            incomeTestCopied = true;
        }
    }

    public void processIVMIncomeTest() throws ServiceException {
        IncomeTest incomingTest = getIncomeTest();
        String source = getIncomeTestSourceCode(incomingTest);
        if (IncomeTestSource.CODE_IVM.getCode().equals(source)) {
            IncomeTest resultTest = getOrCreateIncomeTest();
            //merge income test excluding gmt address.
            Address gmtAddress = resultTest.getGmtAddress();
            getMergeRuleService().mergeFullIncomeTest(incomingTest, resultTest);
            resultTest.setGmtAddress(gmtAddress);
            checkSetFutureTestFlag(resultTest, resultTest.getEffectiveDate());
            //REEG_00005580 set IVM test primary indicator
            resultTest.setPrimaryIncomeTest(Boolean.TRUE);
            incomeTestCopied = true;
        }
    }

    private void checkSetFutureTestFlag(IncomeTest test, Date effectiveDate) {
        if (effectiveDate != null) {
            // CR 8672 - this code CR covers multiple issues. The change below
            // is needed to provide consistent detection of a future test
            if (isAfterIgnoreTime(effectiveDate, getCurrentDate())) {
                test.setFutureTest(Boolean.TRUE);
            } else {
                test.setFutureTest(Boolean.FALSE);
            }
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getMTStatus()
     */
    public String getMTStatus() {
        return this.getMeansTestStatusCode(this.getIncomingIncomeTest(),
                IncomeTestType.CODE_MEANS_TEST);
    }

    public String getIVMActionCode() {
		// CCR 12153
    	FinancialInputData data = getFinancialInputData();
    	if (data != null && data.getIvmFinancialInfo() != null) {
    		return data.getIvmFinancialInfo().getIvmActionCode();
    	}
		return null;
	}

	public boolean hasIVMTestOnFileForIncomingIY() throws ServiceException {
		Integer incomeYear = this.getIncomingIncomeYear();
		if (incomeYear == null) return false;
		IncomeTest onFile = this.getResultPerson().getIncomeTest(incomeYear);
		String source = getIncomeTestSourceCode(onFile);
		if (IncomeTestSource.CODE_IVM.getCode().equals(source)) {
			return true;
		}

		return false;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public IncomeTest getPreviousPrimaryTest(Integer conversionIncomeYear, Person person) throws ServiceException {

		if (conversionIncomeYear == null) return null;

		Map incomeTests = person.getIncomeTests();
		// Get a list of all the income years for the tests
		List incomeYears = new ArrayList(incomeTests.keySet());
		if ((incomeYears == null) || (incomeYears.isEmpty())) {
			return null;
		}

		// Loop through the income years from the most current to the oldest one
		Collections.sort(incomeYears);
		for (int i = incomeYears.size() - 1; i >= 0; i--) {
			// Get an income year
			Integer incomeYear = (Integer) incomeYears.get(i);
			if (conversionIncomeYear.compareTo(incomeYear) <= 0)
				continue;

			// Get the income test
			IncomeTest incomeTest = (IncomeTest) incomeTests.get(incomeYear);

			boolean future = false;
			if (incomeTest.isFutureTest() != null) {
				future = incomeTest.isFutureTest().booleanValue();
			}

			if (future) continue;

			return incomeTest;
		}
		return null;
	}
	public void createCloneIncomeTest() throws ServiceException {
		// PRECONDITION: NO MT exists for same incoming IY

		/*
		 * 4.3. Clone Test
		 *
		 * If there is no primary means test on file for the same income year as
		 * the IVM conversion, the system will clone data from the primary means
		 * test on file.
		 */

		// CCR 12555 use previous primary test that is most recent
		IncomeTest previousPrimaryTest = this.getPreviousPrimaryTest(this.getIncomingIncomeYear(), this.getResultPerson());
		if (previousPrimaryTest != null && this.getIncomingIncomeYear() != null) {
			Person resultPerson = this.getResultPerson();
			Integer conversionIncomeYear = this.getIncomingIncomeYear();
			IncomeTest clonedTest = (IncomeTest)previousPrimaryTest.clone();
			clonedTest.setIncomeYear(conversionIncomeYear);
			IncomeTest conversion =  new IncomeTest();
			getMergeRuleService().mergeFullIncomeTest(clonedTest, conversion);
			resultPerson.setIncomeTest(conversionIncomeYear, conversion);
			VAFacility facility = previousPrimaryTest.getSiteConductingTest();
			Integer currentIncomeYear = previousPrimaryTest.getIncomeYear();

			if (facility != null && currentIncomeYear != null) {
				// first get existing one
				PatientVisitSummary resultVisitSummary =
					getResultPerson().getPatientVisitSummary(conversionIncomeYear,
							facility);
				if (resultVisitSummary == null) {
					// try getting it from current test
					resultVisitSummary = resultPerson.getPatientVisitSummary(currentIncomeYear,
							facility);
					if (resultVisitSummary != null) {
						// copy data and use the incoming IY to set on person
						PatientVisitSummary clonedPatientVisitSummary = new PatientVisitSummary();
						getMergeRuleService().mergePatientVisitSummary(resultVisitSummary, clonedPatientVisitSummary);
						clonedPatientVisitSummary.setIncomeYear(conversionIncomeYear);
						resultPerson.setPatientVisitSummary(conversionIncomeYear,
								facility, clonedPatientVisitSummary);
					}
				}
			}
			// The requirements say to use All Dependent Information From previous means test
			// We get statement from same year as the current income test.
			FinancialStatement onfileFs = resultPerson.getFinancialStatement(currentIncomeYear);
			// We expect this to be null since there was no existing income test for the conversion IY.
			if (onfileFs != null) {
				FinancialStatement clonedFs = (FinancialStatement)onfileFs.clone();
				clonedFs.setIncomeYear(conversionIncomeYear);
				FinancialStatement conversionFs = new FinancialStatement();
				getMergeRuleService().mergeFinancialStatement(clonedFs, conversionFs);
				resultPerson.setFinancialStatement(conversionIncomeYear, conversionFs);
			}
		}
		else {
			throw new ServiceException( "Unable to clone a test for IVM conversion process: veteran has no income test");
		}
	}

	public void partialMergeIVMConversionData(IncomeTest ivmData, IncomeTest esrIncomeTest) throws ServiceException {
		// IVM Data Elements Shared with ESR
		// IVM only sends partial income test data so cannot use regular merge.
		if (ivmData == null || esrIncomeTest == null) return;

		// Income Test Type Code
		if (ivmData.getType() == null) {
			throw new ServiceException( "IVM Conversion data does not contain Means Test Type for person id:" +
					esrIncomeTest.getPerson().getPersonEntityKey().getKeyValueAsString());
		}
		esrIncomeTest.setType(ivmData.getType());
		esrIncomeTest.setPrimaryIncomeTest(Boolean.TRUE);

		// Number of Dependent Effective for the Income Year
		esrIncomeTest.setTotalNumberOfDependents(ivmData.getTotalNumberOfDependents());
		// Means Test Determine Status Code
		IncomeTestStatus mtIncomeTestStatusESR = esrIncomeTest.getIncomeTestStatus(IncomeTestType.CODE_MEANS_TEST);
		IncomeTestStatus mtIncomeTestStatusIncoming = ivmData.getIncomeTestStatus(IncomeTestType.CODE_MEANS_TEST);
		MeansTestStatus mtTestDeterminedStatus = mtIncomeTestStatusESR != null ? mtIncomeTestStatusESR.getDeterminedStatus() : null;

		IncomeTestStatus rxIncomeTestStatusESR = esrIncomeTest.getIncomeTestStatus(IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST);
		IncomeTestStatus rxIncomeTestStatusIncoming = ivmData.getIncomeTestStatus(IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST);
		MeansTestStatus rxTestDeterminedStatus = rxIncomeTestStatusESR != null ? rxIncomeTestStatusESR.getDeterminedStatus() : null;

		boolean mtStatusAlreadyMerged = false;
		boolean rxStatusAlreadyMerged = false;
		if (mtIncomeTestStatusESR == null && mtIncomeTestStatusIncoming != null) {
			mtIncomeTestStatusESR = new IncomeTestStatus();
			this.getMergeRuleService().mergeIncomeTestStatus(mtIncomeTestStatusIncoming, mtIncomeTestStatusESR);
			esrIncomeTest.addIncomeTestStatus(mtIncomeTestStatusESR);
			mtStatusAlreadyMerged = true;
		}
		if (rxIncomeTestStatusESR == null && rxIncomeTestStatusIncoming != null) {
			rxIncomeTestStatusESR = new IncomeTestStatus();
			this.getMergeRuleService().mergeIncomeTestStatus(rxIncomeTestStatusIncoming, rxIncomeTestStatusESR);
			esrIncomeTest.addIncomeTestStatus(rxIncomeTestStatusESR);
			rxStatusAlreadyMerged = true;
		}

		// Income Test Status Code
		if (!mtStatusAlreadyMerged && mtIncomeTestStatusESR != null && mtIncomeTestStatusIncoming != null) {
			this.getMergeRuleService().mergeIncomeTestStatus(mtIncomeTestStatusIncoming, mtIncomeTestStatusESR);
		}
		if (!rxStatusAlreadyMerged && rxIncomeTestStatusESR != null && rxIncomeTestStatusIncoming != null) {
			this.getMergeRuleService().mergeIncomeTestStatus(rxIncomeTestStatusIncoming, rxIncomeTestStatusESR);
		}
		if (mtIncomeTestStatusIncoming == null && mtIncomeTestStatusESR != null) {
			// This is for documentation only.
			// When incoming status is null and existing status is not null, ESR
			// will keep the existing status.
		}
		if (rxIncomeTestStatusIncoming == null && rxIncomeTestStatusESR != null) {
			// This is for documentation only.
			// When incoming status is null and existing status is not null, ESR
			// will keep the existing status.
		}
		if (mtIncomeTestStatusESR != null && mtTestDeterminedStatus != null) {
			// restore original determined status
			// Means Test Determine Status Code
			mtIncomeTestStatusESR.setDeterminedStatus(mtTestDeterminedStatus);
		}
		if (rxIncomeTestStatusESR != null && rxTestDeterminedStatus != null) {
			// restore original determined status
			// Means Test Determine Status Code
			rxIncomeTestStatusESR.setDeterminedStatus(rxTestDeterminedStatus);
		}
		// Income Test Source Code
		// -using the source set by builder
		// Income Test Effective Date
		esrIncomeTest.setEffectiveDate(ivmData.getEffectiveDate());
		// Income Test Completed Date
		esrIncomeTest.setIVMTestCompletionDate(ivmData.getIVMTestCompletionDate());
		//	Find a Complete Date that is not null. All statuses get set to same date.
		if (mtIncomeTestStatusESR != null && mtIncomeTestStatusIncoming != null && mtIncomeTestStatusIncoming.getCompletedDate() != null) {
			esrIncomeTest.setCompletedDate(mtIncomeTestStatusIncoming.getCompletedDate());
		}
		else if (rxIncomeTestStatusESR != null && rxIncomeTestStatusIncoming != null && rxIncomeTestStatusIncoming.getCompletedDate() != null) {
			esrIncomeTest.setCompletedDate(rxIncomeTestStatusIncoming.getCompletedDate());
		}
		// IVM Conversion Date
		esrIncomeTest.setIvmConversionDate(ivmData.getIvmConversionDate());
		// Income Test Source Code
		esrIncomeTest.setSource(ivmData.getSource());
		// Mean Test Threshold
		esrIncomeTest.setThresholdA(ivmData.getThresholdA());
		// Agree to Pay Deductible Indicator
		// CCR 12532 - have ESR set the Agree to Pay
		String mtStatusCode = (mtIncomeTestStatusESR != null && mtIncomeTestStatusESR.getStatus() != null) ?
				mtIncomeTestStatusESR.getStatus().getCode() : null;
		if (MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getCode().equals(mtStatusCode) ||
				MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getCode().equals(mtStatusCode))
			esrIncomeTest.setAgreesToPayDeductible(Boolean.TRUE);
		else
			esrIncomeTest.setAgreesToPayDeductible(null);
		// PG8 Relaxation Indicator
		esrIncomeTest.setPg8RelaxationIndicator(ivmData.getPg8RelaxationIndicator());
		// IVM Case Status
		esrIncomeTest.setWorkflowCaseStatus(ivmData.getWorkflowCaseStatus());
		/*
		 * The following are not accepted from IVM per Randhir Manhas' direction. "…
		 * even though IVM might send spouse and dependent info (DEP and SPO
		 * tagged elements per spreadsheet), it was decided that ESR would not
		 * use it coming from IVM but would populate it from ADR and use it as
		 * ESR would have more accurate data.”
		 * Gender Type Code
		 * Spouse SSN
		 * Spouse Last Name
		 * Spouse Prefix
		 * Spouse First Name
		 * Spouse Middle Name
		 * Spouse Maiden Name
		 * Spouse Name Suffix
		 * Spouse Date of Birth
		 * Spouse Marriage Date
		 * Spouse Retirement
		 * Gender Type
		 * Veteran Relationship
		 * Dependent SSN
		 * Dependent Last Name
		 * Dependent Prefix
		 * Dependent First Name
		 * Dependent Middle Name
		 * Dependent Suffix
		 * Dependent Date of Birth
		 * Dependent Effective Date
		 * Dependent Incapable of Self Support Indicator
		 */
	}
	public void createOrUpdateTestFromIVMConversionData(boolean testExists) throws ServiceException {
		/*
		 * 4.4. Create New Test
		 *
		 * The system creates a new test using data received from the IVM
		 * converted test and the cloned test. The following table lists the
		 * fields and source of data to be used for the new means test.
		 *
		 * 5.2. Update Means Test
		 * If there is a primary means test on file for
		 * the same income year as the IVM data, the system will remove all
		 * financial totals from the test on file and update that test using the
		 * IVM conversion data received and change the test source to IVM. The
		 * use case resumes at step 4.5 Update Test Status in the basic flow.
		 *
		 * -----Original Message----- From: Manhas, Randhir (HP) Sent: Monday,
		 * June 17, 2013 2:20 PM To: Ruiz, Carlos (HP); Palaniappan, Meena (HP)
		 *
		 * Subject: RE: <internal>RE: CodeCR ESR 3.12_CodeCR12448 Approved 2 -
		 * Major 2 - Give High Attention GMT Address Fields empty after
		 * receiving IVM conversion
		 *
		 * I sent you another email but Carlos there is another thing that you
		 * need to know, even though IVM might send spouse and dependent info
		 * (DEP and SPO tagged elements per spreadsheet), it was decided that
		 * ESR would not use it coming from IVM but would populate it from ADR
		 * and use it as ESR would have more accurate data.
		 *
		 * -Randhir
 		 *
		 */
		IncomeTest incomingTest = getIncomeTest();
		IncomeTest resultTest = getOrCreateIncomeTest();
		if (incomingTest == null || resultTest == null) return;
		IncomeTest pristineTest = (IncomeTest)resultTest.clone();
		Person resultPerson = this.getResultPerson();
		Integer incomingIncomeYear = this.getIncomingIncomeYear();
		// CCR 12447
		// The builder provides what is essentially a Data Tranfer Object, the income test. It is only partially filled with
		// data so cannot use a regular merge.
		this.partialMergeIVMConversionData(incomingTest, resultTest);

		/*****************************************************************
		 * Apply processing for "exists" and "clone" scenarios. Processing for new test
		 * may apply more changes.
		 ******************************************************************/
		// Although there are no directions to set Primary Test, we must do so.
		resultTest.setPrimaryIncomeTest(Boolean.TRUE);
		// Net Worth	Set to NULL
		resultTest.setNetWorth(null);
		// Financial Summary	Total Income	Set to NULL
		resultTest.setTotalIncome(null);
		// Net Income	Set to NULL
		resultTest.setNetIncome(null);
		// Total Deductible Expenses	Set to NULL
		resultTest.setDeductibleExpenses(null);

		// Skip if conversion to existing test
		if (!testExists) {
			/*****************************************************************
			 * Continue processing if conversion is for new test, a.k.a. clone scenario
			 ******************************************************************/
			/*
			 * Restore specific values from the pristine test
			 */
			// Source of Financial Information Set to "IVM"
			// -Done in merge
			// Financial Assessment Disclose Financial Income Set to "Yes"
			resultTest.setDiscloseFinancialInformation(Boolean.TRUE);
			// Agree to Pay Deductible Set to "Yes"
			// CCR 12532 ESR will set for two specific statuses otherwise will be null
			// Primary Test Type Set to "Means Test"
			resultTest.setType(this.getLookupService().getIncomeTestTypeByCode(IncomeTestType.CODE_MEANS_TEST.getCode()));
			// Test Effective Date 1/1 (January 1) of calendar year for the income year
			// -use IVM value
			// Test Determined Status From previous means test
			// -use ESR value
			// Test Status Set by IVM
			// Site Conducting Test From previous means test
			// -use ESR value
			// Date Test Completed Set to "current date"
			// -use IVM value
			// Centralized Anniversary Date Adjusted Set to "No"
			resultTest.setCadAdjusted(Boolean.FALSE);
			// Adjudication Date Set to NULL
			resultTest.setAdjudicationDate(null);
			// Pharmacy Co-Pay Exemption Status Set to status from IVM
			// Set in merge
			// Long Term Care Status Set to NULL
			IncomeTestStatus ltc1 = resultTest.getIncomeTestStatus(IncomeTestType.CODE_LTC_CO_PAY_TEST);
			IncomeTestStatus ltc2 = resultTest.getIncomeTestStatus(IncomeTestType.CODE_LTC_CO_PAY_EXEMPTION_TEST);
			if (ltc1 != null) resultTest.removeIncomeTestStatus(ltc1);
			if (ltc2 != null) resultTest.removeIncomeTestStatus(ltc2);
			Hardship hardship = resultTest.getHardship();
			if (hardship != null) {
				// Hardship Granted Set to "No"
				hardship.setHardshipGranted(Boolean.FALSE);
				// Hardship	Hardship Reason	Set to NULL
				hardship.setReason(null);
				// Site Granting Hardship	Set to NULL
				hardship.setSiteGrantingHardship(null);
				// Effective Date	Set to NULL
				hardship.setEffectiveDate(null);
				// Review Date	Set to NULL
				hardship.setReviewDate(null);
			}
			FinancialStatement financialStatement = resultPerson.getFinancialStatement(incomingIncomeYear);

			// Adjusted Medical Expenses	Set to NULL
			if (financialStatement != null) {
				Expense adjMedical = financialStatement.getExpense(ExpenseType.EXPENSE_TYPE_ADJUSTED_MEDICAL);
				if (adjMedical != null && adjMedical.getAmount() != null && adjMedical.getAmount().compareTo(new BigDecimal(0)) > 0) {
					adjMedical.setAmount(new BigDecimal(0));
				}
			}
			// Number of Inpatient Days From previous means test
			// -DONE by either clone step or already exists
			// Number of Outpatient Days From previous means test
			// -DONE by either clone step or already exists
			// Married Last Calendar Year From previous means test
			// -DONE by either clone step or already exists
			// Dependents	All Dependent Information From previous means test
			// -DONE by either clone step or already exists
			// Relationship to Relaxation %	IVM will return a PG8 Indicator
			// -DONE by merge
			// GMT Address From previous means test
			// -done by merge
			// Income Verification IV Status Set to NULL
			resultTest.setIncomeVerificationStatus(null);
			// IV Status Date	Set to NULL
			resultTest.setIncomeVerificationStatusDate(null);
			// Date IVM MT Completed	Set to "current date"
			// -use date from IVM
			// Co-Pay Exemption Test Status	See "Pharmacy Copay Status" field above [set to IVM value]
			// -DONE in merge
			// Effective Date	Same as "Test Effective Date" above
			// -DONE ABOVE. There is only one effective date
			// Completed Date	Same as "Date Test Completed" above
			// -use date from IVM
			// Means Test Status	Received from IVM
			// -DONE in merge
			// Test Determined Status	From previous means test
			// -done in partial merge
			// Effective Date	1/1 (January 1) of calendar year for the income year
			// -use date from IVM
			// Completed Date	 Date conversion received from IVM
			// -use date from IVM
			// Threshold	Means Test	From ES (SDS table)
			if (resultTest.getThresholdA() == null) {
				resultTest.setThresholdA(this.getCalculatedMTThreshold());
			}
			// GMT	Based on GMT Address from previous MT and income year
			if (resultTest.getGmtThresholdAmount() == null && pristineTest != null) {
				resultTest.setGmtThresholdAmount(pristineTest.getGmtThresholdAmount());
			}
			// Net Worth Threshold	From ES (SDS table)
			if (resultTest.getNetworthThreshold() == null) {
				resultTest.setNetworthThreshold(this.getCalculatedNetworthThreshold());
			}
			// Basic Pension	From ES (SDS table)
			if (resultTest.getPensionThreshold() == null) {
				resultTest.setPensionThreshold(getCalculatedPensionThreshold());
			}
			//  Child Income Exclusion	From ES (SDS table)
			if (resultTest.getChildIncomeExclusionThreshold() == null) {
				resultTest.setChildIncomeExclusionThreshold(getCalculatedIncomeExclusionThreshold());
			}
			// Aid & Attendance	From ES (SDS table)
			// PER JOHN INNIS, don't collect
			// Beneficiary Travel	Set to NULL
			VAFacility siteConductingTest = resultTest.getSiteConductingTest();
			BeneficiaryTravel travel = (siteConductingTest != null) ?
					resultPerson.getBeneficiaryTravel(incomingIncomeYear, siteConductingTest) : null;
			if (travel != null) {
				resultPerson.setBeneficiaryTravel(resultTest.getIncomeYear(), siteConductingTest, null);
			}
		}
		// CCR 12345
		// The system will set the date/ time last edited for a converted test to the
		// current system Date/Time.
		// The Last Edit Date actually resides in each incometeststatus
		// so we need to set towards the end of process.
		resultTest.setLastEditedDate(this.getCurrentDate());

		FinancialInputData data = getFinancialInputData();
		if (data != null && data.getIvmFinancialInfo() != null) {
			// CCR 13147 Receive Beneficiary Travel Financial Indicator in ES
			resultTest.setBtFinancialInd(data.getIvmFinancialInfo().getBtFinancialInd());
			// update event data for Z06
			this.updateIVMEventData(data.getIvmFinancialInfo().getIncomeYear(),
					resultTest, pristineTest, data.getIvmFinancialInfo().getIvmActionCode());
		}
	}

	@SuppressWarnings("unused")
	private Date getEffectiveDateForIVMIncomeYear(Integer incomeYear) {
		// IVM conversions are effective on 1st day of current year.
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.set(Calendar.YEAR, incomeYear);
        calendar.add(Calendar.YEAR, 1); // effective date has to be the following calendar year
        calendar.add(Calendar.MONTH, 0);
        calendar.add(Calendar.DAY_OF_MONTH, 0);
        return calendar.getTime();
	}

	public void reverseDoesNotExistMeansTest() throws ServiceException {
		/*
		 * 5.1. Process Reversal
		 * ....
		 * ELSE
		 * If the data received is for a reversal and the conversion was NOT to
		 * an existing means test then delete the cloned/created means test and
		 * make the most recent Primary test the Current test.
		 */
		if (this.getIncomingIncomeYear() == null) return;

		Person result = this.getResultPerson();
		result.setIncomeTest(this.getIncomingIncomeYear(), null);
		if (result.getFinancialStatement(this.getIncomingIncomeYear()) != null) {
			result.setFinancialStatement(this.getIncomingIncomeYear(), null);
		}

		FinancialInputData data = getFinancialInputData();
		if (data != null && data.getIvmFinancialInfo() != null) {
			//CCR 12425 update flags, etc., for Z06 and Z10
			IncomeTest pristineTest = this.getPristinePerson().getIncomeTest(this.getIncomingIncomeYear());
			this.updateIVMEventData(this.getIncomingIncomeYear(), null, pristineTest,
					data.getIvmFinancialInfo().getIvmActionCode());

			IncomeTest current = getHelperService().getCurrentIncomeTest(result);
			if (current != null) {
				// Set year for Z10
				data.getIvmFinancialInfo().setCurrentIncomeYear(current.getIncomeYear());
			}
		}
	}

	@SuppressWarnings("rawtypes")
	public void reverseExistingMeansTest() throws ServiceException {
		/*
		 * PRECONDITIONS: IVM sent reversal and the conversion exists.
		 *
		 * 5.1. Process Reversal
		 * If the data received is for a reversal and the conversion was to an
		 * existing means test then: - Change the test source from IVM to VAMC
		 * if there was a site conducting test OR - Change the test source from
		 * IVM to HEC if the test on file was a HEC test (site conducting test =
		 * null) AND - Recalculate the totals from the saved financial data
		 */
		Integer incomeYear = this.getIncomingIncomeYear();
		if (incomeYear == null) return;

		// Test is from history
		IncomeTest existingBase = this.getConversionBaseTest(incomeYear);
		if (existingBase != null) {
			IncomeTest onFileTest = this.getResultPerson().getIncomeTest(incomeYear);
			if (onFileTest != null) {
				List statuses = null;
				try {
					statuses = getFinancialsService().getIncomeTestStatusesForIVMReversal(
							existingBase.getHistoryId());
				} catch (RuleException e) {
					throw new ServiceException(e);
				}
				IncomeTest conversionTest = (IncomeTest)onFileTest.clone();
				IncomeTestStatus mtIncomeTestStatus = onFileTest.getIncomeTestStatus(IncomeTestType.CODE_MEANS_TEST);
				IncomeTestStatus rxIncomeTestStatus = onFileTest.getIncomeTestStatus(IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST);
				getMergeRuleService().mergeFullIncomeTest(existingBase, onFileTest);

				// ES 4.2_CodeCR13221_CR_VistA to Send BTFI to ES - and - ES to Send BTFI to VistA
				// Have to do manual copy here since BtFinancialInd is in exclusion list of CopyService.
				onFileTest.setBtFinancialInd(existingBase.getBtFinancialInd());

				if (statuses != null && !statuses.isEmpty()) {
					onFileTest.removeAllStatuses();
					Iterator iter = statuses.iterator();
					for (Iterator i = iter ; i.hasNext();) {
						IncomeTestStatus status = (IncomeTestStatus)i.next();
						IncomeTestStatus temp = onFileTest.getIncomeTestStatus(status.getType());
						// Avoid adding duplicates since the statuses were from history
						if (temp == null) {
							// recycle the existing statuses when possible with update
							if (mtIncomeTestStatus != null &&
									status.getType().getCode().equals(mtIncomeTestStatus.getType().getCode())) {
								// restore the status
								onFileTest.addIncomeTestStatus(mtIncomeTestStatus);
								this.getMergeRuleService().mergeIncomeTestStatus(status, mtIncomeTestStatus);
							}
							else
							if (rxIncomeTestStatus != null &&
									status.getType().getCode().equals(rxIncomeTestStatus.getType().getCode())) {
								// restore the status
								onFileTest.addIncomeTestStatus(rxIncomeTestStatus);
								this.getMergeRuleService().mergeIncomeTestStatus(status, rxIncomeTestStatus);
							}
							else {
								// Will allow new status for LTC since that is rare
								IncomeTestStatus newStatus = new IncomeTestStatus();
								onFileTest.addIncomeTestStatus(newStatus);
								this.getMergeRuleService().mergeIncomeTestStatus(status, newStatus);
							}
						}
					}
				}
				else {
					throw new ServiceException( "Failed to find IncomeTestStatus during IVM reversal: income test id: " +
							onFileTest.getEntityKey().getKeyValueAsString());
				}

				FinancialInputData data = getFinancialInputData();
				if (data != null && data.getIvmFinancialInfo() != null) {
					this.updateIVMEventData(incomeYear, onFileTest, conversionTest,
							data.getIvmFinancialInfo().getIvmActionCode());
				}
			}

		}

	}
	/*
	 * The update for BT Financial Indicator occurs only from IVM Bidirectional interface.
	 * Note: The merge will not copy incoming indicator. Must be done separately, hence this
	 * method.
	 *
	 * @see gov.va.med.esr.common.rule.FinancialInput#updateBTFinancialInd()
	 */
	public void updateBTFinancialInd() {
		Integer iy = this.getIncomingIncomeYear();
		if (iy != null) {
			IncomeTest result = getResultIncomeTest();
			FinancialInputData data = getFinancialInputData();
			IVMFinancialInfo info = data != null ? data.getIvmFinancialInfo() : null;
			if ( result != null && info != null) {
				result.setBtFinancialInd(info.getBtFinancialInd());
			}
		}
	}

	public void updateIVMEventData(Integer iy, IncomeTest resultTest, IncomeTest existingTest, String action) {
		FinancialInputData data = this.getFinancialInputData();
		if (data != null) {
			// the existing test can be null
			IncomeTestStatus mtIncomeTestStatus = resultTest != null ?
					resultTest.getIncomeTestStatus(IncomeTestType.CODE_MEANS_TEST) : null;
			IncomeTestStatus mtIncomeTestStatusExisting = existingTest != null ?
					existingTest.getIncomeTestStatus(IncomeTestType.CODE_MEANS_TEST) : null;

			IncomeTestStatus rxIncomeTestStatus = resultTest != null ?
					resultTest.getIncomeTestStatus(IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST) : null;
			IncomeTestStatus rxIncomeTestStatusExisting = existingTest != null ?
					existingTest.getIncomeTestStatus(IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST) : null;

			MeansTestStatus mtStatus = mtIncomeTestStatus != null ? mtIncomeTestStatus.getStatus() : null;
			MeansTestStatus mtStatusExisting = mtIncomeTestStatusExisting != null ? mtIncomeTestStatusExisting.getStatus() : null;

			MeansTestStatus rxStatus = rxIncomeTestStatus != null ?
					rxIncomeTestStatus.getStatus() : null;
			MeansTestStatus rxStatusExisting = rxIncomeTestStatusExisting != null ?
					rxIncomeTestStatusExisting.getStatus() : null;


			if (mtStatus != null) {
				if ("C".equals(action)) {
					data.setMTConversion(true);
				}
				if ("R".equals(action)) {
					data.setMTReversal(true);
				}
			}
			if (rxStatus != null) {
				if ("C".equals(action)) {
					data.setRXConversion(true);
				}
				if ("R".equals(action)) {
					data.setRXReversal(true);
				}
			}

			// Handle reversal where current test was deleted
			if (resultTest == null && "R".equals(action)) {
				if (mtStatusExisting != null) {
					data.setMTReversal(true);
				}
				if (rxStatusExisting != null) {
					data.setRXReversal(true);
				}
			}
		}
	}



	public boolean wasConversionToExistingMeansTest(Integer incomeYear) throws ServiceException {
		// PRECONDITIONS: conversion exists in result person
		IncomeTest baseTest = this.getConversionBaseTest(incomeYear);
		return baseTest != null;
	}

	private IncomeTest getConversionBaseTest(Integer incomeYear) throws ServiceException {
		if (this.conversionBaseTest == null) {
			if (incomeYear == null) return null;
			Person result = this.getResultPerson();
			IncomeTest resultIncomeTest = (result != null)? result.getIncomeTest(incomeYear) : null;
			try {
				this.conversionBaseTest = getFinancialsService().findConversionBaseTest(resultIncomeTest);
			} catch (RuleException e) {
				throw new ServiceException(e);
			}
		}
		return this.conversionBaseTest;
	}
	public boolean getPg8RelaxationIndicator(IncomeTest test) {
    	// CCR 12054 support PG 8 indicator from IVM
    	if (test != null && test.getPg8RelaxationIndicator() != null) {
    		return test.getPg8RelaxationIndicator().booleanValue();
    	}
		return false;
	}

	@SuppressWarnings("rawtypes")
	public boolean hasNoPriorIYTestOrAllPriorTestsMTCopayRequired() throws ServiceException {
    	// CCR 12054 support PG 8 indicator from IVM
        if (getResultPerson() == null) return false;
        if (this.getResultPerson().getIncomeTests() != null &&
        		this.getResultPerson().getIncomeTests().isEmpty()) {
        	// Has no prior income test
        	return true;
        }
        Map incomeTestMap = this.getResultPerson().getIncomeTests();
        Collection incomeTests = incomeTestMap.values();
        Iterator incomeTestsIterator = incomeTests.iterator();
        while (incomeTestsIterator.hasNext()) {
            IncomeTest incomeTest = (IncomeTest) incomeTestsIterator.next();
            String code = this.getMeansTestStatusCode(incomeTest);
            if (!MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getCode().equals(code)) {
            	// Test was not MT Copay Required
                return false;
            }
        }
        // All tests must have been MT Copay Required
        return true;
	}

	public Integer getGMTThreshold(IncomeTest incomeTest) {
        if (incomeTest != null && incomeTest.getGmtThresholdAmount() != null) {
            return new Integer(incomeTest.getGmtThresholdAmount().intValue());
        }
        return null;
    }

    public Integer getMTThreshold(IncomeTest incomeTest) {
        if (incomeTest != null && incomeTest.getThresholdA() != null) {
            return new Integer(incomeTest.getThresholdA().intValue());
        }
        return null;
    }

    public Date getHardshipEffectiveDate() {
        return getHardshipEffectiveDate(getIncomeTest());
    }

    public Date getHardshipEffectiveDate(IncomeTest incomeTest) {
        Hardship hardship = getHardship(incomeTest);
        return (hardship != null) ? hardship.getEffectiveDate() : null;
    }

    public Hardship getHardship(IncomeTest incomeTest) {
        return (incomeTest != null) ? incomeTest.getHardship() : null;
    }

    public void setHardship(Hardship sourceHardship) throws ServiceException {
        IncomeTest test = getResultIncomeTest();
        IncomeTest srcTest = getIncomingIncomeTest();
        Hardship targetHardship = test.getHardship();
        if (targetHardship == null && sourceHardship != null) {
            targetHardship = new Hardship();
            test.setHardship(targetHardship);
            getMergeRuleService().mergeHardship(sourceHardship, targetHardship);
        } else if (targetHardship != null && sourceHardship == null) {
            test.setHardship(null);
            // CR7349: Apply Reqt 4670 - revert Test Determined Status
            IncomeTestStatus status = test
                    .getIncomeTestStatusOfType(IncomeTestType.CODE_MEANS_TEST);
            if (status != null) {
                // Set status back to determined status
                status.setStatus(status.getDeterminedStatus());
            }
        } else if (sourceHardship != null) {
            getMergeRuleService().mergeHardship(sourceHardship, targetHardship);
        }

        // Reqt 5075 - update income test status from the message if the
        // hardship is accepted
        if (sourceHardship != null && srcTest != null){
            //check to makesure the types are same
            if (srcTest.getType().getCode().equals(test.getType().getCode())) {
                test.setIncomeTestStatus(test.getType(),srcTest.getStatus());
            }
        }
    }

    public String getSiteGrantingHardship(IncomeTest incomeTest) {
        Hardship hardship = getHardship(incomeTest);
        VAFacility site = hardship != null ? hardship.getSiteGrantingHardship()
                : null;
        return getLookupCode(site);
    }

    public Date getHardshipReviewDate(IncomeTest incomeTest) {
        Hardship hardship = getHardship(incomeTest);
        return hardship == null ? null : hardship.getReviewDate();
    }

    public boolean isIncomeTestDeleted(String source) {
        return getIncomingIncomeTest() == null;
    }

    public String getMeansTestStatus() {
        return getMeansTestStatusCode(getIncomingIncomeTest(),
                IncomeTestType.CODE_MEANS_TEST);
    }

    public String getPristineMeansTestStatus() {
        return getMeansTestStatusCode(getPristineIncomeTest(),
                IncomeTestType.CODE_MEANS_TEST);
    }

	public boolean isMTCopyStatusChanged() throws ServiceException {
		try {

			// incoming meanstest.
			String incomingMeansTestStatus = getMeansTestStatus();
			// pristine IncomeTest
			IncomeTest pristineIncomeTest = this.getCurrentIncomeTest(this
					.getPristinePerson());

			String pristineMeansTestStatus = "";
			if (pristineIncomeTest != null) {
				// pristineMeansTestStatus not useing the
				// getPristineMeansTestStatus as it give pervious  year meanstest.
				pristineMeansTestStatus = getMeansTestStatusCode(
						pristineIncomeTest, IncomeTestType.CODE_MEANS_TEST);
			}// comparaing
			if (incomingMeansTestStatus != null
					&& pristineMeansTestStatus != null
					&& !pristineMeansTestStatus.equals("")
					&& pristineMeansTestStatus.equals(incomingMeansTestStatus)) {
				return false;
			} else if (incomingMeansTestStatus != null
					&& pristineMeansTestStatus != null
					&& !pristineMeansTestStatus.equals("")
					&& !pristineMeansTestStatus.equals(incomingMeansTestStatus)) {
				return true;
			} else if ((incomingMeansTestStatus != null && (pristineMeansTestStatus == null || !pristineMeansTestStatus
					.equals("")))
					|| (incomingMeansTestStatus == null && (pristineMeansTestStatus != null && !pristineMeansTestStatus
							.equals("")))) {
				return true;

			} else

				return false;

		} catch (ServiceException e) {
			throw new ServiceException(e);
		}
	}
    public boolean isPrimary(IncomeTest incomeTest) {
        if (incomeTest == null)
            return false;
        return Boolean.TRUE.equals(incomeTest.isPrimaryIncomeTest());
    }

    public void setPrimaryInd(IncomeTest test, Boolean primary) {
        if (test != null) {
            test.setPrimaryIncomeTest(primary);
        }
    }

    public boolean isCADAdjusted() {
        return isCADAdjusted(getIncomeTest());
    }

    public boolean isCADAdjusted(IncomeTest incomeTest) {
        if (incomeTest == null)
            return false;
        Boolean cadAdjust = incomeTest.getCadAdjusted();
        return cadAdjust != null && cadAdjust.booleanValue();
    }

    public boolean isAgreetoPayCoPay() {
        return isAgreetoPayCoPay(getIncomeTest());
    }

    public boolean isAgreetoPayCoPay(IncomeTest incomeTest) {
        if (incomeTest == null)
            return false;
        Boolean payDeductible = incomeTest.getAgreesToPayDeductible();
        return (payDeductible != null && payDeductible.booleanValue());
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getIncomeTestSource(gov.va.med.esr.common.model.financials.IncomeTest)
     */
    public IncomeTestSource getIncomeTestSource(IncomeTest incomeTest) {
        return (incomeTest == null ? null : incomeTest.getSource());
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getIncomeTestSourceCode(gov.va.med.esr.common.model.financials.IncomeTest)
     */
    public String getIncomeTestSourceCode(IncomeTest incomeTest) {
        IncomeTestSource testSrc = getIncomeTestSource(incomeTest);
        return (testSrc == null ? null : testSrc.getCode());
    }

    public boolean isTestTypeMeansTest() {
        return isTestTypeMeansTest(getIncomeTest());
    }

    public boolean isTestTypeMeansTest(IncomeTest incomeTest) {
        if (incomeTest == null)
            return false;
        return IncomeTestType.CODE_MEANS_TEST.getName().equals(
                getIncomeTestTypeCode(incomeTest));
    }


    public String getIncomeTestTypeCode(IncomeTest incomeTest) {
        IncomeTestType type = incomeTest == null ? null : incomeTest.getType();
        return type == null ? null : type.getCode();
    }

    public boolean isTestTypePharmacyCopayTest(IncomeTest incomeTest) {
        if (incomeTest == null)
            return false;
        return IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST.getName().equals(
                getIncomeTestTypeCode(incomeTest));
    }

    public String getDeterminedStatus(IncomeTest incomeTest) {
        if (incomeTest == null)
            return null;
        MeansTestStatus mtStatus = incomeTest.getDeterminedStatus();
        return (mtStatus == null ? null : mtStatus.getCode());
    }

    public String getDeterminedStatus() {
        return getDeterminedStatus(getIncomeTest());
    }

    public String getIncomeTestStatus(IncomeTest incomeTest) {
        if (incomeTest == null)
            return null;
        MeansTestStatus status = incomeTest.getStatus();
        return (status == null ? null : status.getCode());
    }

    public boolean isDuplicateIncomeTest() {
        return isIncomeTestDuplicate(getIncomingIncomeTest(),
                getPristineIncomeTest());
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#hasTestonfileForIncomingIncomeYear()
     */
    public boolean hasTestonfileForIncomingIncomeYear() throws ServiceException {
        Integer year = this.getIncomingIncomeYear();
        IncomeTest test = year != null ? this.getPristinePerson()
                .getIncomeTest(year) : null;
        return test != null ? true : false;
    }

    // CR 9803 and CCR 10023
    @SuppressWarnings("rawtypes")
	public boolean checkForPresenceOfIncomeTestMeetingP8RecheckCriteria() throws ServiceException {
        // CCR 10311 - rewrite method to NOT use database query
        if (getResultPerson() == null) return false;

        Map incomeTestMap = this.getResultPerson().getIncomeTests();
        Collection incomeTests = incomeTestMap.values();
        Iterator incomeTestsIterator = incomeTests.iterator();
        while (incomeTestsIterator.hasNext()) {
            IncomeTest incomeTest = (IncomeTest) incomeTestsIterator.next();
            if (incomeTest != null && meetsP8FinancialCriteria(incomeTest)) {
                return true;
            }
        }

        return false;
    }
    @SuppressWarnings("rawtypes")
	public boolean checkForPresenceOfIVMIncomeTestMeetingP8RecheckCriteria() throws ServiceException {
        // CCR 12054 - check tests for one that meets pg8 ivm criteria
    	/**
		 * In step, [3560] Priority and EGT Comparison Decision Table, if the
		 * veteran is being placed in a rejected status due to the table [3563]
		 * then check for the presence of an Income Year 2008 or later income
		 * test where the financial data source is IVM and the means test status
		 * is MT Copay Required and the PG8 indicator = Yes. If a test meeting
		 * these criteria exists, set the Priority Sub-group to “b” if the
		 * veteran is 0% SC and noncompensable or “d” if the veteran is
		 * Nonservice Connected and the veteran shall be continuously enrolled.
		 */
        if (getResultPerson() == null) return false;

        Map incomeTestMap = this.getResultPerson().getIncomeTests();
        Collection incomeTests = incomeTestMap.values();
        Iterator incomeTestsIterator = incomeTests.iterator();
        while (incomeTestsIterator.hasNext()) {
            IncomeTest incomeTest = (IncomeTest) incomeTestsIterator.next();
            if (incomeTest != null && meetsIVMP8FinancialCriteria(incomeTest)) {
                return true;
            }
        }

        return false;
    }
    private boolean meetsP8FinancialCriteria(IncomeTest incomeTest)
            throws ServiceException {
        if (incomeTest == null)
            return false;
        Integer incomeYear = this.getIncomeYear(incomeTest);
        if (incomeYear != null && incomeYear.compareTo(YEAR_2008) >= 0) {
            MeansTestStatus mts = incomeTest.getStatus();
            if (mts != null
                    && (MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
                            .getCode().equals(mts.getCode()) || MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED
                            .getCode().equals(mts.getCode()))) {
                BigDecimal gmtThreshold = incomeTest.getGmtThresholdAmount();
                BigDecimal mtThreshold = incomeTest.getThresholdA();
                String source = getIncomeTestSourceCode(incomeTest);
                if (gmtThreshold != null
                        && mtThreshold != null
                        && Boolean.TRUE.equals(incomeTest
                                .getDiscloseFinancialInformation())
                        && source != null
                        && !IncomeTestSource.CODE_IVM.getCode().equals(source)) {

                    float relaxPercentage = getRelaxPercentage(incomeYear);
                    /*
                     * Notice the use of >=. If the thresholds are EQUAL, which
                     * is not likely, it won't matter which one is used for the
                     * comparison.
                     */
                    if (((gmtThreshold.compareTo(mtThreshold) >= 0) && !isIncomeGreaterThanGMTplusRelaxPercentForCE(
                            relaxPercentage, incomeTest))
                            || ((mtThreshold.compareTo(gmtThreshold) >= 0) && !isIncomeGreaterThanMTTplusRelaxPercentForCE(
                                    relaxPercentage, incomeTest))) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public void addIncomeTestDeletedEvent() {
		Integer incomeYear = this.getIncomingIncomeYear();
		if (incomeYear == null) return;
		this.getResultPerson().setChangeEvent(UseCaseName.INCOMETEST_DELETED, incomeYear.toString());
	}

	@SuppressWarnings({ "rawtypes", "unused" })
	public void postProcessIVMEvents() {
		// CCR 12232
		// Only need one change to occur and we can stop looking.
		Integer incomeYear = this.getIncomingIncomeYear();
		if (incomeYear == null) return;
		FinancialStatement fsResult = this.getResultPerson().getFinancialStatement(incomeYear);
		FinancialStatement fsPristine = this.getPristinePerson().getFinancialStatement(incomeYear);
		if (fsResult != null && fsPristine == null ||
				fsResult == null && fsPristine != null) {
			this.getResultPerson().setChangeEvent(UseCaseName.FIN_DEPENDENT_CHANGE, incomeYear.toString());
			return;
		}
		if (fsResult != null && fsPristine != null) {
			Set updatedDepFins = fsResult.getDependentFinancials();
			Set pristineDepFins = fsPristine.getDependentFinancials();
			if (updatedDepFins.size() != pristineDepFins.size()) {
				this.getResultPerson().setChangeEvent(UseCaseName.FIN_DEPENDENT_CHANGE, incomeYear.toString());
				return;
			}

			if (updatedDepFins.size() == pristineDepFins.size()) {
				Iterator updatedIter = updatedDepFins.iterator();
				Iterator pristineIter = pristineDepFins.iterator();
				while(updatedIter.hasNext()) {
					DependentFinancials dep = (DependentFinancials)updatedIter.next();
					DependentFinancials matched = (DependentFinancials)this.getMergeRuleService().getMatchRuleService().findMatchingElement(
							dep, pristineDepFins);
					if (matched != null) {
						if (ObjectUtils.didObjectChange(matched.getIncapableOfSelfSupport(),
								dep.getIncapableOfSelfSupport()) ||
							ObjectUtils.didObjectChange(matched.getContributedToSupport(),
								dep.getContributedToSupport()) ||
							ObjectUtils.didObjectChange(matched.getHasIncome(), dep.getHasIncome()) ||
							ObjectUtils.didObjectChange(matched.getAmountContributedToSupport(),
								dep.getAmountContributedToSupport()) ||
							ObjectUtils.didObjectChange(matched.getIncomeAvailableToPatient(),
								dep.getIncomeAvailableToPatient())) {
							this.getResultPerson().setChangeEvent(UseCaseName.FIN_DEPENDENT_CHANGE, incomeYear.toString());
							return;
						}

						Dependent depU = dep.getReportedOn();
						Dependent depP = matched.getReportedOn();
						if (ObjectUtils.didObjectChange(depP.getOfficialSsn(), depU.getOfficialSsn()) ||
							ObjectUtils.didObjectChange(depP.getName(),	depU.getName()) ||
							ObjectUtils.didObjectChange(depP.getDob(), depU.getDob()) ||
							ObjectUtils.didObjectChange(depP.getGender(), depU.getGender()) ||
							ObjectUtils.didObjectChange(depP.getRelationship(), depU.getRelationship()) ||
							ObjectUtils.didObjectChange(depP.getStartDate(), depU.getStartDate())) {
							this.getResultPerson().setChangeEvent(UseCaseName.FIN_DEPENDENT_CHANGE, incomeYear.toString());
							return;
						}
					}
				}
			}

		}

		IncomeTest resultTest = this.getResultPerson().getIncomeTest(this.getIncomingIncomeYear());
		IncomeTest pristineTest = this.getPristinePerson().getIncomeTest(this.getIncomingIncomeYear());
		// Other change scenarios detected by advice. Concentrate here on income test status
		if ((resultTest != null && pristineTest == null) ||
				(resultTest == null && pristineTest != null)) {
			this.getResultPerson().setChangeEvent(UseCaseName.FIN_INCOMETEST_CHANGE, incomeYear.toString());
			return;
		}
		if (resultTest != null && pristineTest != null) {
			if (compareIncomeStatusesForIVM(resultTest.getStatuses(), pristineTest.getStatuses())) {
				this.getResultPerson().setChangeEvent(UseCaseName.FIN_INCTSTSTATUS_CHANGE, incomeYear.toString());
				return;
			}
			if (!resultTest.matchesDomainValues(pristineTest)) {
				this.getResultPerson().setChangeEvent(UseCaseName.FIN_INCOMETEST_CHANGE, incomeYear.toString());
			}
		}

	}
	@SuppressWarnings("rawtypes")
	private boolean compareIncomeStatusesForIVM(Set resultSet, Set oldSet) {
		if (resultSet == null && oldSet != null)
			return true;
		if (resultSet != null && oldSet == null)
			return true;
		if (resultSet != null && oldSet != null) {
			if (resultSet.size() != oldSet.size()) {
				return true;
			}
			Iterator resultIter = resultSet.iterator();
			while (resultIter.hasNext()) {
				IncomeTestStatus resultStatus = (IncomeTestStatus) resultIter.next();
				IncomeTestStatus matched = (IncomeTestStatus)this.getMergeRuleService().getMatchRuleService().findMatchingElement(
						resultStatus, oldSet);
				if (matched != null) {
					if (ObjectUtils.didObjectChange(matched.getDeterminedStatus(), resultStatus.getDeterminedStatus()) ||
							ObjectUtils.didObjectChange(matched.getStatus(), resultStatus.getStatus()) ||
							ObjectUtils.didObjectChange(matched.getType(), resultStatus.getType()))
						return true;
				}
				else {
					return true;
				}
			}
		}

		return false;
	}
	private boolean meetsIVMP8FinancialCriteria(IncomeTest incomeTest) throws ServiceException {
    	if (incomeTest == null)
    		return false;
    	Integer incomeYear = this.getIncomeYear(incomeTest);
    	if (incomeYear != null && incomeYear.compareTo(YEAR_2008) >= 0) {
    		MeansTestStatus mts = incomeTest.getStatus();
    		if (mts != null
    				&& (MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED
    						.getCode().equals(mts.getCode()))) {
    			String source = getIncomeTestSourceCode(incomeTest);
    			if (Boolean.TRUE.equals(incomeTest.getDiscloseFinancialInformation())
    				&& source != null && IncomeTestSource.CODE_IVM.getCode().equals(source) &&
    				incomeTest.getPg8RelaxationIndicator() != null &&
    				incomeTest.getPg8RelaxationIndicator().booleanValue()) {
    					return true;
    			}
    		}
    	}
    	return false;
    }
    // CR 9803 and CCR 10023
    @SuppressWarnings("rawtypes")
	public boolean hasVerifiedEnrollmentAndP8Eligible() throws ServiceException {
        List incomeYears = this.getIncomeYearsFromVerifiedEnrollmentDueToP8();

        if (incomeYears != null) {
            for (int i = 0; i < incomeYears.size(); i++) {
                Integer incomeYear = (Integer) incomeYears.get(i);
                IncomeTest it = this.getResultPerson().getIncomeTest(incomeYear);

                if (it != null && meetsP8FinancialCriteria(it)) {
                    return true;
                }
            }
        }
        return false;
    }

    // CR 9803 and CCR 10023
    @SuppressWarnings("rawtypes")
	private List getIncomeYearsFromVerifiedEnrollmentDueToP8() throws ServiceException {
        List incomeYears = null;
        try {
            incomeYears = this.getEligibilityEnrollmentService()
            .getIncomeYearsFromVerifiedEnrollmentDueToP8(
                    this.getIncomingPerson().getEntityKey());

        } catch (RuleException e) {
            throw new ServiceException(e);
        }
        return incomeYears;
    }

    private boolean isIncomeTestDuplicate(IncomeTest test1, IncomeTest test2) {
        // Get the test from Pristine
        String test1FacilityCode = getSiteConductingTestFacilityCode(test1);
        String test2FacilityCode = getSiteConductingTestFacilityCode(test2);
        if (StringUtils.equals(getIncomeTestSourceCode(test1),
                getIncomeTestSourceCode(test2))
                && StringUtils.equals(test1FacilityCode, test2FacilityCode)
                && StringUtils.equals(getIncomeTestTypeCode(test1),
                        getIncomeTestTypeCode(test2))
                && isEquals(getLastEditDate(test1), getLastEditDate(test2))) {
            return true;
        }
        return false;
    }

    public boolean isIncompleteIncomeTest() {
        return (isIncompleteIncomeTest(getIncomeTest()));
    }

    public boolean isIncompleteIncomeTest(IncomeTest incomeTest) {

        if (incomeTest == null)
            return true; // TODO true or false if no test

        String detTestStatus = getDeterminedStatus(incomeTest);

        // Means Test Check
        if (IncomeTestType.CODE_MEANS_TEST.getName().equals(
                getIncomeTestTypeCode(incomeTest))) {
            if (!StringUtils.contains(incompleteMTStatuses, detTestStatus)
                    || getCompletedDate() == null) {
                return true;
            }
        } // Pharmacy Co-Pay test check
        else if (IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST.getName().equals(
                getIncomeTestTypeCode(incomeTest))) {
            if (!StringUtils.contains(incompleteCopayTestStatuses,
                    detTestStatus)
                    || getCompletedDate() == null) {
                return true;
            }
        }

        return false;
    }

    /**
     * Is the incoming test determined to be primary
     */
    public boolean isIncomingTestDeterminedPrimary() {
        if (determinedPrimaryFlag == null) {
            determinedPrimaryFlag = Boolean.valueOf(isDeterminedPrimary(
                    getIncomeTest(), getPristineIncomeTest()));
        }
        return determinedPrimaryFlag.booleanValue();
    }

    /**
     * Determines if the passed in income test can be the primary test.
     *
     * @param incomeTest
     *            The new income test.
     * @param pristineIncomeTest
     *            The on-file income test.
     * @return True if new incomeTest can be the primary one or false if not.
     */
    private boolean isDeterminedPrimary(IncomeTest incomeTest,
            IncomeTest pristineIncomeTest) {
        // 33.4.1.1 ??
        String type = getIncomeTestTypeCode(incomeTest);
        if (type == null) {
            return false;
        }

        // check for non primary types (LTC type 3 & 4)
        if (isNonPrimaryType(type)) {
            return false;
        }
        // No existing primary test
        if (isTestNonPrimary(pristineIncomeTest)) {
            return true;
        }

        // check type and financial disclosure or agree to pay indicators are
        // different
        if (IncomeTestType.CODE_MEANS_TEST.getName().equals(
                incomeTest.getType().getCode())) {

            if (!isEquals(getAgreeToPayIndicator(incomeTest),
                    getAgreeToPayIndicator(pristineIncomeTest))
                    || !isEquals(getDisclosure(incomeTest),
                            getDisclosure(pristineIncomeTest))) {
                return true;
            }
            // MT takes precedence over Pharmacy copay test
            String pristineIncomeTestType = getIncomeTestTypeCode(pristineIncomeTest);
            if (IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST.getName().equals(
                    pristineIncomeTestType)) {
                return true;
            }
        }

        // If income test is pharmacy co-pay and the on-file one is means test,
        // accept it
        // only if the on-file has a means test status of No Longer Required.
        if ((IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST.getName()
                .equals(incomeTest.getType().getCode()))
                && (IncomeTestType.CODE_MEANS_TEST.getName()
                        .equals(pristineIncomeTest.getType().getCode()))) {
            IncomeTestStatus onFileStatus = pristineIncomeTest
                    .getIncomeTestStatus();
            if (onFileStatus != null && onFileStatus.getStatus() != null) {
                if (MeansTestStatus.MT_STATUS_NO_LONGER_REQUIRED.getName()
                        .equals(onFileStatus.getStatus().getCode())) {
                    return true;
                }
            }
        }

        // if the test are of the same type cmpare the sites
        if (StringUtils.equals(getIncomeTestTypeCode(incomeTest),
                getIncomeTestTypeCode(pristineIncomeTest))) {

            // Check the sites first.
            if (StringUtils.equals(
                    getSiteConductingTestFacilityCode(incomeTest),
                    getSiteConductingTestFacilityCode(pristineIncomeTest))) {
                Date effectiveDate =  getEffectiveTestDate(incomeTest);
                if (effectiveDate != null
                        && !effectiveDate.after(getCurrentDate())) {
                    return true;
                }
            }
            // check the source of test
            if (IncomeTestSource.CODE_HEC.getName().equals(
                    getIncomeTestSourceCode(incomeTest))) {
                return true;
            }
            if (!StringUtils.equals(getIncomeTestSourceCode(incomeTest),
                    getIncomeTestSourceCode(pristineIncomeTest))) {
                // HEC takes precedence over VAMC
                if (IncomeTestSource.CODE_VAMC.getName().equals(
                        getIncomeTestSourceCode(incomeTest))
                        && IncomeTestSource.CODE_HEC.getName().equals(
                                getIncomeTestSourceCode(pristineIncomeTest))
                        && !isPreferred(pristineIncomeTest, incomeTest)) {
                    return true;
                }
            }
            // Same type and same source
            if (StringUtils.equals(getIncomeTestSourceCode(incomeTest),
                    getIncomeTestSourceCode(pristineIncomeTest))) {
                // select the one with earlist effective date
                boolean incomingIsBefore = isBefore(
                        getEffectiveTestDate(incomeTest),
                        getEffectiveTestDate(pristineIncomeTest));
                if (incomingIsBefore
                        || (!incomingIsBefore && !isPreferred(
                                pristineIncomeTest, incomeTest))) {
                    if (isPreferred(incomeTest,pristineIncomeTest)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public boolean isNonPrimaryType(String type) {
        return IncomeTestType.CODE_LTC_CO_PAY_EXEMPTION_TEST.getName().equals(
                type)
                || IncomeTestType.CODE_LTC_CO_PAY_TEST.getName().equals(type);
    }

    public boolean isTestNonPrimary(IncomeTest test) {
        return test == null || test.getType() == null
                || test.getType().getCode() == null
                || isNonPrimaryType(test.getType().getCode());
    }

    /**
     * Test does not get precedence if its status is MT Status = Required or is
     * a Pharmacy Copay Test with incomplete status.
     *
     * @param test1
     * @param test2
     * @return True if the income test is preferred or false if not.
     */
    public boolean isPreferred(IncomeTest test1, IncomeTest test2) {
        if (MeansTestStatus.MT_STATUS_REQUIRED.getName().equals(
                getIncomeTestStatus(test1))
                || (isTestTypePharmacyCopayTest(test1) && MeansTestStatus.MT_STATUS_INCOMPLETE_RX_COPAY_ONLY
                        .getName().equals(getIncomeTestStatus(test1)))) {
            return false;
        } else {
            return true;
        }
    }

    public void mergeIncomeTestStatus(IncomeTestType.Code typeCode)
            throws ServiceException {
        IncomeTest incomingTest = getIncomingIncomeTest();
        IncomeTest resultTest = getResultIncomeTest();

        IncomeTestStatus resultStatus = resultTest != null ? resultTest
                .getIncomeTestStatus(typeCode) : null;
        IncomeTestStatus incomingStatus = incomingTest != null ? incomingTest
                .getIncomeTestStatus(typeCode) : null;
        if (incomingStatus == null && resultStatus != null) {
            resultTest.removeStatus(resultStatus);
        } else if (incomingStatus != null && resultStatus == null) {
            resultStatus = new IncomeTestStatus();
            resultTest.addStatus(resultStatus);
            getMergeRuleService().mergeIncomeTestStatus(incomingStatus,
                    resultStatus);
        } else if (incomingStatus != null && resultStatus != null) {
            getMergeRuleService().mergeIncomeTestStatus(incomingStatus,
                    resultStatus);
        }
    }

    public void processFinancialStatement() throws ServiceException {
        try {
            FinancialStatement inStmt = getIncomingFinancialStatement();
            FinancialStatement pristineStmt = getPristineFinancialStatement();
            if (inStmt == null && pristineStmt != null) {
                // delete request
                setFinancialStatement(null);
            } else if (inStmt != null) {
                FinancialStatement rsStmt = getOrCreateFinancialStatement();
                // getMergeRuleService().mergeFinancialStatement(inStmt,
                // rsStmt);
                // fix for CR_8881
                getMergeRuleService().mergeFinancialStatement(inStmt, rsStmt,
                        this.isDataFromZ07);
                getFinancialsHelperService().processSsn(rsStmt);
                getFinancialsHelperService().updatePseudoSsnClock(
                        getResultPerson(), rsStmt);
                financialStatementCopied = true;
            }
        } catch (Exception e) {
            if(logger.isErrorEnabled()){
                logger.error(e);
            }
            throw new ServiceException(e);
        }
    }

    public void setFinancialStatement(FinancialStatement stmt)
            throws ServiceException {
        getResultPerson().setFinancialStatement(getIncomingIncomeYear(), stmt);
        getFinancialsHelperService().updatePseudoSsnClock(getResultPerson(),
                stmt);
    }

    // PatientVisitSummary related Methods
    public void setPatientVisitSummary(PatientVisitSummary visitSummary) {
        getResultPerson().setPatientVisitSummary(getIncomingIncomeYear(),
                getSourceFacility(), visitSummary);
    }

    public void processPatientVisitSummary() throws ServiceException {
        PatientVisitSummary incomingVisitSummary = getPatientVisitSummary(getIncomingPerson());
        PatientVisitSummary pristineVisitSummary = getPatientVisitSummary(getPristinePerson());
        if (incomingVisitSummary == null && pristineVisitSummary != null) {
            // delete request.
            setPatientVisitSummary(null);
        } else if (incomingVisitSummary != null) {
            getMergeRuleService().mergePatientVisitSummary(
                    incomingVisitSummary, getOrCreatePatientVisitSummary());
        }
    }

    public Date getDateOfLastVisit() {
        PatientVisitSummary visitSummary = null;

        // Operates in different contexts
        if (this.getFinancialInputData() != null) {
            visitSummary = this.getPatientVisitSummary(getIncomingPerson());
        } else if (this.getEEInputData() != null) {
            IncomeTest incomeTest = getHelperService().getCurrentIncomeTest(
                    getIncomingPerson());
            if (incomeTest != null) {
                Integer year = incomeTest.getIncomeYear();
                VAFacility facility = incomeTest.getSiteConductingTest();
                if (year != null && facility != null) {
                    visitSummary = this.getIncomingPerson()
                            .getPatientVisitSummary(year, facility);
                }
            }

        }

        return visitSummary != null ? visitSummary.getLastVisitDate() : null;
    }

    public PatientVisitSummary getPatientVisitSummary(Person person) {
        VAFacility facility = getSourceFacility();
        Integer incomeYear = getIncomingIncomeYear();
        if (person != null && facility != null && incomeYear != null) {
            return person.getPatientVisitSummary(incomeYear, facility);
        }
        return null;
    }

    private PatientVisitSummary getOrCreatePatientVisitSummary() {
        VAFacility facility = getSourceFacility();
        Integer incomeYear = getIncomingIncomeYear();
        PatientVisitSummary visitSummary = getResultPerson()
                .getPatientVisitSummary(incomeYear, facility);
        if (visitSummary == null) {
            visitSummary = new PatientVisitSummary();
            visitSummary.setFacilityVisited(facility);
            visitSummary.setIncomeYear(incomeYear);
            getResultPerson().setPatientVisitSummary(incomeYear, facility,
                    visitSummary);
        }
        return visitSummary;
    }

    // BeneficiaryTravel related methods.
    public BeneficiaryTravel getBeneficiaryTravel(Person person) {
        VAFacility facility = getSourceFacility();
        Integer incomeYear = getIncomingIncomeYear();
        if (person != null && facility != null && incomeYear != null) {
            return person.getBeneficiaryTravel(incomeYear, facility);
        }
        return null;
    }

    private BeneficiaryTravel getOrCreateBeneficiaryTravel() {
        VAFacility facility = getSourceFacility();
        Integer incomeYear = getIncomingIncomeYear();
        BeneficiaryTravel bt = getResultPerson().getBeneficiaryTravel(
                incomeYear, facility);
        if (bt == null) {
            bt = new BeneficiaryTravel();
            bt.setFacilityVisited(facility);
            bt.setYear(incomeYear);
            getResultPerson().setBeneficiaryTravel(incomeYear, facility, bt);
        }
        return bt;
    }

    public void setBeneficiaryTravel(BeneficiaryTravel travel) {
        getResultPerson().setBeneficiaryTravel(getIncomingIncomeYear(),
                getSourceFacility(), travel);
    }

    public void processBeneficiaryTravel() throws ServiceException {
        BeneficiaryTravel incomingBTravel = getBeneficiaryTravel(getIncomingPerson());
        BeneficiaryTravel pristineBTravel = getBeneficiaryTravel(getPristinePerson());
        boolean btChanged = false;
    	BeneficiaryTravel btOnFile = null;
        if (incomingBTravel == null && pristineBTravel != null) {
            // delete request.
            setBeneficiaryTravel(null);
        } else if (incomingBTravel != null
                && incomingBTravel.getClaimDate() == null) {
            // delete request.
            setBeneficiaryTravel(null);
        } else if (incomingBTravel != null) {
        	btOnFile = getOrCreateBeneficiaryTravel();
        	Boolean btIndIncoming = (incomingBTravel.isEligible() != null) ? incomingBTravel.isEligible().booleanValue() : null;
        	Boolean btIndOnFile = (btOnFile.isEligible() != null) ? btOnFile.isEligible().booleanValue() : null;
            getMergeRuleService().mergeBeneficiaryTravel(incomingBTravel, btOnFile);
            // CCR 13127 bt trigger for Null to Yes/No; Yes to No; No to Yes
            if (btIndOnFile == null && btIndIncoming != null)
            	btChanged = true;
            else if (btIndOnFile != null && btIndIncoming != null)
            	btChanged = !btIndOnFile.equals(btIndIncoming);
        }
        if (btChanged && btOnFile != null) {
            // CCR 13127 bt trigger
        	String incomingYear = this.getIncomingIncomeYear() != null ? this.getIncomingIncomeYear().toString() : null;
        	String year = btOnFile.getYear() != null ? btOnFile.getYear().toString() : incomingYear;
        	// Need non-null income year otherwise we ignore change
        	if (year != null) {
        		this.getResultPerson().setChangeEvent(UseCaseName.BT_IND_CHANGE, year);
        	}
        }
    }

    @SuppressWarnings("unused")
	private boolean areCodeValuesEqual(AbstractLookup a, AbstractLookup b) {
        if (a == null && b == null) {
            return true;
        }
        if ((a == null && b != null) || (b == null && a != null)) {
            return false;
        }
        return this.isEqual(a.getCode(), b.getCode());
    }

    /**
     * UC 33.7 Determine if Test is Effective Immediately
     *
     * @throws ServiceException
     */
    public boolean processEffectiveImmediately() throws ServiceException {
    	// CCR 11781 - makes all tests with the following statuses effective immediately
    	// 4.30 [4662] Determine if Test is Effective Immediately
    	/*
		 * SUC100.7 A new Means Test received from a site or the UI for a later
		 * income year where the MT status is MT Copay Exempt, GMT Copay
		 * Required, MT Copay Required or Pending Adjudication will become
		 * effective immediately.
		 *
		 * SUC100.7.1 A new pharmacy copay test from a site or the UI for a
		 * later income year where the pharmacy (Rx) Copay status is Exempt or
		 * Nonexempt will become effective immediately.
		 *
		 */
    	IncomeTest currentTest = getCurrentIncomeTest();
    	IncomeTest incomingIncomeTest = getIncomingIncomeTest();

    	if (currentTest == null) { // there is no current test.
    		return true;
    	}
    	if (isTestTypeMeansTest(currentTest) && isTestTypeMeansTest(incomingIncomeTest)) {
    		String incomingStatus = getMeansTestStatusCode(incomingIncomeTest);
    		Integer currentYear = currentTest.getIncomeYear();
    		Integer incomingYear = incomingIncomeTest.getIncomeYear();
    		boolean yearIsLater = (currentYear != null && incomingYear != null) ?
    				(incomingYear.compareTo(currentYear) > 0 ? true : false) : false;

    		String rul1Statuses[] = {
    				MeansTestStatus.MT_STATUS_MT_COPAY_EXEMPT.getName(),
    				MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName(),
    				MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName(),
    				MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION.getName() };

    		if (yearIsLater && StringUtils.contains(rul1Statuses, incomingStatus)) {
    			return true;
    		}
    	} else if (isTestTypePharmacyCopayTest(incomingIncomeTest)) {
    		String incomingStatus = getPharmacyCopayStatusCode(incomingIncomeTest);
    		Integer currentYear = currentTest.getIncomeYear();
    		Integer incomingYear = incomingIncomeTest.getIncomeYear();
    		boolean yearIsLater = (currentYear != null && incomingYear != null) ?
    				(incomingYear.compareTo(currentYear) > 0 ? true : false) : false;

    		if (yearIsLater && (MeansTestStatus.MT_STATUS_NON_EXEMPT.getName().equals(incomingStatus)  ||
    				MeansTestStatus.MT_STATUS_EXEMPT.getName().equals(incomingStatus))) {
    			return true;
    		}
    	}

    	return false;
//        // TODO what is computed status and incomg status are they different
//        IncomeTest currentTest = getCurrentIncomeTest();
//        IncomeTest resultIncomeTest = getIncomingIncomeTest();
//        IncomeTest onFileIncomeTest = this.getResultIncomeTest();
//
//        if (currentTest == null) { // there is no current test.
//            return true;
//        }
//
//        //If we are editing a future test from GUI skip next two checks
//        if (isUpdateFromGUI() && resultIncomeTest.isFutureTest() != null
//                && resultIncomeTest.isFutureTest().booleanValue()) {
//            //next two rules are specific to messaging
//        } else {
//            if (isIncomeTestDuplicate(resultIncomeTest, currentTest)) {
//                return false;
//            }
//            /*
//             * CR 8672 - multiple issues- the issue below based on tentative
//             * requirements change allows Effective Imm rules to be applied to
//             * new/existing income year.
//             */
//            // IncomeYear New ??
////          if (!isTestNonPrimary(getPristineIncomeTest())) {
////              return false;
////          }
//        }
//
//        if (isTestTypeMeansTest(currentTest)
//                && isTestTypeMeansTest(resultIncomeTest)) {
//
//            String currentStatus = getMeansTestStatusCode(currentTest);
//            String incomingStatus = getMeansTestStatusCode(resultIncomeTest);
//            // String determinedIncomingStatus = getDeterminedStatus(incoming);
//            String currentRxCopayStatus = getPharmacyCopayStatusCode(currentTest);
//            String incomingRxCopayStatus = getPharmacyCopayStatusCode(resultIncomeTest);
//
//            String rul1Statuses[] = {
//                    MeansTestStatus.MT_STATUS_MT_COPAY_EXEMPT.getName(),
//                    MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName(),
//                    MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName(),
//                    MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION.getName() };
//
//            if (isExpired(currentTest)) {
//                // CR3957 - New rule added before rule 1.
//                if (MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName()
//                        .equals(currentStatus)
//                        && StringUtils.contains(rul1Statuses, incomingStatus)) {
//                    return true;
//                }
//            } else { // non expired tests - eff date < 365 days old
//
//                // If there is a change in Source, Site, date/Time Last Edited,
//                // or Type of Test AND
//                // The current Primary Test is Means Test AND
//                // The Type of Test Received/or being manually Added or edited
//                // is Means
//                // Test AND
//                // The Current MT Status is MT Copay Required AND
//                // The HL7 MT Status or MT Status computed during manual entry
//                // is MT
//                // Copay Required
//
//                logger.debug("currentTest.getSource() "
//                        + ((currentTest.getSource() == null) ? null
//                                : currentTest.getSource().getCode()));
//                logger.debug("resultIncomeTest.getSource() "
//                        + ((resultIncomeTest.getSource() == null) ? null
//                                : resultIncomeTest.getSource().getCode()));
//                logger
//                        .debug("currentTest.getSiteConductingTest() "
//                                + ((currentTest.getSiteConductingTest() == null) ? null
//                                        : currentTest.getSiteConductingTest()
//                                                .getCode()));
//                logger
//                        .debug("resultIncomeTest.getSiteConductingTest() "
//                                + ((resultIncomeTest.getSiteConductingTest() == null) ? null
//                                        : resultIncomeTest
//                                                .getSiteConductingTest()
//                                                .getCode()));
//                logger.debug("currentTest.getType() "
//                        + ((currentTest.getType() == null) ? null : currentTest
//                                .getType().getCode()));
//                logger.debug("resultIncomeTest.getType() "
//                        + ((resultIncomeTest.getType() == null) ? null
//                                : resultIncomeTest.getType().getCode()));
//                logger.debug("currentTest.getEffectiveDate() "
//                        + ((currentTest.getEffectiveDate() == null) ? null
//                                : currentTest.getEffectiveDate()));
//                logger.debug("currentTest.getStatus().getCode() "
//                        + ((currentTest.getStatus() == null) ? null
//                                : currentTest.getStatus().getCode()));
//                logger.debug("resultIncomeTest.getStatus().getCode()"
//                        + ((resultIncomeTest.getStatus() == null) ? null
//                                : resultIncomeTest.getStatus().getCode()));
//                logger.debug("onFileIncomeTest.getStatus().getCode() "
//                        + ((onFileIncomeTest.getStatus() == null) ? null
//                                : onFileIncomeTest.getStatus().getCode()));
//
//                Integer resultGMTThreshold = getCalculatedGMTThreshold();
//                Integer resultMTThreshold = isUpdateFromGUI() ? new Integer(
//                        getNotNull(getCalculatedMTThreshold()).intValue())
//                        : getMTThreshold(resultIncomeTest);
//                Integer currentGMTThreshold = getGMTThreshold(currentTest);
//                Integer currentMTThreshold = getMTThreshold(currentTest);
//
//                resultGMTThreshold = resultGMTThreshold == null ? new Integer(0)
//                        : resultGMTThreshold;
//                resultMTThreshold = resultMTThreshold == null ? new Integer(0)
//                        : resultMTThreshold;
//                currentGMTThreshold = currentGMTThreshold == null ? new Integer(
//                        0)
//                        : currentGMTThreshold;
//                currentMTThreshold = currentMTThreshold == null ? new Integer(0)
//                        : currentMTThreshold;
//
//                /*
//                 * CODECR 8672 Changes needed under codeCR 8672 for CR 9584: IF
//                 * (current MT status = MT Copay Required OR current MT status =
//                 * Pending Adjudication where the GMTT<MTT) AND (incoming MT
//                 * status = MT Copay Required OR incoming MT status = Pending
//                 * Adjudication where the GMTT<MTT) AND (incoming income is
//                 * Below the MT Threshold OR incoming income <= 10% above MT or
//                 * GMT Threshold (whichever is higher)) THEN make effective END
//                 * IF
//                 *
//                 * NOTE: the logic below will not consider the condition about
//                 * "incoming income is Below the MT Threshold" since this is
//                 * already covered by existing logic.
//                 *
//                 * modifying RELAX_PERCENT to use getRelaxPercentage() for CR9803
//                 */
//                boolean currentStatusEqualsPendingAdj = MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
//                        .getName().equals(currentStatus);
//                boolean incomingStatusEqualsPendingAdj = MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
//                        .getName().equals(incomingStatus);
//                boolean currentStatusPendingAdjWhereGMTTlessThanMTT = currentStatusEqualsPendingAdj
//                        && (currentGMTThreshold.intValue() < currentMTThreshold
//                                .intValue());
//                boolean incomingStatusPendingAdjWhereGMTTlessThanMTT = incomingStatusEqualsPendingAdj
//                        && (resultGMTThreshold.intValue() < resultMTThreshold
//                                .intValue());
//
//                if ((!isEquals(getLastEditDate(currentTest),
//                        getLastEditDate(resultIncomeTest))
//                        || !areCodeValuesEqual(currentTest.getSource(),
//                                resultIncomeTest.getSource())
//                        || !areCodeValuesEqual(currentTest
//                                .getSiteConductingTest(), resultIncomeTest
//                                .getSiteConductingTest()) || !areCodeValuesEqual(
//                        currentTest.getType(), resultIncomeTest.getType()))
//                        && isTestTypeMeansTest(currentTest)
//                        && isTestTypeMeansTest(resultIncomeTest)
//                        && (currentStatusPendingAdjWhereGMTTlessThanMTT || MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED
//                                .getName().equals(
//                                        currentTest.getStatus().getCode()))
//                        && (incomingStatusPendingAdjWhereGMTTlessThanMTT
//                                || MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED
//                                        .getName().equals(
//                                                resultIncomeTest.getStatus()
//                                                        .getCode()) || MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED
//                                .getName().equals(
//                                        onFileIncomeTest.getStatus().getCode()))) {
//
//                    Integer incomeYear = getIncomeYear(resultIncomeTest);
//                    // fix for 8826
//                    if ((currentStatusPendingAdjWhereGMTTlessThanMTT || MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED
//                            .getName().equals(
//                                    getMeansTestStatusCode(currentTest)))
//                            && (incomingStatusPendingAdjWhereGMTTlessThanMTT || MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED
//                                    .getName()
//                                    .equals(
//                                            getMeansTestStatusCode(resultIncomeTest)))
//                            && this.isGreaterThan(incomeYear, 2007)) {
//                        Date today = new Date();
//                        // CCR10057 fix uncovered null pointer error below. Have
//                        // to use different methods for UI versus Messaging
//                        boolean p8CriteriaMet = false;
//
//                        float relaxPercentage = getRelaxPercentage(incomeYear);
//
//                        if (isUpdateFromGUI()) {
//                            if (YES_INDICATOR.equals(getDisclosure())
//                                    && (!isIncomeGreaterThanGMTplusRelaxPercent(relaxPercentage) || !isIncomeGreaterThanMTTplusRelaxPercent(relaxPercentage))) {
//                                p8CriteriaMet = true;
//                            }
//                        } else {
//                            // For Messages, borrow CE methods since they don't
//                            // recalculate and cause null pointer
//                            if (YES_INDICATOR.equals(getDisclosure())
//                                    && (!isIncomeGreaterThanGMTplusRelaxPercentForCE(relaxPercentage) || !isIncomeGreaterThanMTTplusRelaxPercentForCE(relaxPercentage))) {
//                                p8CriteriaMet = true;
//                            }
//                        }
//                        if (p8CriteriaMet) {
//                            if (this.isAfter(resultIncomeTest
//                                    .getEffectiveDate(), today)) {
//                                resultIncomeTest.setEffectiveDate(new Date());
//                                onFileIncomeTest.setEffectiveDate(new Date());
//                            }
//                            return true;
//                        }
//                    }
//
//                }
//
//                // Rule 1
//                if (MeansTestStatus.MT_STATUS_NO_LONGER_REQUIRED.getName()
//                        .equals(currentStatus)
//                        && StringUtils.contains(rul1Statuses, incomingStatus)) {
//                    return true;
//                }
//
//                // Rule 2
//                if (StringUtils.equals(currentStatus, incomingStatus)) {
//                    if (MeansTestStatus.MT_STATUS_NON_EXEMPT.getName().equals(
//                            currentRxCopayStatus)
//                            && MeansTestStatus.MT_STATUS_EXEMPT.getName()
//                                    .equals(incomingRxCopayStatus)) {
//                        return true;
//                    }
//                }
//
//                // Rule 3
//                String rul3Statuses[] = {
//                        MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName(),
//                        MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName(),
//                        MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
//                                .getName() };
//
//                if (StringUtils.contains(rul3Statuses, currentStatus)
//                        && MeansTestStatus.MT_STATUS_MT_COPAY_EXEMPT.getName()
//                                .equals(incomingStatus)) {
//                    return true;
//                }
//
//                // Rule 4
//
//                // CodeCR10367 It appears that there is a hole in the ‘Is Effective Immediately’
//                // table  (see Process Financial and Manage Financial UCSs).
//                // TBL406 5 will be modified to include Pending Adjudication
//                // in column "Current MT Status (EDB)".
//
//                String rul4Statuses[] = {
//                		MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName(),
//                		MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION.getName()};
//
//                if (StringUtils.contains(rul4Statuses, currentStatus)
//                        && MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED
//                                .getName().equals(incomingStatus)) {
//                    return true;
//                }
//
//                // Rule 5
////                Integer resultGMTThreshold = getCalculatedGMTThreshold();
//                // Integer resultMTThreshold = isUpdateFromGUI() ? new
//                // Integer(getNotNull(getCalculatedMTThreshold()).intValue()) :
//                // getMTThreshold(resultIncomeTest);
////                Integer currentGMTThreshold = getGMTThreshold(currentTest);
////                Integer currentMTThreshold = getMTThreshold(currentTest);
////
//                // resultGMTThreshold = resultGMTThreshold == null ? new
//                // Integer(0) : resultGMTThreshold;
//                // resultMTThreshold = resultMTThreshold == null ? new
//                // Integer(0) : resultMTThreshold;
//                // currentGMTThreshold = currentGMTThreshold == null ? new
//                // Integer(0) : currentGMTThreshold;
//                // currentMTThreshold = currentMTThreshold == null ? new
//                // Integer(0) : currentMTThreshold;
//
//                if (MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName()
//                        .equals(currentStatus)
//                        && MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
//                                .getName().equals(incomingStatus)) {
//                    // Implement GMT Threshold > MT Threshold
//                    if (resultGMTThreshold.intValue() > resultMTThreshold
//                            .intValue()) {
//                        return true;
//                    }
//                }
//
//                // Rule 6
//                if (MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION.getName()
//                        .equals(currentStatus)
//                        && MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
//                                .getName().equals(incomingStatus)) {
//                    // Implement GMT Threshold > MT Threshold
//                    if (currentGMTThreshold.intValue() <= currentMTThreshold
//                            .intValue()
//                            && resultGMTThreshold.intValue() > resultMTThreshold
//                                    .intValue()) {
//                        return true;
//                    }
//                }
//            }
//        }
//
//        // 7,8,9
//        if (isTestTypePharmacyCopayTest(currentTest) && !isExpired(currentTest)
//                && isTestTypePharmacyCopayTest(resultIncomeTest)) {
//            // process
//            String currentStatus = getPharmacyCopayStatusCode(currentTest);
//            String incomingStatus = getPharmacyCopayStatusCode(resultIncomeTest);
//
//            // rules 7 & 8
//            if (MeansTestStatus.MT_STATUS_INCOMPLETE_RX_COPAY_ONLY.getName()
//                    .equals(currentStatus)) {
//                if (MeansTestStatus.MT_STATUS_NON_EXEMPT.getName().equals(
//                        incomingStatus)
//                        || MeansTestStatus.MT_STATUS_EXEMPT.getName().equals(
//                                incomingStatus)) {
//                    return true;
//                }
//
//            }
//            // rule 9
//            if (MeansTestStatus.MT_STATUS_NON_EXEMPT.getName().equals(
//                    currentStatus)) {
//                if (MeansTestStatus.MT_STATUS_EXEMPT.getName().equals(
//                        incomingStatus)) {
//                    return true;
//                }
//            }
//        }
//
//        // CodeCR7300 - If the primary test types are different then the new
//        // test should be current
//        if (currentTest.getType() != null
//                && !currentTest.getType().equals(resultIncomeTest.getType())) {
//            return true;
//        }
//
//        return false;
    }

    /**
     * Verify whether the given test is expired or not
     *
     * @param incomeTest
     * @return True if the income test is expired or false if not.
     */
    public boolean isExpired(IncomeTest incomeTest) {
        // TODO check for specific rules if any exist as default
        if (incomeTest != null) {
            Date today = getCurrentDate();
            Date effDate = getEffectiveTestDate(incomeTest);
            // expiry = effective date + one year
            Date expDate = effDate == null ? null : addDaysToDate(effDate, 365);

            // Check with the current date to see expired or not
            if (expDate != null && expDate.before(today)) {
                return true;
            }
        }
        return false;
    }

    public void processHardship() throws ServiceException {
        Hardship currentHardship = getHardship(getPristineIncomeTest());
        Hardship incomingHardship = getHardship(getIncomeTest());
        Date currentEffectiveDate = currentHardship == null ? null
                : currentHardship.getEffectiveDate();
        Date incomingEffectiveDate = incomingHardship == null ? null
                : incomingHardship.getEffectiveDate();

        // All rules except delete
        if (incomingEffectiveDate != null) {
            // Rule 2 (ignore 1)
            if (currentEffectiveDate == null
                    && !isEarlier(incomingEffectiveDate,
                            getEffectiveTestDate(getResultIncomeTest()))) {
                setHardship(incomingHardship);
            }

            // rule 6 (ignore 3,4)
            if (currentEffectiveDate != null
                    && isSiteGrantingHardshipSameAsSiteTransmittingData(currentHardship)
                    && (!isEquals(currentEffectiveDate, incomingEffectiveDate) || !isEquals(
                            currentHardship.getReviewDate(), incomingHardship
                                    .getReviewDate()))) {
                setHardship(incomingHardship);
            }
        }
        // Rule 7
        else if (incomingEffectiveDate == null
                && currentEffectiveDate != null
                && isSiteGrantingHardshipSameAsSiteTransmittingData(currentHardship)) {
            // allow delete
            setHardship(null);
        }
    }

    private boolean isSiteGrantingHardshipSameAsSiteTransmittingData(
            Hardship currentHardship) {
        return isEqual(
                getLookupCode(currentHardship.getSiteGrantingHardship()),
                getReceivingFacilityCode());
    }

    public Date addYearToDate(Date date) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.YEAR, 1);
        if (calendar.get(Calendar.MONTH) == Calendar.FEBRUARY
                && calendar.get(Calendar.DAY_OF_MONTH) == 29) {
            calendar.set(Calendar.DAY_OF_MONTH, 28);
        }
        return calendar.getTime();
    }

    public Date addDaysToDate(Date date, int days) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, days);
        return calendar.getTime();
    }

    private Address getFacilityAddress(VAFacility facility) {
        if (facility != null && facility.getStreetAddress() != null) {
            VAFacility.VAFacilityAddress facilityAddr = facility
                    .getStreetAddress();

            Address siteAddress = new Address();
            siteAddress.setLine1(facilityAddr.getLine1());
            siteAddress.setLine2(facilityAddr.getLine2());
            siteAddress.setLine3(facilityAddr.getLine3());
            siteAddress.setCity(facilityAddr.getCity());
            siteAddress.setState(facilityAddr.getState() == null ? null
                    : facilityAddr.getState().getCode());
            siteAddress.setZipCode(facilityAddr.getZipCode());
            return siteAddress;
        }
        return null;
    }

    private VAFacility getRecentMsgTransmittedFacility()
            throws ServiceException {
        return getMessagingSevice().findSiteLastTransmittedMsg(
                getResultPerson().getEntityKey());
    }

    public boolean getDeterminedPrimaryFlag() {
        return isIncomingTestDeterminedPrimary();
    }

    public void setDeterminedPrimaryFlag(boolean determinedPrimaryFlag) {
        this.determinedPrimaryFlag = Boolean.valueOf(determinedPrimaryFlag);
    }

    // Required methods for is Financials can be added/edited
    // Verify whether the effective date is older than 365 days (1 year)
    public boolean isCurrentTestOlderThan365Days() throws ServiceException {
        return getCurrentIncomeTest() != null
                && isBefore(addYearToDate(getCurrentIncomeTest()
                        .getEffectiveDate()), new Date());
    }

    // Check whether the Harhship is granted for the incometest
    public boolean isHardShipGranted(IncomeTest incomeTest) {
        Hardship hardShip = getHardship(incomeTest);
        if (hardShip != null && hardShip.getHardshipGranted() != null) {
            return hardShip.getHardshipGranted().booleanValue();
        } else {
            return false;
        }
    }

    public boolean isIncomingIncomeTestYearGreaterThanCurrentYear(){
        Calendar calendar=Calendar.getInstance();
        return this.isGreaterThan(this.getIncomingIncomeYear(), calendar
                .get(Calendar.YEAR));
    }

    public boolean isHardShipGranted() {
        return isHardShipGranted(getPristineIncomeTest());
    }

    public boolean isIncomeTestCurrent() throws ServiceException {
        return getCurrentIncomeTest() != null;
    }

    public boolean isMTPermitted() {
        if (meansTestPermitted == null) {
            meansTestPermitted = Boolean.valueOf(getFinancialsHelperService()
                    .isMeansTestPermitted(getResultPerson()));
        }
        return meansTestPermitted.booleanValue();
    }

    public void setMTPermitted(boolean value) {
        meansTestPermitted = Boolean.valueOf(value);
    }

    public boolean isRXCopayApplicableForVeteran() {
        /*
         * SUC461.35.13 Assign Pharmacy Copay Status based on results from the
         * Compute Pharmacy Copay Status decision table.
         * For VOA the "Conditions" (Rx Copay Test is Applicable) is always "Yes".
         */
        if (isMessageFromVOA())
            return true;

        if (primaryCopayApplicable == null) {
            if (this.getRuleDataAware() instanceof EEInputData) {
                EEInputData eid = (EEInputData)this.getRuleDataAware();
                Person clonePerson = (Person)this.getResultPerson().clone();
                clonePerson.setEnrollmentDetermination(eid
                        .getResultEnrollmentDetermination());
                primaryCopayApplicable = Boolean
                        .valueOf(getFinancialsHelperService()
                                .isPhramacyCoPayApplicable(clonePerson));
            } else {
                primaryCopayApplicable = Boolean
                .valueOf(getFinancialsHelperService()
                        .isPhramacyCoPayApplicable(getResultPerson()));
            }
        }
        return primaryCopayApplicable.booleanValue();
    }


    //ToDO update with correect code.

   public boolean isPersonFromVISTA() {
        EEInputData data = this.getEEInputData();
        if (data != null && data.getSendingFacility() != null)
        {
            return VAFacility.CODE_MHV.getCode().equals(data.getSendingFacility().getStationNumber());
        }
        return false;
    }
    public void setRXCopayApplicableForVeteran(boolean copayApplicableForVeteran) {
        primaryCopayApplicable = Boolean.valueOf(copayApplicableForVeteran);
    }

    public boolean isVeteranSubjectToMeansTesting() throws ServiceException {
        if ( this.subjectToMeansTest == null ) {
            if (this.getRuleDataAware() instanceof EEInputData) {
                EEInputData eid = (EEInputData)this.getRuleDataAware();
                Person clonePerson = (Person)this.getResultPerson().clone();
                clonePerson.setEnrollmentDetermination(eid
                        .getResultEnrollmentDetermination());
                subjectToMeansTest = Boolean
                        .valueOf(getFinancialsHelperService()
                        .isSubjectToMeansTest(clonePerson));
            } else {
                subjectToMeansTest = Boolean
                        .valueOf(getFinancialsHelperService()
                        .isSubjectToMeansTest(getResultPerson()));
            }
        }
        return subjectToMeansTest.booleanValue();
    }

    // Methods that can be moved to rules

    // Rule 4
    public boolean isAllowedtoEditCurrentTest() throws ServiceException {
        return getFinancialsHelperService().isAllowedtoEditTest(
                getPristinePerson(), getIncomingIncomeYear());
    }

    // Rules 5,7
    public boolean isAllowedtoAddMeansTest() throws ServiceException {
        return getFinancialsHelperService().isAllowedtoAddMeansTest(
                getPristinePerson(), getIncomingIncomeYear());
    }

    // Rules 6,8
    public boolean isAllowedtoAddPharmacyCopayTest() throws ServiceException {
        return getFinancialsHelperService().isAllowedtoAddPharmacyCopayTest(
                getPristinePerson(), getIncomingIncomeYear());
    }

    // Rule 9
    public boolean isAllowedtoAddTest() throws ServiceException {
        return getFinancialsHelperService().isAllowedtoAddTest(
                getPristinePerson(), getIncomingIncomeYear());
    }

    public void setVeteranSubjectToMeansTesting(
            boolean veteranSubjectToMeansTesting) {
        subjectToMeansTest = Boolean.valueOf(veteranSubjectToMeansTesting);
    }

    public MessagingService getMessagingSevice() {
        if (messagingSevice == null) {
            setMessagingSevice((MessagingService) getApplicationContext()
                    .getBean(MESSAGING_SERVICE_BEAN_NAME));
        }
        return messagingSevice;
    }

    private boolean getSendCaseToAdjudication(IncomeTest incomeTest)
            throws AdjudicationResponseRequiredException {
        if (incomeTest != null && incomeTest.getSendForAdjudication() == null) {
            throw new AdjudicationResponseRequiredException(
                    "Send to Adjudication response is missing.");
        }
        return Boolean.TRUE.equals(incomeTest != null ? incomeTest
                .getSendForAdjudication() : null);
    }

    public String getCalculatedMeansTestStatusCode() throws ServiceException {
        if (calculatedMeansTestStatusCode == null) {
            calculatedMeansTestStatusCode = determineMeansTestStatusCode();
        }
        return calculatedMeansTestStatusCode;
    }

    /**
     * Assign Means Test Status Rules
     */
    public MeansTestStatus determineMeansTestStatus() throws ServiceException {
        String meansTestStatusCode = determineMeansTestStatusCode();
        return (meansTestStatusCode == null ? null : getLookupService()
                .getMeansTestStatusByCode(meansTestStatusCode));
    }

    public boolean isAdjudicationResponseRequired() throws ServiceException {
        try {
            determineMeansTestStatusCode();
        } catch (AdjudicationResponseRequiredException ex) {
            return true;
        }
        return false;
    }

    public void setSubjectToOrPermittedTrue()
    {
        this.subjectToMeansTest = Boolean.TRUE;
        this.meansTestPermitted = Boolean.TRUE;
    }
    /**
     * Determines the means test status code
     *
     * @return
     * @throws ServiceException
     */
    public String determineMeansTestStatusCode() throws ServiceException {
        BigDecimal gmtThreshold = convert(getCalculatedGMTThreshold());
        BigDecimal mtThreshold = getCalculatedMTThreshold();
        BigDecimal netIncome = getCalculatedNetIncome();
        BigDecimal networth = getCalculatedNetworth();
        BigDecimal networthThreshold = getCalculatedNetworthThreshold();
        FinancialStatement finStmt = getIncomingFinancialStatement();
        Boolean isPost2005Format = (finStmt != null) ? finStmt
                .getIsPost2005Format()
                : FinancialStatement.DEFAULT_POST_2005_FORMAT;
        return determineMeansTestStatusCode(gmtThreshold, mtThreshold,
                netIncome, networth, networthThreshold, isPost2005Format);
    }

    // this is the method that VFA-SP1 assign means test decision table SUC1434.8.1 1434.51 was implemented
    private String determineMeansTestStatusCode(BigDecimal gmtThreshold,
            BigDecimal mtThreshold, BigDecimal netIncome, BigDecimal netWorth,
            BigDecimal netWorthThreshold, Boolean isPost2005Format)
            throws ServiceException, AdjudicationResponseRequiredException {

        String meansTestStatusCode = null;

        mtThreshold = getNotNull(mtThreshold);
        gmtThreshold = getNotNull(gmtThreshold);
        netIncome = getNotNull(netIncome);
        netWorth = getNotNull(netWorth);
        netWorthThreshold = getNotNull(netWorthThreshold);
        BigDecimal incomeAndNetworth = netIncome.add(netWorth);

        // Initial qualfying condition
        if (!(isVeteranSubjectToMeansTesting() || isMTPermitted())) {
            // Pre condition is not satisfied
            return meansTestStatusCode;
        }
        // TBL772 1 rule 1 - prev rule 14
        IncomeTest incomeTest = getIncomeTest();
        if (Boolean.FALSE.equals(getDisclosure(incomeTest))) {
            return MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName();
        }
        // TBL763 2 rule 2 - prev rule 1 - income year later than 2008 was VFA-SP1 specific - removed per request
        if ((netIncome.compareTo(mtThreshold) <= 0)
                && (incomeAndNetworth.compareTo(netWorthThreshold) < 0) ) {
                // && !(this.isIncomeYearLaterThan2008())) {
            return MeansTestStatus.MT_STATUS_MT_COPAY_EXEMPT.getName();
        }
        // TBL763 3 VFA-SP1 rule 3 specific removed per request
        /*
        if ((netIncome.compareTo(mtThreshold) <= 0)
                && isIncomeYearLaterThan2008() ) {
            return MeansTestStatus.MT_STATUS_MT_COPAY_EXEMPT.getName();
        }
        */
        // Ranch, Farm, Property //CR 5322 and CCR 4933
        BigDecimal otherIncome = isPost2005Format.booleanValue() ? getTotalIncomeAmount(IncomeType.INCOME_TYPE_FARM_RANCH_PROPERTY_OR_BUSINESS_INCOME)
                : getTotalIncomeAmount(IncomeType.INCOME_TYPE_TOTAL_ALL_OTHER_INCOME);

        // restored old means test decision table codes without VFA-SP1 NW discontinuation requirement
        //  rule 3&4 - prev rule 2 & 3
        if ((netIncome.compareTo(mtThreshold) > 0)
                && (netIncome.compareTo(gmtThreshold) <= 0)
                && (otherIncome.compareTo(ZERO_AMOUNT) > 0)) {
            return getSendCaseToAdjudication(incomeTest) ? MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
                    .getName()
                    : MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName();
        }
        // rule 5&6 - prev rule 4,5
        if ((netIncome.compareTo(mtThreshold) > 0)
                && (netIncome.compareTo(gmtThreshold) >= 0)
                && (otherIncome.compareTo(ZERO_AMOUNT) > 0)) {
            return getSendCaseToAdjudication(incomeTest) ? MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
                    .getName()
                    : MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName();
        }
        // rule 7,8 - prev rule 6,7
        if ((incomeAndNetworth.compareTo(netWorthThreshold) > 0)
                && (gmtThreshold.compareTo(mtThreshold) > 0)
                && (otherIncome.compareTo(ZERO_AMOUNT) > 0)) {
            return getSendCaseToAdjudication(incomeTest) ? MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
                    .getName()
                    : MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName();
        }
        //TBL2209 - continuation of 9803 CCR10111
        if ((netIncome.compareTo(mtThreshold) <= 0)
                && (netIncome.compareTo(gmtThreshold) <= 0)
                && (incomeAndNetworth.compareTo(netWorthThreshold) > 0)
                && (gmtThreshold.compareTo(mtThreshold) < 0)
                && (this.isIncomeYearLaterThan2008())) {
            return getSendCaseToAdjudication(incomeTest) ? MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
                            .getName()
                            : MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName();
        }
        // rule 9,10 - prev rule 8,9
        if ((incomeAndNetworth.compareTo(netWorthThreshold) > 0)
                && (gmtThreshold.compareTo(mtThreshold) < 0)
                && (otherIncome.compareTo(ZERO_AMOUNT) > 0)) {
            return getSendCaseToAdjudication(incomeTest) ? MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
                    .getName()
                    : MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName();
        }

        // rule 11 - prev rule 10
        if ((netIncome.compareTo(mtThreshold) <= 0)
                && (incomeAndNetworth.compareTo(netWorthThreshold) >= 0)
                && (gmtThreshold.compareTo(mtThreshold) > 0)) {
            return getSendCaseToAdjudication(incomeTest) ? MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
                    .getName()
                    : MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName();
        }

        // rule 12 - prev rule 11
        if ((netIncome.compareTo(mtThreshold) <= 0)
                && (incomeAndNetworth.compareTo(netWorthThreshold) >= 0)
                && (gmtThreshold.compareTo(mtThreshold) < 0)) {
            return getSendCaseToAdjudication(incomeTest) ? MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
                    .getName()
                    : MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName();
        }

        // rule 13 - prev rule 12
        if ((netIncome.compareTo(mtThreshold) > 0)
                && (netIncome.compareTo(gmtThreshold) <= 0)) {
            return MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName();
        }

        // rule 14 - prev rule 13
        if ((netIncome.compareTo(mtThreshold) > 0)
                && (netIncome.compareTo(gmtThreshold) > 0)) {
            return MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName();
        }

        return null;
        // Following codes are VFA-SP1 NW discontinuation related - removed per request as of April 12, 2010
        /*
        // TBL764 4 TBL835 4 rule 3&4 - prev rule 2 & 3 Pre 2005 test - income year has to no later than 2008
        // if no check income year, it would prevent TBL771 15 rule to be executed
        // since otherIncome was used by both pre 2005 test and Feb 2005 test format
        if ((netIncome.compareTo(mtThreshold) > 0)
                && (netIncome.compareTo(gmtThreshold) <= 0)
                && (otherIncome.compareTo(ZERO_AMOUNT) > 0)
                && !(this.isIncomeYearLaterThan2008())) {
             return getSendCaseToAdjudication(incomeTest) ? MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION.getName(): MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName();
             //  return MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName();
        }

        //  VFA-SP1 TBL765 5  TBL836 6 Pre 2005 test - income year has to no later than 2008
        //  if no check income year, it would prevent TBL771 16 rule to be executed
        // since otherIncome was used by both pre 2005 test and Feb 2005 test format
        if ((netIncome.compareTo(mtThreshold) > 0)
                && (netIncome.compareTo(gmtThreshold) > 0)
                && (otherIncome.compareTo(ZERO_AMOUNT) > 0)
                && !(this.isIncomeYearLaterThan2008())) {
             return getSendCaseToAdjudication(incomeTest) ? MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION.getName(): MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName();
             // return MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName();
        }

        // TBL766 7 TBL837 8 (rule 7,8 - prev rule 6,7)
        if ((incomeAndNetworth.compareTo(netWorthThreshold) > 0)
                && (gmtThreshold.compareTo(mtThreshold) > 0)
                && (otherIncome.compareTo(ZERO_AMOUNT) > 0)
                && !(this.isIncomeYearLaterThan2008())) {
            return getSendCaseToAdjudication(incomeTest) ? MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
                    .getName(): MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName();
            // return MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName();
        }

        // TBL767 9 TBL838 10 (rule 9,10 - prev rule 8,9)
        if ((incomeAndNetworth.compareTo(netWorthThreshold) > 0)
                && (gmtThreshold.compareTo(mtThreshold) <= 0)
                && (otherIncome.compareTo(ZERO_AMOUNT) > 0)
                && !(this.isIncomeYearLaterThan2008())) {
            return getSendCaseToAdjudication(incomeTest) ? MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
                    .getName(): MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName();
            // return MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName();
        }

        // TBL 768 11 rule 11 - prev rule 10
        if ((netIncome.compareTo(mtThreshold) <= 0)
                && (incomeAndNetworth.compareTo(netWorthThreshold) >= 0)
                && (gmtThreshold.compareTo(mtThreshold) > 0)
                && !(this.isIncomeYearLaterThan2008())) {
            return getSendCaseToAdjudication(incomeTest) ? MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
                    .getName(): MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName();
            // return MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName();
        }

        // TBL 769 12 rule 12 - prev rule 11
        if ((netIncome.compareTo(mtThreshold) <= 0)
                && (incomeAndNetworth.compareTo(netWorthThreshold) >= 0)
                && (gmtThreshold.compareTo(mtThreshold) <= 0)
                && !(this.isIncomeYearLaterThan2008())) {
            return getSendCaseToAdjudication(incomeTest) ? MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
                    .getName(): MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName();
            // return MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName();
        }

        // TBL770 13 rule 13 - prev rule 12
        if ((netIncome.compareTo(mtThreshold) > 0)
                && (netIncome.compareTo(gmtThreshold) <= 0)
                && ( otherIncome.compareTo(ZERO_AMOUNT) <= 0 )){
            return MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName();
        }

        // TBL771 14 rule 14 - prev rule 13
        if ((netIncome.compareTo(mtThreshold) > 0)
                && (netIncome.compareTo(gmtThreshold) > 0)
                && (otherIncome.compareTo(ZERO_AMOUNT) <= 0 )) {
            return MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName();
        }

        // TBL 771 15 VFA-SP1 rule 15
        if ((netIncome.compareTo(mtThreshold) > 0)
                && (netIncome.compareTo(gmtThreshold) <= 0)
                && (otherIncome.compareTo(ZERO_AMOUNT) > 0)
                && isIncomeYearLaterThan2008()){
            return MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName();
        }

        // TBL 771 16 VFA-SP1 rule 16
        if ((netIncome.compareTo(mtThreshold) > 0)
                && (netIncome.compareTo(gmtThreshold) > 0)
                && (otherIncome.compareTo(ZERO_AMOUNT) > 0)
                && isIncomeYearLaterThan2008()) {
            return MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName();
        }
        return null;
        */
    }

    public String getCalculatedRxCopayStatusCode() throws ServiceException {
        if (calculatedRxCopayStatusCode == null) {
            calculatedRxCopayStatusCode = determineRxCopayStatusCode();
        }
        return calculatedRxCopayStatusCode;
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#convertMTToRxCopayTest()
     */
    public void convertMTToRxCopayTest() throws RuleException {
        IncomeTest incomeTest = this.getHelperService().getCurrentIncomeTest(
                this.getResultPerson());
        // Rules only calls this if test is originally MT type
        if (incomeTest != null) {
            IncomeTestStatus rxStatus = incomeTest.getPharmacyCoPayStatus();
            Date completed = incomeTest.getCompletedDate();

            if (rxStatus != null) {
                rxStatus.setDeterminedStatus(rxStatus.getStatus());
                incomeTest.setType(rxStatus.getType());
                if (completed == null) {
                    completed = this.getCurrentDate(); //avoid a null date
                }
                rxStatus.setCompletedDate(completed);
                rxStatus.setLastEditedDate(this.getCurrentDate());
            }
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#convertToMT()
     */
    public void convertToMT() throws RuleException {
        IncomeTest incomeTest = this.getHelperService()
                .getCurrentIncomeTestForEE(this.getResultPerson());
        if (incomeTest != null) {
            try {
                IncomeTestType type = getLookupService()
                        .getIncomeTestTypeByCode(
                                IncomeTestType.CODE_MEANS_TEST.getName());
                Date completed = incomeTest.getCompletedDate();
                IncomeTestStatus status = incomeTest
                        .getIncomeTestStatusOfType(IncomeTestType.CODE_MEANS_TEST);
                if (status != null) {
                    // Set status to determined status
                    status.setStatus(status.getDeterminedStatus());
                    incomeTest.setType(type);
                    if (completed == null) {
                        completed = this.getCurrentDate();
                    }
                    status.setCompletedDate(completed);
                    status.setLastEditedDate(this.getCurrentDate());
                }
            } catch (ServiceException e) {
                throw new RuleException("Failed to convert income test to MT",
                        e);
            }

        }
    }

    /**
     * @return
     * @throws ServiceException
     */
    private String determineRxCopayStatusCode() throws ServiceException {
        if (isRXCopayApplicableForVeteran()) {
            BigDecimal totalIncome = getCalculatedNetIncome();
            BigDecimal pensionThreshold = getCalculatedPensionThreshold();

            if (Boolean.FALSE.equals(getDisclosure(getIncomeTest()))
                    || totalIncome.compareTo(pensionThreshold) > 0) {
                return MeansTestStatus.MT_STATUS_NON_EXEMPT.getName();
            } else {
                return MeansTestStatus.MT_STATUS_EXEMPT.getName();
            }
        }
        return null;
    }

    /**
     * Due to the circualr reference problems bean is not configured
     *
     * @param messagingSevice
     */
    private void setMessagingSevice(MessagingService messagingSevice) {
        this.messagingSevice = messagingSevice;
    }

    public boolean isFinancialStatementCopied() {
        return financialStatementCopied;
    }

    public void setFinancialStatementCopied(boolean financialStatementCopied) {
        this.financialStatementCopied = financialStatementCopied;
    }

    public boolean isIncomeTestCopied() {
        return incomeTestCopied;
    }

    public void setIncomeTestCopied(boolean incomeTestCopied) {
        this.incomeTestCopied = incomeTestCopied;
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getAdd90DayHospitalDeductible()
     */
    public BigDecimal getAdd90DayHospitalDeductible() throws RuleException {
        // COMMS context
        IncomeThreshold  incomeThreshold = this.getIncomeThresholdForComms();
        return (incomeThreshold != null && incomeThreshold
                .getAdd90DayHospitalDeductible() != null) ? incomeThreshold
                .getAdd90DayHospitalDeductible() : new BigDecimal(0);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getAdd90DayHospitalMedicareDeductible()
     */
    public BigDecimal getAdd90DayHospitalMedicareDeductible()
            throws RuleException {
        // COMMS context
        //IncomeThreshold  incomeThreshold = this.getIncomeThresholdForComms();
        /*
         * return (incomeThreshold != null &&
         * incomeThreshold.getMedicareDeductible() != null) ?
         * incomeThreshold.getMedicareDeductible() : new BigDecimal(0);
                */
        return new BigDecimal(0);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getOutpatientSpecialCopay()
     */
    public BigDecimal getOutpatientSpecialCopay() throws RuleException {
        IncomeThreshold  incomeThreshold = this.getIncomeThresholdForComms();
        return (incomeThreshold != null && incomeThreshold
                .getOutpatientSpecialtyCopay() != null) ? incomeThreshold
                .getOutpatientSpecialtyCopay() : new BigDecimal(0);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getGMTInpatient90DayCopay()
     */
    public BigDecimal getGMTInpatient90DayCopay() throws RuleException {
        // COMMS context
        IncomeThreshold  incomeThreshold = this.getIncomeThresholdForComms();
        return (incomeThreshold != null && incomeThreshold
                .getGmtInpatient90DayCopay() != null) ? incomeThreshold
                .getGmtInpatient90DayCopay() : new BigDecimal(0);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getGMTInpatientAdd90DayCopay()
     */
    public BigDecimal getGMTInpatientAdd90DayCopay() throws RuleException {
        // COMMS context
        IncomeThreshold  incomeThreshold = this.getIncomeThresholdForComms();
        return (incomeThreshold != null && incomeThreshold
                .getGmtInpatientAdd90DayCopay() != null) ? incomeThreshold
                .getGmtInpatientAdd90DayCopay() : new BigDecimal(0);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getGMTInpatientPerDiem()
     */
    public BigDecimal getGMTInpatientPerDiem() throws RuleException {
        // COMMS context
        IncomeThreshold  incomeThreshold = this.getIncomeThresholdForComms();
        return (incomeThreshold != null && incomeThreshold
                .getGmtInpatientPerDiem() != null) ? incomeThreshold
                .getGmtInpatientPerDiem() : new BigDecimal(0);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getMedicationCopay()
     */
    public BigDecimal getMedicationCopay() throws RuleException {
        // COMMS context
        IncomeThreshold  incomeThreshold = this.getIncomeThresholdForComms();
        return (incomeThreshold != null && incomeThreshold.getMedicationCopay() != null) ? incomeThreshold
                .getMedicationCopay()
                : new BigDecimal(0);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getNinetyDayHospitalCopay()
     */
    public BigDecimal getNinetyDayHospitalCopay() throws RuleException {
        // COMMS context
        IncomeThreshold  incomeThreshold = this.getIncomeThresholdForComms();
        return (incomeThreshold != null && incomeThreshold
                .getNinetyDayHospitalCopay() != null) ? incomeThreshold
                .getNinetyDayHospitalCopay() : new BigDecimal(0);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getOutpatientCopay()
     */
    public BigDecimal getOutpatientCopay() throws RuleException {
        // COMMS context
        IncomeThreshold  incomeThreshold = this.getIncomeThresholdForComms();
        return (incomeThreshold != null && incomeThreshold.getOutpatientCopay() != null) ? incomeThreshold
                .getOutpatientCopay()
                : new BigDecimal(0);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getOutpatientPreventiveCopay()
     */
    public BigDecimal getOutpatientPreventiveCopay() throws RuleException {
        // COMMS context
        IncomeThreshold  incomeThreshold = this.getIncomeThresholdForComms();
        return (incomeThreshold != null && incomeThreshold
                .getOutpatientPreventiveCopay() != null) ? incomeThreshold
                .getOutpatientPreventiveCopay() : new BigDecimal(0);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getIncomeYear(gov.va.med.esr.common.model.financials.IncomeTest)
     */
    public Integer getIncomeYear(IncomeTest incomeTest) {
        // COMMS context
        return incomeTest != null ? incomeTest.getIncomeYear() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getInpatientPerDiem()
     */
    public BigDecimal getInpatientPerDiem() throws RuleException {
        // COMMS context
        IncomeThreshold  incomeThreshold = this.getIncomeThresholdForComms();
        return (incomeThreshold != null && incomeThreshold
                .getInpatientPerDiem() != null) ? incomeThreshold
                .getInpatientPerDiem() : new BigDecimal(0);
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getPriorMeansTestStatusCode()
     */
    public String getPriorMeansTestStatusCode() throws RuleException {
        // COMMS context
        IncomeTest incomeTest = this.getPriorIncomeTest();
        if (incomeTest != null) {
            return this.getMeansTestStatusCode(incomeTest);
        }

        return null;
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getPriorPharmacyCopayStatusCode()
     */
    public String getPriorPharmacyCopayStatusCode() throws RuleException {
        // COMMS context

        IncomeTest incomeTest = this.getPriorIncomeTest();
        if (incomeTest != null) {
            return this.getPharmacyCopayStatusCode(incomeTest);
        }
        return null;
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getDateRefusedCopay()
     */
    public Date getDateRefusedCopay() throws RuleException {
        IncomeTest test = null;
        Date dateRefused = null;
        try {
            test = getCurrentIncomeTest(this.getResultPerson());
            dateRefused = (test != null) ? test.getEffectiveDate() : null;
        } catch (ServiceException e) {
            throw new RuleException("Failed to calculate income threshold", e);
        }
        return dateRefused;
    }

    public Boolean getSpouseLivesWithVeteran() {
        FinancialStatement fstmt = getIncomingFinancialStatement();
        if (fstmt != null) {
            SpouseFinancials fianacial = fstmt.getRecentSpouseFinancials();
            if (fianacial != null)
                return fianacial.getLivedWithPatient();
        }
        return null;
    }

    /**
     * upload only LTCs from the input This method is called when the source is
     * other
     */
    public void updateLTCStatuses() throws ServiceException {
        // if the result income test is null create a new Income test and add to
        // the person
        //set the type to LTC
        //set primary indicator to false
        //copy other required attributes from the input
        IncomeTest incomingTest = getIncomingIncomeTest();
        IncomeTest resultTest = getResultIncomeTest();

        IncomeTestStatus ltcCopayStatus = null;
        IncomeTestStatus ltcCopayExemtStatus = null;

        if (incomingTest != null && resultTest == null) {
            ltcCopayStatus = incomingTest
                    .getIncomeTestStatus(IncomeTestType.CODE_LTC_CO_PAY_TEST);
            ltcCopayExemtStatus = incomingTest
                    .getIncomeTestStatus(IncomeTestType.CODE_LTC_CO_PAY_EXEMPTION_TEST);
            if (ltcCopayStatus != null || ltcCopayExemtStatus != null) {
                //create a new income test
                String testTypeCode = IncomeTestType.CODE_LTC_CO_PAY_TEST
                        .getCode();
                if (ltcCopayStatus == null) {
                    testTypeCode = IncomeTestType.CODE_LTC_CO_PAY_EXEMPTION_TEST
                            .getCode();
                }

                IncomeTestType type = getLookupService()
                        .getIncomeTestTypeByCode(testTypeCode);
                resultTest = new IncomeTest();
                resultTest.setIncomeYear(incomingTest.getIncomeYear());
                resultTest.setType(type);
                resultTest.setSource(incomingTest.getSource());
                resultTest.setSiteConductingTest(incomingTest
                        .getSiteConductingTest());
                resultTest.setEffectiveDate(incomingTest.getEffectiveDate());
                resultTest.setPrimaryIncomeTest(Boolean.FALSE);
                resultTest.setFutureTest(null);
                getResultPerson().setIncomeTest(incomingTest.getIncomeYear(),
                        resultTest);
            }
        }
        mergeIncomeTestStatus(IncomeTestType.CODE_LTC_CO_PAY_TEST);
        mergeIncomeTestStatus(IncomeTestType.CODE_LTC_CO_PAY_EXEMPTION_TEST);
    }

    public IncomeTest getPristinePrimaryIncomeTest()
    {
        return super.getPristineIncomeTest();
    }

    private IncomeThreshold getIncomeThresholdForComms() throws RuleException {
        // COMMS context
        if ( this.incomeThresholdForComms == null ) {
            IncomeTest incomeTest = this.getIncomeTest();
            if (incomeTest != null) {
                Integer year = incomeTest.getIncomeYear();
                Integer total = (incomeTest.getTotalNumberOfDependents() != null) ? incomeTest
                        .getTotalNumberOfDependents()
                        : new Integer(0);
                try {
                    if (year != null) {
                        this.incomeThresholdForComms = this
                                .getFinancialsHelperService()
                                .calculateIncomeThresholds(year, total);
                    }
                } catch (ServiceException e) {
                    throw new RuleException(
                            "Failed to calculate income threshold", e);
                }

            }

        }
        return this.incomeThresholdForComms;
    }

    private IncomeTest getPriorIncomeTest()throws RuleException {
        if ( this.priorIncomeTest == null ) {
            this.priorIncomeTest = this.getPriorIncomeTest(this
                    .getIncomingPerson());
        }
        return this.priorIncomeTest;
    }

    private IncomeTest getPriorIncomeTest(Person person) throws RuleException {
        Validate.notNull(person, "A person must not be null ");
        try {
            Person prior = this.getPriorEEPerson();
            return prior != null ? this.getCurrentIncomeTest(prior) : null;
        } catch (ServiceException ex) {
            throw new RuleException("Error retrieving prior income test", ex);
        }
    }

    public boolean hasIncomingSpouse() {
        if (this.getIncomingSpouse()== null )
            return false;
        else
            return true;
    }

    public Spouse getIncomingSpouse(){
        // CCR 10057 The line below not related to 10057 but was causing null pointer. I added a guard.
        //SpouseFinancials spouseFinancials=(SpouseFinancials)this.getIncomingFinancialStatement().getActiveSpouseFinancials();
        SpouseFinancials spouseFinancials = this.getIncomingFinancialStatement() != null ? (SpouseFinancials)this.getIncomingFinancialStatement().getActiveSpouseFinancials() : null;
        if ( spouseFinancials == null ){
        	logger.info("spouseFinancial is null");
            return null;
        }
        return spouseFinancials.getReportedOn();
    }

    public Employment getIncomingSpouseEmployment(){
       if ( getIncomingSpouse() == null )
           return null;
       return getIncomingSpouse().getEmployment();
    }

    public Spouse getResultSpouse(){
        if (this.getResultFinancialStatement()!= null &&
                this.getResultFinancialStatement().getActiveSpouseFinancials() != null)
        {
            SpouseFinancials spouseFinancials=(SpouseFinancials)this.getResultFinancialStatement().getActiveSpouseFinancials();
            return spouseFinancials.getReportedOn();
        }
        return null;
    }
    public Employment getResultSpouseEmployment(){

        return getResultSpouse() == null ? null : getResultSpouse().getEmployment();
    }

    private class AdjudicationResponseRequiredException extends
            ServiceException {
        /**
       * An instance of serialVersionUID
       */
      private static final long serialVersionUID = -3676890144455555033L;

      public AdjudicationResponseRequiredException(String msg) {
            super(msg);
        }
    }

    /**
     * Check if Income is greater than GMT plus Relax Percent
     *
     * @param relaxPercent
     * @return flag
     */
    public boolean isIncomeGreaterThanGMTplusRelaxPercent(float relaxPercent)
            throws ServiceException {
        boolean flag=true;
        try {
            BigDecimal netIncome=getCalculatedNetIncome();

            BigDecimal gmtthresholdVal = new BigDecimal(
                    (getNotNull(getCalculatedGMTThreshold())).intValue());
            BigDecimal gmtthresholdRelaxVal = this.percentageOf(
                    gmtthresholdVal, relaxPercent);
            BigDecimal adjustedIncome = this.add(gmtthresholdVal,
                    gmtthresholdRelaxVal);

            flag=this.isGreaterThan(netIncome, adjustedIncome);
        } catch (Exception e) {
            if(logger.isErrorEnabled()){
                logger.error(e);
            }
            throw new ServiceException(e);
        }
        return flag;
    }

    /**
     * Check if Income is greater than MTT plus Relax Percent
     *
     * @param relaxPercent
     * @return flag
     */
    public boolean isIncomeGreaterThanMTTplusRelaxPercent(float relaxPercent)
            throws ServiceException {
        boolean flag=true;
        BigDecimal netIncome=getCalculatedNetIncome();

        Integer mtthresholdValDecimal = isUpdateFromGUI() ? new Integer(
                getNotNull(getCalculatedMTThreshold()).intValue())
                : getMTThreshold(getIncomingIncomeTest());
        BigDecimal mtthresholdVal = new BigDecimal(mtthresholdValDecimal
                .intValue());
        BigDecimal thresholdRelaxVal= this.percentageOf(mtthresholdVal, relaxPercent);
        BigDecimal adjustedIncome=this.add(mtthresholdVal, thresholdRelaxVal);

        flag=this.isGreaterThan(netIncome, adjustedIncome);
        return flag;
    }

    /**
     * This method is specifically for use in calculating Continuous Enrollment.
     * The method should not be used in financials computations. This method
     * does not have dependencies on FinancialInputData.
     *
     * @see gov.va.med.esr.common.rule.FinancialInput#isIncomeGreaterThanGMTplusRelaxPercentForCE(float)
     */


    public boolean isIncomeGreaterThanGMTplusRelaxPercentForCE(
            float relaxPercent) throws ServiceException {
        boolean flag = true; // true gives lowest benefit
        IncomeTest test = getHelperService().getCurrentIncomeTest(
                getIncomingPerson());
        if (test != null) {
            flag = this
                    .determineIfNetIncomeGreaterThanThresholdPlusRelaxPercentForCE(
                            relaxPercent, test.getGmtThresholdAmount(), test);
        }

        return flag;
    }

    /**
     * This method is specifically for use in calculating Continuous Enrollment.
     * The method should not be used in financials computations. This method
     * does not have dependencies on FinancialInputData.
     *
     * @see gov.va.med.esr.common.rule.FinancialInput#isIncomeGreaterThanMTTplusRelaxPercentForCE(float)
     */
    public boolean isIncomeGreaterThanMTTplusRelaxPercentForCE(
            float relaxPercent) throws ServiceException {
        boolean flag = true; // true gives lowest benefit
        IncomeTest test = getHelperService().getCurrentIncomeTest(
                getIncomingPerson());
        if (test != null) {
            flag = this
                    .determineIfNetIncomeGreaterThanThresholdPlusRelaxPercentForCE(
                            relaxPercent, test.getThresholdA(), test);
        }

        return flag;
    }

    /**
     * Method to compare net income versus a particular threshold. This method
     * is specifically for use in calculating Continuous Enrollment. The method
     * should not be used in financials computations. This method does not have
     * dependencies on FinancialInputData.
     *
     * @param relaxPercent
     * @param threshold
     * @return
     * @throws ServiceException
     */
    private boolean determineIfNetIncomeGreaterThanThresholdPlusRelaxPercentForCE(
            float relaxPercent, BigDecimal threshold, IncomeTest test)
            throws ServiceException {
        boolean flag=true;  // true gives lowest benefit
        if (test != null) {
            if (threshold != null) {
                BigDecimal netIncome= test.getNetIncome();
                //CCR12571 - do not default null income to $0 for CE if it's an IVM converted test
                if (netIncome == null && !IncomeTestSource.CODE_IVM.getCode().equals(test.getSource().getCode())) {
                    // Tests from HL7 don't have net income provided so need to
                    // calculate
                    netIncome = this.calculateNetIncomeForCE(test);
                }
                // Calculated net income should have default of 0,
                // so this is just a guard.
                if (netIncome != null) {
                    BigDecimal thresholdRelaxVal = this.percentageOf(threshold,
                            relaxPercent);
                    BigDecimal adjustedIncome = this.add(threshold,
                            thresholdRelaxVal);
                    flag=this.isGreaterThan(netIncome, adjustedIncome);
                }
            }
        }
        return flag;
    }

    public boolean isDataFromZ07() {
        return isDataFromZ07;
    }

    public void setDataFromZ07(boolean isDataFromZ07) {
        this.isDataFromZ07 = isDataFromZ07;
    }

    private boolean isIncomeGreaterThanGMTplusRelaxPercentForCE(
            float relaxPercent, IncomeTest incomeTest) throws ServiceException {
        boolean flag = true; // true gives lowest benefit

        if (incomeTest != null) {
            flag = this
                    .determineIfNetIncomeGreaterThanThresholdPlusRelaxPercentForCE(
                            relaxPercent, incomeTest.getGmtThresholdAmount(),
                            incomeTest);
        }

        return flag;
    }

    public boolean isIncomeGreaterThanMTTplusRelaxPercentForCE(
            float relaxPercent, IncomeTest incomeTest) throws ServiceException {
        boolean flag = true; // true gives lowest benefit
        if (incomeTest != null) {
            flag = this
                    .determineIfNetIncomeGreaterThanThresholdPlusRelaxPercentForCE(
                            relaxPercent, incomeTest.getThresholdA(),
                            incomeTest);
        }

        return flag;
    }

    @SuppressWarnings("rawtypes")
	public boolean ifConditionsMetMakeFutureTestEffectiveImmediately()
            throws ServiceException {
        boolean shouldFDTBeMadeEffectiveImmediately = false;
        IncomeTest resultTest = this.getResultIncomeTest();

        FinancialInputData data =  this.getFinancialInputData();
        if (data == null) {
            // Wrong context
            return false;
        }

        // find future dated means test
        Map incomeTestMap = this.getResultPerson().getIncomeTests();
        Collection incomeTests = incomeTestMap.values();
        Iterator incomeTestsIterator = incomeTests.iterator();
        IncomeTest futureIncomeTest = null;
        while (incomeTestsIterator.hasNext()) {
            IncomeTest incomeTest = (IncomeTest) incomeTestsIterator.next();
            if (incomeTest.isFutureTest() != null && incomeTest.isFutureTest().booleanValue()) {
                futureIncomeTest = incomeTest;
                break;
            }
        }

        // if a future dated means test exists
        if (futureIncomeTest != null) {
            boolean isResultTestMeansTest = isTestTypeMeansTest(resultTest);
            boolean isFutureTestMeansTest = isTestTypeMeansTest(futureIncomeTest);
            boolean isResultTestRxTest = isTestTypePharmacyCopayTest(resultTest);
            boolean isFutureTestRxTest = isTestTypePharmacyCopayTest(futureIncomeTest);
            String resultMeansTestStatus = getMeansTestStatusCode(resultTest);
            String futureMeansTestStatus = getMeansTestStatusCode(futureIncomeTest);
            String resultRxCopayStatus = getPharmacyCopayStatusCode(resultTest);
            String futureRxCopayStatus = getPharmacyCopayStatusCode(futureIncomeTest);

            // if the result test is of type means test AND result test is <=
            // 365 days
            // old AND result test status is MT copay required
            // AND the FDT is of type means test AND FDT status is MT copay
            // required AND (FDT income < MTT OR (FDT MTT < GMTT AND FDT income
            // <=
            // GMTT*110%) OR (FDT income <= MTT*110%) )

            Integer incomeYear = getIncomeYear(futureIncomeTest);
            float relaxPercentage = getRelaxPercentage(incomeYear);

            if (isResultTestMeansTest
                    && !isExpired(resultTest)
                    && MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getCode()
                            .equals(resultMeansTestStatus)
                    && isFutureTestMeansTest
                    && MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getCode()
                            .equals(futureMeansTestStatus)
                    && (this.isLessThan(this
                            .calculateNetIncomeForCE(futureIncomeTest),
                            futureIncomeTest.getThresholdA())
                            || (this.isLessThan(futureIncomeTest
                                    .getThresholdA(), futureIncomeTest
                                    .getGmtThresholdAmount()) && !isIncomeGreaterThanGMTplusRelaxPercentForCE(
                                    relaxPercentage, futureIncomeTest)) || !isIncomeGreaterThanMTTplusRelaxPercentForCE(
                            relaxPercentage, futureIncomeTest))) {

                shouldFDTBeMadeEffectiveImmediately = true;
            }
            // else if the result test is of type means test AND result test is
            // <= 365
            // days old AND result test status is MT copay required
            // AND the FDT is of type means test AND FDT MTT > GMTT AND FDT
            // status
            // is FDT pending adjudication AND FDT income <= MTT+110%
            if (isResultTestMeansTest
                    && !isExpired(resultTest)
                    && MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getCode()
                            .equals(resultMeansTestStatus)
                    && isFutureTestMeansTest
                    && MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION.getCode()
                            .equals(futureMeansTestStatus)
                    && this.isLessThan(
                            futureIncomeTest.getGmtThresholdAmount(),
                            futureIncomeTest.getThresholdA())
                    && !isIncomeGreaterThanMTTplusRelaxPercentForCE(
                            relaxPercentage, futureIncomeTest)) {

                shouldFDTBeMadeEffectiveImmediately = true;
            }
            // else if the result test is of type means test AND result test is
            // <= 365
            // 365 days old AND result test status is MT copay required
            // AND the FDT is of type means test AND (FDT status is MT Copay
            // Exempt OR FDT status is GMT Copay Required OR (FDT GMTT > MTT AND
            // FDT
            // status is pending adjudication))
            else if (isResultTestMeansTest
                    && !isExpired(resultTest)
                    && MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getCode()
                            .equals(resultMeansTestStatus)
                    && isFutureTestMeansTest
                    && (MeansTestStatus.MT_STATUS_MT_COPAY_EXEMPT.getCode()
                            .equals(futureMeansTestStatus)
                            || MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED
                                    .getCode().equals(futureMeansTestStatus) || (this
                            .isLessThan(futureIncomeTest.getThresholdA(),
                                    futureIncomeTest.getGmtThresholdAmount()) && MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
                            .getCode().equals(futureMeansTestStatus)))) {

                shouldFDTBeMadeEffectiveImmediately = true;
            }
            // else if the result test is of type means test AND result test is
            // <= 365
            // days old AND result test status is the same as FDT status AND
            // result test Rx copay
            // status is Non-Exempt
            // AND the FDT is of type means test AND FDT Rx copay status is
            // Exempt
            else if (isResultTestMeansTest
                    && !isExpired(resultTest)
                    && this.isEqual(resultMeansTestStatus,
                            futureMeansTestStatus)
                    && MeansTestStatus.MT_STATUS_NON_EXEMPT.getName().equals(
                            resultRxCopayStatus)
                    && isFutureTestMeansTest
                    && MeansTestStatus.MT_STATUS_EXEMPT.getName().equals(
                            futureRxCopayStatus)) {

                shouldFDTBeMadeEffectiveImmediately = true;
            }
            // else if the result test is of type means test AND result test is
            // <= 365
            // days old AND result test status is GMT copay required
            // AND the FDT is of type means test AND FDT status is MT Copay
            // Exempt
            else if (isResultTestMeansTest
                    && !isExpired(resultTest)
                    && MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getCode()
                            .equals(resultMeansTestStatus)
                    && isFutureTestMeansTest
                    && MeansTestStatus.MT_STATUS_MT_COPAY_EXEMPT.getCode()
                            .equals(futureMeansTestStatus)) {

                shouldFDTBeMadeEffectiveImmediately = true;
            }
            // else if the result test is of type means test AND result test is
            // <= 365
            // days old AND result test status is the same as FDT status AND
            // result test Rx copy
            // status is Incomplete
            // AND the FDT is of type means test AND (FDT Rx copy status is
            // Exempt OR FDT Rx copy status is NonExempt)
            else if (isResultTestMeansTest
                    && !isExpired(resultTest)
                    && this.isEqual(resultMeansTestStatus,
                            futureMeansTestStatus)
                    && MeansTestStatus.MT_STATUS_INCOMPLETE_RX_COPAY_ONLY
                            .getName().equals(resultRxCopayStatus)
                    && isFutureTestMeansTest
                    && (MeansTestStatus.MT_STATUS_EXEMPT.getName().equals(
                            futureRxCopayStatus) || MeansTestStatus.MT_STATUS_NON_EXEMPT
                            .getName().equals(futureRxCopayStatus))) {

                shouldFDTBeMadeEffectiveImmediately = true;
            }
            // else if the result test is of type Pharmacy Copay Test AND result
            // test is
            // <= 365 days old AND result test Rx copay status is Non-Exempt
            // AND the FDT is of type Pharmacy Copay Test AND FDT Rx copay
            // status
            // is Exempt
            else if (isResultTestRxTest
                    && !isExpired(resultTest)
                    && MeansTestStatus.MT_STATUS_NON_EXEMPT.getName().equals(
                            resultRxCopayStatus)
                    && isFutureTestRxTest
                    && MeansTestStatus.MT_STATUS_EXEMPT.getName().equals(
                            futureRxCopayStatus)) {

                shouldFDTBeMadeEffectiveImmediately = true;
            }

            // Save the indentified FDT for later processing
            data.setExistingFDTTest(futureIncomeTest);
        }
        return shouldFDTBeMadeEffectiveImmediately;
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#makeExistingFDTEffectiveImmediately()
     */
    public void makeExistingFDTEffectiveImmediately() throws ServiceException {
        Date now = this.getCurrentDate();
        IncomeTest fdt = getExistingFDT();

        if (fdt != null) {
            // set future dated test's effective date, date/time completed, and last
            // edited date to today
            Date testEffectiveDate = null;

            Calendar calendar = DateUtils.createCalendar(now);
            int year = calendar.get(Calendar.YEAR);
            int month = calendar.get(Calendar.MONTH);
            int day = calendar.get(Calendar.DATE);
            calendar.clear();
            calendar.set(year, month, day);
            testEffectiveDate = calendar.getTime();

            fdt.setEffectiveDate(testEffectiveDate);
            fdt.setPrimaryIncomeTest(Boolean.TRUE); // Make sure shows as primary
            fdt.setCompletedDate(now);
            fdt.setLastEditedDate(now);
            fdt.setFutureTest(new Boolean(false));
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.FinancialInput#getExistingFDT()
     */
    public IncomeTest getExistingFDT() throws ServiceException {
        return this.getFinancialInputData() != null ? this.getFinancialInputData().getExistingFDTTest() : null;
    }

    public boolean isSpouseAddressAfter() {
        if ( getIncomingSpouse()== null )
            return false;
        Address incomingAddress = this.getIncomingSpouse().getAddress();

        // CCR10140 -- intentionally allow deletion of address
        if (incomingAddress == null )
            return true;

        // CCR10140 -- add guard to prevent null pointer exception
        if (getResultSpouse() == null)
            return true;
        Address onFileAddress = this.getResultSpouse().getAddress();
        if(onFileAddress == null )
            return true;
        Date incoming = incomingAddress.getChangeDate();
        Date current = onFileAddress.getChangeDate();
        return isAfter(incoming, current);
    }

    public void setResultSpouse(Spouse resultSpouse){
        if (this.getResultFinancialStatement() != null) {
            SpouseFinancials spouseFinancials=(SpouseFinancials)this.getResultFinancialStatement().getActiveSpouseFinancials();
            spouseFinancials.setReportedOn(resultSpouse);
        }
    }

    // CCR10140 -- rework update spouse address so that it performs a proper merge
    public void updateSpouseAddress() throws ServiceException {
        if (getIncomingSpouse()!= null && getResultSpouse() != null){
            this.getMergeRuleService().mergeSpouseAddress(this.getIncomingSpouse(), this.getResultSpouse());
        }
    }


    // START CCR 10535 - Fix multiple Spouse Employment issues
    /**
     *
	 * @see gov.va.med.esr.common.rule.FinancialInput#setSpouseOccupation(gov.va.med.esr.common.model.person.Employment, gov.va.med.esr.common.model.person.Employment)
	 */
	public void setSpouseOccupation(Employment incoming, Employment result) {
		if (incoming != null) {
			if (incoming != null) {
				if (result == null) {
					result = this.getOrCreateSpouseEmployment();
				}
				if (result != null) {
					result.setOccupation(incoming.getOccupation());
				}
			}
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.FinancialInput#setSpouseDateOfRetirement(gov.va.med.esr.common.model.person.Employment, gov.va.med.esr.common.model.person.Employment)
	 */
	public void setSpouseDateOfRetirement(Employment incoming, Employment result) {
		if (incoming != null) {
			if (result == null) {
				result = this.getOrCreateSpouseEmployment();
			}
			if (result != null) {
				result.setRetirementDate(incoming.getRetirementDate());
			}
		}
	}

	private Employment getOrCreateSpouseEmployment() {
		Spouse spouse = this.getResultSpouse();
		if (spouse != null) {
			Employment result = spouse.getEmployment();
			if (result == null) {
				result = new Employment();
				spouse.setEmployment(result);
			}
			return result;
		}
		return null;
	}

	/**
	 * @see gov.va.med.esr.common.rule.FinancialInput#setSpouseEmployerAddressCity(gov.va.med.esr.common.model.person.Employment, gov.va.med.esr.common.model.person.Employment)
	 */
	public void setSpouseEmployerAddressCity(Employment incoming, Employment result) {
		if (incoming != null && incoming.getEmployerAddress() != null) {
			Address incomingAddress = incoming.getEmployerAddress();

			if (result == null) {
				result = this.getOrCreateSpouseEmployment();
			}
			if (result != null) {
				Address address = result.getEmployerAddress();
				if (address == null) {
					address = new Address();
					result.setEmployerAddress(address);
				}
				address.setCity(incomingAddress.getCity());
			}
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.FinancialInput#setSpouseEmployerAddressLine1(gov.va.med.esr.common.model.person.Employment, gov.va.med.esr.common.model.person.Employment)
	 */
	public void setSpouseEmployerAddressLine1(Employment incoming, Employment result) {
		if (incoming != null && incoming.getEmployerAddress() != null) {
			Address incomingAddress = incoming.getEmployerAddress();

			if (result == null) {
				result = this.getOrCreateSpouseEmployment();
			}
			if (result != null) {
				Address address = result.getEmployerAddress();
				if (address == null) {
					address = new Address();
					result.setEmployerAddress(address);
				}
				address.setLine1(incomingAddress.getLine1());
			}
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.FinancialInput#setSpouseEmployerAddressLine2(gov.va.med.esr.common.model.person.Employment, gov.va.med.esr.common.model.person.Employment)
	 */
	public void setSpouseEmployerAddressLine2(Employment incoming, Employment result) {
		if (incoming != null && incoming.getEmployerAddress() != null) {
			Address incomingAddress = incoming.getEmployerAddress();

			if (result == null) {
				result = this.getOrCreateSpouseEmployment();
			}
			if (result != null) {
				Address address = result.getEmployerAddress();
				if (address == null) {
					address = new Address();
					result.setEmployerAddress(address);
				}
				address.setLine2(incomingAddress.getLine2());
			}
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.FinancialInput#setSpouseEmployerAddressLine3(gov.va.med.esr.common.model.person.Employment, gov.va.med.esr.common.model.person.Employment)
	 */
	public void setSpouseEmployerAddressLine3(Employment incoming, Employment result) {
		if (incoming != null && incoming.getEmployerAddress() != null) {
			Address incomingAddress = incoming.getEmployerAddress();

			if (result == null) {
				result = this.getOrCreateSpouseEmployment();
			}
			if (result != null) {
				Address address = result.getEmployerAddress();
				if (address == null) {
					address = new Address();
					result.setEmployerAddress(address);
				}
				address.setLine3(incomingAddress.getLine3());
			}
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.FinancialInput#setSpouseEmployerAddressState(gov.va.med.esr.common.model.person.Employment, gov.va.med.esr.common.model.person.Employment)
	 */
	public void setSpouseEmployerAddressState(Employment incoming, Employment result) {
		if (incoming != null && incoming.getEmployerAddress() != null) {
			Address incomingAddress = incoming.getEmployerAddress();

			if (result == null) {
				result = this.getOrCreateSpouseEmployment();
			}
			if (result != null) {
				Address address = result.getEmployerAddress();
				if (address == null) {
					address = new Address();
					result.setEmployerAddress(address);
				}
				address.setState(incomingAddress.getState());
			}
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.FinancialInput#setSpouseEmployerAddressZip(gov.va.med.esr.common.model.person.Employment, gov.va.med.esr.common.model.person.Employment)
	 */
	public void setSpouseEmployerAddressZip(Employment incoming, Employment result) {
		if (incoming != null && incoming.getEmployerAddress() != null) {
			Address incomingAddress = incoming.getEmployerAddress();

			if (result == null) {
				result = this.getOrCreateSpouseEmployment();
			}
			if (result != null) {
				Address address = result.getEmployerAddress();
				if (address == null) {
					address = new Address();
					result.setEmployerAddress(address);
				}
				address.setZipCode(incomingAddress.getZipCode());
			}
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.FinancialInput#setSpouseEmployerName(gov.va.med.esr.common.model.person.Employment, gov.va.med.esr.common.model.person.Employment)
	 */
	public void setSpouseEmployerName(Employment incoming, Employment result) {
		if (incoming != null) {
			if (result == null) {
				result = this.getOrCreateSpouseEmployment();
			}
			if (result != null) {
				result.setEmployerName(incoming.getEmployerName());
			}
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.FinancialInput#setSpouseEmployerPhoneNumber(gov.va.med.esr.common.model.person.Employment, gov.va.med.esr.common.model.person.Employment)
	 */
	public void setSpouseEmployerPhoneNumber(Employment incoming, Employment result) {
		if (incoming != null) {
			if (result == null) {
				result = this.getOrCreateSpouseEmployment();
			}
			if (result != null) {
				result.setEmployerPhone(incoming.getEmployerPhone());
			}
		}
	}

	/**
	 * @see gov.va.med.esr.common.rule.FinancialInput#setSpouseEmploymentStatus(gov.va.med.esr.common.model.person.Employment, gov.va.med.esr.common.model.person.Employment)
	 */
	public void setSpouseEmploymentStatus(Employment incoming, Employment result) {
		if (incoming != null) {
			if (result == null) {
				result = this.getOrCreateSpouseEmployment();
			}
			if (result != null) {
				result.setEmploymentStatus(incoming.getEmploymentStatus());
			}
		}
	}

    // END CCR 10535 - Fix multiple Spouse Employment issues

	public boolean isSpouseHomePhoneAfter() {
        if (isMessageFromVOA()) return true;

		if ( getIncomingSpouse()==null )
            return false;
       Phone incomingPhone =this.getIncomingSpouse().getHomePhone();
        if(incomingPhone ==null )
            return false;

        // CCR10081 -- add guard against null pointer exception
        if (getResultSpouse() == null)
            return true;

        Phone onFilePhone=this.getResultSpouse().getHomePhone();
        if(onFilePhone ==null )
            return true;
        Date incoming = incomingPhone.getChangeDate();
        Date current =onFilePhone.getChangeDate();
        return isAfter(incoming, current);
    }

    /**
	 * @see gov.va.med.esr.common.rule.FinancialInput#getPristineSpouseEmployment()
	 */
	public Employment getPristineSpouseEmployment() {
		FinancialStatement pristineStatement = getFinancialStatement(getPristinePerson(),getIncomingIncomeYear());
		SpouseFinancials spouseFinancials= (pristineStatement != null) ? (SpouseFinancials)pristineStatement.getActiveSpouseFinancials() : null;
		if (spouseFinancials == null) return null;
		Spouse spouse = spouseFinancials.getReportedOn();
		if (spouse == null) return null;
		return spouse.getEmployment();
	}

	// CCR10140 -- rework update spouse home phone so that it performs a proper merge
    public void updateSpouseHomePhone() throws ServiceException {

    	if (getIncomingSpouse()!= null && getResultSpouse() != null){
            this.getMergeRuleService().mergeSpouseHomePhone(this.getIncomingSpouse(), this.getResultSpouse());
        }


    }

    /**
     * CCR9660 SUC461.35.1.1
     * Test if there is a primary means test on file for this veteran
    **/
    public boolean hasPrimaryMeansTest() {

        IncomeTest onFile = getPristineIncomeTest();
        return isPrimary(onFile) && isTestTypeMeansTest(onFile);

    }

    /**
     * CCR9660 SUC461.35.2 Disclosed Financial Data
     * If the HL7 Disclose financial Data indicator is set to "No" then
     * set the ESR MT Status to "MT Copay Required" and return true
     *
     * @return true is disclose financial data indicator is set to "No"
     */
    public boolean isVoaDiscloseFinancialDataNo()
        throws UnknownLookupTypeException, UnknownLookupCodeException
    {
        Boolean disclosure = getDisclosure(getIncomeTest());
        if (Boolean.FALSE.equals(disclosure)) {
            setMeansTestStatus(MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName());
            return true;
        }
        // else
        return false;
    }


    public void preProcessVOAFinancialData() throws RuleException {
      preProcessIncomeTestDateAndSource();
      preProcessLivedWithPatientForSpouse();
      preProcessDependentFinancials();
    }

    /** CCR11869 - VOA backend
     * 5.11.5.2.	SUC2031.32.5.2 - Lived With Patient Indicator For Spouse
     * The system sets the Lived with Patient Indicator to "Yes".
 	 *
     * 5.11.5.3.	 SUC2031.32.5.3 - Amount Contributed for Spouse Support
     * (ES Wireframe Label: Contributed to Spousal Support)  If on the incoming message
     * the Amount Contributed For Spousal Support is greater than zero then
     * set the ES Lived With Patient Indicator for spouse to "No".
     *
     */
    @SuppressWarnings("rawtypes")
	private void preProcessLivedWithPatientForSpouse() {

      FinancialStatement stmt = this.getIncomingFinancialStatement();

      // CCR10081 -- add guards against null pointer exception
        if (stmt != null && stmt.getSpouseFinancials() != null) {
            for (Iterator i = stmt.getSpouseFinancials().iterator(); i
                    .hasNext();) {
                SpouseFinancials sf = (SpouseFinancials) i.next();
                if (sf != null) {

                	if (Boolean.TRUE.equals(sf.getLivedWithPatient())) {
                		stmt.setContributedToSpouseInd(null);
                	}
                	//CCR13373 check indicator first and then amount if needed for backward compatibility
                    //if (Boolean.TRUE.equals(stmt.getContributedToSpouseInd()) ||
                    		//(stmt.getContributionToSpouse() != null && stmt.getContributionToSpouse().longValue() > 0) ) {
                       // sf.setLivedWithPatient(Boolean.FALSE);
                        //stmt.setContributedToSpouseInd(Boolean.TRUE); //in case it's the old one uses amount only
                   // } else {
                      //  sf.setLivedWithPatient(Boolean.TRUE);
                       // stmt.setContributedToSpouseInd(null); //in case it's the old one uses amount only
                   // }
                }
            }
        }
    }

    /**
	* 5.11.7.	SUC2031.32.7 - Dependent Financial Data Upload Income Test
	* Dependents provided through the Veteran's Online Application are treated as unmarried. Calculate the age of each dependent using the incoming message Date of Birth.
	* If any of the following statements are true:
	* 1.	The dependent is under the age of 18,
	* 2.	The Child was permanently and Totally Disabled Before the Age of 18 - incoming message indicator is equal to yes,
	* 3.	The Child is between 18 and 23 years of age AND incoming message Attended School Last Calendar Year indicator is equal to yes,
	* 4.	And the  Date Child Became Dependent is less than the Expiration Date of this Income Year Test
	* then upload the incoming message dependent financial data and indicators for this dependent and process next dependent.

     *
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
	private void preProcessDependentFinancials() {

      FinancialStatement stmt = this.getIncomingFinancialStatement();

      if (stmt == null){
    	  return;
      }
        List stList = new ArrayList();

        Date dependentDOB = null;
        GregorianCalendar today = new GregorianCalendar();

        // 18 years -1 day
        GregorianCalendar eighteenYearAgo = ((GregorianCalendar)today.clone());
        eighteenYearAgo.set(Calendar.YEAR, today.get(Calendar.YEAR)-18);
        eighteenYearAgo.set(Calendar.DAY_OF_YEAR, today.get(Calendar.DATE)-1);

        //23 years -1 day
        GregorianCalendar twentyThreeYearAgo = ((GregorianCalendar)today.clone());
        twentyThreeYearAgo.set(Calendar.YEAR, today.get(Calendar.YEAR)-23);
        twentyThreeYearAgo.set(Calendar.DAY_OF_YEAR, today.get(Calendar.DATE)-1);

        Set dfSet = stmt.getDependentFinancials();
        for (Iterator iter = dfSet.iterator(); iter.hasNext();) {
            DependentFinancials df = (DependentFinancials) iter.next();

            dependentDOB = this.getRelationBirthDate(df.getReportedOn());

            if (Boolean.TRUE.equals(df.getLivedWithPatient())) {
            	df.setContributedToSupport(null);
            }

            if ((dependentDOB != null && DateUtils.isBeforeIgnoreTime(eighteenYearAgo.getTime(), dependentDOB)) ||
                (df.getIncapableOfSelfSupport() != null && df.getIncapableOfSelfSupport().booleanValue()) ||
                (df.getAttendedSchool() != null &&  df.getAttendedSchool().booleanValue() &&
                 dependentDOB != null && dependentDOB.after(twentyThreeYearAgo.getTime()) ))
                 //&& df.isActive()) )
            {
              stList.add(df);
              preProcessDependentFinancials(df);
            }
        }

        // upload only dependent financials that passed rules in 451.35.7
        stmt.removeAllDependentFinancials();
        for (Iterator iter = stList.iterator(); iter.hasNext();)
          stmt.addDependentFinancials((DependentFinancials) iter.next());

    }

    /**
      * CCR11869 VOA backend
	* 5.11.7.1.	SUC2031.32.7.1 - Dependent is Active
	* The system sets the Dependent Active for any part of the income year period of the current primary income test
	* equal to "Yes".
	* 5.11.7.2.	SUC2031.32.7.2 - Child Has Income Indicator
	* The system sets the ES Child Has Income Indicator to "Yes".
	* 5.11.7.3.	SUC2031.32.7.3 - Expenses Paid by Your Dependent Child for College
	* VOA Expenses Paid by Your Dependent Child for College, Vocational Rehabilitation or Training
	*  (ES Wireframe Label: Education Expenses)

	* If the on the incoming message Dependent's Gross Annual Income from Employment is equal to Null or Zero
	* then the ES Child's Education Expense is set to Zero and ES Child Has Income Indicator is set to "No".
	* 5.11.7.5.	SUC2031.32.7.5 - Child Lived with Patient Indicator
	* The system sets the ES Lived with Patient Indicator for child to "Yes".
	* 5.11.7.5.1.	SUC2031.32.7.5.1 - Contributed To Child Support
	* The system sets the ES Contributed to Child's Support Indicator to "No".
	* 5.11.7.5.2.	SUC2031.32.7.5.2 - Dependent Lived with Applicant
	* Contributed to Child's Support
	* (ES Wireframe Label: Contributed to Child Support)
	* VOA Amount Contributed to Child's Support)
	* (ES Wireframe Label: Amount Contributed

	* If on the incoming message the Amount Contributed to Child's Support is greater than zero then set the ES Lived
	* with Patient indicator for child to "No" and the ES Contributed to Child's Support is set to "Yes".
      */
    private void preProcessDependentFinancials(DependentFinancials df) {

      //hasIncome
        // CCR10081 -- add guards against null pointer exception
        if (df != null) {
          if (df.getTotalEmploymentIncome() != null &&
                  df.getTotalEmploymentIncome().getAmount() != null &&
                  df.getTotalEmploymentIncome().getAmount().longValue() > 0)
          {
              df.setHasIncome(Boolean.TRUE);
          } else
          {
              df.setHasIncome(Boolean.FALSE);
          }

          //Lived with Patient & Contribute to Child's Support
          //CCR13373 check indicator first and then amount if needed for backward compatibility
          /*if (Boolean.TRUE.equals(df.getContributedToSupport()) ||
        		  (df.getAmountContributedToSupport() != null && df.getAmountContributedToSupport().longValue() > 0) )
          {
              df.setLivedWithPatient(Boolean.FALSE);
              df.setContributedToSupport(Boolean.TRUE);//in case it's the old one uses amount only
          } else
          {
              df.setLivedWithPatient(Boolean.TRUE);
              df.setContributedToSupport(Boolean.FALSE);//in case it's the old one uses amount only
          }*/
        }
    }

  /**
	* 5.11.3. SUC2031.32.3 - Set the Source of Income Test
	* The system sets the Source of Income Test to Veteran's Online Application and
	* sets the Site Conducting Income Test to null.
	* 5.11.4.	SUC2031.32.4 - Set Date/Time Completed
	* The system sets the Date/Time Completed equal to today.
	* 5.11.4.1.	SUC2031.32.4.1 - Effective Date of Income Test
	* The system sets the Effective Date of the income test to today.
	*
	* 5.11.8 SUC2031.32.8 - Set as Primary Means Test
	* The system sets the Type of Test to Means Test.
   *
   */

    private void preProcessIncomeTestDateAndSource() throws RuleException {
        //VOA form store Disclose Financial Information indicator in the incoming income test
        //the income year on 1010EZ/R is previous calendar year
        //IncomeTest it = this.getIncomeTest() == null ? new IncomeTest() : this.getIncomeTest();

    	IncomeTest it = this.getIncomeTest();



    	//CCR 236652- now if a person choose not to disclose then the default values is set to incoem test
    	//check if this person as a income test on file (non-disclose sends a null if an existing income test is present for the curr iY
    	IncomeTest currIncomeTest = null;
    	if (it == null){
    		currIncomeTest = this.getResultPerson().getIncomeTest(new Integer(Calendar.getInstance().get(Calendar.YEAR)-1));
    	}

        if (it.getDiscloseFinancialInformation().equals(Boolean.FALSE)  || (currIncomeTest != null && currIncomeTest.getDiscloseFinancialInformation().equals(Boolean.FALSE))){
        	try {

        		//it.setSiteConductingTest(getLookupService().getVaFacilityByStationNumber(VAFacility.CODE_HEC.getCode()));
    			it.setSource(getLookupService().getIncomeTestSourceByCode(IncomeTestSource.CODE_HEC.getCode()));
        		it.setType(getLookupService().getIncomeTestTypeByCode(IncomeTestType.CODE_MEANS_TEST.getCode()));
        		it.setIncomeYear(getIncomingIncomeYear());
        		it.setAgreesToPayDeductible(Boolean.TRUE);
          	    it.setPrimaryIncomeTest(Boolean.TRUE);
          	    it.setSendForAdjudication(Boolean.FALSE);
          	    this.getIncomingPerson().setIncomeTest(getIncomingIncomeYear(), it);

        	}
        	catch (UnknownLookupTypeException e) {
    			throw new RuleException("Can not set Income Test Source/Type of Test HEC", e);
    		}   catch (UnknownLookupCodeException ex) {
    			throw new RuleException("Can not set Income Test Source/Type of Test HEC", ex);
    		}
        }
        else {

        	if (currIncomeTest == null){
        		it = new IncomeTest();
        	}

    	    try {
    	    	if (it != null) {
		    	    	//it.setSiteConductingTest(getLookupService().getVaFacilityByStationNumber(VAFacility.CODE_HEC.getCode()));
		    	    	it.setSource(getLookupService().getIncomeTestSourceByCode(IncomeTestSource.CODE_HEC.getCode()));
		    	    	it.setType(getLookupService().getIncomeTestTypeByCode(IncomeTestType.CODE_MEANS_TEST.getCode()));
		    	    	it.setSendForAdjudication(Boolean.FALSE);
		    	      //set the defaults
		    	      it.setDiscloseFinancialInformation(Boolean.TRUE);
					  it.setAgreesToPayDeductible(Boolean.TRUE);
					  it.setPrimaryIncomeTest(Boolean.TRUE);
    	    	}

    	    }
    	    catch (Exception ex) {
    	      throw new RuleException("VOA process can not set Income Test Source/Type of Test HEC", ex);
    	    }

    	   it.setIncomeYear(getIncomingIncomeYear());
    	   this.getIncomingPerson().setIncomeTest(getIncomingIncomeYear(), it);
        }
  }



  public boolean isPharmacyCopayStatusChanged() throws ServiceException {
	  // CCR 11691 - I noticed the code below in comments was eating exception
	  // so I rewrote it.
//	  try{
//		  String onfileCopayStatus=null;
//		  String incomingCopayStatus=null;
//		IncomeTest pIcomeTest=  getPristineIncomeTest();
//		IncomeTest incomingIcomeTest=  getIncomeTest();
//		if(pIcomeTest !=null){
//	        onfileCopayStatus=getPharmacyCopayStatusCode(pIcomeTest) ;
//		}
//		if(incomingIcomeTest !=null){
//			incomingCopayStatus=getPharmacyCopayStatusCode(incomingIcomeTest);
//		}
//		if(onfileCopayStatus==null && incomingCopayStatus==null){
//		  return false;
//	  }
//	  else if(onfileCopayStatus.equals(incomingCopayStatus)){
//		  return false;
//	  }else{
//		  return true;
//	  }
//	  }catch (Exception ex) {
//		  logger.debug("error in PharmacyCopayStatusCode: " + ex.getStackTrace());  }
//	  return false;
	  String pristineCopayStatus = null;
	  String resultCopayStatus = null;
	  IncomeTest pIcomeTest = this.getCurrentIncomeTest(this.getPristinePerson());
	  IncomeTest resultIcomeTest = this.getCurrentIncomeTest(this.getResultPerson());
	  if (pIcomeTest != null) {
		  pristineCopayStatus = getPharmacyCopayStatusCode(pIcomeTest);
	  }
	  if (resultIcomeTest !=null) {
		  resultCopayStatus = getPharmacyCopayStatusCode(resultIcomeTest);
	  }
	  if (pristineCopayStatus == null && resultCopayStatus == null) {
		  return false;
	  }
	  if (pristineCopayStatus == null && resultCopayStatus != null) {
		  return true;
	  }
	  if (pristineCopayStatus != null && resultCopayStatus == null) {
		  return true;
	  }
	  return !pristineCopayStatus.equals(resultCopayStatus);
  }

  public IncomeTest getIncomeTestFutureTestIncluded() {
	  //Subsequent Rejection Letter ESR 3.6_CodeCR10108
	  //This method was cloned from getIncomeTest method.  The reason for addressing the fix in this cloned method
	  //is to reduce the risk of impact to the rest of the ESR application as many of the ILOG rules do not have a need
	  //to return the Future Dated Income Test.
      return this.getIncomingIncludingFutureIncomeTest();
  }
}