/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.esr.common.model.financials;

//----------------------------------------------------------------------------
//  Import java classes
//----------------------------------------------------------------------------
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.ToStringBuilder;

import gov.va.med.fw.model.AbstractKeyedEntity;

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.MeansTestStatus;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.lookup.WkfCaseStatusType;
import gov.va.med.esr.common.model.party.Address;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.util.CommonDateUtils;

/**
 * @author Commons Team
 * @version 1.0
 */
public class IncomeTest
    extends AbstractKeyedEntity {
    /**
     * An instance of serialVersionUID
     */
    private static final long serialVersionUID             = -7079798377877531292L;

    private Person            person                       = null;
    private Integer           incomeYear                   = null;

    /**
     * CCR 12054 - PG8 Indicator from IVM
     * Indicator provided by IVM that tells ES whether or not Veteran meets PG8 criteria. This
     * indicator will be used (instead of income data) for determining P8 eligibility.
     */
    private Boolean           pg8RelaxationIndicator = null;

    private Date ivmConversionDate = null;
    private WkfCaseStatusType workflowCaseStatus = null;

    /**
     * Disclose Financial Information Indicator is defined as an indicator where
     * the Veteran chooses Yes/No to disclose Financial Information. Disclose
     * Financial InformationIndicator is a required field that is user supplied
     * and occurs only once.
     */
    private Boolean           discloseFinancialInformation = null;

    /**
     * Agree to Pay Deductible Indicator is defined as an indicator where the
     * Veteran chooses Yes or No to pay deductible. Veteran must answer yes or
     * no if veteran has a MT status of non exempt. This indicator is set to YES
     * when an IVM case is closed converted by the IVM process. IF veteran
     * chooses 'No' he is considered Ineligible.
     */
    private Boolean           agreesToPayDeductible        = null;

    /**
     * Effective Date of Test is defined as the effective date of the primary
     * test for the income year. Date must be precise. This date + 364 days is
     * the date that a new MT is required.
     */
    private Date              effectiveDate                = null;

    /**
     * Total Dependents is defined as a summation of the dependents and spouse
     * (if applicable). If the veteran has children, the children will only be
     * collected as dependents if the children lived with veteran last calendar
     * year or if the child did not live with veteran the veteran must have
     * contributed > $25 in child supportIf the veteran is married, the spouse's
     * will only be collected and subsequently included under certain
     * conditions. The veteran and spouse must either live together or if they
     * do not live together, the veteran must have contributed at least $600 to
     * the spouse's support during the previous calendar year.
     */
    private Integer           totalNumberOfDependents      = null;

    /**
     * Previous Years Threshold is defined as a code to indicate if the previous
     * years means test parameters are applied to the test. This should only
     * occur when the new parameters are not yet available to the field.
     * Acceptable choices are Yes/No.
     */
    private Boolean           previousYearThresholdApplies = null;

    /**
     * Primary MT indicator is defined as the indicator that is set by the
     * system when a MT is determined to be primary.
     */
    private Boolean           primaryIncomeTest            = null;

    /**
     * Future test indicator is defined as the indicator that is set by the
     * system when a test is determined to be future income test.
     */
    private Boolean           futureTest            = null;

    /**
     * Source Of Test is defined as the source from which this income test was
     * received. It indicates where the income test originated 1. This is the
     * authoritative source for changes to this particular test. 2. If HEC makes
     * changes to the original MT supplied by the VAMC, other than the effective
     * date of test, making a future test the source of test is changed to DCD
     * (HEC), which will prevent the site that created the test from editing it.
     * 3. If HEC only changes the effective date of test, making it a future
     * test the source remains the original source
     *
     * Choices are: IVM, HEC, OTHER FACILITY, VAMC.
     */
    private IncomeTestSource  source                       = null;

    /**
     * Type of Test is defined as the type of test associated with the financial
     * test. Choices: Means Test, Pharmacy Co-pay Test , Long Term Care Copay,
     * Exemption Test - type 4 test, LTC copay test - Type 3 test.
     */
    private IncomeTestType    type                         = null;

    /**
     * Threshold A is defined as the income threshold A as determined by the
     * system for the veteran. The threshold is determined by the system in the
     * following manner:Cat A Vet Income + Cat A First Dependent+(Cat A Per
     * Dependent*(Num Dependent-First Dependent)). The means test thresholds are
     * released annually by VHA and include the veteran and dependent income for
     * the year.
     */
    private BigDecimal        thresholdA                   = null;

    /**
     * GMT Threshold dollar amount is defined as the income GMT threshold as
     * determined by the system for the veteran. This threshold is derived by a
     * look up to the HUD indices table. The threshold amount is computed as
     * FIPS code + MSA code for # of dependents.
     */
    private BigDecimal        gmtThresholdAmount           = null;

    /**
     * GMT address Identifier is defined as the Address identifier that was used
     * to determine the GMT threshold that is used to determine the MT status
     */
    private Address           gmtAddress                   = null;

    /**
     * Site conducting MT is defined as the station ID of the site that supplied
     * the MT.
     */
    private VAFacility        siteConductingTest           = null;

    /**
     * Date/Time of Adjudication is defined as the last date/time the financial
     * test was adjudicated. Date must be precise
     */
    private Date              adjudicationDate             = null;

    /**
     * Date IVM MT Completed is defined as the Primary EDB MT completed date
     * associated with the IVM closed case.
     */
    private Date              ivmTestCompletionDate        = null;

    /**
     * Income verification status is defined as the outcome status of the income
     * verification process. The choices are: Converted - MT Copay Required,
     * Converted - GMT Copay Required, NOT Converted - MT copay Exempt.
     */
    private MeansTestStatus   incomeVerificationStatus     = null;

    /**
     * Income verification status date is defined as the date the income
     * verification status is set
     */
    private Date              incomeVerificationStatusDate = null;

    /**
     * Refused to sign is defined as an indicator Yes/No where through the
     * Income verification process the veteran refuses to sign 1010EZ verifying
     * his income information
     */
    private Boolean           refusedToSign                = null;

    /**
     * Reason MT status is no longer required is defined as the reason a MT
     * status changes to no longer required. The allowable choices are:PH, VA
     * Pension, Compensable SC, POW, Medicaid Eligible, Discharged for
     * disability.
     */
    private BigDecimal        reasonMTNoLongerRequired     = null;

    /**
     * Date MT no longer required is defined as the date a MT is determined no
     * longer requiredFormat is a date field Date must be precise
     */
    private Date              dateMTNoLongerRequired       = null;

    /**
     * Total income is the summation of applicable income of the
     * veteran, spouse and all the valid dependents.
     */
    private BigDecimal        totalIncome                  = null;

    /**
     * Net income is sum of all the applicable income of veteran, spouse
     * and valid dependents minus applicable expenses.
     */
    private BigDecimal        netIncome                    = null;

    /**
     * Deductible Expenses is defined as the summation of the veteran's
     * deductible expenses (medical, burial and educational)
     */
    private BigDecimal        deductibleExpenses           = null;

    /**
     * Net Worth is defined as a summation of the veteran and spouse (if
     * applicable) net worth minus any debts for the previous calendar year from
     * date of testIf the veteran is married, the spouse's income will only be
     * collected and subsequently included in the test under certain conditions.
     * The veteran and spouse must either live together or if they do not live
     * together, the veteran must have contributed at least $600 to the spouse's
     * support during the previous Calendar year
     *
     * When determining the means test status and subsequent category of care,
     * the system will require net worth be entered for those veteran's who
     * require mandatory care
     *
     * If the income plus net worth exceed the annual property threshold, the
     * case can be sent to adjudication for the test
     *
     * Calculation is as follows:Net worth =(Bank account amount + Stock and
     * Bond amounts + Real Property Value + Other property and assets) - Debts
     */
    private BigDecimal        netWorth                     = null;
    private String            comments                     = null;
    private Hardship          hardship                     = null;
    private Set               internalStatuses             = null;
    private BigDecimal        networthThreshold            = null;
    private BigDecimal        pensionThreshold             = null;
    private BigDecimal        childIncomeExclusionThreshold     = null;

    /**
     * CAD is Centralized Anniversary Date
     */
    private Boolean           cadAdjusted                  = null;

    /**
     * Internal variable to hold the user response whether to send the case for
     * adjudication in certain cases.
     * Value of this variable is used to determine the value of means test status
     * code in those certain cases.
     */
    private Boolean		      sendForAdjudication          = null;

    /*
     * Set by IVM to show whether Veteran's income meets threshold for receiving BT benefit.
     */
    private Boolean btFinancialInd = null;

    /**
     * Creates a new IncomeTest object.
     */
    public IncomeTest() {
        super();
    }

    //----------------------------------------------------------------------------
    // Public method(s)
    //----------------------------------------------------------------------------

    public Person getPerson() {
        return this.person;
    }

    public void setPerson(Person person) {
        this.validateOwner(this.person, person);
        this.person = person;
    }

    public Integer getIncomeYear() {
        return this.incomeYear;
    }

    public void setIncomeYear(Integer incomeYear) {
        this.incomeYear = incomeYear;
    }

    public Boolean getDiscloseFinancialInformation() {
        return this.discloseFinancialInformation;
    }

    public void setDiscloseFinancialInformation(
            Boolean discloseFinancialInformation) {
        this.discloseFinancialInformation = discloseFinancialInformation;
    }

    public Boolean getAgreesToPayDeductible() {
        return this.agreesToPayDeductible;
    }

    public void setAgreesToPayDeductible(Boolean agreesToPayDeductible) {
        this.agreesToPayDeductible = agreesToPayDeductible;
    }

    public Date getEffectiveDate() {
        return this.effectiveDate;
    }

    public void setEffectiveDate(Date effectiveDate) {
        this.effectiveDate = effectiveDate;
    }

    public Integer getTotalNumberOfDependents() {
        return this.totalNumberOfDependents;
    }

    public void setTotalNumberOfDependents(Integer totalNumberOfDependents) {
        this.totalNumberOfDependents = totalNumberOfDependents;
    }

    public Boolean getPreviousYearThresholdApplies() {
        return this.previousYearThresholdApplies;
    }

    public void setPreviousYearThresholdApplies(
            Boolean previousYearThresholdApplies) {
        this.previousYearThresholdApplies = previousYearThresholdApplies;
    }

    public Boolean isPrimaryIncomeTest() {
        return this.primaryIncomeTest;
    }

    public Boolean getPrimaryIncomeTest() {
        return isPrimaryIncomeTest();
    }

    public void setPrimaryIncomeTest(Boolean primaryIncomeTest) {
        this.primaryIncomeTest = primaryIncomeTest;
    }

    /**
     * @return Returns the futureTest.
     */
    public Boolean isFutureTest() {
        return futureTest;
    }

    /**
     * @return Returns the futureTest.
     */
    public Boolean getFutureTest() {
        return futureTest;
    }

    /**
     * @param futureTest The futureTest to set.
     */
    public void setFutureTest(Boolean futureTest) {
        this.futureTest = futureTest;
    }

    public IncomeTestSource getSource() {
        return this.source;
    }

    public void setSource(IncomeTestSource source) {
        this.source = source;
    }

    public IncomeTestType getType() {
        return this.type;
    }

    public void setType(IncomeTestType type) {
        this.type = type;
    }

    public BigDecimal getThresholdA() {
        return this.thresholdA;
    }

    public void setThresholdA(BigDecimal thresholdA) {
        this.thresholdA = thresholdA;
    }

//    public BigDecimal getGMTThresholdAmount() {
//        return this.gmtThresholdAmount;
//    }
//
//    public void setGMTThresholdAmount(BigDecimal gmtThresholdAmount) {
//        this.gmtThresholdAmount = gmtThresholdAmount;
//    }

    public BigDecimal getGmtThresholdAmount() {
        return this.gmtThresholdAmount;
    }

    public void setGmtThresholdAmount(BigDecimal gmtThresholdAmount) {
        this.gmtThresholdAmount = gmtThresholdAmount;
    }

    public Address getGmtAddress() {
        return this.gmtAddress;
    }

    public void setGmtAddress(Address gmtAddress) {
        this.gmtAddress = gmtAddress;
    }

    public VAFacility getSiteConductingTest() {
        return this.siteConductingTest;
    }

    public void setSiteConductingTest(VAFacility siteConductingTest) {
        this.siteConductingTest = siteConductingTest;
    }

    public Date getAdjudicationDate() {
        return this.adjudicationDate;
    }

    public void setAdjudicationDate(Date adjudicationDate) {
        this.adjudicationDate = adjudicationDate;
    }

    public Date getIVMTestCompletionDate() {
        return this.ivmTestCompletionDate;
    }

    public void setIVMTestCompletionDate(Date ivmTestCompletionDate) {
        this.ivmTestCompletionDate = ivmTestCompletionDate;
    }

    private Date getIvmTestCompletionDate() {
        return this.ivmTestCompletionDate;
    }

    private void setIvmTestCompletionDate(Date ivmTestCompletionDate) {
        this.ivmTestCompletionDate = ivmTestCompletionDate;
    }

    public MeansTestStatus getIncomeVerificationStatus() {
        return this.incomeVerificationStatus;
    }

    public void setIncomeVerificationStatus(
            MeansTestStatus incomeVerificationStatus) {
        this.incomeVerificationStatus = incomeVerificationStatus;
    }

    public Date getIncomeVerificationStatusDate() {
        return this.incomeVerificationStatusDate;
    }

    public void setIncomeVerificationStatusDate(
            Date incomeVerificationStatusDate) {
        this.incomeVerificationStatusDate = incomeVerificationStatusDate;
    }

    public Boolean getRefusedToSign() {
        return this.refusedToSign;
    }

    public void setRefusedToSign(Boolean refusedToSign) {
        this.refusedToSign = refusedToSign;
    }

    public Boolean getCadAdjusted() {
        return this.cadAdjusted;
    }

    public void setCadAdjusted(Boolean cadAdjusted) {
        this.cadAdjusted = cadAdjusted;
    }

    public BigDecimal getReasonMTNoLongerRequired() {
        return this.reasonMTNoLongerRequired;
    }

    public void setReasonMTNoLongerRequired(BigDecimal reasonMTNoLongerRequired) {
        this.reasonMTNoLongerRequired = reasonMTNoLongerRequired;
    }

    public Date getDateMTNoLongerRequired() {
        return this.dateMTNoLongerRequired;
    }

    public void setDateMTNoLongerRequired(Date dateMTNoLongerRequired) {
        this.dateMTNoLongerRequired = dateMTNoLongerRequired;
    }

    public BigDecimal getTotalIncome() {
        return this.totalIncome;
    }

    public void setTotalIncome(BigDecimal totalIncome) {
        this.totalIncome = totalIncome;
    }

    /**
     * @return Returns the netIncome.
     */
    public BigDecimal getNetIncome() {
        return netIncome;
    }

    /**
     * @param netIncome The netIncome to set.
     */
    public void setNetIncome(BigDecimal netIncome) {
        this.netIncome = netIncome;
    }

    public BigDecimal getDeductibleExpenses() {
        return this.deductibleExpenses;
    }

    public void setDeductibleExpenses(BigDecimal deductibleExpenses) {
        this.deductibleExpenses = deductibleExpenses;
    }

    public BigDecimal getNetWorth() {
        return this.netWorth;

    }

    public void setNetWorth(BigDecimal netWorth) {
        this.netWorth = netWorth;

    }

/*    public BigDecimal getNetWorth() {
    	//VFA: Populated only if Income Year is earlier than 2009
        return CommonDateUtils.isIncomeYear2009OrLatter(this.getIncomeYear())? null : this.netWorth;
    }

    public void setNetWorth(BigDecimal netWorth) {
    	//VFA: Populated only if Income Year is earlier than 2009
        this.netWorth = CommonDateUtils.isIncomeYear2009OrLatter(this.getIncomeYear())? null : netWorth;
    }
*/
    public String getComments() {
        return this.comments;
    }

    public void setComments(String comments) {
        this.comments = comments;
    }

    public BigDecimal getPensionThreshold() {
        return this.pensionThreshold;
    }

    public void setPensionThreshold(BigDecimal pensionThreshold) {
        this.pensionThreshold = pensionThreshold;
    }

    public BigDecimal getNetworthThreshold() {
        return this.networthThreshold;
    }

    public void setNetworthThreshold(BigDecimal networthThreshold) {
        this.networthThreshold = networthThreshold;
    }

    public BigDecimal getChildIncomeExclusionThreshold() {
        return this.childIncomeExclusionThreshold;
    }

    public void setChildIncomeExclusionThreshold(BigDecimal childIncomeExclusionThreshold) {
        this.childIncomeExclusionThreshold = childIncomeExclusionThreshold;
    }

    public Hardship getHardship() {
        return this.hardship;
    }

    public void setHardship(Hardship hardship) {
        this.hardship = hardship;
    }

	public Boolean getPg8RelaxationIndicator() {
		return pg8RelaxationIndicator;
	}

	public void setPg8RelaxationIndicator(Boolean pg8RelaxationIndicator) {
		this.pg8RelaxationIndicator = pg8RelaxationIndicator;
	}

	public Date getIvmConversionDate() {
		return ivmConversionDate;
	}

	public void setIvmConversionDate(Date ivmConversionDate) {
		this.ivmConversionDate = ivmConversionDate;
	}

	public WkfCaseStatusType getWorkflowCaseStatus() {
		return workflowCaseStatus;
	}

	public void setWorkflowCaseStatus(WkfCaseStatusType workflowCaseStatus) {
		this.workflowCaseStatus = workflowCaseStatus;
	}

	/**
	 * @return Returns the sendForAdjudication.
	 */
	public Boolean getSendForAdjudication() {
		return sendForAdjudication;
	}

	/**
	 * @param sendForAdjudication The sendForAdjudication to set.
	 */
	public void setSendForAdjudication(Boolean sendForAdjudication) {
		this.sendForAdjudication = sendForAdjudication;
	}

	private Set getInternalStatuses() {
        if (this.internalStatuses == null) {
            this.internalStatuses = new HashSet();
        }
        return this.internalStatuses;
    }

    private void setInternalStatuses(Set internalStatuses) {
        this.internalStatuses = internalStatuses;
    }

    /**
     * Unmodifiable
     *
     * @return
     */
    public Set getStatuses() {
        return Collections.unmodifiableSet(getInternalStatuses());
    }

    /**
     * Use the addIncomeTestStatus
     * @param status
     */
    public void addStatus(IncomeTestStatus status) {
        addIncomeTestStatus(status);
    }
    /**
     * Use removeIncomeTestStatus
     * @param status
     */
    public void removeStatus(IncomeTestStatus status) {
        removeIncomeTestStatus(status);
    }

    /**
     * Method name changed to reflect the input parameter type
     * Merge service generates the name based on type
     * @param status
     */
    public void addIncomeTestStatus(IncomeTestStatus status) {
        Validate.notNull(status, "status cannot be null");
        getInternalStatuses().add(status);
        status.setIncomeTest(this);
    }

    public void removeIncomeTestStatus(IncomeTestStatus status) {
        Validate.notNull(status, "Status cannot be null");
        List stList = new ArrayList();
        for (Iterator iter = getInternalStatuses().iterator(); iter.hasNext();) {
			IncomeTestStatus st = (IncomeTestStatus) iter.next();
			if ((status.getEntityKey() != null  && st.getEntityKey() != null && !status.getEntityKey().equals(st.getEntityKey()))
					|| (status != st)
					|| (!status.equals(st))) {
				stList.add(st);
			}
		}
        getInternalStatuses().clear();
        getInternalStatuses().addAll(stList);
        status.setIncomeTest(null);
    }

    public void removeAllStatuses() {
        getInternalStatuses().clear();
    }

    /*
     * (non-Javadoc)
     *
     * @see gov.va.med.fw.model.AbstractKeyedEntity#buildToString(org.apache.commons.lang.builder.ToStringBuilder)
     */
    protected void buildToString(ToStringBuilder builder) {
        super.buildToString(builder);

        builder.append("incomeYear", this.incomeYear);
        builder.append("siteConductingTest", this.siteConductingTest);
        builder.append("discloseFinancialInformation",
                this.discloseFinancialInformation);
        builder.append("agreesToPayDeductible", this.agreesToPayDeductible);
        builder.append("effectiveDate", this.effectiveDate);
        builder.append("totalNumberOfDependents", this.totalNumberOfDependents);
        builder.append("previousYearThresholdApplies",
                this.previousYearThresholdApplies);
        builder.append("primaryIncomeTest", this.primaryIncomeTest);
        builder.append("source", this.source);
        builder.append("type", this.type);
        builder.append("thresholdA", this.thresholdA);
        builder.append("gmtThresholdAmount", this.gmtThresholdAmount);
        builder.append("adjudicationDate", this.adjudicationDate);
        builder.append("ivmTestCompletionDate", this.ivmTestCompletionDate);
        builder.append("incomeVerificationStatus",
                this.incomeVerificationStatus);
        builder.append("incomeVerificationStatusDate",
                this.incomeVerificationStatusDate);
        builder.append("refusedToSign", this.refusedToSign);
        builder.append("reasonMTNoLongerRequired",
                this.reasonMTNoLongerRequired);
        builder.append("dateMTNoLongerRequired", this.dateMTNoLongerRequired);
        builder.append("totalIncome", this.totalIncome);
        builder.append("deductibleExpenses", this.deductibleExpenses);
        builder.append("netWorth", this.netWorth);
        builder.append("comments", this.comments);
        builder.append("hardship", this.hardship);
        builder.append("cadAdjusted", this.cadAdjusted);
        builder.append("pg8RelaxationIndicator", this.pg8RelaxationIndicator);
        builder.append("btFinancialInd", this.btFinancialInd);
    }

    /**
     * Utility methods to get the status info fro the income test
     * @return
     */
    public IncomeTestStatus getIncomeTestStatus() {
        return getIncomeTestStatus(getType());
    }

    public Date getCompletedDate() {
        IncomeTestStatus status = getIncomeTestStatus();
        return status == null ? null : status.getCompletedDate();
    }

    public void setCompletedDate(Date date) {
        //CR 5317 set complted date on all statuses
        for (Iterator i= getInternalStatuses().iterator(); i.hasNext();)
        {
            IncomeTestStatus status = (IncomeTestStatus)i.next();
            status.setCompletedDate(date);
        }
/*        IncomeTestStatus status = getIncomeTestStatus(getType());
        if (status == null) {
            throw new IllegalArgumentException("No IncomeTestStatus found to set the date. Please add a IncomeTestStatus before setting the completed date.");
        }
        status.setCompletedDate(date);*/
    }
    public Date getLastEditedDate() {
        IncomeTestStatus status = getIncomeTestStatus();
        return status == null ? null : status.getLastEditedDate();
    }

    public void setLastEditedDate(Date date) {
        //CR 5317 set last edited date on all statuses
        for (Iterator i= getInternalStatuses().iterator(); i.hasNext();)
        {
            IncomeTestStatus status = (IncomeTestStatus)i.next();
            status.setLastEditedDate(date);
        }
/*        IncomeTestStatus status = getIncomeTestStatus(getType());
        if (status == null) {
            throw new IllegalArgumentException("No IncomeTestStatus found to set the date. Please add a IncomeTestStatus before setting the last edit date.");
        }
        status.setLastEditedDate(date);*/
    }

    public MeansTestStatus getStatus() {
        return (getIncomeTestStatus() != null) ? getIncomeTestStatus().getStatus() : null;
    }

    public boolean isPendingAdjudication() {
    	MeansTestStatus status = getStatus();
    	//if (true) return true; //TODO remove test code
    	return status == null ? false :
    		MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION.getName().equals(status.getCode());
    }

    public void setMeansTestStatus(MeansTestStatus status) {
        Validate.notNull(status, "MeansTestStatus can not be null");
        if (getType() == null) {
            throw new IllegalArgumentException("IncomeTestType must be set before the MeansTestStatus.");
        }
        IncomeTestStatus itStatus = getIncomeTestStatus(getType());
        if (itStatus == null) {
            itStatus = new IncomeTestStatus();
            itStatus.setType(getType());
            addStatus(itStatus);
        }
        itStatus.setStatus(status);
    }

    public void setIncomeTestStatus(IncomeTestType type, MeansTestStatus status) {
        Validate.notNull(type, "IncomeTestType can not be null");
        IncomeTestStatus itStatus = getIncomeTestStatus(type);
        if (itStatus == null) {
            itStatus = new IncomeTestStatus();
            itStatus.setType(type);
            addStatus(itStatus);
        }
        itStatus.setStatus(status);
    }

    public void setIncomeTestStatus(IncomeTestType type, MeansTestStatus status, MeansTestStatus determinedStatus) {
        Validate.notNull(type, "IncomeTestType can not be null");
        IncomeTestStatus itStatus = getIncomeTestStatus(type);
        if (itStatus == null) {
            itStatus = new IncomeTestStatus();
            itStatus.setType(type);
            addStatus(itStatus);
        }
        itStatus.setStatus(status);
        itStatus.setDeterminedStatus(determinedStatus);
    }

    public MeansTestStatus getDeterminedStatus() {
        return (getIncomeTestStatus() != null) ? getIncomeTestStatus().getDeterminedStatus() : null;
    }

    public IncomeTestStatus getPharmacyCoPayStatus() {
        return this.getIncomeTestStatusOfType(IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST);
    }

    public IncomeTestStatus getLongTermCareStatus() {
        return this.getIncomeTestStatusOfType(IncomeTestType.CODE_LTC_CO_PAY_EXEMPTION_TEST);
    }

    public IncomeTestStatus getIncomeTestStatusOfType(
            IncomeTestType.Code typeCode) {
        IncomeTestStatus match = null;
        if (typeCode != null && internalStatuses != null)
            match = getIncomeTestStatusOfType(internalStatuses, typeCode);
        return match;
    }

    public IncomeTestStatus getIncomeTestStatus(IncomeTestType itType) {
        IncomeTestStatus match = null;
        if (itType != null && itType.getCode() != null && internalStatuses != null)
            match = getIncomeTestStatusOfType(internalStatuses, itType);
        return match;
    }

    public IncomeTestStatus getIncomeTestStatus(IncomeTestType.Code typeCode) {
        IncomeTestStatus match = null;
        if (typeCode != null && internalStatuses != null)
            match = getIncomeTestStatusOfType(internalStatuses, typeCode);
        return match;
    }

    public IncomeTestType getPrimaryTestType() {
        if (Boolean.TRUE.equals(getPrimaryIncomeTest())){
            return getType();
        }
        return null;
    }

    /**
     * Helper method to get the IncomeTestStatus of a given type code.
     * This method iterates the collection of statuses and return
     * the IncomeTestStatus of give type code or null if no status for a given
     * type code exists.
     *
     * @param statuses
     * @param typeCode
     * @return IncomeTestStatus if found or null if no IncomeTestStatus with a given type found.
     */
    public static IncomeTestStatus getIncomeTestStatusOfType(Set statuses,
            IncomeTestType.Code typeCode) {
        Validate.notNull(statuses, "The set of statuses must not be null");
        Validate.notNull(typeCode,
                "IncomeTestStatus type code must not be null");
        for (Iterator iter = statuses.iterator(); iter.hasNext();) {
            IncomeTestStatus testStatus = (IncomeTestStatus) iter.next();
            IncomeTestType incomeTestType = testStatus.getType();
            if (incomeTestType != null && incomeTestType.getCode() != null
                    && incomeTestType.getCode().equals(typeCode.getName())) {
                return testStatus;
            }
        }
        return null;
    }

    public static IncomeTestStatus getIncomeTestStatusOfType(Set statuses,
            IncomeTestType type) {
        Validate.notNull(statuses, "The set of statuses must not be null");
        Validate.notNull(type, "IncomeTestType type must not be null");
        for (Iterator iter = statuses.iterator(); iter.hasNext();) {
            IncomeTestStatus testStatus = (IncomeTestStatus) iter.next();
            IncomeTestType testStatusType = testStatus.getType();
            if (testStatusType != null && testStatusType.getCode() != null
                    && type.getCode() != null
                    && testStatusType.getCode().equals(type.getCode())) {
                return testStatus;
            }
        }
        return null;
    }

    public Boolean getBtFinancialInd() {
        return btFinancialInd;
    }

    public void setBtFinancialInd(Boolean btFinancialInd) {
        this.btFinancialInd = btFinancialInd;
    }

}