package gov.va.med.esr.messaging.service.inbound;

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

import gov.va.med.esr.common.model.lookup.AckType;
import gov.va.med.esr.common.model.messaging.MessageLogEntry;
import gov.va.med.esr.messaging.service.outbound.OutboundProcessService;
import gov.va.med.fw.hl7.Message;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.StopWatchLogger;

/**
 * Processes al l inbound Queries e.g QRYZ10, QRYZ11
 * 
 * @author Rajiv Patnaik Created on Dec 13, 2005
 * @version 1.0
 * 
 * Copyright © 2005 VHA. All rights reserved
 */
public class QueryInboundProcessService extends AbstractInboundMessagingService
{

    private OutboundProcessService solicitatedAckAEService;

    private InboundProcessService messageProcessService;
    
    private String NO_DATA_ON_FILE = "NO_DATA_ON_FILE";
    
    /**
     * Type of ORF message that needs to be sent back. e.g a QRYZ10 will need a ORFZ10.
     */
    private String errorMessageType;
    
    /**
     * @return Returns the aeAckService.
     */
    public OutboundProcessService getSolicitatedAckAEService()
    {
        return solicitatedAckAEService;
    }

    /**
     * @param aeAckService
     *            The aeAckService to set.
     */
    public void setSolicitatedAckAEService(OutboundProcessService aeAckService)
    {
        this.solicitatedAckAEService = aeAckService;
    }

    /**
     * @return Returns the errorMessageType.
     */
    public String getErrorMessageType()
    {
        return errorMessageType;
    }
    /**
     * @param errorMessageType The errorMessageType to set.
     */
    public void setErrorMessageType(String errorMessageType)
    {
        this.errorMessageType = errorMessageType;
    }
    /*
     * (non-Javadoc)
     * 
     * @see gov.va.med.esr.messaging.service.inbound.InboundProcessService#processMessage(gov.va.med.fw.hl7.Message)
     */
    public MessageLogEntry processMessage(Message message)
            throws InboundProcessException
    {
        Validate.notNull(message, "Message cannot be null");
        
        MessageLogEntry messageLogEntry = null;

        StopWatchLogger watch = null;
        if( logger.isDebugEnabled() ) {
           watch = new StopWatchLogger( ClassUtils.getShortClassName( getClass() ) + " processMessage" );
           if (watch != null) {
        	   watch.start();
           }
        }
                
        try
        {
            initAuditIdFromMessage(message);

            messageLogEntry = doProcess(message);

        } catch (Throwable e)
        {
            //Log the exception and we are done.
            logger.error("Failed to process Query inbound message " +message.getMessageData(), e);
            
        } finally {
			if (logger.isDebugEnabled()) {
				try {
					StringBuffer info = new StringBuffer(
							"Total time to process ").append(getMessageProcessService().getMessageType().getCode()).append(" ID: ").
							append(super.getControlIdentifier(message));
					if (watch != null) {
						watch.stopAndLog(info.toString());
					}
				} catch (Exception e) {
				}
			}
		}

        return messageLogEntry;
    }

    /**
     * Method to process single message.
     * 
     * <li>Calls appropriate service(e.g QRYZ10ProcessService) to do to do
     * message specific stuff</li>
     * <li>Calls Messaging Service to persist the MessageLogEntry in the
     * transaction log</li>
     * 
     * @param batchControlIdentifier
     * @param batchTransmissionDate
     * @param message
     * @return The message log entry.
     */
    private MessageLogEntry doProcess(Message message)
    {
        MessageLogEntry logEntry = null;
        try
        {
            //Call appropriate service(e.g QRYZ10ProcessService) to do
            // message specific stuff e.g making a rules call
            //which will trigger a response message e.g (ORFZ10 message)
            logEntry = this.messageProcessService.processMessage(message);

        } catch (InboundProcessException e)
        {
            logEntry = e.getLogEntry();
            //The log files were getting filled with this error therefore added this to a debug only case
            if(logEntry.getErrorText().equals(NO_DATA_ON_FILE)){
            	if (logger.isDebugEnabled()){
            		logger.debug("Error processing query message - " + message, e);
            	}            	
            }
            else{
            	logger.error("Error processing query message - " + message, e);            	
            }            
            
        }catch (Throwable e)
        {
       	 try {
				logEntry = super.createErrorMessageLogEntry(message, e, getMessageProcessService().getMessageType());
			} catch (ServiceException ex) {
				// Could neither process the message nor create a
				// MessageLogEntry object to persist into the transaction log.
				// Dump the message data for troubleshooting.
				logger.error("Could not process Query message nor log "
						+ "into transaction log: " + message.getMessageData(),
						ex);
			}         	
        }

        if (logEntry != null)
        {
            //Call Messaging Service to persist the MessageLogEntry in the
            // transaction log
            super.logMessage(logEntry);
            
            sendAEAcknowledgement(message, logEntry);
        }
        
        return logEntry;
    }

    /**
     * Send a ORF AE back to the site
     * 
     * @param message
     * @param messageLogEntry
     */
    private void sendAEAcknowledgement(Message message,
            MessageLogEntry messageLogEntry)
    {

        String ackType = messageLogEntry.getAckType() == null ? null
                : messageLogEntry.getAckType().getCode();
        try
        {
            //If it is not an AA, send a Reject message back to the site
            if (!AckType.CODE_AA.getName().equals(ackType))
            {
                message.setType(this.errorMessageType);

                this.solicitatedAckAEService.processMessage(new Object[] { message,
                        messageLogEntry });
            }

        } catch (ServiceException e)
        {
            // Could not send acknowledgement.
            if (super.logger.isErrorEnabled())
            {
                super.logger.error("Could not send AE acknowledgement for message " +message.getMessageData(), e);
            }

            // Set all the ack dates of the message log entries to null

            messageLogEntry.setAckDate(null);
        }
    }

    /**
     * @return Returns the messageProcessService.
     */
    public InboundProcessService getMessageProcessService()
    {
        return messageProcessService;
    }

    /**
     * @param messageProcessService
     *            The messageProcessService to set.
     */
    public void setMessageProcessService(
            InboundProcessService messageProcessService)
    {
        this.messageProcessService = messageProcessService;
    }
    
}