/********************************************************************
 * Copyright © 2004 EDS. All rights reserved
 ********************************************************************/
//Package
package gov.va.med.esr.messaging.builder.message;

//Java Classes
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import gov.va.med.fw.hl7.BatchMessage;
import gov.va.med.fw.hl7.Message;
import gov.va.med.fw.hl7.constants.DelimeterConstants;
import gov.va.med.fw.hl7.segment.BHS;
import gov.va.med.fw.hl7.segment.BTS;
import gov.va.med.fw.hl7.segment.MSA;
import gov.va.med.fw.hl7.segment.MSH;
import gov.va.med.fw.model.AbstractEntity;
import gov.va.med.fw.util.builder.BuilderException;

import gov.va.med.esr.common.model.lookup.AckType;
import gov.va.med.esr.common.model.messaging.MessageLogEntry;
import gov.va.med.esr.messaging.constants.HL7Constants;
import gov.va.med.esr.common.model.lookup.VAFacility;

/**
 * Class to build the acknowledgement for a message. 
 * 
 * @author Alex Yoon
 * @version 1.0
 */
public class ACKBuilder extends AbstractMessageBuilder {


	//ccr 7443
	public static final  String VOA_PENDING_STATUS = "PENDING";
	public static final  String VOA_ERROR_TEXT = "Person Not Found";
	public static final  String VOA_FINAL_STATUS = "FINAL";

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


   /**
	 * The default constructor.
	 * 
	 */
	public ACKBuilder() {
		super();
	}
	

    public Message build(Object[] args) throws BuilderException {
        
    	if (args[0] instanceof MessageLogEntry)
        {
        	return this.buildVOAFinalAck((MessageLogEntry) args[0]);
        
        }
    	Message message = (Message)args[0];
        return (args.length == 2) ? this.build(message, (List)args[1]) :
            this.build(message);
    }
    
	/**
	 * Method used to build AA ACK Message for VOA sending FINAL Status
	 * 
	 * @param mle
	 *  		Message Log Entry for the original VOA Z07 message.
	 * @return Message ACK message object with "FINAL" status in MSA-3
	 */
	private Message buildVOAFinalAck(MessageLogEntry mle) throws BuilderException {
		if (mle == null) {
			throw new BuilderException("Null Message Log Entry to build a ACK Message");
        }
		
		Message ack = null;
		
	    try {
			List segments = new ArrayList();

			// Build MSH and MSA - adds to segments list

			// 1. Build MSH segment
			MSH segment = new MSH();

			segment.setEncodingCharacters(DelimeterConstants.DEFAULT_ENCODING_ELEMENT);
			segment.setSendingApplication(HL7Constants.HEADER_SENDING_APPLICATION);
			segment.setSendingFacility(getMshBuilder().getHeaderSendingFacility());
			segment.setCreationDate(DateFormatter.formatDateTime(Calendar.getInstance().getTime()).toString());
			segment.setMessageType(HL7Constants.ACK);
//??		segment.setControlID("A" + mle.getControlIdentifier());
			segment.setControlID(mle.getControlIdentifier());
			segment.setProcessingID(getMshBuilder().getProcessingId());
			segment.setVersionID(HL7Constants.VERSION_ID);
			segment.setAcceptAckType(HL7Constants.ACCEPT_ACK_TYPE);
			segment.setApplicationAckType(HL7Constants.APPLICATION_ACK_TYPE_NE);
			segment.setCountryCode(HL7Constants.HEADER_COUNTRY);

			setReceivingApplicationAndFacility(mle, segment);

			segments.add(segment);
			
			// 2. Build MSA segment
			segments.add(buildMSA(AckType.CODE_AA.getName(), mle.getControlIdentifier(), VOA_FINAL_STATUS));

			ack = new Message(segments, HL7Constants.ACK);
		} catch (Exception e) {
			throw new BuilderException(
					"Failed to build ACK message due to an exception ", e);
		}
	    
		return ack;
	}
	
