/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.esr.common.rule.service.impl;

import gov.va.med.esr.common.model.ee.EnrollmentDetermination;
import gov.va.med.esr.common.model.ee.FeeBasis;
import gov.va.med.esr.common.model.financials.DependentFinancials;
import gov.va.med.esr.common.model.financials.FinancialStatement;
import gov.va.med.esr.common.model.financials.IncomeTest;
import gov.va.med.esr.common.model.financials.SpouseFinancials;
import gov.va.med.esr.common.model.lookup.IncomeTestSource;
import gov.va.med.esr.common.model.lookup.MeansTestStatus;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.Employment;
import gov.va.med.esr.common.model.person.SignatureImage;
import gov.va.med.esr.common.rule.FinancialInput;
import gov.va.med.esr.common.rule.PersonInput;
import gov.va.med.esr.common.rule.data.EEInputData;
import gov.va.med.esr.common.rule.data.FeeBasisInputData;
import gov.va.med.esr.common.rule.data.FinancialInputData;
import gov.va.med.esr.common.rule.data.PersonSignatureInputData;
import gov.va.med.esr.common.rule.service.FinancialInfoRuleService;
import gov.va.med.esr.common.rule.service.RuleValidationService;
import gov.va.med.esr.service.EEResultInfo;
import gov.va.med.esr.service.FinancialsHelperService;
import gov.va.med.esr.service.IVMFinancialInfo;
import gov.va.med.esr.service.trigger.IncomeYearTriggerEvent;
import gov.va.med.fw.rule.RuleParameters;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.trigger.TriggerRouter;
import gov.va.med.fw.util.StringUtils;
import gov.va.med.fw.validation.ValidationMessage;
import gov.va.med.fw.validation.ValidationMessages;
import gov.va.med.fw.validation.ValidationServiceException;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.Validate;

/**
 * 
 * Project: Common
 * 
 * @author DNS   LEV
 * @author DNS   MANSOG
 * @author Muddaiah Ranga
 * @version 1.0
 */
