/********************************************************************
 * Copyright © 2004 EDS. All rights reserved
 ********************************************************************/
package gov.va.med.esr.common.builder.entity;

// Java Classes
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

// Library Classes

// Framework Classes
import gov.va.med.fw.util.builder.Builder;
import gov.va.med.fw.util.builder.BuilderException;

// Common Classes
import gov.va.med.esr.common.model.financials.Asset;
import gov.va.med.esr.common.model.financials.Debt;
import gov.va.med.esr.common.model.financials.DependentFinancials;
import gov.va.med.esr.common.model.financials.Expense;
import gov.va.med.esr.common.model.financials.FinancialInformation;
import gov.va.med.esr.common.model.financials.FinancialStatement;
import gov.va.med.esr.common.model.financials.Income;
import gov.va.med.esr.common.model.financials.SpouseFinancials;
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;

// Messaging Classess

/**
 * Class that builds the application from a message.
 *
 * @author Carlos Ruiz
 * @version 1.0
 */
public abstract class FinancialInformationBuilder extends EntityBuilder
{


    private static final long serialVersionUID = -1370346746640850300L;
    private Builder assetBuilder;

    private Builder assetTypeBuilder;

    private Builder debtBuilder;

    private Builder expenseBuilder;

    private Builder expenseTypeBuilder;

    private Builder incomeBuilder;

    private Builder incomeTypeBuilder;
    // ES 4.2_CodeCR13446_CR_Fortify - Race Condition: Singleton Member Field
    //VFA: needed to set the assets for income Year 2009 and latter
    //private Integer incomeYear = null;

    /**
     * Default constructor.
     */
    public FinancialInformationBuilder()
    {
        super();
    }

    public Builder getAssetBuilder()
    {
        return this.assetBuilder;
    }

    public void setAssetBuilder(Builder assetBuilder)
    {
        this.assetBuilder = assetBuilder;
    }

    public Builder getAssetTypeBuilder()
    {
        return this.assetTypeBuilder;
    }

    public void setAssetTypeBuilder(Builder assetTypeBuilder)
    {
        this.assetTypeBuilder = assetTypeBuilder;
    }

    public Builder getDebtBuilder()
    {
        return this.debtBuilder;
    }

    public void setDebtBuilder(Builder debtBuilder)
    {
        this.debtBuilder = debtBuilder;
    }

    public Builder getExpenseBuilder()
    {
        return this.expenseBuilder;
    }

    public void setExpenseBuilder(Builder expenseBuilder)
    {
        this.expenseBuilder = expenseBuilder;
    }

    public Builder getExpenseTypeBuilder()
    {
        return this.expenseTypeBuilder;
    }

    public void setExpenseTypeBuilder(Builder expenseTypeBuilder)
    {
        this.expenseTypeBuilder = expenseTypeBuilder;
    }

    public Builder getIncomeBuilder()
    {
        return this.incomeBuilder;
    }

    public void setIncomeBuilder(Builder incomeBuilder)
    {
        this.incomeBuilder = incomeBuilder;
    }

    public Builder getIncomeTypeBuilder()
    {
        return this.incomeTypeBuilder;
    }

    public void setIncomeTypeBuilder(Builder incomeTypeBuilder)
    {
        this.incomeTypeBuilder = incomeTypeBuilder;
    }

    private void buildAssets(FinancialInformation input,
            AssetMetaData[] metaData, Integer incomeYear) throws BuilderException
    {

        for (int index = 0; index < ((metaData == null) ? 0 : metaData.length); index++)
        {
            this.buildAsset(input, metaData[index], incomeYear);
        }
    }

    private void buildAsset(FinancialInformation input, AssetMetaData metaData, Integer incomeYear)
            throws BuilderException
    {
        AssetType type = this.buildAssetType(metaData.getType());
        input.setAsset(incomeYear, type, this.buildAsset(input.getAsset(type), metaData));
    }

    private Asset buildAsset(Asset input, AssetMetaData metaData)
            throws BuilderException
    {
        if (metaData == null)
        {
            return null;
        }
        metaData.setEntity(input);
        return (Asset) this.assetBuilder.build(metaData);

    }

