/*
 * The Kuali Financial System, a comprehensive financial management system for higher education.
 *
 * Copyright 2005-2016 The Kuali Foundation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.kfs.gl.batch;

import org.apache.commons.io.filefilter.RegexFileFilter;
import org.kuali.kfs.gl.GeneralLedgerConstants;
import org.kuali.kfs.gl.batch.service.impl.OriginEntryFileIterator;
import org.kuali.kfs.gl.businessobject.GlSummary;
import org.kuali.kfs.gl.businessobject.OriginEntryInformation;
import org.kuali.kfs.gl.businessobject.Reversal;
import org.kuali.kfs.gl.report.PosterOutputSummaryReport;
import org.kuali.kfs.gl.service.BalanceService;
import org.kuali.kfs.gl.service.ReversalService;
import org.kuali.kfs.sys.FileUtil;
import org.kuali.kfs.sys.batch.AbstractWrappedBatchStep;
import org.kuali.kfs.sys.batch.service.WrappedBatchExecutorService.CustomBatchExecutor;
import org.kuali.kfs.sys.batch.service.WrappingBatchService;
import org.kuali.kfs.sys.businessobject.SystemOptions;
import org.kuali.kfs.sys.service.FiscalYearAwareReportWriterService;
import org.kuali.kfs.sys.service.OptionsService;
import org.kuali.kfs.sys.service.ReportWriterService;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * A step to generate summary reports from a recent poster run
 */
