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

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

// Java classes
import java.math.BigDecimal;
import gov.va.med.esr.common.model.ee.AgentOrangeExposure;
import gov.va.med.esr.common.model.ee.Application;
import gov.va.med.esr.common.model.ee.CancelDecline;
import gov.va.med.esr.common.model.ee.EGTSetting;
import gov.va.med.esr.common.model.ee.Eligibility;
import gov.va.med.esr.common.model.ee.EnrollmentDetermination;
import gov.va.med.esr.common.model.ee.EnrollmentOverride;
import gov.va.med.esr.common.model.ee.EnvironmentalContaminationExposure;
import gov.va.med.esr.common.model.ee.HealthBenefitPlan;
import gov.va.med.esr.common.model.ee.HealthBenefitProfile;
import gov.va.med.esr.common.model.ee.MedicaidFactor;
import gov.va.med.esr.common.model.ee.ReceivedEligibility;
import gov.va.med.esr.common.model.ee.ReceivedEnrollment;
import gov.va.med.esr.common.model.financials.Hardship;
import gov.va.med.esr.common.model.financials.IncomeTest;
import gov.va.med.esr.common.model.lookup.AgentOrangeExposureLocation;
import gov.va.med.esr.common.model.lookup.DataChangeSource;
import gov.va.med.esr.common.model.lookup.EligibilityFactor;
import gov.va.med.esr.common.model.lookup.EligibilityType;
import gov.va.med.esr.common.model.lookup.EnrollmentCategory;
import gov.va.med.esr.common.model.lookup.EnrollmentPriorityGroup;
import gov.va.med.esr.common.model.lookup.EnrollmentStatus;
import gov.va.med.esr.common.model.lookup.HealthBenefitPlanType;
import gov.va.med.esr.common.model.lookup.MeansTestStatus;
import gov.va.med.esr.common.model.lookup.RegistryType;
import gov.va.med.esr.common.model.lookup.SpinalCordInjuryType;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.registry.Registry;
import gov.va.med.esr.common.rule.EnrollmentInput;
import gov.va.med.esr.common.rule.data.accessor.DemographicAccessor;
import gov.va.med.esr.common.rule.data.accessor.PropertyAccessor;
import gov.va.med.esr.common.util.EnrollmentDeterminationComparatorByEntityKeyValue;
import gov.va.med.esr.service.EligibilityEnrollmentService;
import gov.va.med.esr.service.RegistrySearchCriteria;
import gov.va.med.fw.rule.RuleConfigurationException;
import gov.va.med.fw.rule.RuleException;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.StringUtils;

import gov.va.med.esr.common.clock.Clock;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.Validate;

// ESR classes

/**
 * This class implements an interface which gets mapped to a virtual class in
 * ILOG. The class will support processing, validating, and calculation of final
 * decision. This class needs to be initialized by setting the calculated
 * enrollment.
 *
 * @author Carlos Ruiz
 * @version 1.0
 */