public class FinancialInfoRuleServiceImpl extends
		AbstractRuleValidationServiceAwareImpl implements
		FinancialInfoRuleService {

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

	private String		financialInfoRuleParameters				= null;
	private String		determineIncTestFromEligRuleParameters	= null;
	private String		processPersonSignatureRuleParameters	= null;
	private String		feeBasisRuleParameters					= null;
	// CCR 12153
	private String processIVMFinancialsRuleParameters = null;
	
    private FinancialsHelperService financialsHelperService = null;
    private TriggerRouter triggerRouter = null;

	public FinancialInfoRuleServiceImpl() {
		super();
	}

	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();
		Validate.notNull(this.financialInfoRuleParameters,
				"financialInfoRuleParameters property is required");
		Validate.notNull(this.processPersonSignatureRuleParameters,
				"processPersonSignatureRuleParameters property is required");
		Validate.notNull(this.determineIncTestFromEligRuleParameters,
				"determineIncTestFromEligRuleParameters property is required");
		Validate.notNull(this.feeBasisRuleParameters, "feeBasisRuleParameters property is required");
        Validate.notNull(this.financialsHelperService, "FinancialsHelperService property is required");
        Validate.notNull(this.triggerRouter, "TriggerRouter property is required");
	}

	public String getFinancialInfoRuleParameters() {
		return financialInfoRuleParameters;
	}

	public void setFinancialInfoRuleParameters(
         String incomeTestRuleParameters) {
		this.financialInfoRuleParameters = incomeTestRuleParameters;
	}

	/**
	 * @return Returns the determineIncTestFromEligRuleParameters.
	 */
	public String getDetermineIncTestFromEligRuleParameters() {
		return determineIncTestFromEligRuleParameters;
	}

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

	/**
	 * @return Returns the processPersonSignatureRuleParameters.
	 */
	public String getProcessPersonSignatureRuleParameters() {
		return processPersonSignatureRuleParameters;
	}

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

	/**
	 * @return Returns the feeBasisRuleParameters.
	 */
	public String getFeeBasisRuleParameters() {
		return feeBasisRuleParameters;
	}

	/**
	 * @param feeBasisRuleParameters
	 *            The feeBasisRuleParameters to set.
	 */
	public void setFeeBasisRuleParameters(String feeBasisRuleParameters) {
		this.feeBasisRuleParameters = feeBasisRuleParameters;
	}
	
	
    public String getProcessIVMFinancialsRuleParameters() {
		return processIVMFinancialsRuleParameters;
	}

	public void setProcessIVMFinancialsRuleParameters(
			String processIVMFinancialsRuleParameters) {
		this.processIVMFinancialsRuleParameters = processIVMFinancialsRuleParameters;
	}

	/**
     * @return Returns the financialsHelperService.
     */
    public FinancialsHelperService getFinancialsHelperService() {
        return financialsHelperService;
    }
    
    /**
     * @param financialsHelperService The financialsHelperService to set.
     */
    public void setFinancialsHelperService(FinancialsHelperService financialsHelperService) {
        this.financialsHelperService = financialsHelperService;
    }

    public TriggerRouter getTriggerRouter() {
        return triggerRouter;
    }

    public void setTriggerRouter(TriggerRouter triggerRouter) {
        this.triggerRouter = triggerRouter;
    }

    /**
	 * processFinancialInfo
	 * 
	 * @see gov.va.med.esr.common.rule.service.FinancialInfoRuleService#processFinancialInfo(java.lang.Integer,
	 *      gov.va.med.esr.common.model.lookup.VAFacility,
	 *      gov.va.med.esr.common.model.person.Person,
	 *      gov.va.med.esr.common.model.person.Person)
	 */
	public Person processFinancialInfo(Integer incomeYear,
			VAFacility fromFacility, Person incoming, Person personOnFile)
			throws ServiceException {
		return processFinancialInfo(incomeYear, fromFacility, incoming, personOnFile, null);
	}

	public Person processFinancialInfo(Integer incomeYear, VAFacility fromFacility, Person incoming, Person personOnFile,
            EEResultInfo info) throws ServiceException {
		return processFinancialInfo(incomeYear, fromFacility, incoming, personOnFile, info, false);
	}
    
	
	public Person processIVMFinancials(Person incoming, Person onFile, IVMFinancialInfo ivmFinancialInfo) throws ServiceException {
	        Validate.notNull(incoming, "Incoming Person must not be null");
	        Validate.notNull(onFile, "A current veteran must not be null");
	        Validate.notNull(ivmFinancialInfo, "IVM financial info must not be null");
	        Validate.notNull(ivmFinancialInfo.getIncomeYear(), "income year must not be null");	        

	        Person pristine = getPristinePerson(onFile);
	        FinancialInputData inputData = new FinancialInputData(incoming,onFile, 
	        		pristine, ivmFinancialInfo);
	        RuleParameters ruleParams = this.getRuleParameters(this.getProcessIVMFinancialsRuleParameters());
	        this.invokeRuleFlow( ruleParams, inputData);
	        return onFile;
	}

	/**
     * @see gov.va.med.esr.common.rule.service.FinancialInfoRuleService#processFinancialInfo(java.lang.Integer, gov.va.med.esr.common.model.lookup.VAFacility, gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.person.Person, gov.va.med.esr.service.EEResultInfo)
     */
    public Person processFinancialInfo(Integer incomeYear, VAFacility fromFacility, Person incoming, Person personOnFile,
            EEResultInfo info, boolean isDataFromZ07) throws ServiceException {
        Validate.notNull(incomeYear, "Income year must not be null");
        Validate.notNull(incoming, "Incoming Person must not be null");
        Validate.notNull(personOnFile, "A current veteran must not be null");
        Validate.notNull(fromFacility, "A facility must not be null");

        Person pristine = getPristinePerson(personOnFile);

        FinancialInputData inputData = new FinancialInputData(incoming,
                personOnFile, pristine, incomeYear, fromFacility, false);
        RuleParameters ruleParams = this.getRuleParameters( getFinancialInfoRuleParameters() );
        if(isDataFromZ07){
        	FinancialInput input = (FinancialInput)ruleParams.getRuleParameter("financialRuleParameter");
        	input.setDataFromZ07(true);
        }
        
        //CCR 11869: VOA require determine primary flag = true
        if (VAFacility.CODE_MHV.getCode().equals(fromFacility.getCode()))
        {
        	((FinancialInput)ruleParams.getRuleParameter("financialRuleParameter")).setDeterminedPrimaryFlag(true);
        }
        this.invokeRuleFlow( ruleParams, inputData);
        if (info != null) {
            info.setZ10Sent(inputData.isZ10Sent());
        }
        return personOnFile;
    }
    
    /**
     * @see gov.va.med.esr.common.rule.service.FinancialInfoRuleService#manageFinancialAssessment(java.lang.Integer, gov.va.med.esr.common.model.lookup.VAFacility, gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.person.Person, gov.va.med.esr.service.EEResultInfo)
     */
    public Person manageFinancialAssessment(Integer incomeYear, VAFacility fromFacility, Person incoming, Person personOnFile, EEResultInfo info) throws ServiceException {
        Validate.notNull(personOnFile, "A current veteran must not be null");
        Validate.notNull(incoming, "Incoming Person must not be null");
        Validate.notNull(incomeYear, "Income year must not be null");

        Person pristine = getPristinePerson(personOnFile);
        // Validate Veteran, spouse and dependent's for the amounts.
        validateManageFinancialAssessment(incomeYear, incoming, personOnFile,
                pristine);

        FinancialInputData inputData = new FinancialInputData((Person) incoming
                .clone(), personOnFile, pristine, incomeYear, fromFacility,
                true);
        this.invokeRuleFlow( this.getRuleParameters( getFinancialInfoRuleParameters() ), inputData);
        if (info != null) {
            info.setZ10Sent(inputData.isZ10Sent());
        }        
        return personOnFile;
    }

    /**
	 * manageFinancialAssessment
	 * 
	 * @see gov.va.med.esr.common.rule.service.FinancialInfoRuleService#manageFinancialAssessment(java.lang.Integer,
	 *      gov.va.med.esr.common.model.person.Person,
	 *      gov.va.med.esr.common.model.person.Person)
	 */
	public Person manageFinancialAssessment(Integer incomeYear,
			VAFacility fromFacility, Person incoming, Person personOnFile)
			throws ServiceException {
	    return manageFinancialAssessment(incomeYear, fromFacility, incoming,
                personOnFile, null);
	}

	public Person manageSpouseInfo(Integer incomeYear, SpouseFinancials spouseFinancials,
            Person incoming, Person onFile) throws ServiceException {
		Validate.notNull(incomeYear, "Income year must not be null");
		Validate.notNull(incoming, "Incoming Person must not be null");
		Validate.notNull(onFile, "A current veteran must not be null");
        Validate.notNull(spouseFinancials, "Incoming spouse financials must not be null");
        
		FinancialStatement stmt = incoming.getFinancialStatement(incomeYear);
		FinancialStatement onFileStmt = onFile.getFinancialStatement(incomeYear);

        Validate.notNull(spouseFinancials.getReportedOn(), "Incoming spouse must not be null");

        //TODO update to take spouse finacials as parameter
		ValidationMessages messages = this.getRuleValidationService()
				.validateSpouseFinancials(incomeYear, spouseFinancials, incoming, onFile,
						true, true);
		if (messages != null && !messages.isEmpty()) {
			throw new ValidationServiceException(messages);
		}

		if (onFileStmt == null) {
			onFileStmt = new FinancialStatement();
			onFileStmt.setIncomeYear(incomeYear);
			onFile.setFinancialStatement(incomeYear, onFileStmt);
		}
        getFinancialsHelperService().processSsn(spouseFinancials.getReportedOn());
		getMergeRuleService().mergeFinancialStatement(stmt, onFileStmt);
        Employment incomingEmployment=spouseFinancials.getReportedOn().getEmployment();
        Employment onFileEmployment= onFileStmt.getActiveSpouseFinancials()==null ? null : 
            onFileStmt.getActiveSpouseFinancials().getReportedOn().getEmployment();
        getFinancialsHelperService().updateSpouseEmployment(incomingEmployment,onFileEmployment);
        getFinancialsHelperService().updatePseudoSsnClock(onFile, onFileStmt);
        this.triggerZ10(onFile);

		return onFile;
	}

    public Person manageDependentFinancialInfo(Integer incomeYear,
			DependentFinancials dependentFinancials, Person incoming,
			Person onFile) throws ServiceException {
		Validate.notNull(incomeYear, "Income year must not be null");
		Validate.notNull(incoming, "Incoming Person must not be null");
		Validate.notNull(onFile, "A current veteran must not be null");
        Validate.notNull(dependentFinancials, "Incoming dependent financials must not be null");
        Validate.notNull(dependentFinancials.getReportedOn(), "Incoming dependent must not be null");

		ValidationMessages messages = this.getRuleValidationService()
				.validateDependentFinancials(incomeYear, dependentFinancials,
						incoming, onFile, true, true);
		if (messages != null && !messages.isEmpty()) {
			throw new ValidationServiceException(messages);
		}

		FinancialStatement stmt = incoming.getFinancialStatement(incomeYear);
		FinancialStatement onFileStmt = onFile
				.getFinancialStatement(incomeYear);
		if (onFileStmt == null) {
			onFileStmt = new FinancialStatement();
			onFileStmt.setIncomeYear(incomeYear);
			onFile.setFinancialStatement(incomeYear, onFileStmt);
		}
        getFinancialsHelperService().processSsn(dependentFinancials.getReportedOn());
		getMergeRuleService().mergeFinancialStatement(stmt, onFileStmt);       
        getFinancialsHelperService().updatePseudoSsnClock(onFile, onFileStmt);
        this.triggerZ10(onFile);
        
		return onFile;
	}
    
	/**
     * @see gov.va.med.esr.common.rule.service.FinancialInfoRuleService#managePendingAdjudicationStatus(java.lang.Integer, gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.lookup.MeansTestStatus)
     */
    public Person managePendingAdjudicationStatus(Integer incomeYear, Person incoming, Person onFile, MeansTestStatus status) throws ServiceException {
        IncomeTest currentTest = onFile.getIncomeTest(incomeYear);
        if (currentTest != null) {
            financialsHelperService.adjudicateIncomeTest(currentTest, status);
        }
        return onFile;
    }

    /**
	 * This method is used by E&E calculation.
	 * 
	 * @see gov.va.med.esr.common.rule.service.FinancialInfoRuleService#determineIncomeTestFromElig(gov.va.med.esr.common.model.person.Person,
	 *      gov.va.med.esr.common.model.person.Person,
	 *      gov.va.med.esr.common.model.person.Person)
	 */
	public Person determineIncomeTestFromElig(Person incoming, Person onFile,
			Person pristine) throws ServiceException {
		Validate.notNull(incoming, "An incoming person must not be null");
		Validate.notNull(onFile, "A current person must not be null");
		Validate.notNull(pristine, "A pristine person must not be null");

		// Don't care who caller is in this context
		EEInputData data = new EEInputData(incoming, onFile, pristine, false);
		this.invokeRuleFlow( this.getRuleParameters( this.getDetermineIncTestFromEligRuleParameters() ),
				data);
		return onFile;
	}

	/**
     * This method is used by E&E calculation. 
     * 
     * @see gov.va.med.esr.common.rule.service.FinancialInfoRuleService#determineIncomeTestFromElig(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.ee.EnrollmentDetermination)
     */
    public Person determineIncomeTestFromElig(Person incoming, Person onFile, Person pristine, EnrollmentDetermination result) throws ServiceException {

        return determineIncomeTestFromElig(incoming, onFile, pristine, result, false, null);
    }

    public Person determineIncomeTestFromElig(Person incoming, Person onFile, Person pristine, EnrollmentDetermination result, boolean isFromGUI, VAFacility sendingFacility) throws ServiceException
    {
        Validate.notNull(incoming, "An incoming person must not be null");
        Validate.notNull(onFile, "A current person must not be null");
        Validate.notNull(pristine, "A pristine person must not be null");
        Validate.notNull(result, "An enrollment determination must not be null");        

        EEInputData data = new EEInputData(incoming, onFile, pristine, result, isFromGUI, false, sendingFacility);
        data.setResultEnrollmentDetermination(result);
        this.invokeRuleFlow( this.getRuleParameters( this.getDetermineIncTestFromEligRuleParameters() ),
                data);
        return onFile;
    	
    }

    
    /**
	 * @see gov.va.med.esr.common.rule.service.FinancialInfoRuleService#managePersonSignature(gov.va.med.esr.common.model.person.SignatureImage,
	 *      gov.va.med.esr.common.model.person.Person)
	 */
	public SignatureImage managePersonSignature(SignatureImage signatureImage,
			Person onFile, String dfn) throws ServiceException {
		return this.doProcessPersonSignature(signatureImage, onFile, true, dfn);
	}

	/**
	 * @see gov.va.med.esr.common.rule.service.FinancialInfoRuleService#managePersonSignatures(java.util.Set,
	 *      gov.va.med.esr.common.model.person.Person, java.util.Map)
	 */
	public Person managePersonSignatures(Set signatures, Person onFile, Map dfns)
			throws ServiceException {
		return this.doProcessPersonSignatures(signatures, onFile, true, dfns);
	}

	/**
	 * @see gov.va.med.esr.common.rule.service.FinancialInfoRuleService#processPersonSignature(gov.va.med.esr.common.model.person.SignatureImage,
	 *      gov.va.med.esr.common.model.person.Person)
	 */
	public SignatureImage processPersonSignature(SignatureImage signatureImage,
			Person onFile, String dfn) throws ServiceException {
		return this
				.doProcessPersonSignature(signatureImage, onFile, false, dfn);
	}

	/**
	 * @see gov.va.med.esr.common.rule.service.FinancialInfoRuleService#processPersonSignatures(java.util.Set,
	 *      gov.va.med.esr.common.model.person.Person)
	 */
	public Person processPersonSignatures(Set signatures, Person onFile,
			Map dfns) throws ServiceException {
		return this.doProcessPersonSignatures(signatures, onFile, false, dfns);
	}

	private void validateManageFinancialAssessment(Integer incomeYear,
			Person incoming, Person onFile, Person pristine)
			throws ServiceException {
		// Validate only when the income test is not null
		if (incoming.getIncomeTest(incomeYear) != null
				&& incoming.getFinancialStatement(incomeYear) != null) {
			FinancialStatement incomingStmt = incoming
					.getFinancialStatement(incomeYear);

			ValidationMessages messages = new ValidationMessages();

			RuleValidationService ruleValidationService = this
					.getRuleValidationService();
			// Validate Veteran financials data for amounts
			messages.add(ruleValidationService.validateVeteranFinancials(
					incomeYear, incoming, onFile, pristine, false, true));

			// If the incoming income test cannot be added or edited, do not
			// validate spouse/dependents.
			if (this.isIncomeTestAddedEdited(messages)) {
				// Validate Spouse financials data for amounts
                SpouseFinancials spouseFinancials = incomingStmt.getActiveSpouseFinancials();
                if (spouseFinancials != null){
                    ValidationMessages valMsgs = ruleValidationService
                    .validateSpouseFinancials(incomeYear,
                            spouseFinancials, incoming, onFile,
                            false, true);
                    messages.add(valMsgs, String.valueOf(spouseFinancials.hashCode()));
                }
                /*
                for (Iterator iter = incomingStmt.getSpouseFinancials()
                        .iterator(); iter.hasNext();) {
                    SpouseFinancials spouseFinancials = (SpouseFinancials) iter.next();                    
                    ValidationMessages valMsgs = ruleValidationService
                            .validateSpouseFinancials(incomeYear,
                                    spouseFinancials, incoming, onFile,
                                    false, true);
                    messages.add(valMsgs, String.valueOf(spouseFinancials.hashCode()));
                } */               
				// Validate Dependent financials data for amounts
				for (Iterator iter = incomingStmt.getDependentFinancials()
						.iterator(); iter.hasNext();) {
					DependentFinancials dependentFinancials = (DependentFinancials) iter
							.next();
					ValidationMessages valMsgs = ruleValidationService
							.validateDependentFinancials(incomeYear,
									dependentFinancials, incoming, onFile,
									false, true);
					messages.add(valMsgs, String.valueOf(dependentFinancials
							.hashCode()));
				}
			}

			if (messages != null && !messages.isEmpty()) {
				throw new ValidationServiceException(messages);
			}
		}
	}

	private boolean isIncomeTestAddedEdited(ValidationMessages messages) {
		for (Iterator iter = messages.get(); iter.hasNext();) {
			ValidationMessage message = (ValidationMessage) iter.next();
			String msgKey = message.getKey();
			if (RuleValidationMessage.INCOME_TEST_CANNOT_BE_ADDED.getName()
					.equals(msgKey)
					|| RuleValidationMessage.INCOME_TEST_CANNOT_BE_EDITED
							.getName().equals(msgKey)
					|| RuleValidationMessage.INCOME_TEST_CANNOT_BE_ADDED_OR_EDITED
							.getName().equals(msgKey)) {
				return false;
			}
		}
		return true;
	}

	private SignatureImage doProcessPersonSignature(
			SignatureImage signatureImage, Person onFile,
			boolean isDataFromGUI, String dfn) throws ServiceException {
		Validate.notNull(signatureImage, "A signature image must not be null");
		Validate.notNull(onFile, "An on file person must not be null");
		Person pristine = getPristinePerson(onFile);
		Person incoming = signatureImage.getPerson();
		PersonSignatureInputData data = new PersonSignatureInputData(incoming,
				onFile, pristine, signatureImage, isDataFromGUI);
		data.setDfn(dfn);
		invokeRuleFlow( this.getRuleParameters( this.getProcessPersonSignatureRuleParameters() ), data);

		// Get back the one we just just updated or added
		SignatureImage processed = (SignatureImage) this.findMatchingElement(
				signatureImage, onFile.getSignatureImages());
		return processed;
	}

	private Person doProcessPersonSignatures(Set signatures, Person onFile,
			boolean isDataFromGUI, Map dfns) throws ServiceException {
		Validate.notNull(onFile, "A veteran on file must not be null");

		Set updatedSignatures = new HashSet();

		if (signatures == null || signatures.size() == 0) {
			onFile.removeAllSignatureImages();
		} else {
			for (Iterator i = signatures.iterator(); i.hasNext();) {
				SignatureImage signatureImage = (SignatureImage) i.next();
				String dfn = (dfns != null) ? (String) dfns.get(signatureImage
						.getSiteSubmittingImage()) : null;
				SignatureImage processed = (isDataFromGUI) ? this
						.managePersonSignature(signatureImage, onFile, dfn)
						: this.processPersonSignature(signatureImage, onFile,
								dfn);
				if (processed != null) {
					updatedSignatures.add(processed);
				}
			}
			// The following steps allow us to delete any on file records that
			// are not in
			// the incoming person
			onFile.removeAllSignatureImages();
			for (Iterator i = updatedSignatures.iterator(); i.hasNext();) {
				onFile.addSignatureImage((SignatureImage) i.next());
			}
		}
		return onFile;
	}

	/**
	 * @see gov.va.med.esr.common.rule.service.FinancialInfoRuleService#processFeeBasis(gov.va.med.esr.common.model.lookup.VAFacility,
	 *      gov.va.med.esr.common.model.person.Person,
	 *      gov.va.med.esr.common.model.person.Person)
	 */
	public Person processFeeBasis(VAFacility fromFacility, Person incomingPerson,
			Person personOnFile, boolean isDataFromGUI) throws ServiceException {
		Validate.notNull(fromFacility, "A facility must not be null");
		Validate.notNull(incomingPerson, "Incoming Person must not be null");
		Validate.notNull(personOnFile, "A current veteran must not be null");

		Person pristinePerson = getPristinePerson(personOnFile);

        //Remove all the deleted FeeBasis from onFile
        Set onFileFeeBasis = personOnFile.getFeeBasis(fromFacility);
        Set incomingFeeBasis = incomingPerson.getFeeBasis(fromFacility);
        
        for (Iterator i=onFileFeeBasis.iterator(); i.hasNext();){
            FeeBasis onFile = (FeeBasis)i.next();
            if (findMatchingElement(onFile,incomingFeeBasis) == null){
                //remove onfile fee basis
                personOnFile.removeFeeBasis(onFile);
            }
        }
        
        //Process the incoming FeeBasis
        for (Iterator i=incomingFeeBasis.iterator(); i.hasNext();){
            FeeBasis incoming = (FeeBasis)i.next();
            FeeBasis onFile = (FeeBasis)findMatchingElement(incoming,onFileFeeBasis);
            
            FeeBasisInputData inputData = new FeeBasisInputData(
                    incomingPerson, personOnFile, pristinePerson,
                    fromFacility, incoming, onFile, isDataFromGUI);
            
            this.invokeRuleFlow( this.getRuleParameters( getFeeBasisRuleParameters() ), inputData);
        }
		return personOnFile;
	}
    
    /**
     * Sends Z10.
     * @param person
     * @throws ServiceException
     */
    private void triggerZ10(Person person) throws ServiceException {
        Validate.notNull(person, "Person can not be null");
        
        IncomeTest currentIncomeTest = this.getFinancialsHelperService().getCurrentIncomeTest(person);

        if(currentIncomeTest != null) {
            Integer incomeYear = currentIncomeTest.getIncomeYear();
            IncomeTestSource incomeTestSource = currentIncomeTest.getSource();

            //Send out if income test source is null(no income test present) or non IVM source 
            if(incomeYear != null && (incomeTestSource == null || (!StringUtils.equals(IncomeTestSource.CODE_IVM.getCode(), incomeTestSource.getCode())))) {
                // Create the trigger event
                IncomeYearTriggerEvent incomeEvent = IncomeYearTriggerEvent.notifyVistaForFinancial(incomeYear);
        
                incomeEvent.setPersonId(person.getPersonEntityKey());
                incomeEvent.setIdentityTraits(person.getIdentityTraits());            
                   
                triggerRouter.processTriggerEvent(incomeEvent);
            }
        }
    }
}
