/********************************************************************
 * Copyright © 2004 EDS. All rights reserved
 ********************************************************************/
package gov.va.med.esr.messaging.service.outbound;

// Java Classes
import java.util.Date;

import gov.va.med.fw.hl7.BatchMessage;
import gov.va.med.fw.hl7.InvalidMessageException;
import gov.va.med.fw.hl7.Message;
import gov.va.med.fw.hl7.constants.SegmentConstants;
import gov.va.med.fw.hl7.segment.MSH;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.builder.Builder;
import gov.va.med.fw.util.builder.BuilderException;

import gov.va.med.esr.common.model.lookup.MessageStatus;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.messaging.MessageLogEntry;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.messaging.constants.HL7Constants;
import gov.va.med.esr.messaging.service.AbstractMessagingService;
import gov.va.med.esr.messaging.service.MessageProcessServiceUtil;
import gov.va.med.esr.messaging.service.MessagePublisher;

/**
 * Abstract class that should be extended by outbound message services.
 * 
 * @author Carlos Ruiz
 * @version 1.0
 */
public abstract class AbstractOutboundProcessService
    extends AbstractMessagingService
{
    private Builder batchBuilder;
    private Builder messageBuilder;
    private MessagePublisher messagePublisher;

    /**
     * Constructor that initializes object with DAO's.
     */
    public AbstractOutboundProcessService()
    {
        super();
    }

    public Builder getBatchBlr()
    {
        return this.batchBuilder;
    }

    public void setBatchBuilder(Builder batchBuilder)
    {
        this.batchBuilder = batchBuilder;
    }

    /**
     * @return Returns the messageBuilder.
     */
    public Builder getMessageBuilder()
    {
        return this.messageBuilder;
    }

    /**
     * @param messageBuilder
     *        The messageBuilder to set.
     */
    public void setMessageBuilder(Builder messageBuilder)
    {
        this.messageBuilder = messageBuilder;
    }

    /**
     * @return Returns the messagePublisher.
     */
    public MessagePublisher getMessagePublisher()
    {
        return this.messagePublisher;
    }

    /**
     * @param messagePublisher
     *        The messagePublisher to set.
     */
    public void setMessagePublisher(MessagePublisher messagePublisher)
    {
        this.messagePublisher = messagePublisher;
    }

    protected final BatchMessage buildBatch(Message message)
        throws BuilderException
    {
        return this.buildBatch(new Message[] { message });
    }

    protected final BatchMessage buildBatch(Message[] messages)
        throws BuilderException
    {
        return (BatchMessage)this.batchBuilder.build(messages);
    }

    protected final Message buildMessage(Object input) throws BuilderException
    {
        return (Message)this.messageBuilder.build(input);
    }

    protected final Date buildTransmissionDate(Message message)
        throws BuilderException,
        InvalidMessageException
    {
        String dateValue = ((MSH)message.getSegment(SegmentConstants.MSH))
            .getCreationDate();

        return this.buildDate(dateValue);
    }

    protected final MessageLogEntry createMessageLog(
        Message message,
        String batchControlIdentifier,
        String messageControlIdentifier,
        Person person,
        VAFacility vaFacility,
        Date transmissionDate)
    {
        try
        {
        	MessageLogEntry logEntry = MessageProcessServiceUtil
                .createMessageLog(
                    messageControlIdentifier,
                    batchControlIdentifier,
                    super.getMessageType(),
                    super
                        .getMessageStatus(MessageStatus.AWAITING_ACKNOWLEDGEMENT),
                    vaFacility, message.getMessageData(),
                    MessageProcessServiceUtil.getFormattedMessageBody(super
                        .formatMessage(getMessageWithUnwrappedSegments(message))), person, transmissionDate,
                    null, null, null, null);
        	
        	if(logger.isDebugEnabled())
        		logger.debug("MessageLogEntry object created: " + logEntry.getFormattedBody());
        	
        	return logEntry;
        }
        catch(BuilderException e)
        {
            //ASSERT: Should not happen if the message was built correctly.
            throw new RuntimeException(
                "Failed format and validate the message", e);
        }
    }
    
    protected final MessageLogEntry createMessageLog(
            Message message,
            String batchControlIdentifier,
            String messageControlIdentifier,
            Person person,
            VAFacility vaFacility,
            Date transmissionDate,
            MessageStatus.Code messageStatus)
        {
            try
            {
            	MessageLogEntry logEntry = MessageProcessServiceUtil
                    .createMessageLog(
                        messageControlIdentifier,
                        batchControlIdentifier,
                        super.getMessageType(),
                        super
                            .getMessageStatus(messageStatus),
                        vaFacility, message.getMessageData(),
                        MessageProcessServiceUtil.getFormattedMessageBody(super
                            .formatMessage(getMessageWithUnwrappedSegments(message))), person, transmissionDate,
                        null, null, null, null);
            	
            	if(logger.isDebugEnabled())
            		logger.debug("MessageLogEntry object created: " + logEntry.getFormattedBody());
            	
            	return logEntry;
            }
            catch(BuilderException e)
            {
                //ASSERT: Should not happen if the message was built correctly.
                throw new RuntimeException(
                    "Failed format and validate the message", e);
            }
        }
    
    protected final MessageLogEntry createMessageLog(
            Message message,
            String batchControlIdentifier,
            String messageControlIdentifier,
            Person person,
            VAFacility vaFacility,
            Date transmissionDate,
            String retransmissionInfo)
        {
            try
            {
            	MessageLogEntry logEntry = MessageProcessServiceUtil
                    .createMessageLog(
                        messageControlIdentifier,
                        batchControlIdentifier,
                        super.getMessageType(),
                        super
                            .getMessageStatus(MessageStatus.AWAITING_ACKNOWLEDGEMENT),
                        vaFacility, message.getMessageData(),
                        MessageProcessServiceUtil.getFormattedMessageBody(super
                            .formatMessage(getMessageWithUnwrappedSegments(message))), person, transmissionDate,
                        null, null, null, null, retransmissionInfo);
            	
            	if(logger.isDebugEnabled())
            		logger.debug("MessageLogEntry object created: " + logEntry.getFormattedBody());
            	
            	return logEntry;
            }
            catch(BuilderException e)
            {
                //ASSERT: Should not happen if the message was built correctly.
                throw new RuntimeException(
                    "Failed format and validate the message", e);
            }
        }
            
    
    protected final MessageLogEntry createMessageLog (
        Message message,
        String batchControlIdentifier,
        String messageControlIdentifier,
        Person person,
        VAFacility vaFacility,
        Date transmissionDate,
        int retransmissionCount,
        MessageLogEntry initiatingMessage,
        MessageStatus.Code messageStatus)
    {
        try
        {
            return MessageProcessServiceUtil
            .createMessageLog(
                    messageControlIdentifier,
                    batchControlIdentifier,
                    super.getMessageType(),
                    super
                    .getMessageStatus(messageStatus),
                    vaFacility, message.getMessageData(),
                    MessageProcessServiceUtil.getFormattedMessageBody(super
                            .formatMessage(getMessageWithUnwrappedSegments(message))), person, transmissionDate,
                            null, null, null, null, retransmissionCount, initiatingMessage);
        }
        catch(BuilderException e)
        {
            //ASSERT: Should not happen if the message was built correctly.
            throw new RuntimeException(
                    "Failed format and validate the message", e);
        }
    }
    
    protected final MessageLogEntry createMessageLog (
            Message message,
            String batchControlIdentifier,
            String messageControlIdentifier,
            Person person,
            VAFacility vaFacility,
            Date transmissionDate,
            int retransmissionCount,
            MessageLogEntry initiatingMessage)
        {
            try
            {
                return MessageProcessServiceUtil
                .createMessageLog(
                        messageControlIdentifier,
                        batchControlIdentifier,
                        super.getMessageType(),
                        super
                        .getMessageStatus(MessageStatus.AWAITING_ACKNOWLEDGEMENT),
                        vaFacility, message.getMessageData(),
                        MessageProcessServiceUtil.getFormattedMessageBody(super
                                .formatMessage(getMessageWithUnwrappedSegments(message))), person, transmissionDate,
                                null, null, null, null, retransmissionCount, initiatingMessage);
            }
            catch(BuilderException e)
            {
                //ASSERT: Should not happen if the message was built correctly.
                throw new RuntimeException(
                        "Failed format and validate the message", e);
            }
        }
    
    /**
     * Gets the formatted message body and, since this is for outbound messages, omits a call 
     * to the messageFormatter build method which transforms the HL7 message to XML and 
     * validates it against a schema.  The transformation and validation was an expensive
     * operation that was hurting outbound messaging performance.
     *
     * @param message Hibernate object for HL7 message
     *
     * @return String with the formatted message body.
     * @throws BuilderException If there errors occur in transforming or validating the message.
     */
    protected final String doGetFormattedMessageBody(Message message) throws BuilderException {
        return MessageProcessServiceUtil.getFormattedMessageBody( message.getMessageData() );
    }
    
    protected final void publishMessage(Message message)
        throws ServiceException
    {
        this.messagePublisher.publish(message);
    }
    
    /**
     * Convert the outbound message which could contain wrapped segments into a message with no wrapped segments.
     * This will ensure correct parsing and validation of the when passed through a XML formatter
     * 
     * @param message
     * @return
     */
    private Message getMessageWithUnwrappedSegments(Message message)
    {
        try {
        	return  new Message(message.getSegments(), HL7Constants.ORFZ10, false);
			
		} catch (InvalidMessageException e) {
            throw new RuntimeException(
                    "Failed to format and validate the message", e);
		}

    }
}