public class EnrollmentInputParameter extends BaseParameter implements
        EnrollmentInput {

    /**
     * An instance of serialVersionUID
     */
    private static final long serialVersionUID = -5776487792673041771L;

    private static final String TEN_PERCENT = "10";

    private static final int MAX_ALLOWED_RECORDS = 20;

    // The calculated enrollment determination
    private EnrollmentDetermination resultEnrollmentDetermination = null;

    // The incoming enrollment determination
    private EnrollmentDetermination incomingEnrollmentDetermination = null;

    // Enrollment for continuous enrollment purposes, nothing else.
    private EnrollmentDetermination ceEnrollmentDetermination = null;

    // The pristine enrollment determination
    private EnrollmentDetermination pristineEnrollmentDetermination = null;

    private String cancelDecline = null;

    private String calculatedPrimaryEligibilityCode = null;

    private String receivedPrimaryEligibilityCode = null;

    private String currentPrimaryEligibilityCode = null;
    // CR 9803 & CCR 10023
    private String mostRecentPriorityGroupOfVerifiedEnrollment = null;
    // CR 9803 & CCR 10023
    private String mostRecentPrioritySubGroupOfVerifiedEnrollment = null;

    private Date applicationDate = null;

    private Date earliestUnverifiedApplicationDate = null;

    // CCR 10023
    private Date earliestEffDateForVerifiedUnlessCancelled = null;
    private Boolean hasEverHadVerifiedEnrollment = null;

    private Date cancelledDeclinedDate = null;

    private static final String VETERAN = "V";

    private static final String NONVETERAN = "N";

    private String priorEnrollmentPriority = null;

    private Date earliestEnrollmentEffectiveDate = null;

    private Date earliestVerifiedEnrollmentDate = null;

    private String mostRecentNonNullPriorityCode = null;

    //CCR12064
    private HealthBenefitProfile pristineHealthBenefitProfile = null;

    /**
     * Default constructor
     */
    public EnrollmentInputParameter() {
        super();
    }

    public boolean getVeteranIndicator() {
        return (this.getIncomingPerson().getVeteran() != null) ? this
                .getIncomingPerson().getVeteran().booleanValue() : false;
    }

    public boolean getResultVeteranIndicator() {
        return (this.getResultPerson().getVeteran() != null) ? this
                .getResultPerson().getVeteran().booleanValue() : false;
    }

    public boolean getPristineVeteranIndicator() {
        return (this.getPristinePerson().getVeteran() != null) ? this
                .getPristinePerson().getVeteran().booleanValue() : false;
    }


    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getCalculatedEnrollmentDetermination()
     */
    public EnrollmentDetermination getCalculatedEnrollmentDetermination()
            throws RuleException {
        return this.getResultEnrollmentDetermination();
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setEnrollmentPriority(java.lang.String)
     */
    public void setEnrollmentPriority(String value) throws RuleException {
        try {
            if (value == null) {
                // reset priority group
                this.getResultEnrollmentDetermination().setPriorityGroup(null);
            } else {
                this.getHelperService().setEnrollmentPriority(value,
                        this.getResultEnrollmentDetermination());
            }
        } catch (ServiceException e) {
            throw new RuleException("Failed to set enrollment priority", e);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setSubPriority(java.lang.String)
     */
    public void setSubPriority(String code) throws RuleException {
        try {
            if (code == null) {
                this.getResultEnrollmentDetermination().setPrioritySubGroup(
                        null);
            } else {
                this.getHelperService().setEnrollmentPrioritySubGroup(code,
                        this.getResultEnrollmentDetermination());
            }
        } catch (ServiceException e) {
            throw new RuleException("failed to set sub priority", e);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#addToOtherEligibilityList(java.lang.String)
     */
    public void addToOtherEligibilityList(String value) throws RuleException {
        try {
            Eligibility eligibility = this.getHelperService()
                    .createOtherEligibility(value);
            if (!this.containsEligibility(
                    this.getResultEnrollmentDetermination()
                            .getOtherEligibilities(), eligibility)) {
                this.getResultEnrollmentDetermination().addOtherEligibility(
                        eligibility);
             }
        } catch (ServiceException e) {
            throw new RuleException("Failed to add to other eligibility list",
                    e);
        }
   }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#addHBPToList(java.lang.String)
     */
    public void addHBPToList(String hbpCodeStr) throws RuleException {
        try {
            HealthBenefitPlan newPlan = new HealthBenefitPlan();
            newPlan.setPlanType(this.getLookupService().getHealthBenefitPlanTypeByCode(hbpCodeStr));
            newPlan.setChangeDate(new Date());
            newPlan.setChangeSource((DataChangeSource)getLookupService().getByCode(DataChangeSource.class,DataChangeSource.CODE_ESR.getCode()));
            this.getIncomingPerson().getHealthBenefitProfile().addHealthBenefitPlan(newPlan);
       } catch (ServiceException e) {
            throw new RuleException("Failed to add to health benefit Plan list",
                    e);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#removeHBPFromList(java.lang.String)
     */
    public void removeHBPFromList(String hbpCodeStr) throws RuleException {
        try {
            HealthBenefitPlanType planTypeByCodeStr = this.getLookupService().getHealthBenefitPlanTypeByCode(hbpCodeStr);
            if (planTypeByCodeStr != null)
                this.getIncomingPerson().getHealthBenefitProfile().removeHealthBenefitPlanByType(planTypeByCodeStr);
        } catch (ServiceException e) {
            throw new RuleException("Failed to remove from health benefit Plan list",
                    e);
        }
    }
    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#addToSecondaryEligibilityList(java.lang.String)
     */
    public void addToSecondaryEligibilityList(String value)
            throws RuleException {
        try {
            Eligibility eligibility = this.getHelperService()
                    .createEligibility(value);
            if (!this.containsEligibility(this
                    .getResultEnrollmentDetermination()
                    .getSecondaryEligibilities(), eligibility)) {
                this.getResultEnrollmentDetermination()
                        .addSecondaryEligibility(eligibility);
            }
        } catch (ServiceException e) {
            throw new RuleException(
                    "Failed to add to secondary eligibility list", e);
        }

    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#hasEligibilityCodeEqualTo(java.lang.String)
     */
    public boolean hasReceivedEligibilityCodeEqualTo(String eligibilityCode) {
        Person incoming = getIncomingPerson();
        boolean found = this.containsReceivedEligibility(incoming
                .getReceivedSecondaryEligibilities(), eligibilityCode);
        if (!found) {
            ReceivedEligibility primary = incoming
                    .getReceivedPrimaryEligibility();
            found = (primary != null && primary.getType() != null && primary
                    .getType().getCode().equals(eligibilityCode));
        }
        return found;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setPrimaryEligilityCode(java.lang.String)
     */
    public void setPrimaryEligilityCode(String value) throws RuleException {
        try {
            this.getResultEnrollmentDetermination().setPrimaryEligiblity(
                    this.getHelperService().createEligibility(value));
        } catch (ServiceException e) {
            throw new RuleException("Failed to set primary eligibility code", e);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getCalculatedPrimaryEligibilityCode()
     */
    public String getCalculatedPrimaryEligibilityCode() throws RuleException {
        if (calculatedPrimaryEligibilityCode == null) {
            Eligibility eligibility = this.getResultEnrollmentDetermination()
                    .getPrimaryEligibility();
            this.calculatedPrimaryEligibilityCode = (eligibility != null && eligibility
                    .getType() != null) ? eligibility.getType().getCode()
                    : null;
        }
        return this.calculatedPrimaryEligibilityCode;
    }

    public String getIncomingPrimaryEligibilityCode() throws RuleException {
        Eligibility eligibility = this.getIncomingEnrollmentDetermination()
                .getPrimaryEligibility();
        String result = (eligibility != null && eligibility.getType() != null) ? eligibility
                .getType().getCode()
                : null;
        logger.debug(result);
        return result;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getReceivedPrimaryEligibilityCode()
     */
    public String getReceivedPrimaryEligibilityCode() {
        if (this.receivedPrimaryEligibilityCode == null) {
            this.receivedPrimaryEligibilityCode = this
                    .getReceivedPrimaryEligibilityCode(this.getIncomingPerson());
        }
        return this.receivedPrimaryEligibilityCode;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getApplicationDate()
     */
    public Date getApplicationDate() {
        if (this.applicationDate == null) {
            this.applicationDate = (this.getIncomingPerson().getApplication() != null) ? this
                    .getIncomingPerson().getApplication().getApplicationDate()
                    : null;
        }
        return this.applicationDate;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getReceivedEnrollmentDate()
     */
    public Date getReceivedEnrollmentDate() {
        return (this.getReceivedEnrollment(getIncomingPerson()) != null) ? this
                .getReceivedEnrollment(getIncomingPerson()).getEnrollmentDate()
                : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getReceivedEnrollmentEffectiveDate()
     */
    public Date getReceivedEnrollmentEffectiveDate() {
        return (this.getReceivedEnrollment(getIncomingPerson()) != null) ? this
                .getReceivedEnrollment(getIncomingPerson()).getEnrollmentDate()
                : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getReceivedEnrollmentStatus()
     */
    public String getReceivedEnrollmentStatus()
            throws RuleConfigurationException {
        EnrollmentStatus enrollmentStatus = (this
                .getReceivedEnrollment(getIncomingPerson()) != null) ? this
                .getReceivedEnrollment(getIncomingPerson())
                .getEnrollmentStatus() : null;
        return (enrollmentStatus != null) ? enrollmentStatus.getCode() : null;
    }

    public String getCancelledDeclinedReason() {
        CancelDecline cd = getIncomingPerson().getCancelDecline();
        return (cd != null && cd.getReason() != null) ? cd.getReason()
                .getCode() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getCancelledDeclined()
     */
    public String getCancelledDeclined() {
        if (this.cancelDecline == null) {
            this.cancelDecline = this
                    .getCancelDecline(this.getIncomingPerson());
        }
        return this.cancelDecline;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getCancelledDeclinedDate()
     */
    public Date getCancelledDeclinedDate() {
        if (this.cancelledDeclinedDate == null) {
            this.cancelledDeclinedDate = this.getCancelledDeclinedDate(this
                    .getIncomingPerson());
        }
        return this.cancelledDeclinedDate;
    }

    public String getPrimaryEligibilityCodeFromSite() throws RuleException {
        return getReceivedPrimaryEligibilityCode();
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getApplicationDateFromVAMC()
     */
    public Date getApplicationDateFromVAMC() {
        return this.getApplicationDate();
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentDate()
     */
    public Date getEnrollmentDate() {
        return (this.getIncomingEnrollmentDetermination() != null) ? this
                .getIncomingEnrollmentDetermination().getEnrollmentDate()
                : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentDateFromVAMC()
     */
    public Date getEnrollmentDateFromVAMC() {
        return (this.getReceivedEnrollment(this.getIncomingPerson()) != null) ? this
                .getReceivedEnrollment(this.getIncomingPerson())
                .getEnrollmentDate()
                : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentEffectiveDate()
     */
    public Date getEnrollmentEffectiveDate() {
        return (this.getIncomingEnrollmentDetermination() != null) ? this
                .getIncomingEnrollmentDetermination().getEffectiveDate() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentEffectiveDateFromVAMC()
     */
    public Date getEnrollmentEffectiveDateFromVAMC() {
        return (this.getReceivedEnrollment(this.getIncomingPerson()) != null) ? this
                .getReceivedEnrollment(this.getIncomingPerson())
                .getEffectiveDate()
                : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentEndDateFromVAMC()
     */
    public Date getEnrollmentEndDateFromVAMC() {
        return (this.getReceivedEnrollment(this.getIncomingPerson()) != null) ? this
                .getReceivedEnrollment(this.getIncomingPerson()).getEndDate()
                : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentPriority()
     */
    public String getEnrollmentPriority() throws RuleException {
        // ILOG wants the calculated enrollment priority
        return (this.getResultEnrollmentDetermination() != null && this
                .getResultEnrollmentDetermination().getPriorityGroup() != null) ? this
                .getResultEnrollmentDetermination().getPriorityGroup()
                .getCode()
                : null;
    }

    public String getCalculatedEnrollmentStatus() throws RuleException {
        return (this.getResultEnrollmentDetermination() != null && this
                .getResultEnrollmentDetermination().getEnrollmentStatus() != null) ? this
                .getResultEnrollmentDetermination().getEnrollmentStatus()
                .getCode()
                : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#getEnrollmentPriority(gov.va.med.esr.common.model.person.Person)
     */
    public String getEnrollmentPriority(Person person) throws RuleException {
        EnrollmentDetermination ed = person != null ? person
                .getEnrollmentDetermination() : null;
        return (ed != null && ed.getPriorityGroup() != null) ? ed
                .getPriorityGroup().getCode() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentPriorityFromVAMC()
     */
    public String getEnrollmentPriorityFromVAMC() {
        return (this.getReceivedEnrollment(this.getIncomingPerson()) != null && this
                .getReceivedEnrollment(this.getIncomingPerson())
                .getPriorityGroup() != null) ? this.getReceivedEnrollment(
                this.getIncomingPerson()).getPriorityGroup().getCode() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentSourceFromMessage()
     */
    public String getEnrollmentSourceFromMessage() {
        return (this.getReceivedEnrollment(this.getIncomingPerson()) != null && this
                .getReceivedEnrollment(this.getIncomingPerson())
                .getCalculationSource() != null) ? this.getReceivedEnrollment(
                this.getIncomingPerson()).getCalculationSource().getCode()
                : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentStatus()
     */
    public String getEnrollmentStatus() throws RuleException {
        return this.getEnrollmentStatusCode(this.getIncomingPerson());
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentStatusFromVAMC()
     */
    public String getEnrollmentStatusFromVAMC() {
        String code = null;
        // Don't process the received enrollment again.
        if (this.getReceivedEnrollment(this.getIncomingPerson()) != null) {
            if (this.getReceivedEnrollment(this.getIncomingPerson())
                    .getEntityKey() == null) {
                code = (this.getReceivedEnrollment(this.getIncomingPerson())
                        .getEnrollmentStatus() != null) ? this
                        .getReceivedEnrollment(this.getIncomingPerson())
                        .getEnrollmentStatus().getCode() : null;
            }
        }
        return code;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentSubPriority()
     */
    public String getEnrollmentSubPriority() throws RuleException {
        // ILOG wants result
        return (this.getResultEnrollmentDetermination() != null && this
                .getResultEnrollmentDetermination().getPrioritySubGroup() != null) ? this
                .getResultEnrollmentDetermination().getPrioritySubGroup()
                .getCode()
                : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentSubPriorityFromVAMC()
     */
    public String getEnrollmentSubPriorityFromVAMC() {
        return (this.getReceivedEnrollment(this.getIncomingPerson()) != null && this
                .getReceivedEnrollment(this.getIncomingPerson())
                .getPrioritySubGroup() != null) ? this.getReceivedEnrollment(
                this.getIncomingPerson()).getPrioritySubGroup().getCode()
                : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getMostRecentUnverifiedApplicationDate()
     */
    public Date getMostRecentUnverifiedApplicationDate() throws RuleException {
        if (this.earliestUnverifiedApplicationDate == null) {
            // CR7328 always use app date from incoming
            this.earliestUnverifiedApplicationDate = (this.getIncomingPerson()
                    .getApplication() != null) ? this.getIncomingPerson()
                    .getApplication().getApplicationDate() : null;

            // If date is null, try to get one from history
            if (this.earliestUnverifiedApplicationDate == null) {
                this.earliestUnverifiedApplicationDate = this
                        .getEarliestUnverifiedEnrollmentApplicationDate();
            }
        }
        return this.earliestUnverifiedApplicationDate;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getPreviousEnrollmentDate()
     */
    public Date getPreviousEnrollmentDate() {
        Date previous = (this.getPristineEnrollmentDetermination() != null) ? this
                .getPristineEnrollmentDetermination().getEnrollmentDate()
                : null;
        if (previous == null) {
            previous = new Date(); // use today's date according to I3
            // requirements
        }
        return previous;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getPreviousEnrollmentPriority()
     */
    public String getPreviousEnrollmentPriority() {
        return (this.getPristineEnrollmentDetermination() != null && this
                .getPristineEnrollmentDetermination().getPriorityGroup() != null) ? this
                .getPristineEnrollmentDetermination().getPriorityGroup()
                .getCode()
                : null;
    }

    /**
     * get the last verified enrollment priority before PH was added CCR#6981
     * 630G letter being triggered when requirment states a 630D should be
     * triggered
     */
    public String getEnrollmentPriorityBeforePH() throws RuleException {

        String priority = null;
        Person person = this.getIncomingPerson();

        if (person != null && person.getEntityKey() != null) {
            try {
                String[] priGrps = this.getEligibilityEnrollmentService()
                        .getEnrollmentPriorityBeforePH(person.getEntityKey());
                if (priGrps != null)
                    priority = priGrps[0];
            } catch (ServiceException serviceEx) {
                throw new RuleException(
                        "Error getting enrollment priority before PH was added",
                        serviceEx);
            }
        }
        return priority;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getPreviousEnrollmentStatus()
     */
    public String getPreviousEnrollmentStatus() throws RuleException {
        return this.getEnrollmentStatusCode(this.getPristinePerson());
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getPreviousEnrollmentSubPriority()
     */
    public String getPreviousEnrollmentSubPriority() {
        return (this.getPristineEnrollmentDetermination() != null && this
                .getPristineEnrollmentDetermination().getPrioritySubGroup() != null) ? this
                .getPristineEnrollmentDetermination().getPrioritySubGroup()
                .getCode()
                : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#hasPreviousVerifiedEnrollmentStatus()
     */
    public boolean hasPreviousVerifiedEnrollmentStatus() throws RuleException {
        return this.getEnrollmentStatusCode(this.getPristinePerson()) != null
                && this.getEnrollmentStatusCode(this.getPristinePerson())
                        .equals(EnrollmentStatus.CODE_VERIFIED.getName());
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#hasPreviousVerifiedEnrollmentStatus()
     */
    public boolean hasPriorVerifiedEnrollmentStatus() throws RuleException {
        return this.hasEverHadVerifiedEnrollment();
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setApplicationDate(java.util.Date)
     */
    public void setApplicationDate(Date applicationDate) {
        Application application = this.getResultPerson().getApplication();
        if (application == null && applicationDate == null) {
            // don't do anything since caller was trying to null application
        } else {
            if (application == null) {
                application = new Application();
                this.getResultPerson().setApplication(application);
            }
            application.setApplicationDate(applicationDate);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setEnrollmentCategory(java.lang.String)
     */
    public void setEnrollmentCategory(String category) throws RuleException {
        try {
            this.getHelperService().setEnrollmentCategory(category,
                    this.getResultEnrollmentDetermination());
        } catch (ServiceException e) {
            throw new RuleException("Failed to set enrollment category", e);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setEnrollmentDate(java.util.Date)
     */
    public void setEnrollmentDate(Date enrollmentDate) throws RuleException {
        this.getResultEnrollmentDetermination().setEnrollmentDate(
                enrollmentDate);
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setEnrollmentEffectiveDate(java.util.Date)
     */
    public void setEnrollmentEffectiveDate(Date effectiveDate)
            throws RuleException {
        this.getResultEnrollmentDetermination().setEffectiveDate(effectiveDate);
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setEnrollmentEndDate(java.util.Date)
     */
    public void setEnrollmentEndDate(Date enrollmentEndDate)
            throws RuleException {
        this.getResultEnrollmentDetermination().setEndDate(enrollmentEndDate);
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setEnrollmentSource(java.lang.String)
     */
    public void setEnrollmentSource(String source) throws RuleException {
        // TODO there was a problem in I2 where ILOG source codes were wrong.
        // Make sure they are corrected.
        try {
            this.getHelperService().setEnrollmentSource(source,
                    this.getResultEnrollmentDetermination());
        } catch (ServiceException e) {
            throw new RuleException("Failed to set enrollment source", e);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setEnrollmentStatus(java.lang.String)
     */
    public void setEnrollmentStatus(String status) throws RuleException {
        try {
            this.getHelperService().setEnrollmentStatus(status,
                    this.getResultEnrollmentDetermination());
        } catch (ServiceException e) {
            throw new RuleException("Failed to set enrollment status", e);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setFacilityReceivedFrom(java.lang.String)
     */
    public void setFacilityReceivedFrom(VAFacility.Code facility)
            throws RuleException {
        Validate.notNull(facility, "A facility must not be null");
        setFacilityReceivedFrom(facility.getName());
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setFacilityReceivedFrom(java.lang.String)
     */
    public void setFacilityReceivedFrom(String facility) throws RuleException {
        try {
            this.getHelperService().setFacilityReceivedFrom(facility,
                    this.getResultEnrollmentDetermination());
        } catch (ServiceException e) {
            throw new RuleException("Failed to set facility received", e);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setFacilityReceivedToHEC()
     */
    public void setFacilityReceivedToHEC() throws RuleException {
        this.setFacilityReceivedFrom(VAFacility.CODE_HEC);
    }

    /**
     * Removes received secondary eligibility objects
     */
    public void removeVeteranReceivedEligibilityCodes() {
        Set toRemove = new HashSet();

        Set onFiles = this.getResultPerson()
                .getReceivedSecondaryEligibilities();

        for (Iterator iter = onFiles.iterator(); iter.hasNext();) {
            ReceivedEligibility re = (ReceivedEligibility) iter.next();
            boolean found = false;
            for (Iterator iterRes = getIncomingPerson()
                    .getReceivedSecondaryEligibilities().iterator(); iterRes
                    .hasNext();) {
                ReceivedEligibility incoming = (ReceivedEligibility) iterRes
                        .next();
                if (this.getMergeRuleService().getMatchRuleService().match(re,
                        incoming)) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                toRemove.add(re);
            }
        }
        for (Iterator iter = toRemove.iterator(); iter.hasNext();) {
            ReceivedEligibility remove = (ReceivedEligibility) iter.next();
            this.getResultPerson().removeReceivedSecondaryEligibility(remove);
        }

    }

    public void updateVeteranReceivedEligibilityCodes() throws RuleException {
        Person incoming = this.getIncomingPerson();
        Person onFile = this.getResultPerson();
        try {
            this.getMergeRuleService().mergeReceivedEligibility(incoming,
                    onFile);
        } catch (ServiceException e) {
            throw new RuleException("Failed to merge received eligibilities", e);
        }

    }

    public boolean isContainedReceivedEligibilityCode(String codes) {
        Set eligs = this.getReceivedEligibilities(this.getIncomingPerson());
        return codesAreValid(eligs, codes);
    }

    /**
     * Method to match incoming and onFile received eligibilities
     */
    public boolean isContainedVeteranReceivedEligibilityCode(String codes) {
        boolean result = false;
        Set eligs = this.getReceivedEligibilities(this.getResultPerson());
        boolean valid = codesAreValid(eligs, codes);

        if (valid) {
            result = matchReceivedEligibilities(getIncomingPerson(), this
                    .getResultPerson());
        }

        return result;
    }

    /**
     * General purpose convenience method for use by other clients. Returns the
     * person's enrollment.
     *
     * @param person
     * @return
     */
    public EnrollmentDetermination getEnrollmentDetermination(Person person) {
        return this.getHelperService().getEnrollmentDetermination(person);
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getCurrentApplicationDateOnFile()
     */
    public Date getCurrentApplicationDateOnFile() {
        Application application = this.getApplication(this.getPristinePerson());
        return application != null ? application.getApplicationDate() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getCurrentEffDateOnFile()
     */
    public Date getCurrentEffDateOnFile() {
        EnrollmentDetermination ed = this.getEnrollmentDetermination(this
                .getPristinePerson());
        return ed != null ? ed.getEffectiveDate() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getCurrentEnrollmentStatusOnFile()
     */
    public String getCurrentEnrollmentStatusOnFile() throws RuleException {
        return this.getEnrollmentStatusCode(this.getPristinePerson());
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEarliestEnrollmentEffectiveDate()
     */
    public Date getEarliestEnrollmentEffectiveDate() throws RuleException {

        if (this.earliestEnrollmentEffectiveDate == null) {
            try {
                EligibilityEnrollmentService enrollmentService = this
                        .getEligibilityEnrollmentService();
                Person person = this.getIncomingPerson();
                if (person != null && person.getEntityKey() != null) {
                    this.earliestEnrollmentEffectiveDate = enrollmentService
                            .getFirstNotNullEffDate(this.getIncomingPerson()
                                    .getEntityKey());
                }
            } catch (ServiceException serviceEx) {
                throw new RuleException(
                        "Error getting earliest enrollment effective date",
                        serviceEx);
            }
        }

        return this.earliestEnrollmentEffectiveDate;
    }

    public Date getEffectiveDateForEarliestVerifiedUnlessCancelled()
            throws RuleException {

        Date earliestEnrollEffectiveDate = null;
        try {
            EligibilityEnrollmentService enrollmentService = this
                    .getEligibilityEnrollmentService();
            Person person = this.getIncomingPerson();
            if (person != null && person.getEntityKey() != null) {
                earliestEnrollEffectiveDate = enrollmentService
                        .getEffectiveDateForEarliestVerifiedUnlessCancelledOrRejectedBelowEnrollmentThreshold(this
                                .getIncomingPerson().getEntityKey());
            }
        } catch (ServiceException serviceEx) {
            throw new RuleException(
                    "Error getting earliest enrollment effective date",
                    serviceEx);
        }
        return earliestEnrollEffectiveDate;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#getEarliestVerifiedEnrollmentDate()
     */
    public Date getEarliestVerifiedEnrollmentDate() throws RuleException {
        if (this.earliestVerifiedEnrollmentDate == null) {
            try {
                EligibilityEnrollmentService enrollmentService = this
                        .getEligibilityEnrollmentService();
                Person person = this.getIncomingPerson();
                if (person != null && person.getEntityKey() != null) {
                    this.earliestVerifiedEnrollmentDate = enrollmentService
                            .getEnrollmentDateForEarliestVerifiedUnlessCancelled(person
                                    .getEntityKey());
                }
            } catch (ServiceException serviceEx) {
                throw new RuleException(
                        "Error getting earliest verified enrollment date",
                        serviceEx);
            }
        }

        return this.earliestVerifiedEnrollmentDate;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#hasEverHadVerifiedEnrollment()
     */
    public boolean hasEverHadVerifiedEnrollment() throws RuleException {
    	if (this.hasEverHadVerifiedEnrollment == null) {
    		try {
    			boolean tmp = this.getEligibilityEnrollmentService().isVerifiedEnrollmentExists(this.getIncomingPerson().getEntityKey());
    			this.hasEverHadVerifiedEnrollment = new Boolean(tmp);
    		} catch (ServiceException ex) {
    			throw new RuleException("Error getting verified enrollment", ex);
    		}
    	}
    	return this.hasEverHadVerifiedEnrollment != null ? this.hasEverHadVerifiedEnrollment.booleanValue() : false;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentOverridenOnFile()
     */
    public Boolean getEnrollmentOverridenOnFile() {
        EnrollmentOverride eo = this.getEnrollmentOverride(this
                .getPristinePerson());
        return eo != null ? eo.getOverride() : Boolean.FALSE;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#setEnrollmentOverriden(boolean)
     */
    public void setEnrollmentOverriden(boolean b) throws RuleException {
        this.getResultEnrollmentDetermination().setOverridden(new Boolean(b));
        // Need to keep EnrollmentOverride object in sync when rules sets
        // override to false
        // This can happen when priority changes
        if (!b) {
            EnrollmentOverride resultEo = this.getEnrollmentOverride(this
                    .getResultPerson());
            if (resultEo != null) {
                resultEo.setOverride(Boolean.FALSE);
                resultEo.setOverrideReason(null);
                resultEo.setOverrideComment(null);
            }
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getCurrentPrimaryEligibilityCode()
     */
    public String getCurrentPrimaryEligibilityCode() {
        if (this.currentPrimaryEligibilityCode == null) {
            EnrollmentDetermination enrollment = this
                    .getPristineEnrollmentDetermination();
            if (enrollment != null) {
                Eligibility primary = enrollment.getPrimaryEligibility();
                if (primary != null) {
                    EligibilityType type = primary.getType();
                    this.currentPrimaryEligibilityCode = (type != null) ? type
                            .getCode() : null;
                }
            }
        }
        return this.currentPrimaryEligibilityCode;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentOverride()
     */
    public Boolean getEnrollmentOverride() {
        EnrollmentOverride eo = this.getEnrollmentOverride(getIncomingPerson());
        return eo != null ? eo.getOverride() : Boolean.FALSE;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getProcessOverrideFromEnrollmentOverride()
     */
    public Boolean getProcessOverrideFromEnrollmentOverride() {
        EnrollmentOverride eo = this.getEnrollmentOverride(getIncomingPerson());
        return eo != null ? eo.getProcessOverride() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#getSiteVerifiedEnrollment()
     */
    public Boolean getSiteVerifiedEnrollment() throws RuleException {
        Person siteData = this.getVerifiedSiteData();
        Boolean hasSiteData = Boolean.FALSE;
        if (siteData != null) {
            hasSiteData = this.getEnrollmentDetermination(siteData) != null ? Boolean.TRUE
                    : Boolean.FALSE;
        }
        return hasSiteData;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#getPrimaryEligibilityCodeOfVerifiedSite()
     */
    public String getPrimaryEligibilityCodeOfVerifiedSite()
            throws RuleException {
        String code = null;

        // Get an enrollment determination from a verified site record
        Person siteData = this.getVerifiedSiteData();
        if (siteData != null) {
            EnrollmentDetermination enrollment = this
                    .getEnrollmentDetermination(siteData);
            Eligibility primary = null;
            if (enrollment != null
                    && (primary = enrollment.getPrimaryEligibility()) != null) {
                EligibilityType type = primary.getType();
                code = type != null ? type.getCode() : null;
            }
        }
        return code;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#setProcessOverride(boolean)
     */
    public void setProcessOverride(boolean b) {
        EnrollmentOverride eo = this.getResultPerson().getEnrollmentOverride();
        if (eo != null) {
            eo.setProcessOverride(new Boolean(b));
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#isTypeOfPrimaryEligibilityCodeEqualTo(java.lang.String)
     */
    public boolean isTypeOfPrimaryEligibilityCodeEqualTo(String eligibilityType)
            throws RuleException {

        // The method accepts a parameter of eligibility type.
        // (V - for veteran)
        // (N - for non-veteran)
        // And return true/false if the type of the primary eligibility
        // code assigned to veteran matches the input parameter.

        boolean equal = false;
        if (NONVETERAN.equals(eligibilityType)
                && isNonVeteranPrimaryEligibility(getCalculatedPrimaryEligibilityCode())) {
            equal = true;
        } else if (VETERAN.equals(eligibilityType)
                && !isNonVeteranPrimaryEligibility(getCalculatedPrimaryEligibilityCode())) {
            equal = true;
        }
        return equal;
    }

    public boolean isTypeOfReceivedPrimaryEligibilityCodeEqualTo(
            String eligibilityType) throws RuleException {
        // The method accepts a parameter of received eligibility type.
        // (V - for veteran)
        // (N - for non-veteran)
        // And return true/false if the type of the received primary eligibility
        // code assigned to veteran matches the input parameter.

        boolean equal = false;
        if (NONVETERAN.equals(eligibilityType)
                && isNonVeteranPrimaryEligibility(this
                        .getReceivedPrimaryEligibilityCode())) {
            equal = true;
        } else if (VETERAN.equals(eligibilityType)
                && !isNonVeteranPrimaryEligibility(this
                        .getReceivedPrimaryEligibilityCode())) {
            equal = true;
        }
        return equal;
    }

    /**
     * @param person
     * @param type
     * @return
     * @throws RuleException
     */
    public boolean isTypeOfSiteRecordEligibilityCodeEqual(Person person,
            String vetType) throws RuleException {

        EnrollmentDetermination ed = person != null ? person
                .getEnrollmentDetermination() : null;
        Eligibility e = ed != null ? ed.getPrimaryEligibility() : null;
        EligibilityType type = e != null ? e.getType() : null;
        boolean equal = false;
        if (NONVETERAN.equals(vetType)
                && isNonVeteranPrimaryEligibility(type != null ? type.getCode()
                        : null)) {
            equal = true;
        } else if (VETERAN.equals(vetType)
                && !isNonVeteranPrimaryEligibility(type != null ? type
                        .getCode() : null)) {
            equal = true;
        }
        return equal;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#isCalculatedSecondaryEligibilityCodeEqual(java.lang.String)
     */
    public boolean doesCalculatedSecondaryEligibilityCodeContain(
            String eligibilityCode) throws RuleException {
        EnrollmentDetermination ed = this.getResultEnrollmentDetermination();
        if (ed != null && ed.getSecondaryEligibilities().size() > 0) {
            Set eligs = ed.getSecondaryEligibilities();
            Iterator itr = eligs != null ? eligs.iterator() : null;
            while (itr != null && itr.hasNext()) {
                Eligibility elig = (Eligibility) itr.next();
                if (isEligibilityOfType(elig, eligibilityCode)) {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean doesCalculatedPrimaryOrSecondaryEligibilitiesContain(
            String eligTypeCode) throws RuleException {
        boolean result = false;
        EnrollmentDetermination ed = null;
        if (this.getResultPerson() != null)
            ed = getResultEnrollmentDetermination();
        else
            ed = getIncomingEnrollmentDetermination();
        if (ed != null) {
            Eligibility elig = ed.getPrimaryEligibility();
            if (isEligibilityOfType(elig, eligTypeCode))
                result = true;
            else {
                // check the secondary eligs
                Set eligs = ed.getSecondaryEligibilities();
                Iterator itr = eligs != null ? eligs.iterator() : null;
                while (itr != null && itr.hasNext()) {
                    elig = (Eligibility) itr.next();
                    if (isEligibilityOfType(elig, eligTypeCode)) {
                        result = true;
                        break;
                    }
                }
            }
        }
        return result;
    }

    private boolean isEligibilityOfType(Eligibility elig, String eligTypeCode) {
        if (elig != null && elig.getType() != null
                && elig.getType().getCode().equals(eligTypeCode))
            return true;
        return false;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#getCurrentEnrollmentPrioirity()
     */
    public String getCurrentEnrollmentPrioirity() throws RuleException {
        // Returns the current enrollment priorirty on file for the veteran
        return this.getEnrollmentPriority();
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#getPriorEnrollmentPriority()
     */
    public String getPriorEnrollmentPriority() throws RuleException {
        if (this.priorEnrollmentPriority == null) {
            EnrollmentDetermination enrollmentDetermination = this
                    .getPriorEnrollment();
            EnrollmentPriorityGroup enrollmentPriorityGroup = (enrollmentDetermination != null) ? enrollmentDetermination
                    .getPriorityGroup()
                    : null;
            this.priorEnrollmentPriority = enrollmentPriorityGroup != null ? enrollmentPriorityGroup
                    .getCode()
                    : null;
        }
        return this.priorEnrollmentPriority;

    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#getPriorEnrollmentStatus()
     */
    public String getPriorEnrollmentStatus() throws RuleException {
        EnrollmentDetermination prior = this.getPriorEnrollment();
        return prior != null && prior.getEnrollmentStatus() != null ? prior
                .getEnrollmentStatus().getCode() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#isEnrollmentPriorityEqualToPrevious()
     */
    public boolean isEnrollmentPriorityEqualToPrevious() throws RuleException {
        // get from history
        String prior = this.getPriorEnrollmentPriority();
        String incoming = this.getEnrollmentPriority();
        if (prior != null && incoming != null) {
            return prior.equals(incoming);
        }
        if (prior == null && incoming == null) {
            return true;
        }
        return false;
    }

    public boolean isEnrollmentPriorityImproved(Person incoming, Person onFile) throws RuleException {
	     try {
	         String incomingEnrollmentPriorityCode = this.getEnrollmentPriority(incoming);
	         String onFileEnrollmentPriorityCode = this.getEnrollmentPriority(onFile);

	         if( StringUtils.isNotEmpty(incomingEnrollmentPriorityCode)&&
	        		 StringUtils.isNotEmpty(onFileEnrollmentPriorityCode) &&
	            		   !incomingEnrollmentPriorityCode.equals(onFileEnrollmentPriorityCode)	) {
	            return ( new Integer(incomingEnrollmentPriorityCode).intValue() < new Integer(
	            		onFileEnrollmentPriorityCode).intValue() );
	         }
	      }
	      catch( Exception ex ) {
	         throw new RuleException("Error comparing enrollment priorities", ex);
	      }

	   return false;
	}
   public boolean isEnrollmentPriorityChanged() throws RuleException {

    	String prior=null;
    	String result=null;
    	if (this.getPristinePerson() != null
				&& this.getPristinePerson().getEnrollmentDetermination() != null
				&& this.getPristinePerson().getEnrollmentDetermination()
						.getPriorityGroup() != null) {
			 prior = this.getPristinePerson()
					.getEnrollmentDetermination().getPriorityGroup().getCode();
    	}
				 result = this.getEnrollmentPriority(); // this call will return the calculated EnrollmentPriority
			if (prior != null && result != null && prior.equals(result)) {
				return false;
			}
			if (prior == null && result == null) {
				return false;
			}

		return true;
	}

	/**
	 * @see gov.va.med.esr.common.rule.EnrollmentInput#getESRImplementationDate()
	 */
    public Date getESRImplementationDate() throws RuleException {
        try {
            return super.getEsrImplementationDate();
        } catch (Exception ex) {
            throw new RuleException("Error getting ESR implementation date", ex);
        }
    }

    /**
     * This method returns the Enrollment Regulation Date. This date is
     * specified in APP_PARAMETER table.
     *
     * @return enrollmentRegulationDate
     */
    public Date getEnrollmentRegulationDate() throws RuleException {
        try {
            return super.getEnrollmentRegulationDate();
        } catch (Exception ex) {
            throw new RuleException("Error getting Enrollment Regulation date",
                    ex);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#hasApplicationDatePriorToEGTEffectiveDate()
     */
    public boolean hasApplicationDatePriorToEGTEffectiveDate()
            throws RuleException {
        try {
            return this.getEligibilityEnrollmentService()
                    .isApplicationDatePriortoEGTEffectiveDate(
                            this.getIncomingPerson().getEntityKey());
        } catch (ServiceException ex) {
            throw new RuleException("Error getting verified enrollment", ex);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#hasVerifiedEnrollmentWithCombatVeteran()
     */
    public boolean hasVerifiedEnrollmentWithCombatVeteran()
            throws RuleException {
        try {
            return this.getEligibilityEnrollmentService()
                    .isVerifiedEnrollmentExistsforCombatVeteran(
                            this.getIncomingPerson().getEntityKey());
        } catch (ServiceException ex) {
            throw new RuleException("Error getting verified enrollment", ex);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#eligForMedicaidChangedToNo365DaysAfterAppDt()
     */
    public boolean eligForMedicaidChangedToNo365DaysAfterAppDt()
            throws RuleException {
        MedicaidFactor mf = this.getIncomingPerson().getMedicaidFactor();
        if (mf != null && mf.isEligibleForMedicaid() != null
                && !mf.isEligibleForMedicaid().booleanValue()) {
            // RULE 16 If Eligible for Medicaid is = Yes and it is changed to =
            // No after 365
            // days of
            // the Enrollment Application Date, then the veteran is continuously
            // enrolled.
            // (use Date
            // Time Medicaid Last Updated in the absence of the application
            // date.
            Date changeDate = mf.getLastUpdateDate();
            Date appDate = this.getApplicationDate();
            if (changeDate != null && appDate != null) {
                GregorianCalendar daysAfter = new GregorianCalendar();
                daysAfter.setTime(appDate);
                daysAfter.add(Calendar.DATE, 365);
                return changeDate.getTime() > daysAfter.getTime().getTime();
            }
        }
        return false;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#eligForMedicaidChangedToNo365DaysAfterMedLastChgDt()
     */
    public boolean eligForMedicaidChangedToNo365DaysAfterMedLastChgDt()
            throws RuleException {
        // there is only one date to check
        return eligForMedicaidChangedToNo365DaysAfterAppDt();
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#hasVerifiedEnrollmentWithAA()
     */
    public boolean hasVerifiedEnrollmentWithAA() throws RuleException {
        return this
                .hasVerifiedEnrollmentWithEligibility(EligibilityType.AID_AND_ATTENDANCE
                        .getCode());
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#hasVerifiedEnrollmentWithEligibleForMedicaid()
     */
    public boolean hasVerifiedEnrollmentWithEligibleForMedicaid()
            throws RuleException {
    	try {
            return this.getEligibilityEnrollmentService()
                    .isVerifiedEnrollmentExistsForMedicaidEligibility(this.getIncomingPerson().getEntityKey());
        } catch (ServiceException ex) {
            throw new RuleException("Error getting verified enrollment", ex);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#hasVerifiedEnrollmentWithHB()
     */
    public boolean hasVerifiedEnrollmentWithHB() throws RuleException {
        return this
                .hasVerifiedEnrollmentWithEligibility(EligibilityType.HOUSEBOUND
                        .getCode());
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#hasVerifiedEnrollmentWithMT()
     */
    public boolean hasVerifiedEnrollmentWithMT() throws RuleException {
        List incomeYears = this.getIncomeYearFromVerifiedEnrollmentDueToMT();

        return this.evaluateCERule15Criteria(incomeYears);
    }

    private boolean evaluateCERule15Criteria(List incomeYears) throws RuleException {
        boolean result = false; // Default result

        if (incomeYears != null) {
        	for(int i=0;i<incomeYears.size();i++){
        		Integer incomeYear = (Integer)incomeYears.get(i);
        		IncomeTest it = this.getIncomingPerson().getIncomeTest(incomeYear);
        		if (it != null) {
        			// Make sure MT status stayed MT Copay Exempt, GMT Copay. This
        			// covers rules 15A/B.
        			MeansTestStatus mts = it.getStatus();
        			if (mts != null
        					&& (MeansTestStatus.MT_STATUS_MT_COPAY_EXEMPT.getCode()
        							.equals(mts.getCode())
        							|| MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED
        							.getCode().equals(mts.getCode()) || MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION
        							.getCode().equals(mts.getCode()))) {
        				// CCR 10023 needs extra check on Pend Adj to make sure other criteria is met on present values of test
        				if (MeansTestStatus.MT_STATUS_PENDING_ADJUDICATION.getCode().equals(mts.getCode())) {
        					BigDecimal thresholdGMT = it.getGmtThresholdAmount();
        					BigDecimal thresholdMTT = it.getThresholdA();

        					BigDecimal resultNetIncome = new BigDecimal(0);
        					BigDecimal netIncome = it.getNetIncome();
        		            if (netIncome != null) {
        		                // Hard work already done
        		            	resultNetIncome = netIncome;
        		            } else {
        		                // Need to calculate on the fly using existing data.
        		                // Note that HL7 will always follow this path
        		                // since they don't provide Net Income
        		                BigDecimal totalIncome = it.getTotalIncome();
        		                BigDecimal deductableExpenses = it.getDeductibleExpenses();
        		                if (totalIncome != null && deductableExpenses != null) {
        		                	resultNetIncome =  isGreaterThan(totalIncome, deductableExpenses) ?
        		                			totalIncome.subtract(deductableExpenses) : new BigDecimal(0);
        		                } else if (totalIncome != null) {
        		                	resultNetIncome = totalIncome;
        		                }
        		                // For other cases we use default of 0
        		            }
        		            if (thresholdGMT != null && thresholdMTT != null && resultNetIncome!= null) {
        		            	if ((thresholdGMT.compareTo(thresholdMTT) > 0) ||
        		            			(thresholdGMT.compareTo(thresholdMTT) < 0 && resultNetIncome.compareTo(thresholdGMT) <= 0)) {
        		            		return true;
        		            	}
        		            }
        				}
        				else {
        					Hardship hardship = it.getHardship();
        					Boolean hasHardship = (hardship != null) ? hardship.getHardshipGranted() : null;
        					boolean isHardshipGranted = (hasHardship != null) ? hasHardship.booleanValue(): false;
        					// Check if the hardship applied to the test
        					if (isHardshipGranted) {
        						// Apply rules dealing with Rule 21 in continuous
        						// enrollment
        						/*
        						 * Rules from Laurie 1. 2006 MT is Test Determined
        						 * Status = GMT Copay Required and a hardship is added
        						 * making him MT Copay Exempt. 2007 MT is MT Copay
        						 * Required. Based on the Test Determined Status of the
        						 * first means test, I would think this person qualifies
        						 * for Continuous Enrollment. Basing it solely on the
        						 * hardship that no longer applies would remove a
        						 * benefit he would otherwise qualify for.
        						 *
        						 * 2. 2006 MT is Test Determined Status = MT Copay
        						 * Required and a hardship is added making him GMT Copay
        						 * Required. 2007 MT is MT Copay Required. Based on the
        						 * earlier enrollment being based solely on the hardship
        						 * that was applied, even though it did not make him MT
        						 * Copay Exempt, the person should be Rejected.
        						 *
        						 */
        						MeansTestStatus determinedStatus = it
        						.getDeterminedStatus();
        						if (determinedStatus != null) {
        							// To simplify, we only deal with conditions
        							// returning True.
        							if (MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getCode().equals(determinedStatus.getCode())) {
        								return true;
        							}
        						}
        					} else {
        						return true; // CCR 10023 - no reason to continue looping
        					}
        				}
        			}
        		}
        	}
        }
        return result;
    }
	public boolean hasIncomeTestThatPreviouslyMetRule15Criteria() throws RuleException {
       	List incomeYears = null;

        try {
            incomeYears = this.getEligibilityEnrollmentService().getHistoricalIncomeYearsForVerifiedMT(
                            this.getIncomingPerson().getEntityKey());
        } catch (ServiceException ex) {
            throw new RuleException("Error getting verified enrollment", ex);
        }

        return incomeYears != null && incomeYears.size() > 0;
	}


    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#hasVerifiedEnrollmentWithVAPension()
     */
    public boolean hasVerifiedEnrollmentWithVAPension() throws RuleException {
        return this
                .hasVerifiedEnrollmentWithEligibility(EligibilityType.NSC_VA_PENSION
                        .getCode());
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#hasVerifiedEnrollmentWithAOLocationDMZ()
     */
    public boolean hasVerifiedEnrollmentWithAOLocationDMZ()
            throws RuleException {

        AgentOrangeExposure aoe = this.getIncomingPerson()
                .getAgentOrangeExposure();
        if (aoe != null) {
            if (aoe.getAgentOrangeExposureIndicator() != null
                    && aoe.getAgentOrangeExposureIndicator().toBoolean() != null
                    && aoe.getAgentOrangeExposureIndicator().toBoolean()
                            .booleanValue()) {
                AgentOrangeExposureLocation location = aoe.getLocation();
                if (location != null
                        && location.getCode().equals(
                                AgentOrangeExposureLocation.CODE_KOREAN_DMZ
                                        .getCode())) {
                    Date enteredDate = aoe.getModifiedOn();
                    Date implementationDate = this.getESRImplementationDate();
                    if (enteredDate != null && implementationDate != null) {
                        return isBefore(enteredDate, implementationDate);
                    }
                }
            }
        }
        return false;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#getEnrollmentOverrideForCE()
     */
    public Boolean getEnrollmentOverrideForCE() throws RuleException {
        EnrollmentDetermination ed = this
                .getMostRecentEnrollmentDeterminationForCE();
        return ed != null ? ed.getOverridden() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#hasVerifiedEnrollmentWithSC_GTE_10PERCENT()
     */
    public boolean hasVerifiedEnrollmentWithSC_GTE_10PERCENT()
            throws RuleException {
        try {
            return this.getEligibilityEnrollmentService()
                    .isVerifiedEnrollmentExistsforSvcConnPercent(
                            this.getIncomingPerson().getEntityKey(),
                            new Integer(TEN_PERCENT));
        } catch (ServiceException ex) {
            throw new RuleException("Error getting verified enrollment", ex);
        }
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#isEGTPriorityCEGreaterThanCurrEGT()
     */
    public boolean isEGTPriorityCEGreaterThanCurrEGT() throws RuleException {
        // This method compares the EGT that was linked to "Continuous
        // Enrollment" enrollment
        // against the current EGT. DON'T USE for anything except CE.
        EnrollmentDetermination ce = this
                .getMostRecentEnrollmentDeterminationForCE();
        EGTSetting egtCE = ce != null ? ce.getEgtSetting() : null;

        EGTSetting currentEGTSetting = this.getCurrentEGTSetting();

        EnrollmentPriorityGroup currPriority = currentEGTSetting != null ? currentEGTSetting
                .getPriorityGroup()
                : null;
        EnrollmentPriorityGroup cePriority = egtCE != null ? egtCE
                .getPriorityGroup() : null;

        String currEGTPriorityCode = currPriority != null ? currPriority
                .getCode() : null;
        String ceEGTPriorityCode = cePriority != null ? cePriority.getCode()
                : null;
        if (currEGTPriorityCode == null || ceEGTPriorityCode == null) {
            return false;
        }
        return ceEGTPriorityCode.compareTo(currEGTPriorityCode) > 0;
    }

    // ------------------------------------------
    // Private Methods
    // ------------------------------------------

    private String getCancelDecline(Person person) {
        // Need to handle three states: Y, N, null
        String result = null;
        CancelDecline cancelDeclineObj = (person != null) ? person
                .getCancelDecline() : null;
        // Need to make sure to handle tri-state nature of Cancel Decline. A
        // null has significant meaning in the business.
        if (cancelDeclineObj != null) {
            boolean cancel = (cancelDeclineObj.getCancelDeclineIndicator() != null) ? cancelDeclineObj
                    .getCancelDeclineIndicator().booleanValue()
                    : false;
            if (cancel && cancelDeclineObj.getEffectiveDate() != null) {
                result = YES_INDICATOR;
            } else if (cancelDeclineObj.getReasonNotProcessed() != null) {
                result = NO_INDICATOR;
            }
        }
        return result;
    }

    private Date getCancelledDeclinedDate(Person person) {
        return (person != null && person.getCancelDecline() != null) ? person
                .getCancelDecline().getEffectiveDate() : null;
    }

    /**
     * Method to check a set for matching eligibility.
     *
     * @param eligibilities
     * @param eligibility
     * @return
     */
    private boolean containsEligibility(Set eligibilities,
            Eligibility eligibility) {
        Eligibility found = (Eligibility) this.getMergeRuleService()
                .getMatchRuleService().findMatchingElement(eligibility,
                        eligibilities);
        return found != null ? true : false;
    }

    private EnrollmentDetermination getIncomingEnrollmentDetermination() {
        if (this.incomingEnrollmentDetermination == null) {
            this.incomingEnrollmentDetermination = this
                    .getEnrollmentDetermination(this.getIncomingPerson());
        }
        return incomingEnrollmentDetermination;
    }

    private EnrollmentDetermination getPristineEnrollmentDetermination() {
        if (this.pristineEnrollmentDetermination == null) {
            this.pristineEnrollmentDetermination = this
                    .getEnrollmentDetermination(this.getPristinePerson());
        }
        return pristineEnrollmentDetermination;
    }

    private EnrollmentDetermination getResultEnrollmentDetermination()
            throws RuleException {
        if (this.resultEnrollmentDetermination == null) {
            this.resultEnrollmentDetermination = this
                    .initializeResultEnrollment();
        }
        return resultEnrollmentDetermination;
    }

    private ReceivedEnrollment getReceivedEnrollment(Person person) {
        return (person != null) ? person.getReceivedEnrollment() : null;
    }

    public String getEnrollmentStatusCode(Person person) throws RuleException {
        // TODO : change name of method to indicate that want current status
        String code = null;
        try {
            EnrollmentStatus enrollmentStatus = getHelperService()
                    .getEnrollmentStatus(person);
            code = (enrollmentStatus != null) ? enrollmentStatus.getCode()
                    : null;
        } catch (ServiceException e) {
            throw new RuleException(
                    "Failed to get a process state of type enrollment status",
                    e);
        }
        return code;
    }

    /**
     * This parameter object can be used in different contexts. If used by EE,
     * it uses an EEInputData source. Otherwise, default source is used.
     *
     * @return
     * @throws RuleException
     */
    private EnrollmentDetermination initializeResultEnrollment()
            throws RuleException {
        EnrollmentDetermination enrollmentDetermination = null;
        EGTSetting egtSetting = this.getCurrentEGTSetting();
        if (this.getEEInputData() != null) {
            enrollmentDetermination = getEEInputData()
                    .getResultEnrollmentDetermination();
        } else {
            enrollmentDetermination = this.getEnrollmentDetermination(this
                    .getResultPerson());
            if (enrollmentDetermination == null) {
                enrollmentDetermination = new EnrollmentDetermination();
                this.getResultPerson().setEnrollmentDetermination(
                        enrollmentDetermination);
            }
        }
        // Set egt setting if current is null or if batch provided a setting
        if (enrollmentDetermination != null
                && (enrollmentDetermination.getEgtSetting() == null || this
                        .getBaseData().getActiveEGTSetting() != null)) {
            enrollmentDetermination.setEgtSetting(egtSetting);
        }

        return enrollmentDetermination;
    }

    /**
     * Search the list for a particular received eligibility type. Return true
     * if found otherwise false.
     *
     * @param received
     *            The set of received eligibilities that will be searched
     * @param code
     *            The type code to look for in the set.
     * @return
     */
    private boolean containsReceivedEligibility(Set received, String code) {
        boolean contained = false;

        if (code != null && received != null && received.size() > 0) {
            Iterator iter = received.iterator();
            while (iter.hasNext()) {
                ReceivedEligibility receivedEligibility = (ReceivedEligibility) iter
                        .next();
                if (code.equals(receivedEligibility.getType().getCode())) {
                    contained = true;
                    break;
                }
            }
        }
        return contained;
    }

    /**
     * A method to retrieve all of the person's received eligibilities,
     * including primary and secondary.
     *
     * @param person
     * @return
     */
    private Set getReceivedEligibilities(Person person) {
        Set secondaries = (person != null) ? person
                .getReceivedSecondaryEligibilities() : null;
        Set eligibilities = new HashSet();
        if (secondaries != null && secondaries.size() > 0) {
            eligibilities.addAll(secondaries);
        }
        ReceivedEligibility receivedEligibility = (person != null) ? person
                .getReceivedPrimaryEligibility() : null;
        if (receivedEligibility != null) {
            eligibilities.add(receivedEligibility);
        }
        return eligibilities;
    }

    /**
     * A method to validate the set of received eligibilities against the valid
     * codes.
     *
     * @param received
     * @param codes
     * @return
     */
    private boolean codesAreValid(Set received, String codes) {
        boolean valid = true;

        // If we do not find a received code description in the string of code
        // descriptions provided, we say the code is invalid
        if (codes != null && received != null && received.size() > 0) {
            Iterator iter = received.iterator();
            while (iter.hasNext()) {
                ReceivedEligibility receivedEligibility = (ReceivedEligibility) iter
                        .next();
                int i = codes.indexOf(receivedEligibility.getType()
                        .getDescription());
                if (i == -1) {
                    valid = false;
                    break;
                }
            }
        }
        // NOTE: It is OK to have zero received eligibilities.

        return valid;
    }

    private boolean matchReceivedEligibilities(Person incoming, Person onFile) {
        boolean result = false;
        Set compare1 = this.getReceivedEligibilities(incoming);
        Set compare2 = this.getReceivedEligibilities(onFile);
        boolean compare1IsEmpty = compare1 == null || compare1.size() == 0;
        boolean compare2IsEmpty = compare2 == null || compare2.size() == 0;

        if (compare1IsEmpty && compare2IsEmpty) {
            result = true;
        } else if (!compare1IsEmpty && !compare2IsEmpty) {
            // only check case where size is equal
            if (compare2.size() == compare1.size()) {
                // get iterator from compare1
                Iterator iter = compare1.iterator();
                while (iter.hasNext()) {
                    ReceivedEligibility receivedEligibility = (ReceivedEligibility) iter
                            .next();
                    // check against compare2
                    ReceivedEligibility found = (ReceivedEligibility) this
                            .getMergeRuleService().getMatchRuleService()
                            .findMatchingElement(receivedEligibility, compare2);
                    if (found == null) {
                        result = false;
                        break;
                    } else
                        result = true;
                }
            }
        }
        return result;

    }

    private String getReceivedPrimaryEligibilityCode(Person person) {
        ReceivedEligibility primary = (person != null) ? person
                .getReceivedPrimaryEligibility() : null;
        return (primary != null && primary.getType() != null) ? primary
                .getType().getCode() : null;
    }

    private boolean isNonVeteranPrimaryEligibility(String code)
            throws RuleException {
        return getHelperService().isNonVeteranEligibilityCode(code);
    }

    private Application getApplication(Person person) {
        return person != null ? person.getApplication() : null;
    }

    private EnrollmentOverride getEnrollmentOverride(Person person) {
        return person != null ? person.getEnrollmentOverride() : null;
    }

    private boolean isValidReceivedCode(ReceivedEligibility re) {
        if (re != null) {
            if (re.getType().getCode().equals(
                    EligibilityType.ALLIED_VETERAN.getName())) {
                if (re.getAlliedCountry() == null) {
                    return false;
                }
            } else if (re.getType().getCode().equals(
                    EligibilityType.OTHER_FEDERAL_AGENCY.getName())) {
                if (re.getOtherFederalAgency() == null) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * @see gov.va.med.esr.common.rule.parameter.EnrollmentInput#getEnrollmentCategory()
     */
    public String getEnrollmentCategory() throws RuleException {
        EnrollmentCategory category = null;
        try {
            category = this.getHelperService().getEnrollmentCategory(
                    this.getIncomingPerson());
        } catch (ServiceException e) {
            throw new RuleException("Failed to get enrollment category", e);
        }
        return (category != null) ? category.getCode() : null;
    }

    public boolean isCalculatedEnrPriorityLessThanEGTSetting()
            throws RuleException {
        try {
            String enrollmentPriorityCode = this.getEnrollmentPriority();

            EnrollmentPriorityGroup enrolPriorityGruop = (this
                    .getCurrentEGTSetting() != null) ? this
                    .getCurrentEGTSetting().getPriorityGroup() : null;
            String currentEGTSettingCode = (enrolPriorityGruop != null) ? enrolPriorityGruop
                    .getCode()
                    : null;

            if (StringUtils.isNotEmpty(enrollmentPriorityCode)
                    && StringUtils.isNotEmpty(currentEGTSettingCode)) {
                return (new Integer(enrollmentPriorityCode).intValue() < new Integer(
                        currentEGTSettingCode).intValue());
            }
        } catch (Exception ex) {
            throw new RuleException("", ex);
        }
        return false;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#getEnrollmentEffectiveDateForCE()
     */
    public Date getEnrollmentEffectiveDateForCE() throws RuleException {
        EnrollmentDetermination ed = this
                .getMostRecentEnrollmentDeterminationForCE();
        return ed != null ? ed.getEffectiveDate() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#getEnrollmentStatusForCE()
     */
    public String getEnrollmentStatusForCE() throws RuleException {
        EnrollmentDetermination ed = this
                .getMostRecentEnrollmentDeterminationForCE();
        return (ed != null && ed.getEnrollmentStatus() != null) ? ed
                .getEnrollmentStatus().getCode() : null;
    }

    /**
     * Return the most recent of (enrollment application date, egt effective
     * date) If both dates are null return current date
     */
    public Date getRecentEnrollmentEffectiveDate() throws RuleException {
        Date egtEffectiveDate = null;
        Date enrollmentApplicationDate = null;

        EGTSetting egtSetting = this.getCurrentEGTSetting();
        // Get the egt effective date
        if (egtSetting != null)
            egtEffectiveDate = egtSetting.getEffectiveDate();
        // get enrollment application effective date
        enrollmentApplicationDate = this.getApplicationDate();

        // If both dates are available return the most recent one
        if (egtEffectiveDate != null && enrollmentApplicationDate != null) {
            if (egtEffectiveDate.after(enrollmentApplicationDate)) {
                return egtEffectiveDate;
            } else {
                return enrollmentApplicationDate;
            }
            // if either date is null return the not null date
        } else if (egtEffectiveDate != null) {
            return egtEffectiveDate;
        } else if (enrollmentApplicationDate != null) {
            return enrollmentApplicationDate;
        }

        // default retrun the current date
        return getCurrentDate();
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#getReceivedPrimaryEligibility(gov.va.med.esr.common.model.person.Person)
     */
    public ReceivedEligibility getReceivedPrimaryEligibility(Person person)
            throws RuleException {
        return (person != null) ? person.getReceivedPrimaryEligibility() : null;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#isEffDatePriorToESRImplDate()
     */
    public boolean isEffDatePriorToESRImplDate() throws RuleException {
        // The enrollment effective date is assumed to be current date
        // Used for Rule 13 in Determine Enrollment.
        Date effectiveDate = this.getCurrentDate();
        Date implementationDate = this.getESRImplementationDate();
        return (effectiveDate != null && implementationDate != null) ? this
                .isBefore(effectiveDate, implementationDate) : false;
    }

    /**
     *
     * Check if Veteran Application date of enrollment is before Enrollment
     * Regulation date.
     *
     * @return boolean
     */
    public boolean isVetAppDatePriorToRegDate() throws RuleException {
        Date effectiveDate = this.getApplicationDate();
        Date regulationDate = this.getEnrollmentRegulationDate();
        return (effectiveDate != null && regulationDate != null) ? this
                .isBefore(effectiveDate, regulationDate) : false;
    }

    /**
     * Check if Application Date on the first Verified Enrollment Record
     * is >= the regulation date
     *
	 * @see gov.va.med.esr.common.rule.EnrollmentInput#isVetAppDateGTEToRegDate()
	 */
	public boolean isVetAppDateGTEToRegDate() throws RuleException {
		Date applicationDate = this.getApplicationDate();
		Date regulationDate = this.getEnrollmentRegulationDate();
		return (applicationDate != null && regulationDate != null) ?
				(isSameDay(applicationDate, regulationDate) ||
				isAfterIgnoreTime(applicationDate, regulationDate)): false;
	}

	/**
     * Check if Earliest Effective date of enrollment is before Enrollment
     * Regulation date.
     *
     * @return boolean
     */
    public boolean isEffDatePriorToRegDate() throws RuleException {
        Date effectiveDate = this
                .getEffectiveDateForEarliestVerifiedUnlessCancelled();
        Date regulationDate = this.getEnrollmentRegulationDate();
        return (effectiveDate != null && regulationDate != null) ? this
                .isBefore(effectiveDate, regulationDate) : false;
    }

    /**
     * Check if Earliest Effective Date of change on
     * the first Verified Enrollment Record is is >= the regulation date.
     * Want to ensure that we only return TRUE when dates exist and conditions
     * are met.
     *
	 * @see gov.va.med.esr.common.rule.EnrollmentInput#isEffDateGTEToRegDate()
	 */
	public boolean isEffDateGTEToRegDate() throws RuleException {
		Date effectiveDate = this.getEffectiveDateForEarliestVerifiedUnlessCancelled();
		Date regulationDate = this.getEnrollmentRegulationDate();
		return (effectiveDate != null && regulationDate != null) ?
				(isSameDay(effectiveDate, regulationDate) ||
				isAfterIgnoreTime(effectiveDate, regulationDate)): false;
	}

	public boolean isTodayGreaterThanOrEqualToRegulationDate() throws RuleException{
        Date regulationDate = this.getEnrollmentRegulationDate();
        Date today = new Date();
        return !this.isBefore(today, regulationDate);
    }
    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#isReceivedPrimaryEligibilityValid(gov.va.med.esr.common.model.ee.ReceivedEligibility)
     */
    public boolean isReceivedPrimaryEligibilityValid(ReceivedEligibility re)
            throws RuleException {
        return isValidReceivedCode(re);
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#getPriorNonNullPriority()
     */
    public String getPriorNonNullPriority() throws RuleException {
        if (this.mostRecentNonNullPriorityCode == null) {
            try {
                this.mostRecentNonNullPriorityCode = this
                        .getEligibilityEnrollmentService()
                        .getMostRecentNonNullPriorityCode(
                                this.getPristinePerson().getEntityKey());
            } catch (ServiceException serviceEx) {
                throw new RuleException(
                        "Error getting most recent non-null enrollment priority",
                        serviceEx);
            }
        }
        return this.mostRecentNonNullPriorityCode;
    }

   /* CCR8753  add Rule 13b */
   public boolean hasVerifiedEnrollmentWithAOLocationVietnam() throws RuleException
   {
       try {
           AgentOrangeExposure aoe = this.getIncomingPerson().getAgentOrangeExposure();

           if (aoe != null) {
               if (aoe.getAgentOrangeExposureIndicator() != null &&
                       aoe.getAgentOrangeExposureIndicator().toBoolean() != null &&
                       aoe.getAgentOrangeExposureIndicator().toBoolean().booleanValue()) {

                   AgentOrangeExposureLocation location = aoe.getLocation();

                   if (location != null && location.getCode().equals(AgentOrangeExposureLocation.CODE_VIETNAM.getCode())) {
                       if (Boolean.FALSE.equals(getSystemParameterService().getAOTreatmentAuthorityIndicator())) {
                           Date enteredDate = aoe.getModifiedOn();
                           Date aoAuthorityExpDate = getSystemParameterService().getAOTreatmentAuthorityExpirationDate();
                           if (enteredDate != null && aoAuthorityExpDate != null) {
                               return isBefore(enteredDate, aoAuthorityExpDate);
                           }
                       }
                   }
               }
           }
       }
       catch (ServiceException e) {
           throw new RuleException("Failed to get Verified Enrollment with AO Location of Vietnam", e);
       }

       return false;
   }

   /*CCR8753 add Rule 13c*/
   public boolean hasVerifiedEnrollmentWithEC() throws RuleException
   {
       try {
           EnvironmentalContaminationExposure ece = this.getIncomingPerson().getEnvironmentalContaminationExposure();
           if (ece != null) {
               if (ece.getEnvironmentalContaminationExposureIndicator() != null &&
                       ece.getEnvironmentalContaminationExposureIndicator().toBoolean() != null &&
                       ece.getEnvironmentalContaminationExposureIndicator().toBoolean().booleanValue()) {

                   if (Boolean.FALSE.equals(getSystemParameterService().getECTreamentAuthorityIndicator())) {
                       Date enteredDate = ece.getModifiedOn();
                       Date ecAuthorityExpDate = getSystemParameterService().getECTreatmentAuthorityExpirationDate();
                       if (enteredDate != null && ecAuthorityExpDate != null) {
                           return isBefore(enteredDate, ecAuthorityExpDate);
                       }
                   }
               }
           }
       }
       catch (ServiceException e) {
           throw new RuleException("Failed to get Verified Enrollment with SW Asia Condition", e);
       }
      return false;
   }

    public boolean isPHOnly(Person person) throws RuleException {
        return this.isRegistryOnly(person, RegistryType.CODE_PH_REGISTRY
                .getCode());
    }

    public boolean isPOWOnly(Person person) throws RuleException {
        return this.isRegistryOnly(person, RegistryType.CODE_POW_REGISTRY
                .getCode());
    }

    public boolean isSHADOnly(Person person) throws RuleException {
        return this.isRegistryOnly(person, RegistryType.CODE_SHAD_REGISTRY
                .getCode());
    }

    public void updateApplication() throws RuleException {
        Person inPerson = this.getIncomingPerson();
        Person result = this.getResultPerson();
        Application incoming = inPerson.getApplication();

        if (incoming != null) {
            if (result.getApplication() == null) {
                result.setApplication(new Application());
            }
            try {
                this.getMergeRuleService().mergeApplication(incoming,
                        result.getApplication());
            } catch (ServiceException e) {
                throw new RuleException("Failed to merge application", e);
            }
        } else {
            result.setApplication(null);
        }
    }

    public void updateReceivedEnrollment() throws RuleException {
        Person inPerson = this.getIncomingPerson();
        Person result = this.getResultPerson();
        ReceivedEnrollment incoming = inPerson.getReceivedEnrollment();

        if (incoming != null) {
            if (result.getReceivedEnrollment() == null) {
                result.setReceivedEnrollment(new ReceivedEnrollment());
            }
            try {
                this.getMergeRuleService().mergeReceivedEnrollment(incoming,
                        result.getReceivedEnrollment());
            } catch (ServiceException e) {
                throw new RuleException("Failed to merge ReceivedEnrollment", e);
            }
        } else {
            result.setReceivedEnrollment(null);
        }
    }

    private boolean isRegistryOnly(Person person, String registryType)
            throws RuleException {
        try {
            RegistrySearchCriteria criteria = this.getRegistryService()
                    .getRegistrySearchCriteria(person, registryType);
            criteria.setMaxAllowedRecords(MAX_ALLOWED_RECORDS); // some
            // reasonable
            // number
            Registry reg = this.getRegistryService().getMatchingRegistry(
                    criteria);
            if (reg != null && reg.getPerson() == null) {
                // Person not linked to registry so this person is only in
                // registry
                return true;
            }
        } catch (ServiceException serviceEx) {
            throw new RuleException("Error searching registry", serviceEx);
        }

        return false;
    }

    /**
     * @see gov.va.med.esr.common.rule.EnrollmentInput#removeAllEligibilityFactors()
     */
    public void removeAllEligibilityFactors() throws RuleException {
        /*
         * 5919 [UC33.3.3.4] When the Veteran Indicator is changed from YES to
         * NO the system automatically sets several fields as follows:
         */
        Person incoming = this.getIncomingPerson();
        if (incoming != null) {
            try {
                this.getEligibilityEnrollmentService()
                        .removeAllEligibilityFactors(incoming);
            } catch (ServiceException ex) {
                throw new RuleException(
                        "Error while removing eligibility factors", ex);
            }
        }

    }

    private EnrollmentDetermination getPriorEnrollment() throws RuleException {
        Person priorPerson = this.getPriorEEPerson();
        return priorPerson != null ? priorPerson.getEnrollmentDetermination()
                : null;
    }

    private Date getEarliestUnverifiedEnrollmentApplicationDate()
            throws RuleException {
        try {
            EligibilityEnrollmentService enrollmentService = this
                    .getEligibilityEnrollmentService();
            Person person = this.getIncomingPerson();
            if (person != null && person.getEntityKey() != null)
                return enrollmentService
                        .getAppDateFromEarliestUnverifiedEnrollment(this
                                .getIncomingPerson().getEntityKey());
        } catch (ServiceException serviceEx) {
            throw new RuleException(
                    "Error getting application date from unverified enrollment",
                    serviceEx);
        }
        return null;
    }

    private boolean hasVerifiedEnrollmentWithEligibility(String code)
            throws RuleException {
        try {
            return this.getEligibilityEnrollmentService()
                    .isVerifiedEnrollmentExistsForEligibilityCode(
                            this.getIncomingPerson().getEntityKey(), code);
        } catch (ServiceException ex) {
            throw new RuleException("Error getting verified enrollment", ex);
        }
    }

    private EnrollmentDetermination getMostRecentEnrollmentDeterminationForCE()
            throws RuleException {

        if (this.ceEnrollmentDetermination == null) {
            try {
                this.ceEnrollmentDetermination = this
                        .getEligibilityEnrollmentService()
                        .getMostRecentEnrollmentforContinuousEnrollment(
                                this.getIncomingPerson().getEntityKey());
            } catch (ServiceException ex) {
                throw new RuleException(
                        "Error getting enrollment for continuous enrollment rules",
                        ex);
            }
        }

        return this.ceEnrollmentDetermination;
    }

    public boolean isMostRecentEnrollmentEffectiveDateYearEqualToThisYear() throws RuleException{
        EnrollmentDetermination enrollmentDetermination = getMostRecentEnrollmentDeterminationForCE();
        Calendar calendar=Calendar.getInstance();
        Calendar theEffectiveDate = Calendar.getInstance();
        // 9617 - the following caused null pointer
        //theEffectiveDate.setTime(enrollmentDetermination.getEffectiveDate());
        if (enrollmentDetermination != null && enrollmentDetermination.getEffectiveDate() != null) {
        	theEffectiveDate.setTime(enrollmentDetermination.getEffectiveDate());
        }
        else return false;


        return this.isEqual(new Integer(theEffectiveDate.get(Calendar.YEAR)),new Integer(calendar.get(Calendar.YEAR)));
    }

    private List getIncomeYearFromVerifiedEnrollmentDueToMT()
            throws RuleException {
    	List incomeYear = null;

        try {
            incomeYear = this.getEligibilityEnrollmentService()
                    .getIncomeYearsForVerifiedMT(
                            this.getIncomingPerson().getEntityKey());
        } catch (ServiceException ex) {
            throw new RuleException("Error getting verified enrollment", ex);
        }
        return incomeYear;
    }

    /**
     * Set the initial priority of the veteran’s resulting enrollment
     * determination. This method leverages PersonHelperService to perform the
     * lookup and set the Initial Priority Code on EnrollmentDetermination.
     *
     * @param code
     *
     */
    public void setInitialEnrollmentPriority(String code) throws RuleException {
        try {
            if (code == null) {
                this.getResultEnrollmentDetermination()
                        .setInitialPriorityGroup(null);
            } else {
                this.getHelperService().setInitialEnrollmentPriority(code,
                        this.getResultEnrollmentDetermination());
            }
        } catch (ServiceException e) {
            throw new RuleException("Failed to set initial priority", e);
        }
    }

    /**
     * Set the initial sub priority of the veteran’s resulting enrollment
     * determination. This method leverages PersonHelperService to perform the
     * lookup and set the Initial SubPriority Code on EnrollmentDetermination.
     *
     * @param code
     */
    public void setInitialSubPriority(String code) throws RuleException {
        try {
            if (code == null) {
                // set initial priority to null on the veteran’s resulting
                // enrollment determination
                this.getResultEnrollmentDetermination()
                        .setInitialPrioritySubGroup(null);
            } else {
                // set initial priority to the specified code on the veteran’s
                // resulting enrollment determination
                this.getHelperService().setInitialEnrollmentPrioritySubGroup(
                        code, this.getResultEnrollmentDetermination());
            }
        } catch (ServiceException e) {
            throw new RuleException("failed to set initial sub priority", e);
        }
    }

    /**
     * This method finds the earliest verified enrollment determination. If
     * canceled/declined enrollment determination exists, use the first verified
     * enrollment determination after the most recent canceled/declined
     * enrollment determination.
     *
     * @param person
     * @return enrollmentDetermination
     *
     */
    private EnrollmentDetermination getEarliestVerifiedEnrollmentDetermination(
            Person person) {
        Set enrollmentDeterminations = person.getEnrollmentDeterminations();
        EnrollmentDetermination enrollmentDetermination = null;
        EnrollmentDeterminationComparatorByEntityKeyValue comparator = new EnrollmentDeterminationComparatorByEntityKeyValue();

        if (enrollmentDeterminations != null) {

            // sort the set by converting to an array and sorting by earliest
            // enrollment effective date
            Object[] enrollmentDeterminationsArray = enrollmentDeterminations
                    .toArray();
            Arrays.sort(enrollmentDeterminationsArray, comparator);

            // starting at the end of the array, iterate backward looking for
            // the last cancelled/declined enrollment determination
            int i = enrollmentDeterminationsArray.length - 1;
            for (; i > -1; i--) {
                EnrollmentDetermination edCandidate = (EnrollmentDetermination) enrollmentDeterminationsArray[i];
                if (edCandidate.getEnrollmentStatus() != null
                        && EnrollmentStatus.CODE_CANCELLED_DECLINED
                                .equals(edCandidate.getEnrollmentStatus()
                                        .getCodeObject())) {
                    break;
                }
            }
            if (i == -1) {
                i = 0;

            }
            // iterate forward looking for the first verified enrollment record
            for (; i < enrollmentDeterminationsArray.length; i++) {
                EnrollmentDetermination edCandidate = (EnrollmentDetermination) enrollmentDeterminationsArray[i];
                if (edCandidate.getEnrollmentStatus() != null
                        && EnrollmentStatus.CODE_VERIFIED.equals(edCandidate
                                .getEnrollmentStatus().getCodeObject())) {
                    enrollmentDetermination = (EnrollmentDetermination) enrollmentDeterminationsArray[i];
                    break;
                }
            }
        }
        return enrollmentDetermination;
    }

    /**
     * Util method to find the earliest Verified Enrollment Determination
     * record.
     *
     * @param person
     * @return
     * @throws RuleException
     */
    private EnrollmentDetermination getEnrollmentDeterminationForEarliestVerifiedUnlessCancelledOrRejectedBelowEnrollmentGroupThreshold(
            Person person) throws RuleException {
        EnrollmentDetermination ed = null;
        try {
            EligibilityEnrollmentService enrollmentService = this
                    .getEligibilityEnrollmentService();

            if (person != null && person.getEntityKey() != null)
                ed = enrollmentService
                        .getEnrollmentDeterminationForEarliestVerifiedUnlessCancelledOrRejectedBelowEnrollmentGroupThreshold(person
                                .getEntityKey());
        } catch (ServiceException serviceEx) {
            throw new RuleException(
                    "Error getting application date from unverified enrollment",
                    serviceEx);
        }
        return ed;
    }


    // CodeCR10023 and CR 9803
    public String getMostRecentVerifiedEnrollmentPriority(Person person) throws RuleException {
    	if (this.mostRecentPriorityGroupOfVerifiedEnrollment == null) {
    		this.initializeMostRecentPriorityGroup(person);
    	}
    	return this.mostRecentPriorityGroupOfVerifiedEnrollment;
	}

    // CodeCR10023 and CR 9803
	public String getMostRecentVerifiedEnrollmentSubPriority(Person person) throws RuleException {
    	if (this.mostRecentPrioritySubGroupOfVerifiedEnrollment == null) {
    		this.initializeMostRecentPriorityGroup(person);
    	}
    	return this.mostRecentPrioritySubGroupOfVerifiedEnrollment;
	}

	private void initializeMostRecentPriorityGroup(Person person) throws RuleException {
            try {
            	if (person != null && person.getEntityKey() != null) {
            		String groups[] = this.getEligibilityEnrollmentService().getMostRecentVerifiedEnrollmentPriorityGroups(person.getEntityKey());
            		if (groups != null) {
            			this.mostRecentPriorityGroupOfVerifiedEnrollment = (groups[0] != null) ? groups[0] : null;
            			this.mostRecentPrioritySubGroupOfVerifiedEnrollment = (groups[1] != null) ? groups[1] : null;
            		}
            	}
            } catch (ServiceException serviceEx) {
                throw new RuleException(
                        "Error initializing most recent priority groups",
                        serviceEx);
            }
	}

	/**
     * This method finds the priority group associated with the earliest
     * verified enrollment determination. if canceled/declined enrollment
     * determination exists, use the first verified enrollment determination
     * after the most recent canceled/declined enrollment determination.
     *
     * @param person
     * @return String
     *
     */
    public String getEarliestEnrollmentPriority(Person person)
            throws RuleException {
        String priority = null;
        EnrollmentDetermination ed = null;
        ed = getEnrollmentDeterminationForEarliestVerifiedUnlessCancelledOrRejectedBelowEnrollmentGroupThreshold(person);
        if (ed != null && ed.getPriorityGroup() != null) {
            priority = ed.getPriorityGroup().getCode();
        }
        return priority;
    }

    /**
     * This method finds the priority sub group associated with the earliest
     * verified enrollment determination. If canceled/declined enrollment
     * determination exists, use the first verified enrollment determination
     * after the most recent canceled/declined enrollment determination.
     *
     * @param person
     * @return String
     *
     */
    public String getEarliestEnrollmentSubPriority(Person person)
            throws RuleException {
        String subPriority = null;
        EnrollmentDetermination ed = null;
        ed = getEnrollmentDeterminationForEarliestVerifiedUnlessCancelledOrRejectedBelowEnrollmentGroupThreshold(person);

        // Make sure that both Enrollment Determinaton and Priority SubGroup are
        // not NULL.
        if (ed != null && ed.getPrioritySubGroup() != null) {
            subPriority = ed.getPrioritySubGroup().getCode();
        }
        return subPriority;
    }

    /**
     * This method finds the veteran’s received primary eligibility code from
     * the resulting veteran’s person object
     *
     * @return primary eligibility Code
     *
     */
    public String getResultReceivedPrimaryEligibilityCode() {
        Person person = this.getResultPerson();
        return getReceivedPrimaryEligibilityCode(person);
    }



	//The following are enhancements to ESR 3.1 VOA, please see
   //SUC_[676] Send Update Message (Z11)

	public boolean isVOASpinalCordIndicatorYes() {

		if (getIncomingPerson().getSpinalCordInjury() != null && getIncomingPerson().getSpinalCordInjury().getSpinalCordInjuryType() != null &&
				!getIncomingPerson().getSpinalCordInjury().getSpinalCordInjuryType().getCode().equals(SpinalCordInjuryType.CODE_NOT_APPLICABLE.getCode())) {
			return true;
		}

		return false;
	}


	//CCR7446
	/**
	 * @see gov.va.med.esr.common.rule.EnrollmentInputParameter#cancelVOAPendingClock()
	 */
	public void cancelVOAPendingClock() throws RuleException {
		try {
			this.getScheduledTaskService().cancelClock(
					this.getIncomingPerson(), Clock.Type.VOA_3_DAY_CLOCK,
					Clock.Group.VOA_CLOCK_GROUP);
		} catch (ServiceException e) {
			throw new RuleException("Failed to cancel VOA pending clock", e);
		}
	}

	//CCR7446
	/**
	 * Sets the VOA pending clock.
	 */
	public void updateVOAPendingClock() throws RuleException {
		try {
			this.getScheduledTaskService().startVOAPendingClock(
					this.getIncomingPerson());
		} catch (ServiceException e) {
			throw new RuleException("Failed to start VOA pending clock", e);
		}
	}

	//CCR7446
	/**
	 * @see gov.va.med.esr.common.rule.EnrollmentInputParameter#hasVOAPendingClockExpired()
	 */
	public boolean hasVOAPendingClockExpired() {
		Clock.Type fired = this.getFiredClockType();
		return fired != null ? Clock.Type.VOA_3_DAY_CLOCK.getName().equals(
				fired.getName()) : true;
	}


	/**
	 * CodeCR10023 and CR 9803
	 */
	public boolean hasVerifiedEnrollmentStatusAfterPeriodOfNoEnrollment() throws RuleException {
		// CodeCR10023 and CR 9803
		// The rationale for using the effective date is that a correctly formed Verified Enrollment
		// will contain an Effective Date. We reuse an existing method to get this date, where the method
		// searches for ANY verified enrollment, but stops if it runs into a Cancel/Decline/Rejected.
		if (this.earliestEffDateForVerifiedUnlessCancelled == null) {
			this.earliestEffDateForVerifiedUnlessCancelled = this.getEffectiveDateForEarliestVerifiedUnlessCancelled();
		}
		return (this.earliestEffDateForVerifiedUnlessCancelled != null) ? true : false;
	}

	//CCR7423
	public boolean isVoaHasAttachments()
	{
		ReceivedEligibility elig = this.getIncomingPerson().getReceivedPrimaryEligibility();
		if (elig != null && elig.isVoaHasAttachment())
			return true;

		return false;
	}

	public void updateVoaAttachmentWithPersonId() throws RuleException {
		if (this.getEEInputData().getMessageControlId() == null)
			return;

		String msgControlId = this.getEEInputData().getMessageControlId();
		BigDecimal personId = (BigDecimal)this.getPristinePerson().getEntityKey().getKeyValue();
		try{
			this.getVoaApplicationService().updateVoaAttachmentWithPersonId(msgControlId, personId);
		} catch (ServiceException e) {
			throw new RuleException("Failed to Update VOA Attachment with PersonId=" + personId + " for Message Control Id="+msgControlId, e);
		}
	}

	// CR 9803 & CCR 10023
	public boolean hasEligibilityCodeEqualTo(String eligibilityCode, Person person) {
		if (person == null || eligibilityCode == null)
			return false;

		EnrollmentDetermination enrollment = person.getEnrollmentDetermination();

		if (enrollment != null) {
			String primaryEligibilityCode = (enrollment.getPrimaryEligibility() != null &&
					enrollment.getPrimaryEligibility().getType() != null) ?
							enrollment.getPrimaryEligibility().getType().getCode() : null;

			if (StringUtils.equals(eligibilityCode, primaryEligibilityCode))
				return true;

			Set secondaries = enrollment.getSecondaryEligibilities();
			for (Iterator i = secondaries.iterator(); i.hasNext();) {
				Eligibility eligibility = (Eligibility) i.next();
				String code = (eligibility.getType() != null) ? eligibility.getType().getCode() : null;
				if (StringUtils.equals(eligibilityCode, code)) {
					return true;
				}
			}
		}
		return false;
	}

	// CR 9803 & CCR 10023
	public boolean hasVeteranCompletedMeansTestForYearOfApplication(Person person) {
		Date appDate =  (person != null && person.getApplication() != null) ? person.getApplication().getApplicationDate() : null;
		if (appDate == null) return false;
        Calendar tempDate = Calendar.getInstance();
        tempDate.setTime(appDate);
        Integer year = new Integer(tempDate.get(Calendar.YEAR));
		IncomeTest test = (year != null) ? person.getIncomeTest(year) : null;
		return (test != null) ? true : false;
	}

	//CCR12064
	public boolean hasVeteranHadNoHealthBenefitPlan(){
		 return !this.getPristineHealthBenefitProfile().containsHealthBenefitPlans();
	}

	private HealthBenefitProfile getPristineHealthBenefitProfile(){
		 return this.getPristinePerson().getHealthBenefitProfile();
	}
	//CCR12064
	public void updateHealthBenefitProfile() throws RuleException {
	        Person inPerson = this.getIncomingPerson();
	        Person result = this.getResultPerson();
	        HealthBenefitProfile incoming = inPerson.getHealthBenefitProfile();

	        if (incoming != null) {
	            try {
	                this.getMergeRuleService().mergeHealthBenefitProfile(incoming,
	                        result.getHealthBenefitProfile());
	            } catch (ServiceException e) {
	                throw new RuleException("Failed to merge HealthBenefitProfile", e);
	            }
	        } else {
	            result.setHealthBenefitProfile(null);
	        }
	    }
  }