 package cdac.medinfo.sdk.hl7282.hl7net.auxiliaryprotocol.batch;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Locale;

import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.StartDocument;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import cdac.medinfo.sdk.hl7282.baselibs.ST;
import cdac.medinfo.sdk.hl7282.baselibs.messages.Message;
import cdac.medinfo.sdk.hl7282.baselibs.segments.BHS;
import cdac.medinfo.sdk.hl7282.baselibs.segments.BTS;
import cdac.medinfo.sdk.hl7282.baselibs.segments.QPD;
import cdac.medinfo.sdk.hl7282.baselibs.segments.RCP;
import cdac.medinfo.sdk.hl7282.commoninterface.ISingleCollection;
import cdac.medinfo.sdk.hl7282.exceptions.BatchException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidMessageException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidSegmentException;
import cdac.medinfo.sdk.hl7282.exceptions.SegmentMissingException;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IHL7Batch;
import cdac.medinfo.sdk.hl7282.utils.EnumSegments;
import cdac.medinfo.sdk.hl7282.utils.HL7Config;
import cdac.medinfo.sdk.hl7282.utils.HL7Constants;
import cdac.medinfo.sdk.hl7282.utils.ResourceBundleWrapper;

/**
 * HL7Batch consist of Batch header, trailer, Collection of messages and
 * Query segments if it is a Query Batch.
 */
public class HL7Batch implements IHL7Batch 
{

	/** The batch header. */
	private BHS objBatchHeader;

	/** The batch trailer. */
	private BTS objBatchTrailer;

	/** The message collection. */
	private ISingleCollection<Message> objMessageCollection;

	

	QPD objQpd ;
	RCP objRcp;

	/**
	 * Set object of Query Parameter Definition Segment
	 * @param objQpd- object of QPD
	 */
	private void setQueryParameterDefinitionSegment(QPD objQpd) 
	{
		this.objQpd = objQpd;
	}

	/**
	 * Set object of Response Control Parameter Segment
	 * @param objRcp - object of RCP
	 */
	private void setResponseControlParameterSegment(RCP objRcp) 
	{
		this.objRcp = objRcp;
	}
	
	
	/**
	 * Retrieve object of Query Parameter Definition Segment
	 * @return QPD - object of QPD
	 */
	public QPD getQueryParameterDefinitionSegment() 
	{
		return objQpd;
	}

	/**
	 * Retrieve object of Response Control Parameter Segment
	 * @return RCP - object of RCP
	 */
	public RCP getResponseControlParameterSegment()
	{
		return objRcp;
	}


	/**
	 * Retrieve String of Query Identifier
	 * @return String - String of RCP
	 */
	public String getQueryIdentifier()
	{
		ST objStQueryIdentifier = null;
		if(objQpd != null)
		{
			objStQueryIdentifier = objQpd.getQueryTag();
		}
		else
			objStQueryIdentifier = null;

		if(objStQueryIdentifier == null)
			return null;
		return (String)objStQueryIdentifier.getValue();
	}


	/**
	 * Checks whether Query Priority is Deferred or Immediate
	 * @return boolean - true, if Query Priority is deferred.
	 */
	public boolean isDeferred()
	{
		if(objRcp != null)
		{
			if(objRcp.getQueryPriority().equals("D"))
				return true;
		}
		return false;
	}

	public HL7Batch()
	{

	}

	/**
	 * Serializes Batch Information in the form of XML.
	 * @return String - Returns Batch in XML format 
	 * @throws IOException
	 * @throws XMLStreamException
	 */
	public String writeBatchToXML_v2() throws IOException, XMLStreamException
	{
		// Create a XMLOutputFactory
		XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
		Writer stringWriter=new StringWriter();
		XMLEventWriter eventWriter = outputFactory.createXMLEventWriter(stringWriter);
	
		// Create a EventFactory
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		// Create and write Start Tag
		StartDocument startDocument = eventFactory.createStartDocument();
		eventWriter.add(startDocument);		
		
		 writeToXML(eventWriter);
		
		eventWriter.add(eventFactory.createEndDocument());
		eventWriter.close();
		return stringWriter.toString();	

	}