	private void setReceivingApplicationAndFacility(MessageLogEntry mle, MSH segment)
	{
		String msgBody = mle.getBody();
		
		String[] fields= msgBody.split("\\^", 5);
	        
        segment.setReceivingApplication( fields[2] );
        segment.setReceivingFacility( fields[3]);
	}
	
	private String getReceivingFacility(MessageLogEntry mle)
	{
		return mle.getVaFacility().getStationNumber();
	}
	/**
	 * Method used to build AA ACK Message
	 * 
	 * @param message
	 *  		Inbound message that is reason for ACK.
	 * @return Message ACK message object
	 */
	public Message build(Message message) throws BuilderException {
		if (message == null) {
			throw new BuilderException("Invalid input parameters to build an object");
        }
		
		Message ack = null;
	    
	    try {
	        List segments = new ArrayList();
	    	
	    	/*if (message instanceof BatchMessage) {
	    		//Build BHS segment
	    		segments.add(buildBHS((BatchMessage)message, AckType.CODE_AA.getName()));
	    	}*/
	    	
	    	//Build MSH and MSA - adds to segments list
	    	buildAAMSHMSA(message, segments);
	    	
	    	/*//Build BTS segment
	    	if (message instanceof BatchMessage) {
	    		segments.add(buildBTS(1));
	    		ack = new BatchMessage(segments, HL7Constants.ACK);
	    	}
	    	else {*/
	    	    ack = new Message(segments, HL7Constants.ACK);
	    	//}
	    }
	    catch (Exception e) {
	    	throw new BuilderException( "Failed to build ACK message due to an exception ", e );
	    }
	    
		return ack;
	}
	
	/** 
	 * Method used to build AE ACK Message
	 * 
	 * @param message
	 * 			Inbound message that is reason for ACK.
	 * @param errors
	 * 			Collection of AE's.
	 * @return Message ACK message object
	 */
	public Message build(Message message, List errors) throws BuilderException {
		if((message == null) 
			|| (errors == null)
			|| (errors.size() == 0)) {
				throw new BuilderException("Invalid input parameters to build an object");
	        }
		
		Message ack = null;
	    
	    try {
	    	List segments = new ArrayList();
	    	
	    	if (message instanceof BatchMessage) {
	    		//Build BHS segment
	    		segments.add(buildBHS((BatchMessage)message, AckType.CODE_AE.getName()));
	    	}
	    	
	    	//Build MSH and MSA - adds to segments list
			buildEachMSHMSA(message, segments, errors);
			
	    	if (message instanceof BatchMessage) {
	    		segments.add(buildBTS(errors.size()));	
	    		ack = new BatchMessage(segments, HL7Constants.ACK);
	    	}
	    	else {
	    	    ack = new Message(segments, HL7Constants.ACK);
	    	}
	    }
	    catch (Exception e) {
	    	throw new BuilderException( "Failed to build ACK message due to an exception ", e );
	    }
	    
		return ack;
	}
	
	/**
	 * Method to create BHS segment from data in a message.
	 * 
	 * @param message
	 * 			The message used as source of data for the BHS segment. 
	 * @param ackType
	 * 			The type of acknowledgement.
	 * @return The BHS segment.
	 * @throws BuilderException
	 */
	private BHS buildBHS(BatchMessage message, String ackType) throws BuilderException {
	    BHS segment = null;
		try {
			BHSMetaData metaData = new BHSMetaData(message);
			metaData.setComment(ackType);
			metaData.setMessageType(HL7Constants.ACK);
    	 
			segment = (BHS)(getBhsBuilder().build(metaData));
		}
		catch (Exception e) {
			throw new BuilderException( "Failed to build BHS Segment due to an exception ", e );
		}
		
		return segment;
	}
	
