package com.fitbank.installment;

import java.math.BigDecimal;
import java.sql.Date;

import com.fitbank.common.ApplicationDates;
import com.fitbank.common.Helper;
import com.fitbank.common.exception.FitbankException;
import com.fitbank.common.helper.CalculationBase;
import com.fitbank.common.helper.Constant;
import com.fitbank.common.helper.Dates;
import com.fitbank.fin.helper.FinancialHelper;
import com.fitbank.hb.persistence.acco.Taccount;
import com.fitbank.hb.persistence.acco.TaccountKey;
import com.fitbank.hb.persistence.gene.Tcurrencyid;
import com.fitbank.hb.persistence.gene.Tfrecuencyid;

/**
 * Genera tabla de amortización con frecuencia de capital diferente a la
 * frecuencia de interés.
 * 
 * @author Soft WareHouse S.A.
 */
public class SpecialGradualInstallment extends AbstractQuota {

    /**
     * Número de cuotas de capital.
     */
    protected int totalCapitalPeriod = 0;
    /**
     * Périodos con capital.
     */
    protected int capitalPeriod = 0;
    /**
     * Número de cuotas de interés.
     */
    protected int totalInterestPeriod = 0;

    protected Date originalInicialDate;

    protected BigDecimal quotaCapital;

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.fitbank.installment.AbstractQuota#calculate(com.fitbank.installment
     * .InstallmentTable)
     */
    public void calculate(InstallmentTable pQuotaTable) throws Exception {
        this.quotaTable = pQuotaTable;
        this.quotanumber = pQuotaTable.getBegincalculationperiod();
        this.reducedcapital = pQuotaTable.getAmount();
        this.generateTable();
    }

    /**
     * Método que genera la tabla de amortización.
     * 
     * @throws Exception
     */
    private void generateTable() throws Exception {
        // Genera las cuotas de capital e interes
        boolean end = false;
        this.fillCapitalQuotas();
        int capitalQuotaCounter = 0;
        BigDecimal capitalZero = Constant.BD_ZERO;
        originalInicialDate = quotaTable.getFirstpaymetdate();
        this.calculateFistpaymentdate(quotaTable);
        for (int i = this.quotaTable.getBegincalculationperiod(); i <= totalInterestPeriod; i++) {
            capitalQuotaCounter = capitalQuotaCounter + 1;
            super.calculatePayDate(this.quotaTable);
            if (i == totalInterestPeriod) {
                end = true;
            }
            super.processByCategory(false);
            if (this.capitalPeriod == capitalQuotaCounter
                    || (originalInicialDate != null && originalInicialDate
                            .compareTo(super.getNextpaydate()) == 0)) {
                if (originalInicialDate == null) {
                    this.calculateCuota(quotaCapital, end);
                    capitalQuotaCounter = 0;
                } else if (originalInicialDate
                        .compareTo(super.getNextpaydate()) == 0) {
                    this.calculateCuota(quotaCapital, end);
                    capitalQuotaCounter = 0;
                    originalInicialDate = null;
                } else {
                    this.calculateCuota(capitalZero, end);
                }
            } else {
                this.calculateCuota(capitalZero, end);
            }
            this.quotanumber++;
        }// end for
    }

    /**
     * Método que valida que la frecuencia de capital sea un múltiplo de la
     * frecuencia de interés, obtiene el valor de capital a cobrar en cada cuota
     * que coincida con la frecuencia de capital.
     * 
     * @throws Exception
     */
    protected void fillCapitalQuotas() throws Exception {
        // Obtiene la moneda de la tabla de pagos
        Tcurrencyid tcurrencyid = FinancialHelper.getInstance().getTcurrencyid(
                this.quotaTable.getCurrency());
        // Define el total de cuotas de capital de la tabla de pagos
        Tfrecuencyid capitalFrequencyId = FinancialHelper.getInstance()
                .getTfrecuencyid(quotaTable.getCapitalFrequency());
        if (this.quotaTable.getAccountnumber() != null) {
            TaccountKey key = new TaccountKey(
                    this.quotaTable.getAccountnumber(),
                    ApplicationDates.DEFAULT_EXPIRY_TIMESTAMP,
                    this.quotaTable.getCompany());
            Taccount taccount = (Taccount) Helper.getBean(Taccount.class, key);
            if (taccount != null) {
                Dates openingDate = new Dates(taccount.getFapertura(),
                        CalculationBase.B365365);
                Dates accountingDate = new Dates(
                        this.quotaTable.getAccountingdate(),
                        CalculationBase.B365365);
                if (capitalFrequencyId != null
                        && capitalFrequencyId.getNumerodias() != null) {
                    int aux = this.quotaTable.getTerm()
                            - (accountingDate.substract(openingDate));
                    if (aux <= capitalFrequencyId.getNumerodias()) {
                        this.totalCapitalPeriod = 1;
                    } else {
                        this.totalCapitalPeriod = (this.quotaTable.getTerm() - (accountingDate
                                .substract(openingDate)))
                                / capitalFrequencyId.getNumerodias();
                    }
                } else {
                    this.totalCapitalPeriod = this.quotaTable.getTotalperiod();
                }
            }
        } else {
            if (capitalFrequencyId != null
                    && capitalFrequencyId.getNumerodias() != null) {
                this.totalCapitalPeriod = this.quotaTable.getTerm()
                        / capitalFrequencyId.getNumerodias();
            } else {
                this.totalCapitalPeriod = this.quotaTable.getTotalperiod();
            }
        }
        // Define el total de cuotas de interes de la tabla de pagos
        Tfrecuencyid interestFrequencyId = FinancialHelper.getInstance()
                .getTfrecuencyid(quotaTable.getIntrestFrequency());
        if (interestFrequencyId.getNumerodias().compareTo(
                capitalFrequencyId.getNumerodias()) > 0) {
            throw new FitbankException("QUO010",
                    "FRECUENCIA DE INTERES TIENE QUE SER MENOR O IGUAL A LA FRECUENCIA DE CAPITAL.");
        }
        if (capitalFrequencyId.getNumerodias()
                % interestFrequencyId.getNumerodias() != 0) {
            throw new FitbankException("QUO011",
                    "FRECUENCIA DE CAPITAL TIENE QUE SER MULTIPLO DE LA FRECUENCIA DE INTERES.");
        }
        super.interestFrquencyDays = interestFrequencyId.getNumerodias();
        if (interestFrequencyId != null
                && interestFrequencyId.getNumerodias() != null) {
            this.totalInterestPeriod = this.quotaTable.getTerm()
                    / interestFrequencyId.getNumerodias();
        } else {
            this.totalInterestPeriod = this.quotaTable.getTotalperiod();
        }
        if (capitalFrequencyId.getNumerodias().compareTo(
                interestFrequencyId.getNumerodias()) > 0) {
            this.capitalPeriod = capitalFrequencyId.getNumerodias()
                    / interestFrequencyId.getNumerodias();
        } else {
            this.capitalPeriod = 1;
        }
        // Obtiene el valor fijo de capital de cada cuota
        quotaCapital = this.quotaTable.getAmount().divide(
                new BigDecimal(this.totalCapitalPeriod),
                tcurrencyid.getNumerodecimales(), BigDecimal.ROUND_HALF_UP);
    }

    /**
     * Calcula tabla de pago dado una cuota fija.
     * 
     * @throws Exception
     */
    private void calculateCuota(BigDecimal pCapital, boolean pEnd)
            throws Exception {
        // Obtiene los valores de interes y comision para cada categoria
        // Obtiene el valor de interes y comision de la cuota
        // Si se trata de la ultima cuota
        BigDecimal capital = pCapital;
        if (pEnd) {
            // Carga todo el capital reducido al capital de la cuota
            capital = this.reducedcapital;
            this.reducedcapital = this.reducedcapital.subtract(capital);
        }
        super.addQuota(this.quotanumber, this.reducedcapital, capital, true);
        if (!pEnd) {
            if (capital.compareTo(this.reducedcapital) > 0) {
                capital = this.reducedcapital;
            }
            this.reducedcapital = this.reducedcapital.subtract(capital);
        }
    }

    /**
     * Calcua la fecha de inicio de pagos.
     * 
     * @param pQuotaTable
     * @throws Exception
     */
    protected void calculateFistpaymentdate(InstallmentTable pQuotaTable)
            throws Exception {
        if (pQuotaTable.getFirstpaymetdate() == null) {
            return;
        }
        Dates dates = null;
        Date date = null;
        int substractQuotas = capitalPeriod * -1;
        if (pQuotaTable.isFixday()) {
            dates = new Dates(pQuotaTable.getFirstpaymetdate(),
                    CalculationBase.B360360);
        } else {
            dates = new Dates(pQuotaTable.getFirstpaymetdate(),
                    pQuotaTable.getCalculationBase());
        }
        while (dates.getDate().compareTo(pQuotaTable.getAccountingdate()) > 0) {
            date = dates.getDate();
            dates.addDaysBased(pQuotaTable.getDaysperperiod() * -1,
                    pQuotaTable.getPayday());
            substractQuotas++;
        }
        if (date != null) {
            pQuotaTable.setFirstpaymetdate(date);
            quotaTable.setTotalperiod(quotaTable.getTotalperiod()
                    + substractQuotas);
        }
    }
}
