/*******************************************************************************
 *  Copyright 2017 Centre for Development of Advanced Computing(C-DAC), Pune
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *    http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *******************************************************************************/
package cdac.medinfo.sdk.hl7282.baselibs.messages;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
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.Attribute;
import javax.xml.stream.events.StartDocument;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import cdac.medinfo.sdk.hl7282.baselibs.segments.MSA;
import cdac.medinfo.sdk.hl7282.baselibs.segments.MSH;
import cdac.medinfo.sdk.hl7282.baselibs.segments.QPD;
import cdac.medinfo.sdk.hl7282.baselibs.segments.SegmentSet;
import cdac.medinfo.sdk.hl7282.commoninterface.IDataType;
import cdac.medinfo.sdk.hl7282.commoninterface.IDelimiter;
import cdac.medinfo.sdk.hl7282.commoninterface.IGroup;
import cdac.medinfo.sdk.hl7282.commoninterface.IHL7Stream;
import cdac.medinfo.sdk.hl7282.commoninterface.ISegment;
import cdac.medinfo.sdk.hl7282.commoninterface.ISegmentKey;
import cdac.medinfo.sdk.hl7282.commoninterface.ISegmentSet;
import cdac.medinfo.sdk.hl7282.commoninterface.ISingleCollection;
import cdac.medinfo.sdk.hl7282.exceptions.DumpException;
import cdac.medinfo.sdk.hl7282.exceptions.GroupMissingException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidFieldException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidMessageException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidSegmentException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidSegmentSetException;
import cdac.medinfo.sdk.hl7282.exceptions.SegmentMissingException;
import cdac.medinfo.sdk.hl7282.hl7streams.HL7FileStream;
import cdac.medinfo.sdk.hl7282.utils.EnumAcknowledgementMode;
import cdac.medinfo.sdk.hl7282.utils.EnumSegments;
import cdac.medinfo.sdk.hl7282.utils.EnumSubscriptionType;
import cdac.medinfo.sdk.hl7282.utils.HL7Config;
import cdac.medinfo.sdk.hl7282.utils.HL7Constants;
import cdac.medinfo.sdk.hl7282.utils.HL7Logger;
import cdac.medinfo.sdk.hl7282.utils.IMap;
import cdac.medinfo.sdk.hl7282.utils.MultiValuedPairedCollection;
import cdac.medinfo.sdk.hl7282.utils.ResourceBundleWrapper;


/**
 * Message is the abstract class which Declares the Methods Parse and Serialize.
 * and a vector variable which contains the list of Segments.
 * This class provides mechanism to serialize, parse and validate message against HL7 v 2.8.2 standard.
 * The class provides accessors & mutators for the elements mentioned above.
 * These elements can be easily populated & populated values can be easily retrieved.
 * The class has validate method which validates Message against HL7 v 2.8.2 standard.
 * It provides user with serialize method to serialize message object to any stream & 
 * parse method to obtain fully populated & validated Message object from any stream.
 */
public abstract class Message
{
	
	/**
	 * Holds all collection of the Segment and Group.
	 */
	private DataValue objDataValue = null;

	/**
	 * Event of Message.
	 */
	private String strTriggerEvent;
	
	/**
	 * Message Code of Message.
	 */
	private String strMessageCode;

	/**
	 * Message structure of Message.
	 */	
	private String strMessageStructure;

	/**
	 * States for SubscriptionType of message.
	 */
	EnumSubscriptionType enumSubscriptionType;

	/**
	 * Parameterized Constructor of Message which takes MessageMap, Message Code, Trigger Event and Message Structure.
	 * @param objMessageMap Object of IMap
	 * @param strMessageCode Message Code of Message
	 * @param strTriggerEvent Event of Message
	 * @param strMessageStructure Message structure of Message
	 */
	public Message(IMap objMessageMap, String strMessageCode, String strTriggerEvent, String strMessageStructure)
	{
		objDataValue = new DataValue(objMessageMap);
		this.strTriggerEvent = strTriggerEvent;
		this.strMessageCode = strMessageCode;
		this.strMessageStructure = strMessageStructure;
	}

	/**
	 * Retrieves Message Structure of Message.
	 * @return String Message Structure
	 */
	public String getMessageStructure()
	{
		return strMessageStructure;
	}