	/**
	 * Method to create MSH and MSA segments from data in a message.
	 * 
	 * @param message
	 * 			The message used as source of data for the MSH and MSA segments.
	 * @param segments
	 * 			The list of segments that is the destination for the MSH and MSA segments.
	 * @throws BuilderException
	 */
	private void buildAAMSHMSA(Message message, List segments) throws BuilderException {
		try {
			//Build MSH segment
			MSHMetaData metaData = new MSHMetaData(message);
			
			//Get control id to use
			String controlID = message.getMessageID();
			
			//If AA, message type is ACK
			metaData.setMessageType(HL7Constants.ACK);
			metaData.setControlID("A" + controlID);
			
			segments.add((getMshBuilder().build(metaData)));
			
			//Build MSA segment
			if (VAFacility.CODE_MHV.getCode().equals(message.getSendingFacility()))
			{
				//ccr 7443: voa PENDING in MSA-3
				segments.add(buildMSA(AckType.CODE_AA.getName(), controlID, VOA_PENDING_STATUS));
			} else
			{
				segments.add(buildMSA(AckType.CODE_AA.getName(), controlID, null));
			}
		}
		catch (Exception e) {
			throw new BuilderException( "Failed to build MSH and MSA Segment due to an exception ", e );
		}
	}
	
	/**
	 * Method to create MSH and MSA segments from data in a message.  
	 * 
	 * @param message
	 * 			The message used as source of data for the MSH and MSA segments.
	 * @param segments
	 * 			The list of segments that is the destination for the MSH and MSA segments.
	 * @param errors
	 * 			The list of errors for which we need to add MSH and MSA segments.
	 * @throws BuilderException
	 */
	private void buildEachMSHMSA(Message message, List segments, List errors) throws BuilderException {
		try {
			MSHMetaData metaData = new MSHMetaData(message);
			Date creationDatetime = Calendar.getInstance().getTime();
			
			boolean isFromVOA = VAFacility.CODE_MHV.getCode().equals(message.getSendingFacility());
			
			for(int i = 0; i<errors.size(); i++) {
				MessageLogEntry log = (MessageLogEntry)errors.get(i);
				
				metaData.setControlID(log.getControlIdentifier());
				metaData.setCreationDatetime(creationDatetime);
				
				//Build MSH segment
				segments.add(getMshBuilder().build(metaData));
				
				//Build MSA segment
		    	if (isFromVOA)
				{
					//ccr 7443: VOA error "Person Not Found" in MSA-3
					segments.add(buildMSA(log.getAckType().getCode(), log.getControlIdentifier(), VOA_ERROR_TEXT));
				} else
				{
					segments.add(buildMSA(log.getAckType().getCode(), log.getControlIdentifier(), log.getErrorText()));
				}
			}
		}
		catch (Exception e) {
			throw new BuilderException( "Failed to build MSH and MSA Segment due to an exception ", e );
		}
	}
	
	/**
	 * Method to create MSA segment from data in a message.
	 * 
	 * @param ackType
	 * 			The type of acknowledgement.
	 * @param controlID
	 * 			The identifier for the message. 
	 * @param errorText
	 * 			The error text related to the error. 
	 * @return The MSA segment
	 * @throws BuilderException
	 */
	private MSA buildMSA(String ackType, String controlID, String errorText) throws BuilderException {
	    MSA segment = null;
		
		try {
			MSAMetaData metaData = new MSAMetaData((AbstractEntity)null);
			
			metaData.setACKCode(ackType);
			metaData.setControlID(controlID);
			metaData.setTextMessage(errorText);
						
			segment = (MSA)(getMsaBuilder().build(metaData));
		}
		catch (Exception e) {
			throw new BuilderException( "Failed to build MSA Segment due to an exception ", e );
		}
		
		return segment;
	}
	
	/**
	 * The method used to build BTS segment.
	 * 
	 * @param count
	 * 			The number of messages in the batch.
	 * @return The BTS segment.
	 * @throws BuilderException
	 */
	private BTS buildBTS(int count) throws BuilderException {
		BTS segment = null;
		
		try {
			BTSMetaData metaData = new BTSMetaData((AbstractEntity)null);
			
			metaData.setMessageCount(count);
									
			segment = (BTS)(getBtsBuilder().build(metaData));
		}
		catch (Exception e) {
			throw new BuilderException( "Failed to build BTS Segment due to an exception ", e );
		}
		
		return segment;
	}
}
