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

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

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

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

import gov.va.med.esr.common.model.person.Dependent;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.Spouse;

/**
 * @author Commons Team
 * @version 1.0
 */
public class FinancialStatement extends FinancialInformation {

    public static final Boolean DEFAULT_POST_2005_FORMAT = Boolean.TRUE; 
    
	/**
	 * Married last calendar year is defined as an Indicator of whether veteran
	 * was married during last calendar year Yes/No
	 */
	private Boolean marriedLastCalendarYear = Boolean.FALSE;

	/**
	 * Dollar amount contributed to spousal support is defined as the dollar
	 * amount paid in spouse support if the spouse did not reside with the
	 * veteran last calendar year, which is the Income year reported. Dollar
	 * amount between 0 and 999999999, 2 Decimal Digits
	 */
	private BigDecimal contributionToSpouse = null;
	
	/**
	 * Replaces the "contribution to spouse" amount from day-forward for use in
	 * determining whether spouse is dependent. 
	 */
	private Boolean contributedToSpouseInd = null;

	/**
	 * The Feb 2005 Format allows the entry of child data for none, one, and up
	 * to and including 19 children.
	 * 
	 * When an actor adds a new financial income test, the Feb 2005 Format
	 * Version shall automatically be set to 1 in order to reflect that this
	 * test is in the Feb 2005 Format. This test will then be viewed and edited
	 * in the Feb 2005 Format Version
	 * 
	 * If the Format Version is not equal to (1) Feb 2005 then the financial
	 * information will be viewed and collected using the (0) before Feb 2005
	 * format.
	 */
	private Boolean isPost2005Format = DEFAULT_POST_2005_FORMAT; //default

	/**
	 * Number of dependent children is defined as the number of qualifying
	 * dependent children this veteran has. This number is derived based on the
	 * dependents DOB
	 */
	private Integer numberOfDependentChildren = null;

	private Set internalSpouseFinancials = null;

	private Set internalDependentFinancials = null;

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

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

	public FinancialStatement(Person person, Integer incomeYear) {
		this();
		setPerson(person);
		setIncomeYear(incomeYear);
	}

	// ----------------------------------------------------------------------------
	// Public method(s)
	// ----------------------------------------------------------------------------
	public void setPerson(Person person) {
		super.setPerson(person);
		setBackReferences(person, getIncomeYear());
	}

	public Integer getIncomeYear() {
		return super.getIncomeYear();
	}

	public void setIncomeYear(Integer incomeYear) {
		super.setIncomeYear(incomeYear);
		setBackReferences(getPerson(), incomeYear);
	}

	public Boolean getMarriedLastCalendarYear() {
		return this.marriedLastCalendarYear;
	}

	public void setMarriedLastCalendarYear(Boolean marriedLastCalendarYear) {
		this.marriedLastCalendarYear = marriedLastCalendarYear;
	}

	public BigDecimal getContributionToSpouse() {
		return this.contributionToSpouse;
	}

	public void setContributionToSpouse(BigDecimal contributionToSpouse) {
		this.contributionToSpouse = contributionToSpouse;
	}

	public Integer getNumberOfDependentChildren() {
		return this.numberOfDependentChildren;
	}

	public void setNumberOfDependentChildren(Integer numberOfDependentChildren) {
		this.numberOfDependentChildren = numberOfDependentChildren;
	}

	public Boolean getIsPost2005Format() {
		return this.isPost2005Format;
	}

	public void setIsPost2005Format(Boolean isPost2005Format) {
		this.isPost2005Format = isPost2005Format;
	}

	/**
	 * Getter for internalSpouseFinancials
	 */
	private Set getInternalSpouseFinancials() {
		if (this.internalSpouseFinancials == null) {
			this.internalSpouseFinancials = new HashSet();
		}
		return internalSpouseFinancials;
	}
    
	/**
	 * Setter for internalSpouseFinancials
	 */
	private void setInternalSpouseFinancials(Set internalSpouseFinancials) {
		this.internalSpouseFinancials = internalSpouseFinancials;
	}

	public Set getSpouseFinancials() {
        return Collections.unmodifiableSet(getInternalSpouseFinancials());
	}
    /**
     * Get Active spouse based in inactive date
     * @return
     */
    public SpouseFinancials getActiveSpouseFinancials() {
        Set financials = getInternalSpouseFinancials();
        for(Iterator iter=financials.iterator(); iter.hasNext();) {
            SpouseFinancials spouseFinancials = (SpouseFinancials)iter.next();
            if (spouseFinancials.isActive()) {
                    return spouseFinancials;
            }
        }
        return null;
    }
    
