/**
 * 
 */


package gov.va.med.mediator.client.messagemediator;


import gov.va.med.cds.util.DateTimeUtil;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.Map;
import java.util.Scanner;

import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;


/**
 * @author DNS   egberb
 *
 */
public class JmsMessageMediatorDelegate
    implements
        MessageMediatorInterface
{

    public static final String SUCCESS = "SUCCESS";
    public static final String ERROR_SECTION = "errorSection";

    private static final String TIUHL7 = "TIUHL7";

    private enum SendingOrganization
    {
        VHA, DOD, IHS
    };

    private static Log logger = LogFactory.getLog( JmsMessageMediatorDelegate.class );

    private Map<String, JmsTemplate> sendingApplicationToJmsTemplateMap;

    private SendingOrganization sendingOrganization;

    private JmsTemplate runtimeJmsTemplate;


    public void setRuntimeJmsTemplate( JmsTemplate runtimeJmsTemplate )
    {
        this.runtimeJmsTemplate = runtimeJmsTemplate;
    }


    /*
     * (non-Javadoc)
     * @see gov.va.med.cds.client.messagemediator.MessageMediatorInterface#isAlive()
     */
    public boolean isAlive( )
    {
        return true;
    }


    /*
     * (non-Javadoc)
     * @see gov.va.med.cds.client.messagemediator.MessageMediatorInterface#processMessage(java.lang.String)
     */
    public String processMessage( String hl7MessageRequest )
    {
        return processMessage( hl7MessageRequest, false );
    }


    /*
     * (non-Javadoc)
     * @see gov.va.med.cds.client.messagemediator.MessageMediatorInterface#processMessage(java.lang.String, java.lang.Boolean)
     */
    public String processMessage( final String hl7MessageRequest, final Boolean persistOriginalMessage )
    {

        JmsTemplate jmsTemplate = null;

        String receivingAppKey = null;

        try
        {

            switch ( sendingOrganization )
            {
            case VHA:
                String sendingApplication = getMessageSendingApplication( hl7MessageRequest );

                // The TIUHL& write app ack has the destination queue name in MSH 6-1
                if ( sendingApplication.equals( TIUHL7 ) )
                {
                    try
                    {
                    	receivingAppKey = getReceivingApplication( hl7MessageRequest );

                        if ( receivingAppKey != null )
                        {
                            jmsTemplate = sendingApplicationToJmsTemplateMap.get( receivingAppKey );
                        }
                    }
                    catch ( Exception e )
                    {
                        // Default queue jms/fpds/DefaultHl7AckQueue will be used  
                    }
                    finally
                    {
                        boolean isQueueInfoInMSH = ( null == jmsTemplate );

                        if ( isQueueInfoInMSH )
                        {
                            jmsTemplate = sendingApplicationToJmsTemplateMap.get( sendingApplication );
                        }
                    }
                }
                else
                {
                    jmsTemplate = sendingApplicationToJmsTemplateMap.get( sendingApplication );
                }

                if ( jmsTemplate == null )
                {
                    throw new IllegalArgumentException( String.format( "No JMS Template defined for '%s'.", sendingApplication ) );
                }

                break;
            case DOD:
                jmsTemplate = sendingApplicationToJmsTemplateMap.get( "CHDR_QUEUE" );
                break;
            case IHS:
            default:
                throw new IllegalArgumentException( "Invalid sending orgainzation." );
            }

            sendFromJMSTemplate( jmsTemplate, hl7MessageRequest, persistOriginalMessage );

        }
        catch ( Exception e )
        {
            logger.debug( "Error processing message.", e );

            return createErrorMessage( e.getMessage() );
        }

        return SUCCESS;
    }


    private String getReceivingApplication( String hl7MessageRequest )
    {
        String receivingApplication = "";
        String[] parsedMSH = null;

        if ( ( hl7MessageRequest != null ) && hl7MessageRequest.startsWith( "MSH" ) )
        {
            char fieldSeparator = hl7MessageRequest.charAt( 3 );
            parsedMSH = hl7MessageRequest.split( String.valueOf( "\\" + fieldSeparator ) );
            receivingApplication = parsedMSH[4];
        }

        return receivingApplication;

    }


    /***
     * The original message control id is contained in the MSA segment
     * @param hl7MessageRequest
     * @return
     */
    private String getOriginalMessageControlId( String hl7MessageRequest )
    {
        Scanner scanner = new Scanner( hl7MessageRequest );

        String messageControlId = "";
        String parsedMSA = null;

        if ( ( hl7MessageRequest != null ) && hl7MessageRequest.startsWith( "MSH" ) )
        {
            String[] parsedHl7Message = null;

            char fieldSeparator = hl7MessageRequest.charAt( 3 );

            while ( scanner.hasNextLine() )
            {
                String line = scanner.nextLine();
                if ( line.startsWith( "MSA" ) )
                {
                    parsedMSA = line;
                    break;
                }

            }
            scanner.close();

            if ( parsedMSA != null )
            {
            	parsedHl7Message = parsedMSA.split( String.valueOf( "\\" + fieldSeparator ) );
            }

            if ( parsedHl7Message != null )
            {
                messageControlId = parsedHl7Message[2];
            }
        }

        return messageControlId;
    }


    private String getFieldComponentInfo( String field, String fieldSeparator, int componentPosition )
    {

        String[] parsedField = field.split( fieldSeparator );

        return parsedField[componentPosition];
    }


    protected String createErrorMessage( String message )
    {
        //get VDEFVIE4 and date
        String vdef = getVDEFString( message );
        String date = createMessageDateTime();

        String errorMessage = "<errorSection>" + "CDS-Socket-Facade: Error sending message to queue. " + date + " " + vdef + "</errorSection>";
        return errorMessage;
    }


    protected String getVDEFString( String message )
    {
        if ( message == null )
            return "";
        int beginIndex = message.indexOf( "VDEF" );
        if ( beginIndex < 0 )
        {
            return "";
        }
        String vdefString = message.substring( beginIndex );
        return ( vdefString.indexOf( " " ) < 0 ) ? vdefString : vdefString.substring( 0, vdefString.indexOf( " " ) );
    }


    // MSH|^~\&|LA7LAB|...  - Nothing else beyond this point matters.
    public String getMessageSendingApplication( String hl7MessageRequest )
        throws Exception
    {
        String messageSendingApplication = "";
        String[] parsedMSH = null;

        if ( ( hl7MessageRequest != null ) && hl7MessageRequest.startsWith( "MSH" ) )
        {
            char fieldSeparator = hl7MessageRequest.charAt( 3 );
            parsedMSH = hl7MessageRequest.split( String.valueOf( "\\" + fieldSeparator ) );
            messageSendingApplication = parsedMSH[2];
        }

        if ( "HDRVTLS".equals( messageSendingApplication ) )
        {
		    if ( ( parsedMSH != null ) && ( parsedMSH.length > 4 ) && ( "HTH HDR".equals( parsedMSH[4] ) ) )
            {
                messageSendingApplication += "-HTH";
            }
        }

        return messageSendingApplication;
    }


    public void setSendingOrganization( String sendingOrganization )
    {
        this.sendingOrganization = SendingOrganization.valueOf( sendingOrganization );
    }


    public void setSendingApplicationToJmsTemplateMap( Map<String, JmsTemplate> sendingApplicationToJmsTemplateMap )
    {
        this.sendingApplicationToJmsTemplateMap = sendingApplicationToJmsTemplateMap;
    }


    public void sendFromJMSTemplate( JmsTemplate jmsTemplate, final String hl7MessageRequest, final Boolean persistOriginalMessage )
    {
        jmsTemplate.send( new MessageCreator()
        {
            public Message createMessage( Session session )
                throws JMSException
            {
                BytesMessage message = session.createBytesMessage();
                message.setBooleanProperty( MessageMediatorInterface.PROP_PERSIST_ORIGINAL, persistOriginalMessage );

                // encode the HL7 message
                Charset charset = Charset.forName( "ISO-8859-1" );
                ByteBuffer buffer = charset.encode( hl7MessageRequest );

                // Set Message Control for TIU App Ack messages
                try
                {
                    if ( getMessageSendingApplication( hl7MessageRequest ).equals( TIUHL7 ) )
                    {
                        message.setJMSCorrelationID( getOriginalMessageControlId( hl7MessageRequest ) );
                    }
                }
                catch ( Exception e )
                {
                    // Correlation Id for TIU not set
                }

                message.writeBytes( buffer.array() );

                return message;
            }
        } );

    }


    protected String createMessageDateTime( )
    {
        return DateTimeUtil.getDateTimeDotSeparated( new Date().getTime(), null );
    }
}
