/********************************************************************
 * Copyright © 2005-2006 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.esr.ui.financials.action;

// Java Classes
import java.math.BigDecimal;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;
import java.util.Collection;

// Library Classes

// Framework Classes
import gov.va.med.fw.conversion.ConversionServiceException;
import gov.va.med.fw.util.StringUtils;

// ESR Classes
import gov.va.med.esr.common.model.financials.Asset;
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.IncomeTest;
import gov.va.med.esr.common.model.financials.IncomeTestStatus;
import gov.va.med.esr.common.model.financials.PatientVisitSummary;
import gov.va.med.esr.common.model.financials.SpouseFinancials;
import gov.va.med.esr.common.model.lookup.ExpenseType;
import gov.va.med.esr.common.model.lookup.IncomeTestType;
import gov.va.med.esr.common.model.lookup.MeansTestStatus;
import gov.va.med.esr.common.model.lookup.NameType;
import gov.va.med.esr.common.model.person.Dependent;
import gov.va.med.esr.common.model.person.Name;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.Spouse;
import gov.va.med.esr.ui.financials.beans.FinancialInfoBean;
import gov.va.med.esr.ui.common.util.JspUtils;
import gov.va.med.esr.ui.util.NameHelper;

/**
 * A UI specific implementation of the conversion service.
 *
 * @author Priya R.
 * @version 1.0
 */
public class FinancialsConversionService extends FinancialDetailsConversionService
{
    /**
     * Default constructor.
     */
    public FinancialsConversionService()
    {
    }

    /**
     * Converts between a Person object that contains the financial information and the FinancialDetailsForm.
     *
     * @param source the source object
     * @param target the target object
     *
     * @throws gov.va.med.fw.conversion.ConversionServiceException if any errors were encountered during the conversion.
     */
    protected void convertBean(Object source, Object target) throws ConversionServiceException
    {
        Person person = null;
        FinancialsForm form = null;

        if (source instanceof Person)
        {
            person = (Person)source;
            if (target instanceof FinancialsForm)
            {
                form = (FinancialsForm)target;
            }
            else
            {
                throw new ConversionServiceException("Target object is of type " + target.getClass().getName() +
                    " and must be of type FinancialsForm.");
            }

            // Convert from the Person to the form
            convertPersonToForm(person, form);
        }
        else
        {
            if (source instanceof FinancialsForm)
            {
                form = (FinancialsForm)source;
                if (target instanceof Person)
                {
                    person = (Person)target;
                }
                else
                {
                    throw new ConversionServiceException("Target object is of type " + target.getClass().getName() +
                        " and must be of type Person.");
                }

                // Convert from the Form to Person
                convertFormToPerson(form, person);
            }
            else
            {
                throw new ConversionServiceException("Source object is of type " + target.getClass().getName() +
                    " and must be of type Person or FinancialsForm.");
            }
        }
    }

