/*
 * Created on Dec 20, 2005
 *
 * 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;

//JAVA PACKAGES
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.Validate;

import gov.va.med.esr.common.model.comms.CommsImportStatistics;
import gov.va.med.esr.common.model.comms.CommsLogEntry;
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.ErrorFileEntry;
import gov.va.med.esr.common.model.comms.MailedFileEntry;
import gov.va.med.esr.common.model.comms.RejectFileEntry;
import gov.va.med.esr.common.model.lookup.ComMailingStatusType;
import gov.va.med.esr.common.model.lookup.MessageType;
import gov.va.med.esr.common.model.person.PersonChangeLogEntry;
import gov.va.med.esr.common.persistent.comms.CommsLogEntryDAO;
import gov.va.med.esr.common.persistent.comms.CommsTransLogDAO;
import gov.va.med.esr.common.persistent.comms.hibernate.ImportFileDAOImpl;
import gov.va.med.esr.common.persistent.person.PersonSubmittedAdvice;
import gov.va.med.esr.service.CommsLogService;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.impl.AbstractRuleAwareServiceImpl;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.service.ServiceException;

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

    /*
     * Some static variables
     */
	//transmission log type codes
	private static final String IMPORT_TRANS_REJECT_TYPE_CODE = "COMREJ-E";
	private static final String IMPORT_TRANS_ERROR_TYPE_CODE = "COMERR-E";
	private static final String IMPORT_TRANS_MAILED_TYPE_CODE = "COMADD-E";
	
	//transmission log types
	private static MessageType IMPORT_TRANSMISSION_REJECT_TYPE = null;
	private static MessageType IMPORT_TRANSMISSION_ERROR_TYPE = null;
	private static MessageType IMPORT_TRANSMISSION_MAILED_TYPE = null;
	
	////comms log status types
	private static ComMailingStatusType REJECT_BY_AAC_STATUS = null;
	private static ComMailingStatusType ERROR_BY_AAC_STATUS = null;
	private static ComMailingStatusType MAILED_BY_AAC_STATUS = null;
	
	//REMAIL STATUS
	private static final String REMAILED_INDICATOR = "REMAILED";
	
	private static final String ERROR_REASONS_SEPERATOR= "^";

    
	private LookupService lookupService = null;
    
    private CommsTransLogDAO transDAO = null;
	private CommsLogEntryDAO commsLogDAO = null;
    private ImportFileDAOImpl fileDAO = null;
    
    private CommsLogService commsLogService;

	
    private void init() throws ServiceException
    {
	    if (lookupService == null )
	    {
	        lookupService = (LookupService)getComponent("lookupService");
	        
	        //transmission type
	        IMPORT_TRANSMISSION_REJECT_TYPE = lookupService.getMessageTypeByCode(IMPORT_TRANS_REJECT_TYPE_CODE);
	        IMPORT_TRANSMISSION_ERROR_TYPE = lookupService.getMessageTypeByCode(IMPORT_TRANS_ERROR_TYPE_CODE);
	        IMPORT_TRANSMISSION_MAILED_TYPE = lookupService.getMessageTypeByCode(IMPORT_TRANS_MAILED_TYPE_CODE);
	        
	        //mailing status
	        REJECT_BY_AAC_STATUS =  lookupService.getComMailingStatusTypeByCode(ComMailingStatusType.REJECT_BY_AAC.getCode());
	        ERROR_BY_AAC_STATUS =  lookupService.getComMailingStatusTypeByCode(ComMailingStatusType.ERROR_BY_AAC.getCode());
	        MAILED_BY_AAC_STATUS = lookupService.getComMailingStatusTypeByCode(ComMailingStatusType.MAILED_BY_AAC.getCode());
	    }        
    }
    
	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();
	
		Validate.notNull(this.commsLogDAO, "A Comms Log Entry DAO must be configured");
		Validate.notNull(this.transDAO, "An Comms Transmission Log DAO must be configured");
		Validate.notNull(this.fileDAO, "An Comms Import File DAO must be configured");
		Validate.notNull(this.commsLogService, "commsLogService is required");		
	}
	
    public CommsImportStatistics execute(String[] args) throws Exception
    {
        init();

		CommsImportStatistics stat = new CommsImportStatistics();
		stat.setProcessStartTime(new Date());
		// Set changeLogs = new HashSet();
		
		//read then process error file entries
		stat.setErrorFileProcessStartTime(new Date());
		try{
			// ArrayList errorFileList = fileDAO.readErrorFile(stat);
			// Set changeLogs = processErrorFile(errorFileList, stat);
			// changeLogs.addAll(processErrorFile(errorFileList, stat));
			processErrorFile(stat);
		}catch (Exception ex1){
			logger.error("Failed to process Aac import error file", ex1);
		}	
		stat.setErrorFileProcessEndTime(new Date());

		//read then process reject file entries
		stat.setRejectFileProcessStartTime(new Date());
		try{
			// ArrayList rejectFileList = fileDAO.readRejectFile(stat);
			// changeLogs.addAll(processRejectFile(rejectFileList, stat));
			processRejectFile(stat);
		}catch (Exception ex2){
			logger.error("Failed to process Aac import rejected file", ex2);
		}	
		stat.setRejectFileProcessEndTime(new Date());

		//read then process mailed file entries
		stat.setMailedFileProcessStartTime(new Date());
		try{
			// ArrayList mailedFileList = fileDAO.readMailedFile(stat);
			// changeLogs.addAll(processMailedFile(mailedFileList, stat));
			processMailedFile(stat);
		}catch (Exception ex3){
			 logger.error("Failed to process Aac import mailed file", ex3);
		}
		stat.setMailedFileProcessEndTime(new Date());
		
			//TODO: update workload for error files 
			// updateWorkload(errorFileList);
	
		stat.setProcessEndTime(new Date());	
		try{
			updateCommsTransLog(stat);
			// if (changeLogs!=null & changeLogs.size()>0)
			//	getPersonService().logChanges(changeLogs);	
		}catch (Exception ex){
			  logger.error("Failed to update comms trans log");
		}
		return stat;
    }
    
    /**
     * @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 fileDAO.
     */
    public ImportFileDAOImpl getFileDAO() {
        return fileDAO;
    }
    /**
     * @param fileDAO The fileDAO to set.
     */
    public void setFileDAO(ImportFileDAOImpl fileDAO) {
        this.fileDAO = fileDAO;
    }
    /**
     * @return Returns the transDAO.
     */
    public CommsTransLogDAO getTransDAO() {
        return transDAO;
    }
    /**
     * @param transDAO The transDAO to set.
     */
    public void setTransDAO(CommsTransLogDAO transDAO) {
        this.transDAO = transDAO;
    }
    
    // This is done so that DataSync for HEC Legacy can send back the status of the mailing response
    private PersonChangeLogEntry createPersonChangeLogEntry(CommsLogEntry cle) {
    	PersonChangeLogEntry p = new PersonChangeLogEntry();
    	p.setPersonEntityKey(cle.getPersonIdEntityKey());
    	p.setSubmitterDescription(PersonSubmittedAdvice.SUBMITTER_DATA_DESTINED_FOR_HECLEGACYDATASYNC);
    	return p;
    }
    
	//5256[UC17.5] Error File Processed 
	//For each record received on this file the system will update the Communication Status to Error by AAC. 
	//
	//    5257[UC17.5.1]  
	//    The system sets the Date/Time Updated to the Date/Time the Communication Status was set. 
	//
	//    5258[UC17.5.2]  
	//    The system updates the Error File Reason with the description of the code or codes received with each record.  
	//
	//    5259[UC17.5.3]  
	//    The system captures and stores the following statistics that will be used to generate a report:
	//    Date/Time File Received
	//    File Name
	//    Number of Error Records Per File
	//    Break out the letters by type and how many per type
	//    For each letter type, the error reason(s)  

    // private Set processErrorFile(List errorFileList, CommsImportStatistics stat) throws Exception
    private void processErrorFile(CommsImportStatistics stat) throws Exception
    {	
    	ArrayList errorFileList = fileDAO.readErrorFile(stat);
        if (errorFileList == null || errorFileList.isEmpty())
            return; // changeLogs;
        
        Set changeLogs = new HashSet();
        CommsLogEntry log = null;
        ErrorFileEntry entry = null;
        String prevBarcode = null;
        String currBarcode = null;
        
        //one letter entry may have multiple error codes
        //assume the error entries are group by barcode id (same letter together)
        for (int i=0; i< errorFileList.size(); i++)
        {
            try {
                entry = (ErrorFileEntry)errorFileList.get(i);
                currBarcode = entry.getBarCode();
                
                if (i == 0 || (currBarcode != null && !currBarcode.equals(prevBarcode)))
                {
                    //getting the new log
                    log = commsLogDAO.findCommsLogEntryByBarcode(currBarcode);
                
                    if (log != null)
                    {
                        //update log in memory
                        log.addMailingStatus(ERROR_BY_AAC_STATUS);
                        log.addAacErrorReason(lookupService.getComAACErrorTypeByCode(entry.getErrorCode()));
                    
                        //update statistics with new letter (log entry)
                        stat.updateErrorStatistics(entry, true);

                        // CCR9192 new - update log for each log entry
                    	changeLogs.clear();
                    	changeLogs.add(createPersonChangeLogEntry(log));  
                    	getPersonService().logChanges(changeLogs);
                    	stat.incrementErrorFileProcessedCount();
                    	
                    } else {
                    	// can't find CommsLogEntry
                    	stat.incrementErrorFileFailedCount();
                    }
                } else {
                    if(log != null) {
	                    //same barcode id (same letter, log entry)
	                    //add error reason in memory
	                    log.addAacErrorReason(lookupService.getComAACErrorTypeByCode(entry.getErrorCode()));
	                
	                    //update statistics with the same letter (log entry)
	                    stat.updateErrorStatistics(entry, false);
	                    stat.incrementErrorFileProcessedCount();
                    } else {
                    	// can't find CommsLogEntry
                    	stat.incrementErrorFileFailedCount();
                    }
                }
                
                prevBarcode = currBarcode;
                
                //update the pervious log in DB at once
                if (log != null) commsLogDAO.update(log);
                
            } catch (Exception ex) {
                stat.incrementErrorFileFailedCount();
                logger.error("Failed to log error file entry bar code = " + currBarcode,ex);
            }
        }
        // return changeLogs;
    }
    
	//5251[UC17.4] Code 1 Reject File Processed 
	//For each record received on this file the system will update the Communication Status to Reject By AAC. 
	//
	//    5252[UC17.4.1]  
	//    The system captures the Date/Time Received. 
	//
	//    5253[UC17.4.2]  
	//    The system sets the Date/Time Updated to the date/time the Communication Status was set. 
	//
	//    5254[UC17.4.3]  
	//    The system updates the Code 1 Reject Reason with the code received with each record. 
	//
	//    5255[UC17.4.4]  
	//    The system captures and stores the following statistics that will be used to generate a report:
	//    Date/Time File Received
	//    File Name
	//    Number of Error Records Per File
	//    Break out the letters by type and how many per type
	//    For each letter type, the error reason 
	//
	//    5334[UC17.4.5]  
	//    There will only be one Code 1 Reject Reason for each letter. 
	//
	//    5469[UC17.4.6]  
	//    If the address used for the letter is the current permanent mailing address then the system will automatically set the Bad Address Reason to Undeliverable Mail. 
	//
	//    	5470[UC17.4.6.1] Send Update Message (Z05) 
	//    	If the Bad Address Reason was updated then the system executes the Send Update Message (Z05) use case to share the Bad Address Reason. 
	//    
   // private Set processRejectFile(List rejectFileList, CommsImportStatistics stat) throws Exception
    private void processRejectFile(CommsImportStatistics stat) throws Exception
    {
    	ArrayList rejectFileList = fileDAO.readRejectFile(stat);
        if (rejectFileList == null || rejectFileList.isEmpty())
            return; // changeLogs;
        
        Set changeLogs = new HashSet();
        CommsLogEntry log = null;
        RejectFileEntry entry = null;
     //   List rejectLetterLogLst = new ArrayList();
        
        for (int i=0; i< rejectFileList.size(); i++)
        {
            entry = (RejectFileEntry)rejectFileList.get(i);
            log = commsLogDAO.findCommsLogEntryByBarcode(entry.getBarCode());
            
            if (log != null)
            {
                try {
                    //update log
                    log.addMailingStatus(REJECT_BY_AAC_STATUS);
                    log.setCode1RejectReason(lookupService.getComAACRejectReasonTypeByCode(entry.getCode1Reject()));
                    commsLogDAO.update(log);
                    
                    //add log into list for rule process later
                   // rejectLetterLogLst.add(log);
                    
                    // CCR 9192 - process each log to fix timeout due to large file size
                    //call rule to handle business requirement 5469 & 5470
                    commsLogService.handleMailingResponses(log);
                    
                    //update statistics
                    stat.updateRejectStatistics(entry);

                  // CCR9192 new - update log for each log entry 
                	changeLogs.clear();
                	changeLogs.add(createPersonChangeLogEntry(log));  
                	getPersonService().logChanges(changeLogs);
                	stat.incrementRejectFileProcessedCount();
                	
                } catch(Exception ex) {
                    stat.incrementRejectFileFailedCount();
                    logger.error("Failed to update comms log status id = " + log.getCommsLogIdString(),ex);
                }
            }
            // CCR9192 - if log is null
            else {
        	// can't find CommsLogEntry
        	stat.incrementRejectFileFailedCount();
            }
        }
        
        //call rule to handle business requirement 5469 & 5470
       // commsLogService.handleMailingResponses(rejectLetterLogLst);
      //  return changeLogs;
    }
    
    //5244[UC17.3] Address File Processed
    //For each record received on this file the system will update the Communication Status to Mailed By AAC. 

	//	  5245[UC17.3.1] 
	//    If the specific letter Remail Indicator on the EDB is Resend then the system automatically sets the Remail Indicator to Remailed. 
	//
	//    5246[UC17.3.2] 
	//    The system captures the Date/Time File Received. 
	//
	//    5247[UC17.3.3] 
	//    The system updates the Date Mailed with the Date Mailed received with each record. 
	//
	//    5248[UC17.3.4] 
	//    The system sets the Date/Time Updated to the Date/Time the Communication Status was set. 
	//
	//    5249[UC17.3.5] 
	//    The system captures and stores the following statistics that will be used to generate a report.:
	//    Date/Time File Received
	//    File Name
	//    Number of Records Per File
	//    Break out the letters by type and how many per type. 
   // private Set processMailedFile(List mailedFileList, CommsImportStatistics stat) throws Exception
    private void processMailedFile(CommsImportStatistics stat) throws Exception
    {
    	ArrayList mailedFileList = fileDAO.readMailedFile(stat);
        if (mailedFileList == null || mailedFileList.isEmpty())
            return;  // changeLogs;
        
        Set changeLogs = new HashSet();
        CommsLogEntry log = null;
        MailedFileEntry entry = null;
        
        for (int i=0; i< mailedFileList.size(); i++)
        {
            entry = (MailedFileEntry)mailedFileList.get(i);
            log = commsLogDAO.findCommsLogEntryByBarcode(entry.getBarCode());
            
            if (log != null)
            {
                try {
                    //update log
                    log.addMailingStatus(MAILED_BY_AAC_STATUS);
                    log.setMailingDate(entry.getMailedDate());
                    if (log.getRemailIndicator() != null)
                        log.setRemailIndicator(REMAILED_INDICATOR);
                    
                    //TODO: need to store the name and address AAC used in logXML
                    
                    commsLogDAO.update(log);
                    
                    //update statistics
                    stat.updateMailedStatistics(entry);
                    
                    // CCR9192 new - update log for each log entry 
                	changeLogs.clear();
                	changeLogs.add(createPersonChangeLogEntry(log));  
                	getPersonService().logChanges(changeLogs);
                	stat.incrementMailedFileProcessedCount();
                	
                } catch (Exception ex) {
                    stat.incrementMailedFileFailedCount();
                    logger.error("Failed to process mailed file entry id = " + log.getCommsLogIdString(),ex);
                }
            }
            // CCR9192 - if log is null
            else {
        	// can't find CommsLogEntry
        	stat.incrementMailedFileFailedCount();
            }
        }
       // return changeLogs;
    }

    //TODO: make sure the trans log has everything needed for Reporting requirement
    private void updateCommsTransLog(CommsImportStatistics stat) throws DAOException
    {
        if (stat == null)
            return;
        
        //transmission log for error file
        CommsTransLog errLog = new  CommsTransLog();
        errLog.setTransmissionType(IMPORT_TRANSMISSION_ERROR_TYPE);
        errLog.setFileName(stat.getErrorFileName());
        errLog.setFileProcessStartDate(stat.getErrorFileProcessStartTime());
        errLog.setFileProcessEndDate(stat.getErrorFileProcessEndTime());
        errLog.setFileReceivedDate(stat.getErrorFileReceivedDate());
        errLog.setFileRecordCount(new Integer(stat.getErrorFileRecordCount()));
        
        updateErrorByFormNumber(errLog, stat);
        
//        transDAO.save(errLog);
        
        
        //trnasmission log for reject file
        CommsTransLog rejLog = new  CommsTransLog();
        rejLog.setTransmissionType(IMPORT_TRANSMISSION_REJECT_TYPE);
        rejLog.setFileName(stat.getRejectFileName());
        rejLog.setFileProcessStartDate(stat.getRejectFileProcessStartTime());
        rejLog.setFileProcessEndDate(stat.getRejectFileProcessEndTime());
        rejLog.setFileReceivedDate(stat.getRejectFileReceivedDate());
        rejLog.setFileRecordCount(new Integer(stat.getRejectFileRecordCount()));
        
        updateRejectByFormNumber(rejLog, stat);
        
//        transDAO.save(rejLog);

        
        //transmission log for mailed file
        CommsTransLog mailedLog = new  CommsTransLog();
        mailedLog.setTransmissionType(IMPORT_TRANSMISSION_MAILED_TYPE);
        mailedLog.setFileName(stat.getMailedFileName());
        mailedLog.setFileProcessStartDate(stat.getMailedFileProcessStartTime());
        mailedLog.setFileProcessEndDate(stat.getMailedFileProcessEndTime());
        mailedLog.setFileReceivedDate(stat.getMailedFileReceivedDate());
        mailedLog.setFileRecordCount(new Integer(stat.getMailedFileRecordCount()));
        
        updateMailedByFormNumber(mailedLog, stat);
        
//        transDAO.save(mailedLog);

    }
    
    private void updateErrorByFormNumber(CommsTransLog errLog, CommsImportStatistics stat)
    {
        if (errLog == null || stat == null || 
                stat.getErrorPerTypeNumberTable() == null)
            return;
        
        String formNumber = null;
        Hashtable errorReasonPerLetterTypeTable = null;
        CommsTransByFormNumber transByForm = null;
        
        Hashtable errorPerTypeNumberTable = stat.getErrorPerTypeNumberTable();
        Enumeration enumer = errorPerTypeNumberTable.keys();
        
        while (enumer.hasMoreElements())
        {
            transByForm = new CommsTransByFormNumber();
            formNumber = (String)enumer.nextElement();
            
            transByForm.setFormNumber(formNumber);
            transByForm.setFormNumberRecordCount((Integer)errorPerTypeNumberTable.get(formNumber));
            
            errorReasonPerLetterTypeTable = stat.getErrorReasonPerLetterTypeTable();
            if (errorReasonPerLetterTypeTable != null)
                transByForm.setFormNumberErrorReasons(
                        getFormNumberErrorReasons((Set)errorReasonPerLetterTypeTable.get(formNumber)));
            
            errLog.addTransByFormNumber(transByForm);
        }
    }
    
    private String getFormNumberErrorReasons (Set errorReasons)
    {
        if (errorReasons == null || errorReasons.isEmpty())
            return null;
        Iterator it = errorReasons.iterator();
        StringBuffer str = new StringBuffer();
        while (it.hasNext())
            str.append(it.next()).append(ERROR_REASONS_SEPERATOR);
        
        return str.substring(0,str.length()-1); //remove the ending ERROR_REASONS_SEPERATOR
    }
    
    private void updateRejectByFormNumber(CommsTransLog rejLog, CommsImportStatistics stat)
    {
        if (rejLog == null || stat == null)
            return;
        
        String formNumber = null;
        Hashtable rejectReasonPerLetterTypeTable = null;
        CommsTransByFormNumber transByForm = null;
        
        Hashtable rejectPerTypeNumberTable = stat.getRejectPerTypeNumberTable();
        Enumeration enumer = rejectPerTypeNumberTable.keys();
        
        while (enumer.hasMoreElements())
        {
            transByForm = new CommsTransByFormNumber();
            formNumber = (String)enumer.nextElement();
            
            transByForm.setFormNumber(formNumber);
            transByForm.setFormNumberRecordCount((Integer)rejectPerTypeNumberTable.get(formNumber));
            
            rejectReasonPerLetterTypeTable = stat.getRejectReasonPerLetterTypeTable();
            if (rejectReasonPerLetterTypeTable != null)
                transByForm.setFormNumberRejectReasons(
                        getFormNumberRejectReasons((Set)rejectReasonPerLetterTypeTable.get(formNumber)));
            
            rejLog.addTransByFormNumber(transByForm);
        }
    }
    
    private String getFormNumberRejectReasons (Set rejectReasons)
    {
        if (rejectReasons == null || rejectReasons.isEmpty())
            return null;
        
        Iterator it = rejectReasons.iterator();
        StringBuffer str = new StringBuffer();
        while (it.hasNext())
            str.append(it.next()).append(ERROR_REASONS_SEPERATOR);
        
        return str.substring(0,str.length()-1); //remove the ending ERROR_REASONS_SEPERATOR
    }
    
    private void updateMailedByFormNumber(CommsTransLog mailedLog, CommsImportStatistics stat)
    {
        if (mailedLog == null || stat == null)
            return;
        
        String formNumber = null;
        CommsTransByFormNumber transByForm = null;
        
        Hashtable mailedPerTypeNumberTable = stat.getMailedPerTypeNumberTable();
        Enumeration enumer = mailedPerTypeNumberTable.keys();
        
        while (enumer.hasMoreElements())
        {
            transByForm = new CommsTransByFormNumber();
            formNumber = (String)enumer.nextElement();
            
            transByForm.setFormNumber(formNumber);
            transByForm.setFormNumberRecordCount((Integer)mailedPerTypeNumberTable.get(formNumber));
            
            mailedLog.addTransByFormNumber(transByForm);
        } 
    }

	/**
	 * @return Returns the commsLogService.
	 */
	public CommsLogService getCommsLogService() {
		return commsLogService;
	}

	/**
	 * @param commsLogService The commsLogService to set.
	 */
	public void setCommsLogService(CommsLogService commsLogService) {
		this.commsLogService = commsLogService;
	}

}
