/*
 * Created on Sep 16, 2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package gov.va.med.esr.common.batchprocess;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;

import gov.va.med.esr.common.builder.comms.DocumentBuilder;
import gov.va.med.esr.common.builder.comms.EnrollTransmission;
import gov.va.med.esr.common.builder.comms.GeneralTransmission;
import gov.va.med.esr.common.builder.comms.GenerateValues;
import gov.va.med.esr.common.builder.comms.ITransmission;
import gov.va.med.esr.common.builder.comms.InsufficientDataException;
import gov.va.med.esr.common.builder.comms.SsnTransmission;
import gov.va.med.esr.common.infra.ImpreciseDate;
import gov.va.med.esr.common.model.comms.AacLetterRequest;
import gov.va.med.esr.common.model.comms.CommsLogEntry;
import gov.va.med.esr.common.model.comms.CommsTemplate;
import gov.va.med.esr.common.model.comms.CommsTransByFormNumber;
import gov.va.med.esr.common.model.comms.CommsTransLog;
import gov.va.med.esr.common.model.comms.ExportSnapshot;
import gov.va.med.esr.common.model.financials.IncomeTest;
import gov.va.med.esr.common.model.lookup.ComLetterTemplateType;
import gov.va.med.esr.common.model.lookup.ComMailingStatusType;
import gov.va.med.esr.common.model.lookup.ComMailingTriggerType;
import gov.va.med.esr.common.model.lookup.MessageType;
import gov.va.med.esr.common.model.lookup.NameType;
import gov.va.med.esr.common.model.person.Association;
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.id.PersonIdEntityKeyImpl;
import gov.va.med.esr.common.persistent.comms.AacLetterRequestDAO;
import gov.va.med.esr.common.persistent.comms.CommsLogEntryDAO;
import gov.va.med.esr.common.persistent.comms.CommsTemplateDAO;
import gov.va.med.esr.common.persistent.comms.CommsTransLogDAO;
import gov.va.med.esr.common.persistent.comms.ExportFileDAO;
import gov.va.med.esr.common.persistent.comms.ExportSnapshotDAO;
import gov.va.med.esr.common.util.CommsLetterConstants;
import gov.va.med.esr.common.util.LetterFormatHelper;
import gov.va.med.esr.service.CommsEmailBulletinService;
import gov.va.med.esr.service.CommsLetterRequestService;
import gov.va.med.esr.service.EligibilityEnrollmentService;
import gov.va.med.esr.service.FinancialsHelperService;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.PersonHelperService;
import gov.va.med.esr.service.impl.AbstractRuleAwareServiceImpl;
import gov.va.med.esr.service.trigger.BulletinTrigger;
import gov.va.med.esr.service.trigger.LetterTriggerEvent;
import gov.va.med.fw.batchprocess.DataProcessExecutionContext;
import gov.va.med.fw.io.writer.FormattedFileWriter;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.service.AbstractComponent;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.DateUtils;

/**
 * @author DNS   TSAIG
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class AacExportCommandImpl extends AbstractRuleAwareServiceImpl implements AacExportCommand{

    /**
     * 
     */
    private static final long serialVersionUID = 1203810770916319684L;
    //--------------------------------------------------------------------
    // Static variable(s)
    //--------------------------------------------------------------------
    //private static int MAX_PRINT_REQUEST = 5000;


    //COMMS MAILING STATUS
    private static ComMailingStatusType SENT_TO_AAC_STATUS = null;
    final private static String ZERO_SIZE_FILE_NAME = "     N/A     "; //with some padding

    private static String EXPORT_TRANS_TYPE_CODE = "COMREQ-A";
    private static MessageType EXPORT_TRANSMISSION_TYPE = null;
    private static int DEFAULT_BATCH_SIZE = 1000;
    private final String BATCH_SIZE_ARG = "-size=";
    private static final String PROCESS_START_DATE_ARG = "-from=";
    
    private CommsLetterRequestService letterReqService = null;
    private EligibilityEnrollmentService eligibilityEnrollmentService;
    private CommsTransLogDAO transDAO = null;
    private CommsLogEntryDAO commsLogDAO = null;
    private CommsTemplateDAO templateDAO;
    private ExportSnapshotDAO snapShotDAO = null;

    private ExportFileDAO fileDAO = null;
    private AacLetterRequestDAO requestDAO = null;
    private PersonHelperService personHelperService;

    private FormattedFileWriter fileWriter;
    
    private int batchSize = DEFAULT_BATCH_SIZE;
    private Date requestStartDate = null;

    public AacExportCommandImpl()
    {
        super();
    }

    public AacExportCommandImpl(AbstractComponent component) throws ServiceException
    {
    }
    /**
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    public void afterPropertiesSet() throws Exception {
        super.afterPropertiesSet();

        Validate.notNull(this.commsLogDAO, "A Comms Log Entry DAO must be configured");
        Validate.notNull(this.requestDAO, "An AAC Letter Request DAO must be configured");
        Validate.notNull(this.transDAO, "An Comms Transmission Log DAO must be configured");
        Validate.notNull(this.snapShotDAO, "An Comms Export Snapshot DAO must be configured");
        Validate.notNull(this.eligibilityEnrollmentService, "An eligibilityEnrollmentService must be configured");
        Validate.notNull(this.templateDAO, "A templateDAO must be configured");
        Validate.notNull(this.personHelperService, "A personHelperService must be configured");
    }


    private void printUsage()
    {

        System.out.println("USAGE:");
//      System.out.println("   ExportFileCommand [-? | -A | -D | -B | -P ]");
        System.out.println("   ExportFileCommand [-? | -B | -P ]");
        System.out.println("   Options");
        System.out.println("     -? Display this help message");
//      System.out.println("     -A delete all print/cancel requests");
//      System.out.println("     -D delete last print/cancel requests");
        System.out.println("     -B process and delete the processed print/cancel requests afterwards");
        System.out.println("     -P process only, default");
        System.out.println("     ");
        System.out.println("The default is just process the export batch, no delete.");
    }

    public CommsExportStatistics execute(String[] args, DataProcessExecutionContext context, InitiateAACLetterExportProcess process) throws Exception
    {
        String option = null;
        requestStartDate = null;
        
        if (args == null || args.length == 0)
        {
            option = "-P"; //default
        }
        else
        {
            for ( int i=0; i< args.length; i++)
            {
                String currArg = args[i];
                if ( currArg.startsWith(BATCH_SIZE_ARG))
                {
                    try
                    {
                        String reqPerBatchSize = currArg.substring(BATCH_SIZE_ARG.length());
                        batchSize = new Integer(reqPerBatchSize).intValue();
                    }
                    catch(Exception e)
                    {
                        // if there is an exception igonre it we will continue
                        // with default batchSize
                        logger.error("Exception to Parse the Args :" +  e);
                    }
                }
                else if(currArg.startsWith(PROCESS_START_DATE_ARG))
                {
                	try
                    {
                        String requestStartDateStr = currArg.substring(PROCESS_START_DATE_ARG.length());
                        requestStartDate = new ImpreciseDate(requestStartDateStr).getDate();
                    }
                    catch(Exception e)
                    {
                        logger.error("Exception to Parse the Args :" +  e);
                    }
                }
                else
                {
                    option = args[0].trim().toUpperCase();
                }


            }
        }
        
        if (option == null || args == null || args.length == 0)
        {
            option = "-P"; //default
        }
        

        if (option.toUpperCase(Locale.ENGLISH).equals("-?"))
        {
            printUsage();
            return null;
        }

        Boolean cleanUp = new Boolean(true); //clean up after processing

        //NOT IMPLEMENTED (need more thoughts): process option -A (delete all) and -D (delete last)
        boolean done = false;

        if(logger.isDebugEnabled()) {
            logger.debug("executing with option option: " + option);
        }
        
        done = preProcess(option, cleanUp);

        if(logger.isDebugEnabled()) {
            logger.debug("Completed preprocess: " + done);
        }

        
        if (done)
            return null;

        //start the real process
        CommsExportStatistics stats = new CommsExportStatistics();
        stats.setStartTime(new Date());

        //get the snap shot of the print request, cancel request, and event log
        //each Id is the last entry of the table when the snap shot were taken.
        //The batch process will not process any entries with id greater than these
        //snap shot ids. This enable the entries to continue to be added even after
        //the batch process starts.

        //getSnapshots();

        ExportSnapshot currSnapShot = snapShotDAO.getCurrentSnapshot();
        ExportSnapshot prevSnapShot = null;
        
        //if requests start date is supplied, then find the start request id
        if(requestStartDate == null)
        {	
        	prevSnapShot = snapShotDAO.getPreviousSnapshot();
        }
        else
        {
        	prevSnapShot = snapShotDAO.getFirstSnapshotOnDate(requestStartDate);
        }
        
        
        long currPrintRequestId = getPrintRequestId(currSnapShot);
        long prevPrintRequestId = getPrintRequestId(prevSnapShot);
        
        
        if(logger.isDebugEnabled()) {
            logger.debug("currPrintRequestId: " + currPrintRequestId);
            logger.debug("prevPrintRequestId: " + prevPrintRequestId);
        }
        
        if ( currPrintRequestId == 0 ||
                currPrintRequestId <= prevPrintRequestId)
        {
            //no entry, update Comms Trans Log and stop the batch process
            stats.setFileName(null);
            updateCommsTransLog(stats);
            return stats;
        }

        init();

        
        
        // preserve the original curr and prev requestids
        long startReqId = prevPrintRequestId;
        long endReqId   = currPrintRequestId;
        long lastProcessedReqId = startReqId;
        StringBuffer totalAacLetterText = new StringBuffer();
        String aacExtractId = fileDAO.getAacExtractId();
        
        
        stats.setFileName(fileDAO.getAacFileName(aacExtractId));
        stats.setOriginalFileName(fileDAO.getOriginalAacFileName(aacExtractId));
        
        while ( startReqId < endReqId)
        {
            List requests = 
                    requestDAO.findAacLetterRequestBetweenRequestIdsByBatch(new BigDecimal(startReqId), new BigDecimal(endReqId),batchSize);
            
            startReqId = executeBatch(context,process,currSnapShot,requests, stats, lastProcessedReqId, totalAacLetterText,aacExtractId);
            
            if ( context.isInterrupted()  )
            {
               //all the house keeping work has been done in the executeBatch for interruped so just beak here.
                break;
            }
        }
        
        //only insert snapshot with the currPrintRequestId if job is not interrupted
        if (!process.isInterrupted(context))
        {
            //insert as a record of snapshot history
            snapShotDAO.insert(currSnapShot);
        }

        //save the text to file
        //CCR 7157: save it only if it is not zero length 
        if ( stats.getNumberSent() <=  0 )
        {
            //since there is no file, set the file name to be zero size file name
            stats.setFileName(ZERO_SIZE_FILE_NAME);
        }
        
        //Now change the .tmp file to .ltr file
        try {
            fileDAO.changeTmpFilesToLtr();
        } catch (IOException e) {
            logger.error("Exception occured while changing the files from .tmp to .ltr ", e);
        }
        
        stats.setEndTime(new Date());

        updateCommsTransLog(stats);

        //Send email to email bulletin
        sendEmailNotification(stats);

        return stats;
    }

    
    public long executeBatch(DataProcessExecutionContext context, 
                                              InitiateAACLetterExportProcess process,ExportSnapshot currSnapShot,
                                              List requests,CommsExportStatistics stats, long lastProcessedReqId,
                                              StringBuffer totalAacLetterText, String aacExtractId) throws Exception
    {
        if (logger.isDebugEnabled()) {
            logger.debug("requests.size(): "
                    + ((requests == null) ? 0 : requests.size()));
        }

        AacLetterRequest request = null;

       

        // Map tracking = new HashMap();
        for (int i = 0; i < requests.size(); i++) {
            
            // letters sent count per request.
            int entryCount = 0;
            try 
            {
                if (logger.isDebugEnabled()) {
                    logger.debug("processing requet: " + i);
                }


                request = (AacLetterRequest) requests.get(i);
                boolean isValidRequest = isValidRequest(request, stats, lastProcessedReqId);
                
                if ( ! isValidRequest )
                {
                    //log and continue to the next request we 
                    // all ready added the stats in isValidRequest()
                    
                    if (logger.isDebugEnabled()) {
                        logger.debug("requet is not valid: " + i);
                    }
                    
                    continue;
                }
                
                AacExportCommand cmd = (AacExportCommand) getComponent("aacExportCommand");
                //do this in new transaction 
                cmd.executeSingle(context, process, request, stats, aacExtractId);
                

            } catch (Exception ex) {
                stats
                        .setNumberWithException(stats.getNumberWithException() + 1);
                
                //Failed to write the file. This is not recoverable.
                if ( ex.getCause() != null && ex.getCause() instanceof java.io.IOException )
                {
                    context.setInterrupted(true);
                }
                
                handleException(context, request, ex);
            } finally {
                //Save after each request is processed
                stats.setNumberSent(stats.getNumberSent() + entryCount );

                lastProcessedReqId = ((BigDecimal) request.getEntityKey()
                        .getKeyValue()).longValue();
                // request = (AacLetterRequest) requests.get(i - 1);
                currSnapShot.setPrintRequestId((BigDecimal) request
                        .getEntityKey().getKeyValue());
                currSnapShot.setCommsLogId((BigDecimal) request
                        .getCommsLogEntry().getEntityKey()
                        .getKeyValue());
                // insert as a record of snapshot history with the
                // current processed print request id
                snapShotDAO.insert(currSnapShot);
                if (process.isInterrupted(context) ) {
                    break;
                }
                
                

                // DNS   doank (CodeCR7542): update the job statistics
                process.updateLiveJobStatistics(context, stats);
            }
        }
        return lastProcessedReqId;
    }
    
       
    public void  executeSingle(DataProcessExecutionContext context, 
            InitiateAACLetterExportProcess process,
            AacLetterRequest request,CommsExportStatistics stats,  String aacExtractId) 
    throws Exception
    {

        // letters sent count per request.
        int entryCount = 0;
        StringBuffer aacLetterText = new StringBuffer();

        CommsLogEntry log = request.getCommsLogEntry();
        Person person = getPersonService().getPerson(
                new PersonIdEntityKeyImpl(request.getCommsLogEntry()
                        .getPersonId()));

        GenerateValues inputValues = this.getInputValues(person, request);
        inputValues.setAacExtractId(aacExtractId);

        // set the mailing address in the log entry so that we can
        // check the letter rules
        log.setAddress(((GeneralTransmission) inputValues.getTransmission())
                .getMailingAdress());

        // make sure the letter pass the rules by ILog Rule Engine
        // and
        // update the log status and error reasons (if any)
        // according to the rule results
        if (logger.isDebugEnabled()) {
            logger.debug("About to call letterReqService ");
        }


        List errorMessageList = letterReqService
                .processLetterRulesForAACExport(person, getFormType(log
                        .getFormNumber()),
                        log.getWorkflowCaseId() == null ? null : log
                                .getWorkflowCaseId().toString(),
                        getTriggerType(log.getComMailingTriggerType()), log
                                .getOverrideIndicator(), log, inputValues
                                .getCategoryType());
        if (containsErrors(errorMessageList)) {

            if (logger.isDebugEnabled()) {
                logger.debug("has errors update log ");
            }
            // fail letter rules, reject at hec, update the log
            // status and error reasons. need to merge as we are in 
            // a transaction now.
            //commsLogDAO.merge(log);

            // update the statistics for reject at HEC
            stats.updateRejectPerReasonPerformTable(log);

            // increment the reject count
            stats.setNumberRejected(stats.getNumberRejected() + 1);
        } else {
            // build the actual ASCII text for AAC file
            List aacLetterTextEntryLst = buildLetterRequests(person,
                    inputValues);

            // update the comms log accordingly, and create log for
            // POA letters if any
            String vetFileData = (String) aacLetterTextEntryLst.get(0);
            updateVeteranCommsLog(log, inputValues, vetFileData);
            aacLetterText.append(vetFileData);
            entryCount++;

            // now handle any POAs
            List poasWithAddresses = inputValues.getTransmission()
                    .getPOAsWithAddresses();
            Association poa = null;
            for (int j = 1; j < aacLetterTextEntryLst.size(); j++) {
                // note these two are tied by the same index (thus
                // ordering is important)
                String poaFileData = (String) aacLetterTextEntryLst.get(j);
                poa = (Association) poasWithAddresses.get(j - 1);

                // 1) create CommsLogEntry (and get new barcode)
                CommsLogEntry poaLogEntry = createPOACommsLog(log, poaFileData,
                        poa);

                // 2) replace barcode in fileData and update poaLog
                poaFileData = StringUtils.replace(poaFileData,
                        LetterFormatHelper.BARCODE_POA_TOKEN,
                        LetterFormatHelper.formatBarcode(poaLogEntry
                                .getBarcode()));
                poaLogEntry.setLogXML(poaFileData);
                commsLogDAO.update(poaLogEntry); // now the POA
                // Log Entry has
                // the right
                // logXML

                // 3) add to total letter text
                aacLetterText.append(poaFileData);

                // 4) increment
                entryCount++; // has POA letter entry
            }

            // update the statistics
            if (CommsLogEntry.REMAIL_INDICATOR_RESEND.equalsIgnoreCase(log
                    .getRemailIndicator()))
                stats.setNumberRemailed(stats.getNumberRemailed() + 1);
            stats.addToCountPerFormTable(request);
        }
        // reset this form for this Person
        request.setCommsPrintRequestDupeCheck(request.getEntityKey()
                .getKeyValueAsString());
        requestDAO.mergeObject(request);
        if (aacLetterText.length() > 0) {
            fileDAO.save(aacLetterText.toString(), aacExtractId);
        }

        stats.setNumberSent(stats.getNumberSent() + entryCount );

    }
    
    
    
    
    
    
    private void handleException(DataProcessExecutionContext executionContext, AacLetterRequest request, Exception e)
    {  
        String errorMessage = "Error processing record in AAC Letter Export: " +
            " Reason: " + e.getMessage(); 
                
        if (request != null && request.getCommsLogEntry() != null) 
        {
            errorMessage = errorMessage + " Person ID: " + request.getCommsLogEntry().getPersonId() + " Form Number: " + 
                request.getCommsLogEntry().getFormNumber();
        }
        
        executionContext.getExceptionData().add(errorMessage);
        
        if(logger.isErrorEnabled())
            logger.error(errorMessage, e);
        //Writes the exception data to a .exception file.
        ArrayList exceptionData = new ArrayList();
        exceptionData.add(errorMessage);
        if ( e != null )
        {
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw));
            String trackTrace =  sw.toString();
            exceptionData.add(trackTrace);
        }
        try {
            getFileWriter().appendData(exceptionData);
        } catch (Exception e1) {
            logger.error("Unable to write the exception to .exception file ", e1);
        }
    }

    private boolean containsErrors(List errors) {
        boolean result = false;
        if(errors != null) {
            result = errors.size() >= 1 ? ! ((List) errors.get(0)).isEmpty() : false;
            if(!result)
                result = errors.size() >= 2 ? ! ((List) errors.get(1)).isEmpty() : false;
        }
        return result;
    }

    private void init() throws ServiceException
    {
        LookupService lookupService = (LookupService)getComponent("lookupService");
        EXPORT_TRANSMISSION_TYPE = lookupService.getMessageTypeByCode(EXPORT_TRANS_TYPE_CODE);
        SENT_TO_AAC_STATUS = (ComMailingStatusType)lookupService.getComMailingStatusTypeByCode(
                ComMailingStatusType.SENT_TO_AAC.getCode());
        letterReqService = (CommsLetterRequestService) getComponent("commsLetterRequestService");
    }

    private long getPrintRequestId(ExportSnapshot snapshot)
    {
        if (snapshot == null || snapshot.getPrintRequestId() == null)
            return 0;

        return snapshot.getPrintRequestId().longValue();
    }



/**
    private void getSnapshots()
        throws Exception
    {
        try{
            currSnapShot = snapShotDAO.getCurrentSnapshot();
            prevSnapShot = snapShotDAO.getPreviousSnapshot();
        } catch (DAOException de)
        {
                //TODO logit!
                throw new Exception ("Error getting snapshots: " + de);
        }
    }
**/
    private GenerateValues getInputValues(Person person, AacLetterRequest request)throws ServiceException
    {
        CommsLogEntry log = request.getCommsLogEntry();
        CommsTemplate templ = log.getTemplate();
        String formNumber = templ.getCommsTemplateFormNumber();

        ITransmission trans = getTransmission(formNumber, person, request.getCommsLogEntry());

           GenerateValues inputValues = new GenerateValues(person, formNumber, null,
                   log.getWorkflowCaseId(), trans);
              inputValues.setPrintLocationCd(CommsLetterConstants.MAIL_CENTER_AAC);
              inputValues.setOrigTemplate(templ);
              inputValues.setCommsLog(log);
              if(request.isVeteranLetter())
                  inputValues.setCategoryType(AacLetterRequest.VETERAN_LETTER);
              else if(request.isSpouseLetter())
                  inputValues.setCategoryType(AacLetterRequest.SPOUSE_LETTER);
              else if(request.isDependentLetter())
                  inputValues.setCategoryType(AacLetterRequest.DEPENDENT_LETTER);


          return inputValues;
    }

    private ITransmission getTransmission(String formNumber, Person person, CommsLogEntry log) throws ServiceException
    {
        if (formNumber == null)
            return null;

        ITransmission trans = null;

        if (ComLetterTemplateType.FORM_NUMBER_290.getCode().equals(formNumber) ||
                ComLetterTemplateType.FORM_NUMBER_291.getCode().equals(formNumber) ||
                ComLetterTemplateType.FORM_NUMBER_292.getCode().equals(formNumber) ||
                ComLetterTemplateType.FORM_NUMBER_293.getCode().equals(formNumber) ||
                ComLetterTemplateType.FORM_NUMBER_298.getCode().equals(formNumber) ||
                ComLetterTemplateType.FORM_NUMBER_299.getCode().equals(formNumber))
        {
            SsnTransmission ssnTrans = new SsnTransmission(person, log.getWorkflowCaseId());
            ssnTrans.setFormNumber(formNumber);
            ssnTrans.setHelperService(this.getHelperService());
            trans = ssnTrans;
        } else
        {
            // get the data objects - transmission and inputValues
            EnrollTransmission enrTrans = new EnrollTransmission(person, log.getWorkflowCaseId());
            enrTrans.setFormNumber(formNumber);
            enrTrans.setHelperService(this.getHelperService());

            // must set "prior" enrollment determination on EnrollTransmission
            // TODO: optimize this api, no need to get the entire Person!
            /**   Commented unused method CR 8110
            Person priorPerson = this.eligibilityEnrollmentService.getPersonForPriorEnrollment(person.getEntityKey());
            if(priorPerson != null) {
                enrTrans.setPriorEnrollmentDetermination(priorPerson.getEnrollmentDetermination());
            }
            ***/
            
            String mostRecentNonNullPriorityLevel = this.eligibilityEnrollmentService.getMostRecentNonNullPriorityLevelByDate(person.getEntityKey(), log.getCreatedOn());
            enrTrans.setMostRecentNonNullPriorityLevel(mostRecentNonNullPriorityLevel);

            // init "current" IncomeTest
            IncomeTest currentIncomeTest = personHelperService.getCurrentIncomeTest(person);
            enrTrans.setLocalIncomeTest(currentIncomeTest);

            
            

            //calculate and set income threshold for financial information
            if (currentIncomeTest != null) {
                Integer currentIncomeYear = currentIncomeTest.getIncomeYear();

                if (currentIncomeYear != null) {
                    Integer total = (currentIncomeTest.getTotalNumberOfDependents() != null) ? currentIncomeTest.getTotalNumberOfDependents() : new Integer(0);
                    enrTrans.setIncomeThreshold(((FinancialsHelperService)this.getComponent("financialsHelperService"))
                            .calculateIncomeThresholds(currentIncomeYear, total));
                }
            }
            trans = enrTrans;

        }

        return trans;
    }

    private List buildLetterRequests(Person person, GenerateValues inputValues) throws ServiceException
    {
           DocumentBuilder builder = new DocumentBuilder();

        //build the ASCII text
           List textLst = null;
           try {
               //builder return a list of ASCII text String
               //for now, index 0 as veteran letter, index 1+ as POA letter if exists
               textLst = builder.build(person, inputValues);
           }
           catch (InsufficientDataException idex) {
               logger.error("ERROR: Insufficient Data to build a letter while requesting an AAC letter", idex);
               throw new ServiceException(idex.toString());
           }

           return textLst;
    }

    private void updateVeteranCommsLog(CommsLogEntry log, GenerateValues inputValues, String vetFileData) throws DAOException
    {
        //Veteran log, update the logXml
        log.setLogXML(vetFileData);
        log.setAacExtractNumber(inputValues.getAacExtractId());
        //log.addMailingStatus(SENT_TO_AAC_STATUS); //already updated when checking letter rules
        
        //no need to call this update as this will be updated as part of the request object on the commit.
        //commsLogDAO.update(log);
    }

    private CommsLogEntry createPOACommsLog(CommsLogEntry vetLog, String poaFileData, Association poa) throws Exception {
        CommsLogEntry poaLog = new CommsLogEntry();

        poaLog.setAacExtractNumber(vetLog.getAacExtractNumber());
        poaLog.setAddress(poa.getAddress());
        poaLog.setRecipient(CommsLogEntry.RECIPIENT_POA);
        poaLog.setLogXML(poaFileData);
        poaLog.addMailingStatus(SENT_TO_AAC_STATUS);
        poaLog.setLetterType(vetLog.getLetterType());
        Name poaName = poa.getRepresentativeName();
        if(poaName == null) {
            // best attempt to get this right
            poaName = new Name();
            poaName.setFamilyName(poa.getOrganizationName());
            poaName.setType(this.getLookupService().getNameTypeByCode(NameType.LEGAL_NAME.getCode()));
        }
        poaLog.setName(poaName);
        poaLog.setOverrideIndicator(vetLog.getOverrideIndicator());
        poaLog.setPersonId(vetLog.getPersonId());
        poaLog.setRemailIndicator(vetLog.getRemailIndicator());
        poaLog.setSsn(vetLog.getSsn());
        poaLog.setTemplate((CommsTemplate) templateDAO.findTemplateListByFormNumber(ComLetterTemplateType.FORM_NUMBER_650A.getCode()).get(0));
        poaLog.setWorkflowCaseId(vetLog.getWorkflowCaseId());

        commsLogDAO.insert(poaLog);

        return poaLog;
    }


    private LetterTriggerEvent.MailType getTriggerType(ComMailingTriggerType triggerType)
    {
        if (triggerType != null)
        {
            String triggerTypeCode = triggerType.getCode();

            if (triggerTypeCode.equals(CommsLogEntry.MAMUAL_TRIGGER_CODE))
                return LetterTriggerEvent.MANUAL_MAIL;
            if (triggerTypeCode.equals(CommsLogEntry.REMAIL_TRIGGER_CODE))
                return LetterTriggerEvent.REMAIL_MAIL;
            if (triggerTypeCode.equals(CommsLogEntry.HISTORICAL_TRIGGER_CODE))
                return LetterTriggerEvent.HISTORIC_MAIL;
            if (triggerTypeCode.equals(CommsLogEntry.AUTOMATIC_TRIGGER_CODE))
                return LetterTriggerEvent.AUTO_MAIL;
        }
        return null;
    }

    private ComLetterTemplateType getFormType(String formNumber) throws Exception
    {
        if (formNumber == null)
            return null;

        return this.getLookupService().getComLetterTemplateTypeByCode(formNumber);

    }

    //Update Comms Transmission Log
    private void updateCommsTransLog(CommsExportStatistics stats) throws Exception
    {

        if (stats.getFileName() == null)
            return;


        Hashtable countPerformTab = stats.getCountPerFormTable();
        if (countPerformTab != null && countPerformTab.size() > 0)
        {

            Enumeration keys = countPerformTab.keys();
            String key = null;

            CommsTransLog entry = null;
            while (keys.hasMoreElements())
            {
                key = (String) keys.nextElement();
                entry = new CommsTransLog();

                //entry.setFileName(stats.getFileName()); Commented this, as this will get .tmp file
                entry.setFileName(stats.getOriginalFileName());
                entry.setFileProcessStartDate(stats.getStartTime());
                entry.setFileProcessEndDate(stats.getEndTime());

                CommsTransByFormNumber transByFormNumber = new CommsTransByFormNumber();
                transByFormNumber.setFormNumber(key);
                transByFormNumber.setFormNumberRecordCount((Integer)countPerformTab.get(key));
                entry.addTransByFormNumber(transByFormNumber);

                entry.setTransmissionType(EXPORT_TRANSMISSION_TYPE);
                transDAO.save(entry);
            }
        }
    }

    /*
      * valid requested are:
      * 1. not Cancel By HEC
      * 2. not Rejecte At HEC
      */

    /*****
    private List getValidRequests(List requests, CommsExportStatistics stats)
    {
        ArrayList validRequsts = new ArrayList();

        int cancelCnt = 0;
        int rejectCnt = 0;
        for (int i =0; i< requests.size(); i++)
        {
            CommsLogEntry entry = (CommsLogEntry)((AacLetterRequest)requests.get(i)).getCommsLogEntry();
            if (entry.isCancelledByHec())
                cancelCnt++;
            else if(entry.getHecRejectionReasons() != null &&
                    entry.getHecRejectionReasons().size() > 0)
            {
                stats.updateRejectPerReasonPerformTable(entry);
                rejectCnt++;
            }
            else
                validRequsts.add(requests.get(i));
        }

        stats.setFileName(fileDAO.getAacFileName());
        stats.setNumberRejected(rejectCnt);
        stats.setNumberCancelled(cancelCnt);

        return validRequsts;
    }
    *****/
    
    private boolean isValidRequest(AacLetterRequest request, CommsExportStatistics stats, long lastProcessedReqId)
    {
        CommsLogEntry entry = (CommsLogEntry)request.getCommsLogEntry();
        boolean isValid = true;
        //we only looks for request that's Send To AAC
    	if (!entry.isSendToAAC())
        	isValid = false;

        return isValid;
    }

    private boolean preProcess(String option, Boolean cleanUp) throws ServiceException
    {
        if (option != null && option.toUpperCase(Locale.ENGLISH).equals("-A"))
        {
            //delete all print/cancel entries
            deleteReuestEntries(true);
            return true;
        }
        else if (option != null && option.toUpperCase(Locale.ENGLISH).equals("-D"))
        {
            //delete last print/cancel entries
            deleteReuestEntries(false);
            return true;
        }
        else if (option != null && option.toUpperCase(Locale.ENGLISH).equals("-B"))
        {
            //process the batch and delete the processed print/cancel entries
            cleanUp = new Boolean(true);
        }
        else if (option != null && option.toUpperCase(Locale.ENGLISH).equals("-P"))
        {
            //process only, default
            cleanUp = new Boolean(false);
        }
        else
        {
            //error: unkonwn option
            String errMsg = "### AAC Export process stopped. ExportFileCommand - Unknown option: " + option;

            printUsage();

            throw new ServiceException (errMsg);
        }

        return false;

    }
    //TODO: Send email to email bulletin
    private void sendEmailNotification(CommsExportStatistics stats) throws ServiceException
    {

        Hashtable dataTab = new Hashtable();
        dataTab.put("CompletedDateTime", stats.getEndTime().toString());
        dataTab.put("NumberOfFiles", "1");
        dataTab.put("NumberLettersSent", stats.getNumberSent()+"");
        dataTab.put("NumberLetterRejected", stats.getNumberRejected()+"");
        dataTab.put("NumberLettersRemailed", stats.getNumberRemailed()+"");
        dataTab.put("CommunicationsPerFormTxt", stats.getCountPerFormTableText());
        dataTab.put("RejectPerReasonPerFormTxt", stats.getRejectPerReasonPerFormText());
        //dataTab.put("FileNameRecordCountTxt", stats.getFileName() + "          " + stats.getNumberSent());
        dataTab.put("FileNameRecordCountTxt", stats.getOriginalFileName() + "          " + stats.getNumberSent());

        CommsEmailBulletinService emailSrv = null;

        emailSrv = (CommsEmailBulletinService) getComponent( "commsEmailBulletinService" );
        emailSrv.sendEmailBulletin(BulletinTrigger.DataType.ENROLLMENT_LETTER_PROCESSING,
                dataTab, null);


/*          Email bulletin statistics and tracking
        Enrollment Letter processing completed on Date/Time_File_Created
        Files created for AAC =  Number_of_Files
        Letters to send to AAC = Number_of_Communications
        Letters rejected =       Number_Rejected

        Total number of form: Form_Number to send to AAC = Communications_per_Form
        Total number of forms rejected sorted by reason
        Reason
        Form_Number = Rejections_per_Reason_per_Form

        FILE NAME          RECORD COUNT
        --------------         ------------
        File_Name           Records_per_File
*/
/*      string fileName =
            (stats.ExportFileName == null) ? "" : stats.ExportFileName.Substring(stats.ExportFileName.Length-12);
        elementList.Add (BulletinElements.TEMPLATEID, EXPORT_MAIL_TEMPLATE_ID);
        elementList.Add (BulletinElements.DATETIME_FILE_CREATED, stats.EndTime.ToString());
        elementList.Add (BulletinElements.NUMBER_OF_FILES, (fileName.Length > 0) ? "1" : "0" );
        elementList.Add (BulletinElements.NUMBER_OF_COMMUNICATIONS, stats.NumberOfRequestSent.ToString() );
        elementList.Add (BulletinElements.NUMBER_OF_REJECTED, stats.NumberOfRequestRejected.ToString());
        elementList.Add (BulletinElements.COMMUNICATIONS_PER_FORM, stats.CountPerFormTableText());
        elementList.Add (BulletinElements.REJECTIONS_PER_RESAON_PER_FORM, stats.RejectPerReasonPerFormText);
        elementList.Add (BulletinElements.FILE_NAME, stats.ExportFileName);
        elementList.Add (BulletinElements.RECORDS_PER_FILE, stats.NumberOfRequestSent.ToString());

        new SendBulletinCommand ().Execute (elementList);
*/
        }

    private void deleteReuestEntries(boolean isAll) throws ServiceException
    {
//        AacLetterRequestDAO requestDAO = null;
//      CancelledPrintRequestDAO cancelDAO = null;


        requestDAO = (AacLetterRequestDAO) getComponent("printRequestDAO");
//      cancelDAO = (CancelledPrintRequestDAO)component.getComponent("cancelledPrintRequestDAO");

/*          if (isAll)
            {
///             requestDAO.deleteAllAacLetterRequests();
///             cancelDAO.deleteAllCancelledRequest();
            }
            else
            {
                ExportSnapshotDAO snapShotDAO = null;
//              ExportSnapshot currSnapShot = null;
//              ExportSnapshot prevSnapShot = null;

                this.getSnapshots();

///             requestDAO.deleteAacLetterRequestsBetweenIds(prevSnapShot.getPrintRequestId(), currSnapShot.getPrintRequestId());
///             cancelDAO.deleteCancelledRequestsBetweenIds(prevSnapShot.getCancelledPrintRequestId(), currSnapShot.getCancelledPrintRequestId());
            }
        } catch (DAOException de)
        {
            de.printStackTrace();
        } catch (Exception e)
        {
            e.printStackTrace();
        }*/
    }



    /**
     * @return Returns the commsLogDAO.
     */
    public CommsLogEntryDAO getCommsLogDAO() {
        return commsLogDAO;
    }
    /**
     * @param commsLogDAO The commsLogDAO to set.
     */
    public void setCommsLogDAO(CommsLogEntryDAO commsLogDAO) {
        this.commsLogDAO = commsLogDAO;
    }
    /**
     * @return Returns the requestDAO.
     */
    public AacLetterRequestDAO getRequestDAO() {
        return requestDAO;
    }
    /**
     * @param requestDAO The requestDAO to set.
     */
    public void setRequestDAO(AacLetterRequestDAO requestDAO) {
        this.requestDAO = requestDAO;
    }
    /**
     * @return Returns the snapShotDAO.
     */
    public ExportSnapshotDAO getSnapShotDAO() {
        return snapShotDAO;
    }
    /**
     * @param snapShotDAO The snapShotDAO to set.
     */
    public void setSnapShotDAO(ExportSnapshotDAO snapShotDAO) {
        this.snapShotDAO = snapShotDAO;
    }
    /**
     * @return Returns the transDAO.
     */
    public CommsTransLogDAO getTransDAO() {
        return transDAO;
    }
    /**
     * @param transDAO The transDAO to set.
     */
    public void setTransDAO(CommsTransLogDAO transDAO) {
        this.transDAO = transDAO;
    }
    /**
     * @return Returns the fileDAO.
     */
    public ExportFileDAO getFileDAO() {
        return fileDAO;
    }
    /**
     * @param fileDAO The fileDAO to set.
     */
    public void setFileDAO(ExportFileDAO fileDAO) {
        this.fileDAO = fileDAO;
    }

    /**
     * @return Returns the eligibilityEnrollmentService.
     */
    public EligibilityEnrollmentService getEligibilityEnrollmentService() {
        return eligibilityEnrollmentService;
    }

    /**
     * @param eligibilityEnrollmentService The eligibilityEnrollmentService to set.
     */
    public void setEligibilityEnrollmentService(
            EligibilityEnrollmentService eligibilityEnrollmentService) {
        this.eligibilityEnrollmentService = eligibilityEnrollmentService;
    }

    /**
     * @return Returns the templateDAO.
     */
    public CommsTemplateDAO getTemplateDAO() {
        return templateDAO;
    }

    /**
     * @param templateDAO The templateDAO to set.
     */
    public void setTemplateDAO(CommsTemplateDAO templateDAO) {
        this.templateDAO = templateDAO;
    }

    /**
     * @return Returns the personHelperService.
     */
    public PersonHelperService getPersonHelperService() {
        return personHelperService;
    }

    /**
     * @param personHelperService The personHelperService to set.
     */
    public void setPersonHelperService(PersonHelperService personHelperService) {
        this.personHelperService = personHelperService;
    }

    public int getBatchSize() {
        return batchSize;
    }

    public void setBatchSize(int batchSize) {
        this.batchSize = batchSize;
    }
    
    public FormattedFileWriter getFileWriter() {
        return fileWriter;
    }

    public void setFileWriter(FormattedFileWriter fileWriter) {
        this.fileWriter = fileWriter;
    }
}
