/**
 * Sender.java Created on June 24, 2004, 9:31 AM
 */

package gov.va.med.esr.vitria;

// Java Classes
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;

import javax.jms.BytesMessage;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;

import org.apache.commons.lang.StringUtils;

import gov.va.med.fw.hl7.HL7MessageUtils;
import gov.va.med.fw.hl7.Message;

import gov.va.med.esr.messaging.constants.HL7Constants;

/**
 * @author DNS   LEV
 */
public class Sender {

   // Optional keys defined in config.properties
   private static int REPEAT_COUNT_DEFAULT = 1;
   private static boolean DELETE_FILE_DEFAULT = true;
   private static final String QRYZ07 = "QRYZ07";

   // Maintain a session and a sender to send all messages 
   // of the same type in one session in one connection
   private static QueueConnectionFactory factory = null;
   private static InitialContext jndi = null;
   private QueueSession session = null;
   private QueueSender sender = null;
   private QueueConnection connection = null;

   
   /** Creates a new instance of Sender */
   public Sender( String context,
                  String factoryName,
                  String serverName,         
                  String queueName ) throws Exception {
      super();

      // This is to emulate Vitria caching initial context and connection factory
      if( jndi == null ) {
         // Create a context
         Hashtable data = new Hashtable();
         data.put( Context.INITIAL_CONTEXT_FACTORY, context );
         data.put( Context.PROVIDER_URL, serverName );
         jndi = new InitialContext( data );
      }
      if( factory == null ) {
         // Look up JMS connection factory  
         factory = (QueueConnectionFactory)PortableRemoteObject.narrow( jndi.lookup( factoryName ), 
                                                                        QueueConnectionFactory.class );
      }
      // Look up JMS connection factory and queue  
      Queue queue = (Queue)PortableRemoteObject.narrow(jndi.lookup(queueName), Queue.class);
      
      // Create connection, session, and sender
      connection = factory.createQueueConnection();
      session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
      sender = session.createSender(queue);
   }

   public void send( List messages, int count ) throws Exception {

      /**
       * text.setStringProperty("Message_Id", this.message_id);
       * text.setStringProperty("Message_Type", this.messageType);
       * text.setStringProperty("Sending_Application", this.sending_Application);
       * text.setStringProperty("Sending_Site", this.sending_Site);
       */
      for( int loop=0; loop<count; loop++ ) {
         for( Iterator i=messages.iterator(); i.hasNext(); ) {
            Message msg = (Message)i.next();
            String messageType = getFormattedMessageType( msg );
            BytesMessage bytes = this.session.createBytesMessage();
            bytes.writeBytes( msg.getMessageData().getBytes() );
            bytes.setStringProperty( MockVitriaConstants.MESSAGE_ID, msg.getMessageID() );
            bytes.setStringProperty( MockVitriaConstants.MESSAGE_TYPE, messageType );
            bytes.setStringProperty( MockVitriaConstants.SENDING_APPLICATION, msg.getSendingApplication() );
            bytes.setStringProperty( MockVitriaConstants.SENDING_SITE, msg.getSendingFacility() );
            
            bytes.setJMSRedelivered( true );
            bytes.setJMSTimestamp( System.currentTimeMillis() );

            long start = System.currentTimeMillis();
            System.out.println("Beging sending messages. Start Time is " + start);
            sender.send(bytes);
            System.out.println("Sent a message of type " + messageType );
            
            long stop = System.currentTimeMillis();
            System.out.println("Stop time is " + stop);
            System.out.println("Elapse time in millis " + ( stop - start ));
         }
      }
      System.out.println( "Complete sending " + (messages.size() * count) + " messages" );
   }
   
   public void close() throws Exception {
      if( connection != null && session != null && sender != null ) {
         connection.close();
         session.close();
         sender.close();
      }
      connection = null;
      session = null;
      sender = null;
   }