	/**
	 * Serializes Batch Information in the form of XML.
	 * @param strFilePath Destination file path
	 * @throws IOException
	 * @throws XMLStreamException
	 */
	public void writeBatchToXML_v2(String strFilePath) throws IOException, XMLStreamException
	{
		// Create a XMLOutputFactory
		XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
		
		// Create XMLEventWriter
		XMLEventWriter eventWriter = outputFactory.createXMLEventWriter(new FileOutputStream(strFilePath));


		// Create a EventFactory
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		// Create and write Start Tag
		StartDocument startDocument = eventFactory.createStartDocument();
		eventWriter.add(startDocument);		
		
		 writeToXML(eventWriter);
		
		eventWriter.add(eventFactory.createEndDocument());
		eventWriter.close();
		
	}
	
	void writeToXML(XMLEventWriter eventWriter) throws IOException, XMLStreamException
	{
		
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		XMLEvent end = eventFactory.createDTD("\n");
		XMLEvent tab = eventFactory.createDTD("\t");
		
		eventWriter.add(end);
		//Add <MESSAGEBATCH> element	
		StartElement objBatchStartElement = eventFactory.createStartElement("", "", "MESSAGEBATCH");
		eventWriter.add(objBatchStartElement);
				
		BHS objBHS = getBatchHeader();	
		objBHS.writeToXML_v2(eventWriter);
				
		eventWriter.add(end);
		eventWriter.add(eventFactory.createStartElement("", "", "MESSAGES"));
		eventWriter.add(end);
				
		for(Message objMessage : objMessageCollection)
		{					
			objMessage.writeToXML_v2(eventWriter);					
			eventWriter.add(end);
		}
						
		BTS objBTS = getBatchTrailer();
		objBTS.writeToXML_v2(eventWriter);
				
		eventWriter.add(eventFactory.createEndElement("", "", "MESSAGES"));
		eventWriter.add(end);
		eventWriter.add(eventFactory.createEndElement("", "", "MESSAGEBATCH"));
		eventWriter.add(end);
	}
	
	
	/**
	 * Constructor for HL7batch.
	 * @param objHL7BatchCollection - object of HL7BatchCollection
	 */
	public HL7Batch(ISingleCollection<Message> objHL7BatchCollection)
	{
		objMessageCollection = objHL7BatchCollection;
	}

	/**
	 * Constructor for HL7batch used to initialize member
	 * @param objBHS- object of BHS
	 * @param objBTS - object of BTS
	 * @param objHL7BatchCollection - object of HL7BatchCollection
	 */
	public HL7Batch(BHS objBHS,BTS objBTS,ISingleCollection<Message> objHL7BatchCollection)
	{
		this.objBatchHeader = objBHS;
		this.objBatchTrailer = objBTS;
		this.objMessageCollection = objHL7BatchCollection;
	}

	/**
	 * Validates QuerySegments.
	 * @return true - if successful
	 * 		   false - otherwise
	 * @throws InvalidMessageException the invalid message exception
	 */
	private boolean validateQuerySegments()throws InvalidMessageException // should throw InvalidBatchException Actually
	{
		try
		{
			if(objQpd!=null)
			{
				// Enhanced Mode
				objQpd.validate();
				if(objRcp!=null)
					objRcp.validate();
				else
					throw new InvalidMessageException(this.getClass().getSimpleName());
			}
			
			else if(objRcp!=null)
				throw new InvalidMessageException(this.getClass().getSimpleName());
		}
		catch(InvalidSegmentException ex)
		{
			throw new BatchException(ResourceBundleWrapper.getValue("exceptions","InvalidSegment",new Locale(HL7Config.createInstance().getLocale())));
		}
		catch(InvalidMessageException ex)
		{
			throw new BatchException(ResourceBundleWrapper.getValue("exceptions","InvalidMessage",new Locale(HL7Config.createInstance().getLocale())));
		}
		return true;
	}