    private AssetType buildAssetType(String type) throws BuilderException
    {
        return (AssetType) this.assetTypeBuilder.build(type);
    }

    private Debt buildDebt(Debt input, DebtMetaData metaData)
            throws BuilderException
    {
        if (metaData == null)
        {
            return null;
        }
        metaData.setEntity(input);
        return (Debt) this.debtBuilder.build(metaData);

    }

    private void buildExpenses(FinancialInformation input,
            ExpenseMetaData[] metaData) throws BuilderException
    {
        for (int index = 0; index < ((metaData == null) ? 0 : metaData.length); index++)
        {
            this.buildExpense(input, metaData[index]);
        }
    }

    private void buildExpense(FinancialInformation input,
            ExpenseMetaData metaData) throws BuilderException
    {
        ExpenseType type = this.buildExpenseType(metaData.getType());
        input.setExpense(type, this.buildExpense(input.getExpense(type),
                metaData));
    }

    private Expense buildExpense(Expense input, ExpenseMetaData metaData)
            throws BuilderException
    {
        if (metaData == null)
        {
            return null;
        }
        metaData.setEntity(input);
        return (Expense) this.expenseBuilder.build(metaData);

    }

    private ExpenseType buildExpenseType(String type) throws BuilderException
    {
        return (ExpenseType) this.expenseTypeBuilder.build(type);
    }

    private void buildIncome(FinancialInformation input,
            IncomeMetaData[] metaData) throws BuilderException
    {
        for (int index = 0; index < ((metaData == null) ? 0 : metaData.length); index++)
        {
            this.buildIncome(input, metaData[index]);
        }
    }

    private void buildIncome(FinancialInformation input, IncomeMetaData metaData)
            throws BuilderException
    {
        IncomeType type = this.buildIncomeType(metaData.getType());
        input
                .setIncome(type, this.buildIncome(input.getIncome(type),
                        metaData));
    }

    private Income buildIncome(Income input, IncomeMetaData metaData)
            throws BuilderException
    {
        if (metaData == null)
        {
            return null;
        }
        metaData.setEntity(input);
        return (Income) this.incomeBuilder.build(metaData);

    }

    private IncomeType buildIncomeType(String type) throws BuilderException
    {
        return (IncomeType) this.incomeTypeBuilder.build(type);
    }

    private boolean shouldKeep(FinancialInformation obj)
    {
        return ((obj.getDebt() != null) || !super.isEmpty(obj.getAssets())
                || !super.isEmpty(obj.getExpenses()) || !super.isEmpty(obj
                .getIncome()));
    }

    protected void transfer(FinancialInformation input,
            FinancialInformationMetaData metaData) throws BuilderException
    {
    	// ES 4.2_CodeCR13446_CR_Fortify - Race Condition: Singleton Member Field - (get rid of instance variable)
        //VFA: added so that IY 2009 and latter can set assets,
    	//otherwise the income year will be null and cause VFA logic to fail
        Integer incomeYear = (super.buildYear(null, metaData.getIncomeYear()));

        input.setDebt(this.buildDebt(input.getDebt(), metaData.getDebt()));

        this.buildAssets(input, metaData.getAssets(), incomeYear);
        this.buildExpenses(input, metaData.getExpenses());
        this.buildIncome(input, metaData.getIncome());
    }