   public static void main(String[] args) throws Exception {

      if( args.length != 1 ) {
         displayHelp();
         System.exit(-1);
      }

      /*----------------- Read Config --------------------*/
      ResourceBundle bundle = getResourceBundle(args[0]);
      int repeatCount = REPEAT_COUNT_DEFAULT;
      try {
         String repeatCountStr = bundle.getString(MockVitriaConstants.REPEAT_COUNT_KEY);
         if( StringUtils.isNotBlank(repeatCountStr) ) {
            repeatCount = Integer.parseInt(repeatCountStr);
         }
      }
      catch( MissingResourceException e ) { /* do nothing */ }

      boolean deleteFile = DELETE_FILE_DEFAULT;
      try {
         String deleteFileStr = bundle.getString(MockVitriaConstants.DELETE_FILE_KEY);
         if( StringUtils.isNotBlank(deleteFileStr) ) {
            deleteFile = Boolean.valueOf(deleteFileStr).booleanValue();
         }
      }
      catch( MissingResourceException e ) { /* do nothing */ }

      // Read configuration to create a sender
      String context = bundle.getString(MockVitriaConstants.JNDI_CTX_KEY);
      String factoryName = bundle.getString(MockVitriaConstants.INBOUND_FACTORY_KEY);
      String serverName = bundle.getString(MockVitriaConstants.INBOUND_URL_KEY);
      
      

		
      
      
      /*------------------ Read and send messages -----------------------------------*/
      File directory = new File(bundle.getString(MockVitriaConstants.INBOUNDMESSAGEDIRECTORY));
      if( !directory.exists() )
         directory.mkdir(); // Create Directory

      if( ( directory != null ) && ( directory.isDirectory() ) ) {
         boolean quitDirectory = false;

         do {
            // Build a map of different message types
            File[] listOfFiles = directory.listFiles();
            Map msgTypes = new HashMap();
            for( int i = 0; i < listOfFiles.length; i++ ) {
               System.out.println("Processing file : " + listOfFiles[i].getName());

               // Create a HL7 message and format a message type
               Message msg = new Message(buildMessage(listOfFiles[i]));
               String msgType = getFormattedMessageType( msg ); 
               
               // Organize messages by types 
               ArrayList messages = null;
               if( msgTypes.containsKey( msgType ) ) {
                  messages = (ArrayList)msgTypes.get( msgType );
               }
               else {
                  messages = new ArrayList();
                  msgTypes.put( msgType, messages );
               }
               messages.add( msg );
               
               if( deleteFile ) {
                  listOfFiles[i].delete();
               }
            }
            
            // Send each message type in its own session
            Set entries = msgTypes.keySet();
            for( Iterator i= entries.iterator(); i.hasNext(); ) {
               // Get a list of messages keyed by a message type
			   String msgType = (String)i.next();
			   
			   String queueName = getMessageQueueName( msgType );
			   String jndiQueueSuffix = "";
			   try {
				jndiQueueSuffix = bundle.getString(MockVitriaConstants.JNDI_QUEUE_SUFFIX_KEY);
			   } catch(Exception e) { }			   
			   queueName = queueName + jndiQueueSuffix;
			   
			   System.out.println(" Sending Message to Queue :" + queueName);
			   System.out.println(" Sending Message to factoryName :" + factoryName);
			   System.out.println(" Sending Message to serverName :" + serverName);
			   
               Sender sender = new Sender( context, 
                                           factoryName, 
                                           serverName, 
                                           queueName );
               
               sender.send( (List)msgTypes.get( msgType ), repeatCount );
               sender.close();
            }

            // Check if we need to quit
            System.out.print("Enter (\"quit\" to quit): ");
            BufferedReader msgStream = new BufferedReader(new InputStreamReader(System.in));
            String line = msgStream.readLine();
            if( line != null && line.trim().length() != 0 ) {
               quitDirectory = line.equalsIgnoreCase("quit");
            }
         }
         while( !quitDirectory );
      }
   }

   private static String getMessageQueueName( String messageType ) {
      
      System.out.println("**** Message type ****" + messageType );

      // Map a message type to a queue name
      String queueName = null;
      if(messageType.startsWith("ORU")) {
         queueName = MockVitriaConstants.INBOUND_DATA_QUEUE_JNDI;
      }
      else if(messageType.startsWith("ORF")) {
         queueName = MockVitriaConstants.INBOUND_SOLICITED_QUEUE_JNDI;
      }
      else if(messageType.startsWith("QRY")) {
         queueName = MockVitriaConstants.INBOUND_QUERY_QUEUE_JNDI;
      }
      else if( HL7Constants.ACK.equals( messageType ) || 
    		  HL7Constants.MFNZEG.equals( messageType ) ) {
         queueName = MockVitriaConstants.INBOUND_ACK_QUEUE_JNDI;
      }
      else {
         throw new RuntimeException("Invalid message to map to a queue");
      }
      return queueName;
   }
   
   private static String getFormattedMessageType( Message message ) throws Exception {
      
      String formattedType = null;
      if( HL7MessageUtils.isAck( message ) ) {
         formattedType = HL7Constants.ACK;
      }
      else {
         String msgType = message.getMessageType();
         if( msgType != null ) {
            formattedType = msgType.substring( 0, 3 ) + msgType.substring( 4, 7 );
         }
         else {
            throw new RuntimeException("Failed to get an invalid message type");
         }
      }
      // This is because an ORFZ07 hasa QRY~Z07 in a MSH segment as a message type
      formattedType = QRYZ07.equals( formattedType ) ? "ORFZ07" : formattedType; 
      return formattedType;
   }
   
   private static String buildMessage(File file) throws Exception {

      StringBuffer message = new StringBuffer();
      FileInputStream stream = new FileInputStream(file);

      BufferedReader reader = new BufferedReader(new InputStreamReader(stream));

      String input = null;
      do {
         input = reader.readLine();
         if( input != null ) {
            message.append(input).append("\n");
         }
      }
      while( input != null );
      reader.close();
      stream.close();
      return message.toString();
   }

   private static ResourceBundle getResourceBundle(String fileName) throws Exception {

      if( fileName == null || fileName.length() == 0 ) {
         throw new IllegalArgumentException("Invalid file name");
      }
      // throw MissingResourceException if failed to locate a resource
      return ResourceBundle.getBundle(fileName);
   }

	private static void displayHelp() {
		System.out.println("\n\n");
		System.out.println("Usage: java gov.va.med.esr.Sender <propertyFile>");
		System.out
				.println("Where: <propertyFile>.properties contains JMS configuration information");
	}
}