    /**
     * Converts the financial data from the Person object to the form.
     *
     * @param person The person
     * @param form The form
     *
     * @throws ConversionServiceException If there were any problems converting the person to the form.
     */
    protected void convertPersonToForm(Person person, FinancialsForm form) throws ConversionServiceException
    {
        try
        {

            if (StringUtils.isEmpty(form.getIncomeYear()))
                return;

            Integer incomeyear = Integer.valueOf(form.getIncomeYear());

            if (form.getIncometest() != null)
            {	
            	
            	Set otherNames = new HashSet(person.getNames());

                Name veteranName = Name.getNameOfType(otherNames, NameType.LEGAL_NAME.getName());

                FinancialStatement fstmt = person.getFinancialStatement(incomeyear);
                form.setFinancialStatement(fstmt);

                //get adjusted medical expense
                if (fstmt != null)
                {
                    for (Iterator i = fstmt.getExpenses().values().iterator(); i.hasNext();)
                    {
                        Expense e = (Expense)i.next();
                        if (e.getAmount() != null)
                        {

                            if (StringUtils.equals(e.getType().getCode(), ExpenseType.EXPENSE_TYPE_ADJUSTED_MEDICAL.getName()))
                                form.setAjustedMedicalExp(e.getAmount());
                        }
                    }
                }

                DependentFinancials df = getDependentFinancials(fstmt);
                form.setDependentFinancials(df);

                SpouseFinancials sf = getSpouseFinancials(fstmt);
                form.setSpouseFinancials(sf);

                BigDecimal spouseIncome = new BigDecimal(0);
                BigDecimal vetIncome = new BigDecimal(0);
                BigDecimal depIncome = new BigDecimal(0);
                BigDecimal spouseExpense = new BigDecimal(0);
                BigDecimal vetExpense = new BigDecimal(0);
                BigDecimal depExpense = new BigDecimal(0);
                BigDecimal spouseAsset = new BigDecimal(0);
                BigDecimal vetAsset = new BigDecimal(0);

                if (fstmt != null)
                {
                    Vector assets = new Vector();
                    Vector expenses = new Vector();
                    Vector incomes = new Vector();
                    Vector debts = new Vector();

                    vetAsset = getAssetInfo(fstmt, assets, getDisplayName(veteranName, FinancialInfoBean.VETERAN));
                    vetIncome = getIncomeInfo(fstmt, incomes, getDisplayName(veteranName, FinancialInfoBean.VETERAN));
                    vetExpense = getExpenseInfo(fstmt, expenses, getDisplayName(veteranName, FinancialInfoBean.VETERAN));
                    getDebtInfo(fstmt, debts, getDisplayName(veteranName, FinancialInfoBean.VETERAN));

                    if (sf != null)
                    {
                        Spouse s = sf.getReportedOn();
                        String spouseDisplayName;
                        if (s != null)
                        {
                            Name sn = s.getName();
                            spouseDisplayName = getDisplayName(sn, FinancialInfoBean.SPOUSE);
                        }
                        else
                        {
                            spouseDisplayName = FinancialInfoBean.SPOUSE;
                        }

                        spouseIncome = getIncomeInfo(sf, incomes, spouseDisplayName);
                        spouseAsset = getAssetInfo(sf, assets, spouseDisplayName);
                        spouseExpense = getExpenseInfo(sf, expenses, spouseDisplayName);
                        getDebtInfo(sf, debts, spouseDisplayName);
                    }

                    Iterator iter = fstmt.getDependentFinancials().iterator();
                    while (iter.hasNext())
                    {
                        DependentFinancials dfin = (DependentFinancials)iter.next();

                        Dependent d = dfin.getReportedOn();
                        String displayName;
                        if (d != null && d.getRelationship() != null)
                        {
                            Name dn = d.getName();
                            displayName = getDisplayName(dn, d.getRelationship().getDescription());
                        }
                        else
                        {
                            displayName = FinancialInfoBean.DEPENDENT;
                        }

                        // Populate the vectors
                        getAssetInfo(dfin, assets, displayName);
                        getIncomeInfo(dfin, incomes, displayName);
                        getExpenseInfo(dfin, expenses, displayName);
                        getDebtInfo(dfin, debts, displayName);
                    }

                    form.setAssets(assets);
                    form.setExpenses(expenses);
                    form.setIncomes(incomes);
                    form.setDebts(debts);
                }

                form.setCalculatedTotalIncome(calculateTotalIncome(
                    form, spouseIncome, vetIncome, depIncome, spouseExpense, vetExpense, depExpense));

                form.setCalculatedNetWorth(calculateNetWorth(form, spouseAsset, vetAsset));

                //PatientVisitSummary pvs = person.getPatientVisitSummary(incometest.getSiteConductingTest());
                //form.setPatientVisitSummary(pvs);

                getPatientVisitTotals(person, incomeyear, form);

                setStatuses(form, form.getIncometest());

                //get test completed date
                if (form.getIncometest().getIncomeTestStatus() != null)
                {
                    IncomeTestStatus st = form.getIncometest().getIncomeTestStatus();
                    if (st != null && st.getCompletedDate() != null)
                        form.setTestCompletedDate(JspUtils.displayValue(st.getCompletedDate(), JspUtils.STANDARD_DATE_FORMAT));
                }

                //test determined status
                if (form.getIncometest().getDeterminedStatus() != null)
                {
                    MeansTestStatus mt = form.getIncometest().getDeterminedStatus();
                    if (mt != null)
                        form.setTestDeterminedStatus(mt.getDescription());
                }

                //test status
                if (form.getIncometest().getStatus() != null)
                {
                    MeansTestStatus mt = form.getIncometest().getStatus();
                    if (mt != null)
                        form.setTestStatus(mt.getDescription());
                }

                //check if previous year
                if (fstmt != null)
                {
                    List list = getDependentInfoList(fstmt);
                    form.setDependentInfoList(list);
                }
            }

            //Display Beneficiary travel info even when income test is null
            Collection beneficiarytravel = person.getBeneficiaryTravels(incomeyear);
            form.setBeneficiaryTravels(beneficiarytravel);
            if (beneficiarytravel != null)
                form.setBeneficiaryTravelsSize(beneficiarytravel.size());

        }
        catch (Exception ex)
        {
            throw new ConversionServiceException("Unable to convert Person to FinancialsForm.", ex);
        }
    }