    public static class FinancialStatementBuilder extends
            FinancialInformationBuilder
    {
        /**
		 * An instance of serialVersionUID
		 */
		private static final long serialVersionUID = 5585272346012577029L;

		private Builder dependentFinancialsBuilder;

        private Builder spouseFinancialsBuilder;

        public FinancialStatementBuilder()
        {
            super();
        }

        public Builder getDependentFinancialsBuilder()
        {
            return this.dependentFinancialsBuilder;
        }

        public void setDependentFinancialsBuilder(
                Builder dependentFinancialsBuilder)
        {
            this.dependentFinancialsBuilder = dependentFinancialsBuilder;
        }

        public Builder getSpouseFinancialsBuilder()
        {
            return this.spouseFinancialsBuilder;
        }

        public void setSpouseFinancialsBuilder(Builder spouseFinancialsBuilder)
        {
            this.spouseFinancialsBuilder = spouseFinancialsBuilder;
        }

        public FinancialStatement build(FinancialStatementMetaData metaData)
                throws BuilderException
        {
            FinancialStatement input = metaData.getEntity();
            FinancialStatement output = (input == null) ? new FinancialStatement()
                    : input;

            this.transfer(output, metaData);

            return this.shouldKeep(output) ? output : null;
        }

        private DependentFinancials buildDependentFinancials(
                DependentFinancials input, DependentFinancialsMetaData metaData)
                throws BuilderException
        {
            if (metaData == null)
            {
                return null;
            }

            metaData.setEntity(input);
            return (DependentFinancials) this.dependentFinancialsBuilder
                    .build(metaData);

        }

        private SpouseFinancials buildSpouseFinancials(SpouseFinancials input,
                SpouseFinancialsMetaData metaData) throws BuilderException
        {
            if (metaData == null)
            {
                return null;
            }

            metaData.setEntity(input);
            return (SpouseFinancials) this.spouseFinancialsBuilder
                    .build(metaData);

        }

        /**
         * Add Spouse and dependent Financial info
         *
         * @param input
         * @param metaData
         * @throws BuilderException
         */
        private void buildSupportingInformation(FinancialStatement input,
                FinancialInformationMetaData[] metaData)
                throws BuilderException
        {

            //Clear any existing Dependent Financials List.
            input.removeAllDependentFinancials();

            //Clear any existing Spouse Financials.
            input.removeSpouseFinancials();

            //List of DependentFinancials from message
            Set dependentFinancialsToAdd = new HashSet();

            for (int index = 0; index < ((metaData == null) ? 0
                    : metaData.length); index++)
            {
                FinancialInformationMetaData currentMetaData = metaData[index];

                //Add the DependentFinancials
                if (currentMetaData instanceof DependentFinancialsMetaData)
                {
                    DependentFinancials dependentFinancials = buildDependentFinancials(
                            null, (DependentFinancialsMetaData) currentMetaData);
                    if (dependentFinancials != null)
                    {
                        dependentFinancialsToAdd.add(dependentFinancials);
                    }

                } else
                {
                    //Add the SpouseFinancials
                    SpouseFinancials spouseFinancialsToAdd = buildSpouseFinancials(
                            null, (SpouseFinancialsMetaData) currentMetaData);
                    if (spouseFinancialsToAdd != null)
                    {
                        input.addSpouseFinancials(spouseFinancialsToAdd);
                    }
                }
            }

            //Added the DependentFinancials from message to the
            // FinancialStatement
            for (Iterator iter = dependentFinancialsToAdd.iterator(); iter
                    .hasNext();)
            {
                input.addDependentFinancials((DependentFinancials) iter.next());
            }

        }

        private boolean shouldKeep(FinancialStatement obj)
        {
            return  true;
        }

        private void transfer(FinancialStatement input,
                FinancialStatementMetaData metaData) throws BuilderException
        {
            super.transfer(input, metaData);

            input.setContributionToSpouse(super.build(input
                    .getContributionToSpouse(), metaData
                    .getContributionToSpouse()));

            input.setContributedToSpouseInd(super.build(input
                    .getContributedToSpouseInd(), metaData
                    .getContributedToSpouseInd()));

            input.setMarriedLastCalendarYear(super.build(input
                    .getMarriedLastCalendarYear(), metaData
                    .getMarriedLastCalendarYear()));
            input.setNumberOfDependentChildren(super.build(input
                    .getNumberOfDependentChildren(), metaData
                    .getNumberOfDependentChildren()));

            Boolean isPost2005Format = super.build(input.getIsPost2005Format(),
                    metaData.getMeansTestVersion());

            if(isPost2005Format == null)
            {
                input.setIsPost2005Format(Boolean.FALSE);
            }else{
                input.setIsPost2005Format(isPost2005Format);
            }

            this.buildSupportingInformation(input, metaData
                    .getSupportingInformation());
        }
    }

}