	/**
	 * Retrieves Message Code of Message.
	 * @return String Message Code
	 */
	public String getMessageCode()
	{
		return strMessageCode;
	}

	/**
	 * Retrieves Trigger Event of Message.
	 * @return String Trigger Event
	 */
	public String getTriggerEvent()
	{
		return strTriggerEvent;
	}

	/**
	 * Add the Segment to the Message.
	 * @param enumSegmentName Object of ISegmentKey provides name of Segment
	 * @param objSegment Object of ISegment provides name of Segment
	 */
	protected void addSegment(ISegmentKey enumSegmentName, ISegment objSegment)
	{
		objDataValue.addSegment(enumSegmentName, objSegment);
	}

	/**
	 * Add the Segment to the Message.
	 * @param enumSegmentName Object of ISegmentKey provides name of Segment
	 * @param objSegmentCollection Object of ISegment provides name of Segment
	 */
	protected void addSegment(ISegmentKey enumSegmentName, ISingleCollection<ISegment> objSegmentCollection)
	{
		if(objSegmentCollection != null)
		{
			for(ISegment objSegment : objSegmentCollection)
				objDataValue.addSegment(enumSegmentName, objSegment);
		}
	}

	/**
	 * Add the Group to the Message.
	 * @param enumSegmentName Object of ISegmentKey provides name of Group
	 * @param objGroup Object of IGroup provides name of Group
	 */
	protected void addGroup(ISegmentKey enumSegmentName, IGroup objGroup)
	{
		objDataValue.addGroup(enumSegmentName, objGroup);
	}

	/**
	 * Add the Group to the Message.
	 * @param enumSegmentName Object of ISegmentKey provides name of Group
	 * @param objGroupCollection Object of IGroup provides name of Group
	 */
	protected void addGroup(ISegmentKey enumSegmentName, ISingleCollection<IGroup> objGroupCollection)
	{
		if(objGroupCollection != null)
		{
			for(IGroup objGroup : objGroupCollection)
				objDataValue.addGroup(enumSegmentName, objGroup);
		}
	}

	/**
	 * Set the Segment to the Message.
	 * @param enumSegmentName Object of ISegmentKey provides name of Segment
	 * @param objSegment Object of ISegment provides name of Group
	 */
	protected void setSegment(ISegmentKey enumSegmentName, ISegment objSegment)
	{
		objDataValue.setSegment(enumSegmentName, objSegment);
	}

	/**
	 * Set the Group to the Message.
	 * @param enumGroupName Object of ISegmentKey provides name of Group
	 * @param objGroup Object of IGroup provides name of Group
	 */
	protected void setGroup(ISegmentKey enumGroupName, IGroup objGroup)
	{
		objDataValue.setGroup(enumGroupName, objGroup);
	}

	/**
	 * Get the Segment Collection from the Message with the help of provided Segment Key.
	 * @param enumSegments Object of ISegmentKey provides name of Segment
	 * @return ISingleCollection&lt;ISegment&gt; - Collection of the Segment
	 */
	public ISingleCollection<ISegment> getSegment(ISegmentKey enumSegments)
	{
		return objDataValue.getSegment(enumSegments);
	}

	/**
	 * Get the Group Collection from the Message with the help of provided Segment Key.
	 * @param enumSegments Object of ISegmentKey provides name of Group
	 * @return ISingleCollection&lt;IGroup&gt; - Collection of the Group
	 */
	public ISingleCollection<IGroup> getGroup(ISegmentKey enumSegments)
	{
		return objDataValue.getGroup(enumSegments);
	}

	/**
	 * Check weather Segment or Group is present or not.
	 * @param enumSegments Object of ISegmentKey provides name of Segment
	 * @return boolean - true: if contains otherwise false
	 */
	public boolean contains(ISegmentKey enumSegments)
	{
		return objDataValue.contains(enumSegments);
	}

	/**
	 * Remove the Segment from the Message.
	 * @param enumSegments Object of ISegmentKey provides name of Segment
	 */
	public void removeSegment(ISegmentKey enumSegments)
	{
		objDataValue.removeSegment(enumSegments);
	}