    /**
     * Converts the form data to the Person object.  Currently no implementation.
     *
     * @param form The form
     * @param person The person
     *
     * @throws ConversionServiceException If there were any problems converting the form to the person.
     */
    protected void convertFormToPerson(FinancialsForm form, Person person) throws ConversionServiceException
    {
    }

    public void setStatuses(FinancialsForm form, IncomeTest incometest)
    {
        Set statuses = incometest.getStatuses();
        if (statuses != null)
        {
            for (Iterator i = statuses.iterator(); i.hasNext();)
            {
                IncomeTestStatus status = (IncomeTestStatus)i.next();
                String statusDesc = null;
                if (status != null && status.getStatus() != null){
                	statusDesc = status.getStatus().getDescription();
                }
                if (isOfType(status, IncomeTestType.CODE_MEANS_TEST.getName()))
                {
                    form.setMeansTest(status);
                }
                else if (isOfType(status, IncomeTestType.CODE_CO_PAY_EXEMPTION_TEST.getName()))
                {
                    form.setRxCopayStatus(statusDesc);
                }
                else if (isOfType(status, IncomeTestType.CODE_LTC_CO_PAY_TEST.getName()))
                {
                    form.setLtcCopayStatus(statusDesc);
                }
                else if (isOfType(status, IncomeTestType.CODE_LTC_CO_PAY_EXEMPTION_TEST.getName()))
                {
                    form.setLtcCopayStatus(statusDesc);
                }
            }
        }
    }

    private boolean isOfType(IncomeTestStatus itStatus, String typeCode) {
        return itStatus != null && itStatus.getType() != null &&
            itStatus.getType().getCode().equalsIgnoreCase(typeCode);
    }

    public BigDecimal calculateTotalIncome(FinancialsForm form, BigDecimal spouseIncome, BigDecimal vetIncome,
        BigDecimal depIncome, BigDecimal spouseExpense, BigDecimal vetExpense, BigDecimal depExpense)
    {
        BigDecimal totalIncome = new BigDecimal(0);

        if (vetIncome != null)
            totalIncome = vetIncome.subtract(vetExpense);

        if (includeSpouseIncome(form))
            totalIncome = totalIncome.add(spouseIncome).subtract(spouseExpense);

        if (includeDependentIncome(form))
            totalIncome = totalIncome.add(depIncome).subtract(depExpense);

        return totalIncome;
    }

    public BigDecimal calculateNetWorth(FinancialsForm form, BigDecimal spouseAsset, BigDecimal vetAsset)
    {
        BigDecimal networth = new BigDecimal(0);
        BigDecimal vetDebt = new BigDecimal(0);
        BigDecimal spouseDebt = new BigDecimal(0);

        if (form.getFinancialStatement() != null && form.getFinancialStatement().getDebt() != null)
            vetDebt = form.getFinancialStatement().getDebt().getAmount();

        if (form.getSpouseFinancials() != null && form.getSpouseFinancials().getDebt() != null)
            spouseDebt = form.getSpouseFinancials().getDebt().getAmount();

        if (vetAsset != null)
            networth = vetAsset.subtract(vetDebt);

        if (includeSpouseIncome(form))
            networth = networth.add(spouseAsset).subtract(spouseDebt);

        return networth;
    }

    public boolean includeSpouseIncome(FinancialsForm form)
    {
        if (form.getFinancialStatement() == null || form.getSpouseFinancials() == null)
            return false;

        Boolean married = form.getFinancialStatement().getMarriedLastCalendarYear();
        Boolean livedWithPatient = form.getSpouseFinancials().getLivedWithPatient();

        if ((married != null && livedWithPatient != null) &&
            (married.booleanValue() && livedWithPatient.booleanValue()))
            return true;
        else
            return false;
    }

    public boolean includeDependentIncome(FinancialsForm form)
    {
        //TODO	Need the determine whether to use dep info based on the
        //determine dependents decision table which is based on dep Age, lived with patient ind,
        //veteran contributed to support in $, student ind.

        return false;
    }

    private BigDecimal getAssetInfo(FinancialInformation fstmt, Vector assets, String source)
    {
        BigDecimal total = new BigDecimal(0);
        if (fstmt == null || fstmt.getAssets() == null || fstmt.getAssets().values() == null)
            return total;

        for (Iterator i = fstmt.getAssets().values().iterator(); i.hasNext();)
        {
            Asset e = (Asset)i.next();
            if (e.getAmount() != null)
            {
                FinancialInfoBean bean = new FinancialInfoBean(e.getType().getDescription(),
                    e.getAmount(), source);
                assets.add(bean);
                total = total.add(e.getAmount());
            }
        }
        return total;
    }