public class PosterSummaryReportStep extends AbstractWrappedBatchStep {
    private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PosterSummaryReportStep.class);
    private static final String DATE_FORMAT = "MMdd";
    private static final String BUD = "bud";
    private static final String ACT = "act";
    private static final String ENC = "enc";
    private OptionsService optionsService;
    private ReportWriterService posterOutputSummaryReportWriterService;
    private FiscalYearAwareReportWriterService posterActualBalanceSummaryReportWriterService;
    private FiscalYearAwareReportWriterService posterBudgetBalanceSummaryReportWriterService;
    private FiscalYearAwareReportWriterService posterEncumbranceSummaryReportWriterService;
    private ReversalService reversalService;
    private BalanceService balanceService;
    private String batchFileDirectoryName;

    /**
     * @see org.kuali.kfs.sys.batch.AbstractWrappedBatchStep#getCustomBatchExecutor()
     */
    @Override
    protected CustomBatchExecutor getCustomBatchExecutor() {
        return new CustomBatchExecutor() {
            /**
             * Runs the process that generates poster summary reports.
             *
             * @return true if the step completed successfully, false if otherwise
             * @see org.kuali.kfs.sys.batch.Step#execute(java.lang.String)
             */
            public boolean execute() {
                synchronized (this) { // why the synchronization?
                    final String CURRENT_YEAR_LOWER = getCurrentYearLowerParameter();
                    final String CURRENT_YEAR_UPPER = getCurrentYearUpperParameter();
                    final String CURRENT_AND_LAST_YEAR = getCurrentAndLastYearParameter();

                    SystemOptions currentYear = optionsService.getCurrentYearOptions();
                    SystemOptions nextYear = optionsService.getOptions(currentYear.getUniversityFiscalYear() + 1);
                    SystemOptions previousYear = optionsService.getOptions(currentYear.getUniversityFiscalYear() - 1);

                    Date runDate = getDateTimeService().getCurrentDate();
                    SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
                    String md = sdf.format(runDate);
                    generatePosterOutputReport(runDate);
                    if ((md.compareTo(CURRENT_YEAR_UPPER) > 0) || (md.compareTo(CURRENT_YEAR_LOWER) < 0)) {
                        // Current year
                        generateGlSummary(runDate, currentYear, BUD);
                        generateGlSummary(runDate, currentYear, ACT);
                        generateGlSummary(runDate, currentYear, ENC);
                    } else if ((md.compareTo(CURRENT_AND_LAST_YEAR) > 0)) {
                        // Current year and Last year
                        generateGlSummary(runDate, currentYear, BUD);
                        generateGlSummary(runDate, previousYear, BUD);
                        generateGlSummary(runDate, currentYear, ACT);
                        generateGlSummary(runDate, previousYear, ACT);
                        generateGlSummary(runDate, currentYear, ENC);
                        generateGlSummary(runDate, previousYear, ENC);
                    } else {
                        // Current year and next year
                        generateGlSummary(runDate, currentYear, BUD);
                        generateGlSummary(runDate, nextYear, BUD);
                        generateGlSummary(runDate, currentYear, ACT);
                        generateGlSummary(runDate, nextYear, ACT);
                        generateGlSummary(runDate, currentYear, ENC);
                        generateGlSummary(runDate, nextYear, ENC);
                    }
                }
                return true;
            }
        };
    }

    /**
     * Generates reports about the output of a poster run.
     *
     * @param runDate the date the poster was run.
     */
    private void generatePosterOutputReport(Date runDate) {
        PosterOutputSummaryReport posterOutputSummaryReport = new PosterOutputSummaryReport();

        // summarize all the entries for the main poster
        File mainPosterFile = FileUtil.getNewestFile(new File(batchFileDirectoryName), new RegexFileFilter((GeneralLedgerConstants.BatchFileSystem.POSTER_INPUT_FILE + "\\.[0-9_\\-]+\\" + GeneralLedgerConstants.BatchFileSystem.EXTENSION)));
        if (mainPosterFile != null && mainPosterFile.exists()) {
            OriginEntryFileIterator mainPosterIterator = new OriginEntryFileIterator(mainPosterFile);
            while (mainPosterIterator.hasNext()) {
                final OriginEntryInformation originEntry = mainPosterIterator.next();
                posterOutputSummaryReport.summarize(originEntry);
            }
        } else {
            LOG.warn("Could not Main Poster Input file with prefix " + GeneralLedgerConstants.BatchFileSystem.POSTER_INPUT_FILE + " for tabulation in the Poster Output Summary Report");
        }

        // summarize today's reversals
        File reversalPosterFile = FileUtil.getNewestFile(new File(batchFileDirectoryName), new RegexFileFilter((GeneralLedgerConstants.BatchFileSystem.REVERSAL_POSTER_VALID_OUTPUT_FILE + "\\.[0-9_\\-]+\\" + GeneralLedgerConstants.BatchFileSystem.EXTENSION)));
        if (reversalPosterFile != null && reversalPosterFile.exists()) {
            OriginEntryFileIterator reversalOriginEntryIterator = new OriginEntryFileIterator(reversalPosterFile);
            while (reversalOriginEntryIterator.hasNext()) {
                final Reversal reversal = new Reversal(reversalOriginEntryIterator.next());
                posterOutputSummaryReport.summarize(reversal);
            }
        } else {
            LOG.warn("Could not Reversal Output file with prefix " + GeneralLedgerConstants.BatchFileSystem.REVERSAL_POSTER_VALID_OUTPUT_FILE + " for tabulation in the Poster Output Summary Report");
        }

        // summarize the icr poster
        File icrPosterFile = FileUtil.getNewestFile(new File(batchFileDirectoryName), new RegexFileFilter(GeneralLedgerConstants.BatchFileSystem.ICR_POSTER_INPUT_FILE + "\\.[0-9_\\-]+\\" + GeneralLedgerConstants.BatchFileSystem.EXTENSION));
        if (icrPosterFile != null && icrPosterFile.exists()) {
            OriginEntryFileIterator icrIterator = new OriginEntryFileIterator(icrPosterFile);
            while (icrIterator.hasNext()) {
                final OriginEntryInformation originEntry = icrIterator.next();
                posterOutputSummaryReport.summarize(originEntry);
            }
        } else {
            LOG.warn("Could not Indirect Cost Recovery Poster Input file with prefix " + GeneralLedgerConstants.BatchFileSystem.ICR_POSTER_INPUT_FILE + " for tabulation in the Poster Output Summary Report");
        }

        posterOutputSummaryReport.writeReport(posterOutputSummaryReportWriterService);
    }

    /**
     * Generates the GL Summary report
     *
     * @param runDate    the run date of the poster service that should be reported
     * @param options    the options of the fiscal year the poster was run
     * @param reportType the type of the report that should be generated
     */
    protected void generateGlSummary(Date runDate, SystemOptions year, String reportType) {
        LOG.debug("generateGlSummary() started");

        FiscalYearAwareReportWriterService fiscalYearAwareReportWriterService;

        List<String> balanceTypeCodes = new ArrayList<String>();
        if (ACT.equals(reportType)) {
            fiscalYearAwareReportWriterService = posterActualBalanceSummaryReportWriterService;
            balanceTypeCodes.add(year.getActualFinancialBalanceTypeCd());
        } else if (BUD.equals(reportType)) {
            fiscalYearAwareReportWriterService = posterBudgetBalanceSummaryReportWriterService;
            balanceTypeCodes.add(year.getBudgetCheckingBalanceTypeCd());
            balanceTypeCodes.add(year.getBaseBudgetFinancialBalanceTypeCd());
            balanceTypeCodes.add(year.getMonthlyBudgetFinancialBalanceTypeCd());
        } else { // ENC
            fiscalYearAwareReportWriterService = posterEncumbranceSummaryReportWriterService;
            balanceTypeCodes.add(year.getExtrnlEncumFinBalanceTypCd());
            balanceTypeCodes.add(year.getIntrnlEncumFinBalanceTypCd());
            balanceTypeCodes.add(year.getPreencumbranceFinBalTypeCd());
            balanceTypeCodes.add(year.getCostShareEncumbranceBalanceTypeCd());
        }

        List<GlSummary> balances = balanceService.getGlSummary(year.getUniversityFiscalYear(), balanceTypeCodes);

        GlSummary totals = new GlSummary();
        for (GlSummary balance : balances) {
            totals.add(balance);
        }
        totals.setFundGroup("Total");

        try {
            fiscalYearAwareReportWriterService.setFiscalYear(year.getUniversityFiscalYear());
            ((WrappingBatchService) fiscalYearAwareReportWriterService).initialize();

            fiscalYearAwareReportWriterService.writeSubTitle("Balance Type of " + balanceTypeCodes + " for Fiscal Year " + year.getUniversityFiscalYearName());
            fiscalYearAwareReportWriterService.writeNewLines(1);

            fiscalYearAwareReportWriterService.writeTableRowSeparationLine(totals);
            fiscalYearAwareReportWriterService.writeTable(balances, true, false);

            fiscalYearAwareReportWriterService.writeTableRowSeparationLine(totals);
            fiscalYearAwareReportWriterService.writeTableRow(totals);
        } finally {
            ((WrappingBatchService) fiscalYearAwareReportWriterService).destroy();
        }
    }

    /**
     * @return current year lower parameter for inner class
     */
    public String getCurrentYearLowerParameter() {
        return getParameterService().getParameterValueAsString(getClass(), GeneralLedgerConstants.GlSummaryReport.CURRENT_YEAR_LOWER);
    }

    /**
     * @return current year upper parameter for inner class
     */
    public String getCurrentYearUpperParameter() {
        return getParameterService().getParameterValueAsString(PosterSummaryReportStep.this.getClass(), GeneralLedgerConstants.GlSummaryReport.CURRENT_YEAR_UPPER);
    }

    /**
     * @return current and last year parameter for inner class
     */
    public String getCurrentAndLastYearParameter() {
        return getParameterService().getParameterValueAsString(PosterSummaryReportStep.this.getClass(), GeneralLedgerConstants.GlSummaryReport.CURRENT_AND_LAST_YEAR);
    }

    /**
     * Sets the optionsService attribute, allowing the injection of an implementation of that service
     *
     * @param os the optionsService implementation to set
     * @see org.kuali.kfs.sys.service.OptionsService
     */
    public void setOptionsService(OptionsService os) {
        optionsService = os;
    }

    /**
     * Gets the posterOutputSummaryReportWriterService attribute.
     *
     * @return Returns the posterOutputSummaryReportWriterService.
     */
    public ReportWriterService getPosterOutputSummaryReportWriterService() {
        return posterOutputSummaryReportWriterService;
    }

    /**
     * Sets the posterOutputSummaryReportWriterService attribute value.
     *
     * @param posterOutputSummaryReportWriterService The posterOutputSummaryReportWriterService to set.
     */
    public void setPosterOutputSummaryReportWriterService(ReportWriterService posterOutputSummaryReportWriterService) {
        this.posterOutputSummaryReportWriterService = posterOutputSummaryReportWriterService;
    }

    /**
     * Sets the posterActualBalanceSummaryReportWriterService attribute value.
     *
     * @param posterActualBalanceSummaryReportWriterService The posterActualBalanceSummaryReportWriterService to set.
     */
    public void setPosterActualBalanceSummaryReportWriterService(FiscalYearAwareReportWriterService posterActualBalanceSummaryReportWriterService) {
        this.posterActualBalanceSummaryReportWriterService = posterActualBalanceSummaryReportWriterService;
    }

    /**
     * Sets the posterBudgetBalanceSummaryReportWriterService attribute value.
     *
     * @param posterBudgetBalanceSummaryReportWriterService The posterBudgetBalanceSummaryReportWriterService to set.
     */
    public void setPosterBudgetBalanceSummaryReportWriterService(FiscalYearAwareReportWriterService posterBudgetBalanceSummaryReportWriterService) {
        this.posterBudgetBalanceSummaryReportWriterService = posterBudgetBalanceSummaryReportWriterService;
    }

    /**
     * Sets the posterEncumbranceSummaryReportWriterService attribute value.
     *
     * @param posterEncumbranceSummaryReportWriterService The posterEncumbranceSummaryReportWriterService to set.
     */
    public void setPosterEncumbranceSummaryReportWriterService(FiscalYearAwareReportWriterService posterEncumbranceSummaryReportWriterService) {
        this.posterEncumbranceSummaryReportWriterService = posterEncumbranceSummaryReportWriterService;
    }

    /**
     * Gets the reversalService attribute.
     *
     * @return Returns the reversalService.
     */
    public ReversalService getReversalService() {
        return reversalService;
    }

    /**
     * Sets the reversalService attribute value.
     *
     * @param reversalService The reversalService to set.
     */
    public void setReversalService(ReversalService reversalService) {
        this.reversalService = reversalService;
    }

    /**
     * Sets the balanceService attribute value.
     *
     * @param balanceService The balanceService to set.
     */
    public void setBalanceService(BalanceService balanceService) {
        this.balanceService = balanceService;
    }

    /**
     * Gets the batchFileDirectoryName attribute.
     *
     * @return Returns the batchFileDirectoryName.
     */
    public String getBatchFileDirectoryName() {
        return batchFileDirectoryName;
    }

    /**
     * Sets the batchFileDirectoryName attribute value.
     *
     * @param batchFileDirectoryName The batchFileDirectoryName to set.
     */
    public void setBatchFileDirectoryName(String batchFileDirectoryName) {
        this.batchFileDirectoryName = batchFileDirectoryName;
    }
}