	/**
	 * Remove the Group from the Message.
	 * @param enumSegments Object of ISegmentKey provides name of Group
	 */
	public void removeGroup(ISegmentKey enumSegments)
	{
		objDataValue.removeGroup(enumSegments);
	}	

	/**
	 * Sets Message Header
	 * @param objMSH Specified Message Header(MSH) 
	 */
	public void setHeader(MSH objMSH)
	{
		setSegment(EnumSegments.HL7_SEG_MSH, objMSH);
	}

	/**
	 * Retrieves Message Header(MSH)<b></b><br>
	 * @return MSH - the Message Header(MSH)  present in this message or null if unavailable
	 */
	public MSH getHeader()
	{
		ISingleCollection<ISegment> objSingleCollection = getSegment(EnumSegments.HL7_SEG_MSH);
		if(objSingleCollection != null)
			return (MSH)objSingleCollection.elementAt(0);
		return null;
	}

	/**
	 * Retrieves collection of User Parameters from QPD Segment of Message, if QPD segment is absent returns null. 
	 * @return ISingleCollection<DataType> SingleCollection of User Parameters
	 */
	public ISingleCollection<IDataType> getQueryUserParameters()
	{
		if(objDataValue.contains(EnumSegments.HL7_SEG_QPD))
		{
			QPD objQPD =(QPD)objDataValue.getSegment(EnumSegments.HL7_SEG_QPD).elementAt(0);
			return objQPD.getUserParameters();
		}
		else
			return null;	
	}

	/**
	 * Retrieves ACK mode.
	 * @return EnumAcknowledgementMode
	 */
	public EnumAcknowledgementMode getACKMode()
	{
		if(getHeader().getAcceptAcknowledgmentType() == null && getHeader().getApplicationAcknowledgmentType() == null)
			return EnumAcknowledgementMode.ORIGINAL_MODE;
		else
			return EnumAcknowledgementMode.ENHANCED_MODE;
	}

	/**
	 * Verifies whether message is Query.
	 * @return boolean
	 */
	public boolean isQuery()
	{
		return false;
	}

	/**
	 * Verifies whether message is Response.
	 * @return boolean
	 */
	public boolean isResponse()
	{
		return false;
	}	
	/**
	 * Returns MessageControlID from MSA segment of this message.
	 * @return String in form of MessageControl ID
	 */	
	public String getMessageControlID()
	{
		ISingleCollection<ISegment> objSingleCollection = objDataValue.getSegment(EnumSegments.HL7_SEG_MSA);
		if(objSingleCollection != null)
		{
			MSA objMSA =(MSA)objSingleCollection.elementAt(0);
			return (String)objMSA.getMessageControlID().getValue();
		}
		return null;
	}

