/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.fw.hl7.builder;

// Java classes
import gov.va.med.fw.hl7.InvalidMessageException;
import gov.va.med.fw.hl7.Message;
import gov.va.med.fw.hl7.MessageParser;
import gov.va.med.fw.service.ConfigurationConstants;
import gov.va.med.fw.util.Reflector;
import gov.va.med.fw.util.builder.AbstractBuilder;
import gov.va.med.fw.util.builder.Builder;
import gov.va.med.fw.util.builder.BuilderException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.jms.JMSException;

import org.apache.commons.lang.builder.ToStringBuilder;

/**
 * @author Vu Le
 * @version 1.0
 */
public class MessageBuilder extends AbstractBuilder {

   /**
	 * An instance of serialVersionUID
	 */
	private static final long serialVersionUID = 2385097269686572241L;

	/**
    * Comment for <code>MESSAGE_TYPE</code>
    */
   public static final String MESSAGE_TYPE = ConfigurationConstants.DEFAULT_MESSAGE_TYPE;

   private static final String ORUZ07 = "ORUZ07";
   private static final String ORFZ07 = "ORFZ07";
   private static final String PID = "PID^";
   private static final String PD1 = "PD1";
   /**
    * Comment for <code>messageTypes</code>
    */
   private Map messages = null;

   private ArrayList builders = null;

   private Class messageClass = null;

   public MessageBuilder() {
      super();
   }

   public ArrayList getBuilders() {
      return builders;
   }

   public void setBuilders( ArrayList builders ) {
      this.builders = builders;
   }

   public Class getMessageClass() {
      return messageClass;
   }

   public void setMessageClass( Class messageClass ) {
      this.messageClass = messageClass;
   }

   /**
    * @return Returns the messageTypes.
    */
   public Map getMessages() {
      return messages;
   }

   /**
    * @param messageTypes The messageTypes to set.
    */
   public void setMessages(Map messages) {
      this.messages = messages;
   }

   public Object build( JMSMetaData data ) throws BuilderException {
      Object output = null;
      try {
         String text = data.getTextMessage();
         if( text != null ) {
            String type = getMessageType( data.getJMSMessage() );
            //CCR 7539, per Terry Moore, only Z07 has PID that may exceed 254 char per line.
            if ((type != null) && (type.equals(ORUZ07) || type.equals(ORFZ07)))
            {
            	text = mergePIDLineBreak(text);
            }
            String msg = (String)getMessages().get( type );
            output = Reflector.instantiate( msg, new Object[] {text,type} );
         }
      }
      catch( Exception e ) {
         if( logger.isDebugEnabled() ) {
            logger.debug( "Input data" );
            logger.debug( ToStringBuilder.reflectionToString( data ) );
         }
         throw new BuilderException( "Failed to build an inbound message", e );
      }
      return output;
   }

   //CCR 7539
   private String mergePIDLineBreak(String txt)
   {
	   boolean beforePID = true; //has not reached PID segment
	   boolean afterPID = false; //has passed PID segment

	   StringBuffer str = new StringBuffer();

	   String[] lines = txt.split(MessageParser.MESSAGE_LINE_SEPARATOR);

	   for (int i=0; i<lines.length; i++)
	   {
		   if (beforePID)
		   {
			   if (lines[i].startsWith(PID))
			   {
	 			   //entering PID segment
				   beforePID = false;
				   if (lines[i+1].startsWith(PD1))
				   {
					   //PID followed by PD1 next line, no wrapp around issue with PID,
					   //return original txt
					   return txt;
				   } else
				   {
					   //PID wrapped, merge without line break
					   str.append(lines[i]);
				   }
			   } else
			   {
				   //has not reached PID segment yet
				   str.append(lines[i]).append(MessageParser.MESSAGE_LINE_SEPARATOR);
			   }
		   } else
		   {
			   if (afterPID)
			   {
				   //after PID segment
				   str.append(lines[i]).append(MessageParser.MESSAGE_LINE_SEPARATOR);
			   } else
			   {
				   if (lines[i].startsWith(PD1))
				   {
					   //left PID segment and entering next PD1 segment
					   //CCR 10412: make sure when starts with PD1, add line break in front of PD1 so
					   // that PD1 will be started as a separate line
					   afterPID = true;
					   str.append(MessageParser.MESSAGE_LINE_SEPARATOR).append(lines[i]).append(MessageParser.MESSAGE_LINE_SEPARATOR);

				   } else
				   {
					   //still within PID segment, merge without line break
					   str.append(lines[i]);
				   }
			   }
		   }
	   }
	   return str.toString();
   }


   public Object build( HL7MetaData data ) throws BuilderException {

      ArrayList builders = getBuilders();
      if( builders == null || builders.isEmpty() ) {
         throw new BuilderException( "A list of segment builders must be configured prior to this call");
      }
      Class msgClass = getMessageClass();
      if( msgClass == null ) {
         throw new BuilderException( "A message class [messageClass] must be configured prior to this call");
      }

      Message message = null;
      try {
         message = (Message)msgClass.newInstance();
         ArrayList segments = new ArrayList();
         for( int i=0; i<builders.size(); i++ ) {
            Object item = builders.get( i );

            // A builder is configured with a list of concrete
            // segment builders which is also of type builder
            if( item instanceof Builder ) {
               Builder builder = (Builder)item;
               Object output = builder.build( data );

               // A segment builder could return one or more segments
               if( output instanceof List ) {
                  segments.addAll( (List)output );
               }
            }
         }
         // Completed building all segments so add all segments to a message
         message.setSegments( segments );
         //CCR12710
         if( logger.isDebugEnabled() ) {
             logger.debug( "MessageBuilder complete for message:" + message );
          }
      }
      catch( Exception e ) {
         if( logger.isDebugEnabled() ) {
            logger.debug( "Input data" );
            logger.debug( ToStringBuilder.reflectionToString( data ) );
         }
         throw new BuilderException( "Failed to build an outbound message", e );
      }
      return message;
   }

   /**
    * Returns a concrete message type
    *
    * @param msg The message text.
    * @return The message type.
    */
   protected String getMessageType( javax.jms.Message msg ) throws InvalidMessageException {
      String type = null;
      try {
         type = msg.getStringProperty( MESSAGE_TYPE );
      }
      catch( JMSException e ) {
         throw new InvalidMessageException( "Failed to get a message type from a message", e );
      }
      return type;
   }
}