    /**
     * Get Most recent spouse based on inactive date for the income year
     * @return
     */
    public SpouseFinancials getRecentSpouseFinancials() {
        Set financials = getInternalSpouseFinancials();
        SpouseFinancials sf = null;
        if (financials.size() > 0) {
            //Single spouse
            if (financials.size() == 1){
                return (SpouseFinancials) financials.iterator().next();
            }
            //Active Spouse
            sf = getActiveSpouseFinancials();
            if (sf != null) {
                return sf;
            }
            
            Iterator iter=financials.iterator();
            sf = (SpouseFinancials) iter.next();
            //get the most recent spouse (active/inactive) based on inactive date
            while (iter.hasNext()) {
                SpouseFinancials spouseFinancials = (SpouseFinancials)iter.next();
                if (spouseFinancials.getEndDate().after(sf.getEndDate())) {
                    sf = spouseFinancials;
                }
            }                    
        }
        return sf;
    }
    
    
    public SpouseFinancials getSpouseFinancialsByEntityKey(EntityKey entityKey) {
        return (SpouseFinancials)getEntityByEntityKey(getInternalSpouseFinancials(),entityKey);
    }

    public void removeActiveSpouseFinancials() {
        SpouseFinancials spouseFinancials = this.getActiveSpouseFinancials();
        if(spouseFinancials != null) 
            this.removeSpouseFinancials(spouseFinancials);
    }
    
   public void removeSpouseFinancials(SpouseFinancials spouseFinancials) {
		Validate.notNull(spouseFinancials, "Null spouse financials specified.");
		getInternalSpouseFinancials().remove(spouseFinancials);

		List stList = new ArrayList();
		for (Iterator iter = getInternalSpouseFinancials().iterator(); iter
				.hasNext();) {
			SpouseFinancials tempSf = (SpouseFinancials) iter.next();
			if ((spouseFinancials.getEntityKey() != null
					&& tempSf.getEntityKey() != null 
					&& !spouseFinancials.getEntityKey().equals(tempSf.getEntityKey()))
				|| (spouseFinancials != tempSf)
				|| (!spouseFinancials.equals(tempSf))) {
				stList.add(tempSf);
			}
		}
		getInternalSpouseFinancials().clear();
		getInternalSpouseFinancials().addAll(stList);
		spouseFinancials.setFinancialStatement(null);
		spouseFinancials.setPerson(null);
	}
       
	public void removeSpouseFinancials() {
		Set financials = getInternalSpouseFinancials();
		financials.clear();
	}

	public void addSpouseFinancials(SpouseFinancials spouseFinancials) {
		Validate.notNull(spouseFinancials,"SpouseFinancials information cannot be null");
		getInternalSpouseFinancials().add(spouseFinancials);
		setRelationFinancialsBackReference(spouseFinancials);
	}

	private void setRelationFinancialsBackReference(RelationFinancials relationFinancials) {
		relationFinancials.setFinancialStatement(this);
		relationFinancials.setIncomeYear(getIncomeYear());
		relationFinancials.setPerson(getPerson());
	}

	/**
	 * Getter for internalDependentFinancials
	 */
	private Set getInternalDependentFinancials() {
		if (this.internalDependentFinancials == null) {
			this.internalDependentFinancials = new HashSet();
		}
		return internalDependentFinancials;
	}

	/**
	 * Setter for internalDependentFinancials
	 */
	private void setInternalDependentFinancials(Set internalDependentFinancials) {
		this.internalDependentFinancials = internalDependentFinancials;
	}

	/**
	 * Get a unmodifiable collection of DependentFinancials
	 * 
	 * @return
	 */
	public Set getDependentFinancials() {
		return Collections.unmodifiableSet(getInternalDependentFinancials());
	}

	public DependentFinancials getDependentFinancialsByEntityKey(EntityKey identifier) {
		return (DependentFinancials) getEntityByEntityKey(getDependentFinancials(), identifier);
	}

	/**
	 * Add a given DependentFinancials to this FinancialStatement.
	 * 
	 * @param dependentFinancials
	 */
	public void addDependentFinancials(DependentFinancials dependentFinancials) {
		Validate.notNull(dependentFinancials,
				"DependentFinancials information cannot be null");
		getInternalDependentFinancials().add(dependentFinancials);
		dependentFinancials.setFinancialStatement(this);
		dependentFinancials.setIncomeYear(getIncomeYear());
		dependentFinancials.setPerson(getPerson());
	}