	/**
	 * Populates Message using provided SegmentSet according to Message Structure specified by HL7 standard.
	 * @param objSegmentSet instance of SegmentSet
	 * @return int - shows status as success or failure
	 * @throws InvalidMessageException If message validation fails
	 */
	public int populate(ISegmentSet objSegmentSet)throws InvalidMessageException
	{
		try
		{
			objDataValue.populate(objSegmentSet);
		}
		catch(InvalidSegmentSetException ex)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex);
		}
		catch (SegmentMissingException ex)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex);
		}
		catch (GroupMissingException ex)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex);
		}
		return HL7Constants.HL7_FUNCTION_SUCCESS;
	}

	/**
	 * Retrieves populated SegmentSet from message structure.
	 * @return ISegmentSet - instance of SegmentSet
	 * @throws InvalidMessageException If message validation fails
	 */
	public ISegmentSet getSegmentSet()throws InvalidMessageException
	{
		ISegmentSet objSegmentSet = new SegmentSet();

		ISingleCollection<ISegment> objSegmentCollection = objDataValue.getSegmentCollection();

		if(objSegmentCollection.size() != 0 || objSegmentCollection != null)
		{
			for(ISegment objSegment : objSegmentCollection)
			{
				objSegmentSet.addSegment(objSegment);
			}
		}
		return objSegmentSet;
	}

	/**
	 * Validates HL7 Message objects.
	 * Validate method checks for validation each and every segments in side the Message object against specified message Structure. 
	 * Validation checks include optionality of segment or group.
	 * It also verifies the number of times segment or group is repeated in message.
	 * e.g 
	 * {PSH} while parsing PSH segment validate method will check for its mandatory presence
	 * [PSH] in this case validate will throw exception if one tries to enter more than one PSH segment    
	 * @return boolean - true If Message and all segments in side it are validated properly
	 *				   - false If segment in side it is not validated properly and if compulsory segments are not present
	 *@throws InvalidMessageException Thrown when Message validation fails
	 */
	public boolean validate()throws InvalidMessageException
	{
		try
		{
			ISingleCollection<ISegment> objSingleCollection = objDataValue.getSegment(EnumSegments.HL7_SEG_MSH);//objDataValue.objSegmentList.get(EnumSegments.HL7_SEG_MSH);
			if(objSingleCollection == null)
				throw new SegmentMissingException(EnumSegments.HL7_SEG_MSH.getValue());
			else if(objSingleCollection.size() != 1)
				throw new InvalidMessageException(ResourceBundleWrapper.getValue("exceptions","MultiplicityConstraint",new Locale(HL7Config.createInstance().getLocale()))+EnumSegments.HL7_SEG_MSH.getValue());

			for(ISegment objISegment : objSingleCollection)
			{
				objISegment.validate();
			}

			MSH objTempMSH = (MSH)(objSingleCollection.elementAt(0));
			//Get the value for the Version ID from the MSH segment
			String strVersion = (String)objTempMSH.getVersionID().getVersionID().getValue();
			//Get the value for Message Code from the MSH segment
			String strMsgCode = (String)objTempMSH.getMessageType().getMessageCode().getValue();
			//Get the value for Trigger Event from the MSH segment
			String strTEvent = (String)objTempMSH.getMessageType().getTriggerEvent().getValue();
			//Get Message Structure
			String strMSGStructure = (String)objTempMSH.getMessageType().getMessageStructure().getValue();
			//Code for checking it for the proper values for the type of message.
			if(!strMsgCode.equals(strMessageCode))
				throw new InvalidFieldException("Message code "+strMessageCode+" is expected" );
			else if(!strTEvent.equals(strTriggerEvent))
				throw new InvalidFieldException("Trigger Event "+strTriggerEvent+" is expected" );
			else if(!strVersion.equals("2.8.2"))
			{
				HL7Logger objHLogger = HL7Logger.getLogger();
				objHLogger.log(ResourceBundleWrapper.getValue("warnings","Version",new Locale(HL7Config.createInstance().getLocale()))+strVersion);
			}
			else if(!strMSGStructure.equals(strMessageStructure))
				throw new InvalidFieldException("Message Structure "+strMessageStructure+" is expected" );			

			objDataValue.validate();

		}
		catch(SegmentMissingException ex)
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();						
				objHL7Logger.log(ex.getMessage());
			}
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex);
		}
		catch(GroupMissingException gme)
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();						
				objHL7Logger.log(gme.getMessage());
			}
			throw new InvalidMessageException(this.getClass().getSimpleName(), gme);
		}
		catch (InvalidSegmentException e) 
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();						
				objHL7Logger.log(e.getMessage());
			}
			throw new InvalidMessageException(this.getClass().getSimpleName(), e);
		}
		return true;
	}

	/**
	 * Retrieves Map of specified Group name.
	 * @param enumGroupName Object of ISegmentKey provides name of Group
	 * @return IMap - Object of IMap
	 */
	public IMap getGroupMap(ISegmentKey enumGroupName)
	{
		return objDataValue.getGroupMap(enumGroupName);
	}

	/**
	 * Serializes message Information in the form of Attribute Name and its Value.
	 * @param objIHL7Stream Object of IHL7Stream
	 * @throws IOException Signals that an I/O exception of some sort has occurred
	 */
	public void dumpMessage(IHL7Stream objIHL7Stream)throws IOException
	{
		try
		{
			objDataValue.dumpMessage(objIHL7Stream);
		}
		catch (SegmentMissingException ex)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex);
		}
		catch (GroupMissingException ex)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex);
		}
		catch(DumpException de)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), de);
		}
	}
	
	/**
	 * Serializes message Information in the form of Attribute Name and its Value.
	 * @param strFilePath Full path with file name where dump will be stored
	 * @throws IOException Signals that an I/O exception of some sort has occurred
	 */
	public void dumpMessage(String strFilePath)throws IOException
	{
		try
		{
			FileOutputStream objFileOutputStream = new FileOutputStream(strFilePath);
			
			HL7FileStream objIHL7Stream = new HL7FileStream();
            objIHL7Stream.setOutputStream(objFileOutputStream);
			objDataValue.dumpMessage(objIHL7Stream);
			
			objFileOutputStream.close();
		}
		catch (SegmentMissingException ex)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex);
		}
		catch (GroupMissingException ex)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex);
		}
		catch(DumpException de)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), de);
		}
	}

	/**
	 * Removes only first level Segments or Group from the HL7 Message.
	 * @param enumSearchSegment enumSearchSegment Object of ISegmentKey provides name of Segment or Group
	 * @param isForecefullyRemove Boolean value if True - Removes the Segment or Group forcefully regardless of whether
	 * resulting HL7 message is valid or not
	 * If false - Remove the Segment or Group from message only if resulting message is valid
	 * @return int HL7Constant Integer value specifying success or failure of function
	 */
	public int removeSegment(ISegmentKey enumSearchSegment, boolean isForcefullyRemove) 
	{
		return objDataValue.removeSegment(enumSearchSegment, isForcefullyRemove);
	}

	/**
	 * Removes collection of first level message or segments from HL7 message
	 * @param objSegmentCollection Object of ISingleCollection of ISegments to be removed
	 * @param isForecefullyRemove Boolean value if True - Removes the Segment or Group forcefully regardless of whether
	 * resulting HL7 message is valid or not
	 * If false - Remove the Segment or Group from message only if resulting message is valid
	 * @return int HL7Constant Integer value specifying success or failure of function
	 */
	public int removeSegments(ISingleCollection<ISegmentKey> objSegmentKeyCollection, boolean isForcefullyRemove)
	{
		int iResult = HL7Constants.HL7_FUNCTION_FAIL;
		for(ISegmentKey objSegmentKey : objSegmentKeyCollection)
		{
			iResult = removeSegment(objSegmentKey, isForcefullyRemove);
		}
		return iResult;
	}

	/**
	 * Removes Attribute of a segment.
	 * @param removeSegmentKey Object of ISegmentKey specifies name of a segment
	 * @param strAttributeName String value for name of attribute
	 * @param isForecefullyRemove Boolean value if True - Removes the Segment or Group forcefully regardless of whether
	 * resulting HL7 message is valid or not
	 * If false - Remove the Segment or Group from message only if resulting message is valid
	 * @return int - integer value 5 for successfully execution of function and 
	 * -1 - for failure of a function
	 */
	public int removeSegmentAttribute(ISegmentKey removeSegmentKey, String strAttributeName , boolean isForcefullyRemove)
	{
		int iResult = HL7Constants.HL7_FUNCTION_SUCCESS;

		ISegmentSet objSegmentSet = getSegmentSet();

		ISingleCollection<ISegment> objSegCollection = objSegmentSet.getList();
		for(ISegment objSegment : objSegCollection)
		{
			ISegmentKey objSegmentKey = objSegment.getName();
			if(objSegmentKey.getValue().equals(removeSegmentKey.getValue()))
			{
				objSegment.removeDataElement(strAttributeName , isForcefullyRemove);
			}
		}
		return iResult;
	}

	/**
	 * Removes Attribute of a segment.
	 * @param objSegmentKeyCollection Object containing Collection of SegmentKey and its item ID
	 * @return integer value 5 - for successfully execution of function and 
	 * 						-1 - for failure of a function
	 */
	public int removeSegmentAttribute(MultiValuedPairedCollection<ISegmentKey, Integer> objSegmentKeyCollection , boolean isForcefullyRemove)
	{
		int iResult = HL7Constants.HL7_FUNCTION_SUCCESS;

		ISegmentSet objSegmentSet = getSegmentSet();

		ISingleCollection<ISegment> objSegCollection = objSegmentSet.getList();
		for(ISegment objSegment : objSegCollection)
		{
			ISegmentKey objSegmentKey = objSegment.getName();
			if(objSegmentKeyCollection.contains(objSegmentKey))
			{
				ISingleCollection<Integer> objSegmentKeyValueCollection = objSegmentKeyCollection.get(objSegmentKey);
				for(int intItemValue : objSegmentKeyValueCollection)
				{
					objSegment.removeDataElement(intItemValue , isForcefullyRemove);
				}
			}
		}

		return iResult;
	}

	/**
	 * Removes Attribute of a segment.
	 * @param removeSegmentKey Object of ISegmentKey specifies name of a segment
	 * @param iItemID Integer value for attribute which is Item ID specified by HL7
	 * @return integervalue 5 - for successfully execution of function and 
	 * -1 - for failure of a function
	 */
	public int removeSegmentAttribute(ISegmentKey removeSegmentKey, int iItemID , boolean isForcefullyRemove)
	{
		int iResult = HL7Constants.HL7_FUNCTION_SUCCESS;

		ISegmentSet objSegmentSet = getSegmentSet();

		ISingleCollection<ISegment> objSegCollection = objSegmentSet.getList();
		for(ISegment objSegment : objSegCollection)
		{
			ISegmentKey objSegmentKey = objSegment.getName();
			if(objSegmentKey.getValue().equals(removeSegmentKey.getValue()))
			{
				objSegment.removeDataElement(iItemID , isForcefullyRemove);
			}
		}

		return iResult;
	}

	/**
	 * Removes Attribute of a segment.
	 * @param removeSegmentKey Object of ISegmentKey specifies name of a segment
	 * @param iItemID Integer value for attribute which is Item ID specified by HL7
	 * @return integervalue 5 - for successfully excecution of function and 
	 * -1 - for failure of a function
	 */
	public int updateSegmentAttribute(ISegmentKey removeSegmentKey, int iItemID, String strSegmentStream, IDelimiter objDelimiter)throws IOException
	{
		int iResult = HL7Constants.HL7_FUNCTION_SUCCESS;

		ISegmentSet objSegmentSet = getSegmentSet();

		ISingleCollection<ISegment> objSegCollection = objSegmentSet.getList();
		for(ISegment objSegment : objSegCollection)
		{
			ISegmentKey objSegmentKey = objSegment.getName();
			if(objSegmentKey.getValue().equals(removeSegmentKey.getValue()))
			{
				iResult = objSegment.updateDataElement(iItemID, strSegmentStream, objDelimiter);
			}
		}

		return iResult;
	}
	
	/**
	 * Removes Attribute of a segment.
	 * @param removeSegmentKey Object of ISegmentKey specifies name of a segment
	 * @param iItemID Integer value for attribute which is Item ID specified by HL7
	 * @return integervalue 5 - for successfully excecution of function and 
	 * -1 - for failure of a function
	 * @throws IOException
	 */
	public int updateSegmentAttribute(ISegmentKey removeSegmentKey, int iItemID, IDataType objIDataType)throws IOException
	{
		int iResult = HL7Constants.HL7_FUNCTION_FAIL;
		
		ISegmentSet objSegmentSet = getSegmentSet();

		ISingleCollection<ISegment> objSegCollection = objSegmentSet.getList();
		for(ISegment objSegment : objSegCollection)
		{
			ISegmentKey objSegmentKey = objSegment.getName();
			if(objSegmentKey.getValue().equals(removeSegmentKey.getValue()))
			{
				iResult = objSegment.updateDataElement(iItemID, objIDataType);
			}
		}

		return iResult;
	}
	
	/**
	 * Sets SubscriptionType for Message.
	 * @param enumSubscriptionType it states for subscription type whether it is new or modified 
	 */
	public void setSubscriptionType(EnumSubscriptionType enumSubscriptionType)
	{
		this.enumSubscriptionType = enumSubscriptionType;
	}
	
	/**
	 * gets SubscriptionType for Message.
	 * @return EnumSubscriptionType gets subscription type for the message
	 */
	public EnumSubscriptionType getSubscriptionType()
	{
		return enumSubscriptionType;
	}

	/**
	 * Serializes message Information in the form of Attribute Name and its Value.
	 * @param strFilePath Destination file path
	 * @return int - Returns success or fail
	 * @throws IOException
	 * @throws XMLStreamException
	 */
	public int writeToXML(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);


		// Create Message Structure open tag
		StartElement configStartElement = eventFactory.createStartElement("","","Message");
		eventWriter.add(configStartElement);
		
		Attribute attribName = eventFactory.createAttribute("Name", getMessageStructure());
		eventWriter.add(attribName);


		objDataValue.writeToXML(eventWriter);

		eventWriter.add(eventFactory.createEndElement("", "", "Message"));
		eventWriter.add(eventFactory.createEndDocument());
		eventWriter.close();
		return HL7Constants.HL7_FUNCTION_SUCCESS;	

	}
	
	/**
	 * Serializes message Information in the form of Attribute Name and its Value.
	 * @param objIHL7Stream object if IHL7Stream
	 * @return int - Returns success or fail
	 * @throws IOException
	 * @throws XMLStreamException
	 */
	public int writeToXML(IHL7Stream objIHL7Stream) throws IOException, XMLStreamException
	{
		
		ByteArrayOutputStream objByteArrayOutputStream = new ByteArrayOutputStream();
		
		// Create a XMLOutputFactory
		XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
		// Create XMLEventWriter
		XMLEventWriter eventWriter = outputFactory.createXMLEventWriter(objByteArrayOutputStream);


		// Create a EventFactory
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		// Create and write Start Tag
		StartDocument startDocument = eventFactory.createStartDocument();
		eventWriter.add(startDocument);


		// Create Message Structure open tag
		StartElement configStartElement = eventFactory.createStartElement("","","Message");
		eventWriter.add(configStartElement);
		
		Attribute attribName = eventFactory.createAttribute("Name", getMessageStructure());
		eventWriter.add(attribName);

		objDataValue.writeToXML(eventWriter);

		eventWriter.add(eventFactory.createEndElement("", "", "Message"));
		eventWriter.add(eventFactory.createEndDocument());
		eventWriter.close();
		
		int length = objByteArrayOutputStream.toByteArray().length;
		objIHL7Stream.writeBuffer(objByteArrayOutputStream.toByteArray(), 0,length);
		
		return HL7Constants.HL7_FUNCTION_SUCCESS;	

	}
	
	/**
	 * Serializes message Information in the form of Attribute Name and its Value.
	 * @param strFilePath Destination file path
	 * @return int - Returns success or fail
	 * @throws IOException
	 * @throws XMLStreamException
	 */
	public void writeMessageToXML_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_v2(eventWriter);
		eventWriter.add(eventFactory.createEndDocument());
		eventWriter.close();
		
	}
	
	/**
	 * Serializes message Information in the form of Attribute Name and its Value.
	 * @param objIHL7Stream object if IHL7Stream
	 * @return int - Returns success or fail
	 * @throws IOException
	 * @throws XMLStreamException
	 */
	public String writeMessageToXML_v2(IHL7Stream objIHL7Stream) throws IOException, XMLStreamException
	{
		
		ByteArrayOutputStream objByteArrayOutputStream = new ByteArrayOutputStream();
		
		// Create a XMLOutputFactory
		XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
		// Create XMLEventWriter
		XMLEventWriter eventWriter = outputFactory.createXMLEventWriter(objByteArrayOutputStream);

		// Create a EventFactory
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		// Create and write Start Tag
		StartDocument startDocument = eventFactory.createStartDocument();
		eventWriter.add(startDocument);

		writeToXML_v2(eventWriter);
		
		eventWriter.add(eventFactory.createEndDocument());
		eventWriter.close();
		
		int length = objByteArrayOutputStream.toByteArray().length;
		objIHL7Stream.writeBuffer(objByteArrayOutputStream.toByteArray(), 0,length);
		
		return objIHL7Stream.toString();	

	}
	
	public void writeToXML_v2(XMLEventWriter eventWriter) throws IOException, XMLStreamException
	{
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		XMLEvent end = eventFactory.createDTD("\n");
		XMLEvent tab = eventFactory.createDTD("\t");

		eventWriter.add(end);

		String messageType=getMessageCode()+"_"+getTriggerEvent();
		// Create Message Type open tag
		StartElement configStartElement = eventFactory.createStartElement("","",messageType);
		eventWriter.add(configStartElement);
		objDataValue.writeToXML_v2(eventWriter,messageType);

		eventWriter.add(eventFactory.createEndElement("", "",messageType));
		eventWriter.add(end);
		
	}
}
