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

//----------------------------------------------------------------------------
//  Import java classes
//----------------------------------------------------------------------------
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

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

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

import gov.va.med.esr.common.builder.entity.metaData.AssetMetaDataFromZICPosition;
import gov.va.med.esr.common.model.lookup.AssetType;
import gov.va.med.esr.common.model.lookup.ExpenseType;
import gov.va.med.esr.common.model.lookup.IncomeType;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.util.CommonDateUtils;

/**
 * @author Commons Team
 * @version 1.0
 */
public abstract class FinancialInformation
    extends AbstractKeyedEntity {
    
    private static final long serialVersionUID = -1370346746640850300L;
    private Person             person             = null;
    // Income Year that this financial information pertains to YYYY
    private Integer            incomeYear         = null;
    private Map                internalAssets     = null;
    private Map                internalExpenses   = null;
    private Map                internalIncomes     = null;
    private Set                internalDebts      = null;

    //TODO: used later
    //private Boolean active = Boolean.TRUE;

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

    //----------------------------------------------------------------------------
    // Public method(s)
    //----------------------------------------------------------------------------
    public Person getPerson() {
        return this.person;
    }

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

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

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

    private Map getInternalAssets() {
        if (this.internalAssets == null) {
            this.internalAssets = new HashMap();
        }
        //VFA: NW Discontinuation
        //resetAssetIfIncomeYear2009OrLatter(this.internalAssets);
        
        return this.internalAssets;
    }

    private void resetAssetIfIncomeYear2009OrLatter(Map assets)
    {
    	if (CommonDateUtils.isIncomeYear2009OrLatter(incomeYear))
    	{
	        for(Iterator iter=assets.keySet().iterator(); iter.hasNext();) 
	        {
	        	//VFA for income year 2009 and latter, set three asset amount null
	        	AssetType type = (AssetType)iter.next();
	        	this.discontinueAsset(type, (Asset)assets.get(type));
	        }
    	}
    }
    private void setInternalAssets(Map internalAssets) {
        this.internalAssets = internalAssets;
    }

    public Asset getCashAndBankAccountBalances() {
        return this.getAsset(AssetType.CODE_CASH);
    }
    
    public Asset getRealEstate() {
        return this.getAsset(AssetType.CODE_REAL_ESTATE);
    }
    
    public Asset getOtherPropertyOrAssets() {
        return this.getAsset(AssetType.CODE_OTHER);
    }
    
    public Map getAssets() {
        return Collections.unmodifiableMap(getInternalAssets());
    }

    public Asset getAsset(AssetType.Code typeCode) {
        for(Iterator iter=this.getInternalAssets().keySet().iterator(); iter.hasNext();) {
            AssetType type = (AssetType)iter.next();
            if(StringUtils.equals(type.getCode(),typeCode.getCode())) {
                return getAsset(type);
            }
        }
        return null;
    }
    
    public Asset getAsset(AssetType type) {
        return (Asset) getInternalAssets().get(type);
    }

    public void setAsset(AssetType type, Asset asset)
    {
    	this.setAsset(this.incomeYear, type, asset);
    }


    public void setAsset(Integer incomeYear, AssetType type, Asset asset) {
        Validate.notNull(type, "AssetType cannot be null");

        Asset currentAsset = this.getAsset(type);
        if (currentAsset != asset) {
            if (currentAsset != null) {
                this.getInternalAssets().remove(type);
            }
            if (asset != null) {
            	
/*	            if(CommonDateUtils.isIncomeYear2009OrLatter(incomeYear))
	            {
	            	//VFA discontinue Net Worth - three asset types
	            	discontinueAsset(type, asset);
	            }
*/	            
                getInternalAssets().put(type, asset);
                asset.setFinancialInformation(this);
                asset.setType(type);
            }
        }
    }
    
    private void discontinueAsset(AssetType type, Asset asset)
    {
        if(AssetType.CODE_CASH.getCode().equals(type.getCode()) ||
         		AssetType.CODE_REAL_ESTATE.getCode().equals(type.getCode()) ||
         		AssetType.CODE_OTHER.getCode().equals(type.getCode())
         )
        {
 	    	//VFA for income year 2009 and latter, set these three asset amount null
 	    	//1. AssetType.CODE_CASH
 	    	//2. AssetType.CODE_REAL_ESTATE
 	    	//3. AssetType.CODE_OTHER
             asset.setAmount(null);
         }
    }

    private Set getInternalDebts() {
        if (this.internalDebts == null) {
            this.internalDebts = new HashSet();
        }
        return this.internalDebts;
    }

    private void setInternalDebts(Set internalDebts) {
        this.internalDebts = internalDebts;
    }

    public Debt getDebt() {
        Set debts = getInternalDebts();
        if (debts.size() >0) {
            return (Debt) this.getInternalDebts().iterator().next();
        }
        return null;
    }

    public void setDebt(Debt debt) {
        Set debts = getInternalDebts();
        debts.clear();
        if (debt != null) {
            getInternalDebts().add(debt);
            debt.setFinancialInformation(this);
        }
    }

    private Map getInternalExpenses() {
        if (this.internalExpenses == null) {
            this.internalExpenses = new HashMap();
        }
        return this.internalExpenses;
    }

    private void setInternalExpenses(Map internalExpenses) {
        this.internalExpenses = internalExpenses;
    }

    public Map getExpenses() {
        return Collections.unmodifiableMap(getInternalExpenses());
    }

    public Expense getExpense(ExpenseType type) {
        return (Expense) getInternalExpenses().get(type);
    }
    
    public Expense getVeteranEducationExpense() {
        return this.getExpense(ExpenseType.EXPENSE_TYPE_COLLEGE_AND_VOCATION);
    }
    
    public Expense getEducationExpense() {
        return this.getExpense(ExpenseType.EXPENSE_TYPE_EDUCATIONAL_EXPENSES_BY_DEPENDENT);
    }
    
    public Expense getNonReimbursableExpense() {
        return this.getExpense(ExpenseType.EXPENSE_TYPE_NON_REIMBURSED_MEDICAL);
    }
    
    public Expense getAdjustedMedicalExpense() {
        return this.getExpense(ExpenseType.EXPENSE_TYPE_ADJUSTED_MEDICAL);
    }
    
    public Expense getFuneralBurialExpense() {
        return this.getExpense(ExpenseType.EXPENSE_TYPE_FUNERAL_AND_BURIAL);
    }
    
    public Expense getExpense(ExpenseType.Code typeCode) {
        for(Iterator iter=this.getInternalExpenses().keySet().iterator(); iter.hasNext();) {
            ExpenseType type = (ExpenseType)iter.next();
            if(StringUtils.equals(type.getCode(),typeCode.getCode())) {
                return getExpense(type);
            }
        }
        return null;
    }

    public void setExpense(ExpenseType type, Expense expense) {
        Validate.notNull(type, "ExpenseType cannot be null");
        Expense currentExpense = this.getExpense(type);
        if (currentExpense != expense) {
            if (currentExpense != null) {
                this.getInternalExpenses().remove(type);
            }
            if (expense != null) {
                getInternalExpenses().put(type, expense);
                expense.setFinancialInformation(this);
                expense.setType(type);
            }
        }
    }

    private Map getInternalIncomes() {
        if (this.internalIncomes == null) {
            this.internalIncomes = new HashMap();
        }
        return this.internalIncomes;
    }

    private void setInternalIncomes(Map internalIncomes) {
        this.internalIncomes = internalIncomes;
    }

    public Map getIncomes() {
        return Collections.unmodifiableMap(getInternalIncomes());
    }
    
    public Map getIncome() {
        return Collections.unmodifiableMap(getInternalIncomes());
    }

    public Income getTotalEmploymentIncome() {
        return this.getIncome(IncomeType.INCOME_TYPE_TOTAL_INCOME_FROM_EMPLOYMENT);
    }
    
    public Income getFarmRanchBusinessIncome() {
        return this.getIncome(IncomeType.INCOME_TYPE_FARM_RANCH_PROPERTY_OR_BUSINESS_INCOME);
    }
    
    public Income getOtherIncome() {
        return this.getIncome(IncomeType.INCOME_TYPE_TOTAL_ALL_OTHER_INCOME);
    }
    
    public Income getIncome(IncomeType type) {
        return (Income) getInternalIncomes().get(type);
    }
    
    public Income getIncome(IncomeType.Code typeCode) {
        for(Iterator iter=this.getInternalIncomes().keySet().iterator(); iter.hasNext();) {
            IncomeType type = (IncomeType)iter.next();
            if(StringUtils.equals(type.getCode(),typeCode.getCode())) {
                return getIncome(type);
            }
        }
        return null;
    }

    public void setIncome(IncomeType type, Income income) {
        Validate.notNull(type, "IncomeType cannot be null");

        Income currentIncome = this.getIncome(type);
        if (currentIncome != income) {
            if (currentIncome != null) {
                this.getInternalIncomes().remove(type);
            }
            if (income != null) {
                getInternalIncomes().put(type, income);
                income.setFinancialInformation(this);
                income.setType(type);
            }
        }
    }

    //Methods added that comform to naming conventions for Map based collections 
    public void addIncome(IncomeType type, Income income) {
    	setIncome(type,income);
    }
    public void addAsset(AssetType type, Asset asset) {
    	setAsset(type,asset);
    }
    public void addExpense(ExpenseType type, Expense expense) {
    	setExpense(type,expense);
    }
   
    /*
     * (non-Javadoc)
     * 
     * @see gov.va.med.fw.model.AbstractKeyedEntity#buildToString(org.apache.commons.lang.builder.ToStringBuilder)
     */
    protected void buildToString(ToStringBuilder builder) {
        super.buildToString(builder);
        builder.append("incomeYear", this.incomeYear);
    }
}