	/**
	 * Remove a given DependentFinancials from this FinancialStatement.
	 * 
	 * @param dependentFinancials
	 */
	public void removeDependentFinancials(
			DependentFinancials dependentFinancials) {
		// Validate.notNull(dependentFinancials,
		// "DependentFinancials information cannot be null");
		// getInternalDependentFinancials().remove(dependentFinancials);
		// dependentFinancials.setFinancialStatement(null);
		// dependentFinancials.setPerson(null);

		Validate.notNull(dependentFinancials,
				"DependentFinancials information cannot be null");
		List stList = new ArrayList();
		for (Iterator iter = getInternalDependentFinancials().iterator(); iter
				.hasNext();) {
			DependentFinancials tempDf = (DependentFinancials) iter.next();
			if ((dependentFinancials.getEntityKey() != null
					&& tempDf.getEntityKey() != null && !dependentFinancials
					.getEntityKey().equals(tempDf.getEntityKey()))
					|| (dependentFinancials != tempDf)
					|| (!dependentFinancials.equals(tempDf))) {
				stList.add(tempDf);
			}
		}
		getInternalDependentFinancials().clear();
		getInternalDependentFinancials().addAll(stList);
		dependentFinancials.setFinancialStatement(null);
		dependentFinancials.setPerson(null);
	}

    /**
     * Remove deletd Spouse financials
     * @param dfSet
     */
    public void removeSpouseFinancials(Set dfSet) {
        if (dfSet != null && dfSet.size() > 0) {
            for (Iterator iter = dfSet.iterator(); iter.hasNext();) {
                 removeSpouseFinancials((SpouseFinancials) iter.next());
            }
        }
    }
    
	/**
	 * Remove all the given DependentFinancials from this FinancialStatement.
	 * 
	 * @param dfSet
	 */
	public void removeDependentFinancials(Set dfSet) {
		if (dfSet != null) {
			for (Iterator iter = dfSet.iterator(); iter.hasNext();) {
					removeDependentFinancials((DependentFinancials) iter.next());
			}
		}
	}

	/**
	 * Removes all DependentFinancials from this FinancialStatement.
	 */
	public void removeAllDependentFinancials() {
		getInternalDependentFinancials().clear();
	}

	
    public Boolean getContributedToSpouseInd() {
        return contributedToSpouseInd;
    }

    public void setContributedToSpouseInd(Boolean contributedToSpouseInd) {
        this.contributedToSpouseInd = contributedToSpouseInd;
    }

    private void setBackReferences(Person person, Integer incomeYear) {
		setSpousesBackReference(person, incomeYear);
		setDependentsBackReference(person, incomeYear);
	}

	private void setSpousesBackReference(Person person, Integer incomeYear) {
		if (internalSpouseFinancials != null) {
			Set spouseFinancials = getInternalSpouseFinancials();
			for (Iterator iter = spouseFinancials.iterator(); iter.hasNext();) {
				SpouseFinancials spouseFinancial = (SpouseFinancials) iter
						.next();
				setRelationFinancialsBackReference(spouseFinancial);
				Spouse reportedOn = spouseFinancial.getReportedOn();
				if (reportedOn != null) {
					reportedOn.setPerson(person);
				}

			}
		}
	}

	private void setDependentsBackReference(Person person, Integer incomeYear) {
		if (internalDependentFinancials != null) {
			Set dependentFinancials = getInternalDependentFinancials();
			for (Iterator iter = dependentFinancials.iterator(); iter.hasNext();) {
				DependentFinancials dependentFinancial = (DependentFinancials) iter
						.next();
				setRelationFinancialsBackReference(dependentFinancial);
				Dependent reportedOn = dependentFinancial.getReportedOn();
				if (reportedOn != null) {
					reportedOn.setPerson(person);
				}
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.fw.model.AbstractKeyedEntity#buildToString(org.apache.commons.lang.builder.ToStringBuilder)
	 */
	protected void buildToString(ToStringBuilder builder) {
		super.buildToString(builder);
		builder.append("marriedLastCalendarYear", this.marriedLastCalendarYear);
		builder.append("contributionToSpouse", this.contributionToSpouse);
		builder.append("numberOfDependentChildren",
				this.numberOfDependentChildren);
		builder.append("isPost2005Format", this.isPost2005Format);
	}
}