	/**
	 * Sets query segments required to form Enhanced mode Query Batch.
	 * @param objQPD Specified Query Parameter definition Segment.
	 * @param objRCP Specified Response control parameter segment.
	 */
	public void setQuerySegments(QPD objQPD,RCP objRCP)
	{
		setQueryParameterDefinitionSegment(objQPD);
		setResponseControlParameterSegment(objRCP);
	}


	/**
	 * Gets the BatchControlID.
	 * @return String - The BatchControlID.
	 */
	public String getBatchControlID()
	{
		return (String)objBatchHeader.getBatchControlID().getValue();
	}

	/**
	 * Gets the batch header.
	 * @return BHS - The batch header
	 */
	public BHS getBatchHeader() 
	{
		return objBatchHeader;
	}

	/**
	 * Sets the batch header.
	 * @param objBatchHeader the new batch header
	 */
	public void setBatchHeader(BHS objBatchHeader) 
	{
		this.objBatchHeader = objBatchHeader;
	}

	/**
	 * Gets the batch trailer.
	 * @return BTS - The batch trailer
	 */
	public BTS getBatchTrailer() 
	{
		return objBatchTrailer;
	}

	/**
	 * Sets the batch trailer.
	 * @param objBatchTrailer the new batch trailer
	 */
	public void setBatchTrailer(BTS objBatchTrailer) 
	{
		this.objBatchTrailer = objBatchTrailer;
	}

	/**
	 * Gets the message collection.
	 * @return ISingleCollection<Message> - The message collection
	 */
	public ISingleCollection<Message> getMessageCollection() 
	{
		return objMessageCollection;
	}

	/**
	 * Sets the message collection.
	 * @param objMessageCollection the new message collection
	 */
	public void setMessageCollection(ISingleCollection<Message> objMessageCollection) 
	{
		this.objMessageCollection = objMessageCollection;
	}




	/**
	 * Checks if is response batch.
	 * @return true - if is response batch
	 * 		   false - otherwise
	 */
	public boolean isResponseBatch()
	{

		if(objQpd!=null)
			return true;
		return false;
	}

	/**
	 * Validates HL7 Batch objects.
	 * @return true - if successful
	 * 		   false - otherwise
	 * @throws InvalidBatchException the invalid message exception
	 */
	public boolean validate()throws BatchException // should throw InvalidBatchException Actually
	{
		try
		{
			// Step 1: Validate BHS and BTS Segments
			if(objBatchHeader == null && objBatchTrailer != null) 
				throw new SegmentMissingException(EnumSegments.HL7_SEG_BHS.getValue());
			if (objBatchTrailer == null && objBatchHeader != null)
				throw new SegmentMissingException(EnumSegments.HL7_SEG_BTS.getValue());

			if(objBatchHeader != null && objBatchTrailer != null)
			{
				objBatchHeader.validate();
				objBatchTrailer.validate();
			}
			// For Response of Query as Batch
			validateQuerySegments();

			// Step 2: Call Validate on messages contained in it.
			if(objMessageCollection!=null)
			{
				for (Message objMessage : objMessageCollection)
				{
					objMessage.validate();
				}
			}
		}
		catch(InvalidSegmentException ex)
		{
			throw new BatchException(ResourceBundleWrapper.getValue("exceptions", "InvalidSegment", new Locale(HL7Config.createInstance().getLocale())));
			//throw new BatchException(this.getClass().getSimpleName(),ex); 
		}
		catch(SegmentMissingException ex)
		{
			throw new BatchException(ResourceBundleWrapper.getValue("exceptions", "SegmentMissing", new Locale(HL7Config.createInstance().getLocale())));
			//throw new BatchException(this.getClass().getSimpleName(),ex); 
		}
		catch(InvalidMessageException ex)
		{
			throw new BatchException(ResourceBundleWrapper.getValue("exceptions", "InvalidMessage", new Locale(HL7Config.createInstance().getLocale())));
			//throw new BatchException(this.getClass().getSimpleName(),ex);  
		}
		return true;
	}

}