    private BigDecimal getIncomeInfo(FinancialInformation fstmt, Vector income, String source)
    {
        BigDecimal total = new BigDecimal(0);
        if (fstmt == null || fstmt.getIncome() == null || fstmt.getIncome().values() == null)
            return total;

        for (Iterator i = fstmt.getIncome().values().iterator(); i.hasNext();)
        {
            Income e = (Income)i.next();
            if (e.getAmount() != null)
            {
                FinancialInfoBean bean = new FinancialInfoBean(e.getType().getDescription(),
                    e.getAmount(), source);
                income.add(bean);
                total = total.add(e.getAmount());
            }
        }
        return total;
    }

    private BigDecimal getExpenseInfo(FinancialInformation fstmt, Vector expenses, String source)
    {
        BigDecimal total = new BigDecimal(0);
        if (fstmt == null || fstmt.getExpenses() == null || fstmt.getExpenses().values() == null)
            return total;
        for (Iterator i = fstmt.getExpenses().values().iterator(); i.hasNext();)
        {
            Expense e = (Expense)i.next();
            if (e.getAmount() != null)
            {
                FinancialInfoBean bean = new FinancialInfoBean(e.getType().getDescription(),
                    e.getAmount(), source);
                expenses.add(bean);
                total = total.add(e.getAmount());
            }
        }
        return total;
    }

    private BigDecimal getDebtInfo(FinancialInformation fstmt, Vector debts, String source)
    {
        BigDecimal total = new BigDecimal(0);
        if (fstmt == null || fstmt.getDebt() == null)
            return total;

        if (fstmt.getDebt().getAmount() != null)
        {
            FinancialInfoBean bean = new FinancialInfoBean(null,
                fstmt.getDebt().getAmount(), source);
            debts.add(bean);
            total = total.add(fstmt.getDebt().getAmount());
        }

        return total;
    }

    /**
     * Check if the given FinancialStatement has a supporting FinancialInformation for Dependent.
     */
    private DependentFinancials getDependentFinancials(FinancialStatement financialStatement)
    {
        if (financialStatement == null || financialStatement.getDependentFinancials() == null)
            return null;
        Iterator iter = financialStatement.getDependentFinancials().iterator();
        while (iter.hasNext())
        {
            Object obj = iter.next();
            if (obj instanceof DependentFinancials)
            {
                return (DependentFinancials)obj;
            }
        }
        return null;
    }

    /**
     * Check if the given FinancialStatement has a supporting FinancialInformation for spouse.
     */
    private SpouseFinancials getSpouseFinancials(FinancialStatement financialStatement)
    {
        if (financialStatement == null || financialStatement.getSpouseFinancials() == null)
            return null;
        return financialStatement.getActiveSpouseFinancials();
    }

    private void getPatientVisitTotals(Person person, Integer incomeyear, FinancialsForm form)
    {
        int totalInpatientDays = 0;
        int totalOutpatientVisits = 0;
        if (person.getPatientVisitSummaries() != null && !person.getPatientVisitSummaries().isEmpty())
        {
            for (Iterator i = person.getPatientVisitSummaries().values().iterator(); i.hasNext();)
            {
                PatientVisitSummary e = (PatientVisitSummary)i.next();
                if (e.getIncomeYear() != null && e.getIncomeYear().equals(incomeyear))
                {
                    if (e.getInpatientDays() != null)
                        totalInpatientDays = totalInpatientDays + e.getInpatientDays().intValue();
                    if (e.getOutpatientDays() != null)
                        totalOutpatientVisits = totalOutpatientVisits + e.getOutpatientDays().intValue();
                }
            }
        }
        form.setTotalInpatientDays(new Integer(totalInpatientDays));
        form.setTotalOutpatientVisits(new Integer(totalOutpatientVisits));
    }

    private String getDisplayName(Name name, String source)
    {
        // Default the name to just the source
        String nameString = source;

        if (name != null)
        {
            // We have a name so format it with the source appended to the end
            nameString = NameHelper.formatNameFirstNameFirst(name);
            if ((StringUtils.isNotBlank(nameString)) && (StringUtils.isNotBlank(source)))
            {
                nameString = nameString + ", " + source.trim();
            }
        }

        // Return the name
        return nameString;
    }
}