/*******************************************************************************
 *  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.hl7model.messages.observationreporting;

import cdac.medinfo.sdk.hl7282.baselibs.messages.Group;
import cdac.medinfo.sdk.hl7282.baselibs.messages.Message;
import cdac.medinfo.sdk.hl7282.baselibs.segments.DSC;
import cdac.medinfo.sdk.hl7282.baselibs.segments.NK1;
import cdac.medinfo.sdk.hl7282.baselibs.segments.NTE;
import cdac.medinfo.sdk.hl7282.baselibs.segments.SFT;
import cdac.medinfo.sdk.hl7282.baselibs.segments.Segment;
import cdac.medinfo.sdk.hl7282.baselibs.segments.UAC;
import cdac.medinfo.sdk.hl7282.commoninterface.ICollectionIterator;
import cdac.medinfo.sdk.hl7282.commoninterface.IGroup;
import cdac.medinfo.sdk.hl7282.commoninterface.ISegment;
import cdac.medinfo.sdk.hl7282.commoninterface.ISegmentSet;
import cdac.medinfo.sdk.hl7282.commoninterface.ISingleCollection;
import cdac.medinfo.sdk.hl7282.exceptions.GroupMissingException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidMessageException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidSegmentException;
import cdac.medinfo.sdk.hl7282.exceptions.ParseException;
import cdac.medinfo.sdk.hl7282.exceptions.SegmentMissingException;
import cdac.medinfo.sdk.hl7282.utils.EnumMessageCode;
import cdac.medinfo.sdk.hl7282.utils.EnumMessageStructure;
import cdac.medinfo.sdk.hl7282.utils.EnumSegmentCardinality;
import cdac.medinfo.sdk.hl7282.utils.EnumSegments;
import cdac.medinfo.sdk.hl7282.utils.EnumTriggerEvent;
import cdac.medinfo.sdk.hl7282.utils.HL7Constants;
import cdac.medinfo.sdk.hl7282.utils.IMap;
import cdac.medinfo.sdk.hl7282.utils.ISegmentItem;
import cdac.medinfo.sdk.hl7282.utils.SingleCollection;

/**
 * OUL_R24 - Unsolicited Order Oriented Observation Message - (Event R24).
 * This message is used to designed for accommodate multispecimen oriented testing. It
 * should be applicable to laboratory automation systems requiring container.
 * <p>Represents a OUL_R24 message structure (see chapter 7.3.10). This structure contains the 
 * following elements: </p>
 * 0: MSH (Message Header Segment) <b>SINGLE_COMPULSARY</b><br>
 * 1: SFT (Software Segment) <b>MULTIPLE_OPTIONAL</b><br>
 * 2: UAC (User Authentication Credential) <b>SINGLE OPTIONAL</b><br/>
 * 3: NTE (Notes and Comments Segment) <b>SINGLE_OPTIONAL</b><br>
 * 4: PATIENT (a Group of segments in OUL_R24 message) <b>SINGLE_OPTIONAL</b><br>
 * 5: NK1 (Next of Kin  Segment) <b>MULTIPLE_OPTIONAL</b><br>
 * 6: ORDER (a Group of segments in OUL_R24 message) <b>MULTIPLE_COMPULSARY</b><br>
 * 7: DSC (Continuation Pointer Segment) <b>SINGLE_OPTIONAL</b><br>
 * This class provides mechanism to 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.
 *
 * <p>Following is the step to create message<br>
 * Before that, respective Message Source and Message Recipient to be set on Message Factory.</p>
 * <p>OUL_R24 objR24 = (OUL_R24)MessageFactory.createInstance().createSendSupportedMessage("OUL", "R24");</p>
 */
public final class OUL_R24 extends Message 
{
	boolean isGroupIdentified = false;
	
	
	private IMap objMessageMap = null;

	/**
	 * Constructs the OUL_R24 message.
	 */
	OUL_R24(IMap objMessageMap)
	{
		super(objMessageMap, EnumMessageCode.OUL.getValue(), EnumTriggerEvent.R24.getValue(), EnumMessageStructure.OUL_R24.getValue());
		this.objMessageMap = objMessageMap;
	}

	/**
	 * Retrieves Collection of Software Segments(SFT) present in this message.
	 * @return ISingleCollection<SFT> - Collection of Software Segments(SFT) present in this message.
	 */
	@SuppressWarnings("unchecked")
	public ISingleCollection<SFT> getSoftwareSegments()
	{
		ISingleCollection<?> objSegmentCollection = getSegment(EnumSegments.HL7_SEG_SFT);
		return (ISingleCollection<SFT>)objSegmentCollection;
	}

	/**
	 * Adds Software Segment(SFT) to this message.
	 * @param objSFT Specified Software Segment(SFT) to be added in this messageto this message.
	 */
	public void addSoftwareSegment(SFT objSFT)
	{
		addSegment(EnumSegments.HL7_SEG_SFT, objSFT);
	}

	/**
	 * Adds collection of Software Segment(SFT) to this message.
	 * @param objSFTSegmentCollection Collection of Software Segments to add in this message
	 */
	@SuppressWarnings("unchecked")
	public void addSoftwareSegment(ISingleCollection<SFT>  objSFTSegmentCollection)
	{
		ISingleCollection<?> objSegmentCollection =  objSFTSegmentCollection;
		addSegment(EnumSegments.HL7_SEG_SFT, (ISingleCollection<ISegment>)objSegmentCollection);
	}

	/**
	 * Retrieves User Authentication Credential(UAC) segment present in this message
	 * @return UAC - Specified UAC Segment
	 */
	public UAC getUserAuthenticationCredentialSegment()
	{
		ISingleCollection<ISegment> objSingleCollection = getSegment(EnumSegments.HL7_SEG_UAC);
		if(objSingleCollection != null)
			return (UAC)objSingleCollection.elementAt(0);
		return null;
	}

	/**
	 * Sets value of User Authentication Credential segment for this message,overwrites if already present.
	 * @param objUAC Specified UAC Segment
	 */
	public void setUserAuthenticationCredentialSegment(UAC objUAC)
	{
		setSegment(EnumSegments.HL7_SEG_UAC,objUAC);
	}



	/**
	 * Retrieves Notes And Comments(NTE) segment present in this message
	 * @return NTE - the Notes And Comments Segment present in this message or null if unavailable.
	 */
	public NTE getNotesAndCommentsSegment()
	{
		ISingleCollection<ISegment> objSingleCollection = getSegment(EnumSegments.HL7_SEG_NTE);
		if(objSingleCollection != null)
			return (NTE)objSingleCollection.elementAt(0);
		return null;
	}

	/**
	 * Sets value of Notes And Comments segment for this message,overwrites if already present.
	 * @param objNTE Specified Notes And Comments Segment 
	 */
	public void setNotesAndCommentsSegment(NTE objNTE)
	{
		setSegment(EnumSegments.HL7_SEG_NTE,objNTE);
	}

	/**
	 * Retrieves Patient Group present in this message.
	 * @return IGroup - Patient Group present in this message.
	 */
	public IGroup getPatientGroup()
	{
		ISingleCollection<IGroup> objGroupCollection = getGroup(EnumSegments.HL7_GROUP_PATIENT);
		if(objGroupCollection != null)
			return objGroupCollection.elementAt(0);
		return null;
	}

	/**
	 * Sets value of Patient Group for this message,overwrites if already present.
	 * @param objGroup Specified Patient Group 
	 */
	public void setPatientGroup(IGroup objGroup)
	{
		setGroup(EnumSegments.HL7_GROUP_PATIENT,objGroup);
	}

	/**
	 * Adds Next of Kin Segment(NK1) to this message.
	 * @param objNK1 Specified Next of Kin Segment(NK1) to add in message.
	 */
	public void addNextOfKinSegment(NK1 objNK1)
	{
		addSegment(EnumSegments.HL7_SEG_NK1, objNK1);
	}

	/**
	 * Adds collection of Next of Kin Segments(NK1) to this message.
	 * @param objNK1SegmentCollection Collection of Next of Kin Segment(NK1) to add in this message
	 */
	@SuppressWarnings("unchecked")
	public void addNextOfKinSegment(ISingleCollection<NK1>  objNK1SegmentCollection)
	{
		ISingleCollection<?> objSegmentCollection =  objNK1SegmentCollection;
		addSegment(EnumSegments.HL7_SEG_NK1, (ISingleCollection<ISegment>)objSegmentCollection);
	}

	/**
	 * Retrieves Collection of Next of Kin Segment(NK1) present in this message.
	 * @return ISingleCollection<NK1> - Collection of Next of Kin Segment(NK1) present in this message.
	 */
	@SuppressWarnings("unchecked")
	public ISingleCollection<NK1> getNextOfKinSegments()
	{
		ISingleCollection<?> objSegmentCollection = getSegment(EnumSegments.HL7_SEG_NK1);
		return (ISingleCollection<NK1>)objSegmentCollection;
	}


	/**
	 * Retrieves Collection of ORDER GROUP present in this message.
	 * @return ISingleCollection<IGroup> - Collection of ORDER  Group present in this Message.
	 */
	public ISingleCollection<IGroup> getOrderGroups()
	{
		return getGroup(EnumSegments.HL7_GROUP_ORDER );
	}

	/**
	 * Adds Collection of ORDER GROUP to this message.
	 * @param objSingleCollection Specified  ORDER  GROUP Collection.
	 */
	public void addOrderGroups(ISingleCollection<IGroup> objSingleCollection)
	{
		addGroup(EnumSegments.HL7_GROUP_ORDER, objSingleCollection);
	}

	/**
	 * Adds ORDER GROUP to this message.
	 * @param objGroup Specified ORDER GROUP. 
	 */
	public void addOrderGroup(IGroup objGroup)
	{
		addGroup(EnumSegments.HL7_GROUP_ORDER , objGroup);
	}

	/**
	 * Retrieves Continuation Pointer(DSC) segment present in this message
	 * @return DSC - the Continuation Pointer Segment present in this message or null if unavailable.
	 */
	public DSC getContinuationPointerSegment()
	{
		ISingleCollection<ISegment> objSingleCollection = getSegment(EnumSegments.HL7_SEG_DSC);
		if(objSingleCollection != null)
			return (DSC)objSingleCollection.elementAt(0);
		return null;
	}

	/**
	 * Sets value of Continuation Pointer segment for this message,overwrites if already present.
	 * @param objDSC Specified Continuation Pointer Segment 
	 */
	public void setContinuationPointerSegment(DSC objDSC)
	{
		setSegment(EnumSegments.HL7_SEG_DSC,objDSC);
	}

	/**
	 * 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
		{
			int i = 1;
			ISegmentItem objSegmentItem = null;
			int iRead = 0;
			/*
			 * Iterating through Segment structure list
			 * Parses the segment or group according to the segment item.
			 */
			while(i <= objMessageMap.size())
			{
				objSegmentItem = objMessageMap.getSegmentItem(i);

				if(objSegmentItem.isGroup())
				{
					ISingleCollection<IGroup> objGroupList = new SingleCollection<IGroup>();
					iRead = populateGroup(objSegmentSet, objSegmentItem, objGroupList, iRead);
					for(IGroup objGroup : objGroupList)
						addGroup(objSegmentItem.getName(),objGroup);
				}
				else
				{
					ISingleCollection<ISegment> objTempSegmentList = new SingleCollection<ISegment>();
					iRead = populateSegment(objSegmentSet,objSegmentItem,objTempSegmentList,iRead);
					for(ISegment objSegment : objTempSegmentList)
						addSegment(objSegmentItem.getName(),objSegment);
				}	

				i++;

				if(objSegmentSet.getSegment(iRead) == null)
				{
					/*
					 * If no data is available on stream then checking whether all compulsory segments 
					 * for this message has been parsed or not.
					 * Checking that parsed message is validated or not.
					 */
					break;
				}
			}

			/*
			 * If all segment item has been processed then no data should be available on stream
			 * It shows end of message.
			 */
			if(objSegmentSet.getSegment(iRead) != null ||(!validate()))
				throw new InvalidMessageException(this.getClass().getSimpleName());

		}
		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;
	}

	/*
	 * Populates the segments and sub groups of a Group.
	 */
	private int populateGroup(ISegmentSet objSegmentSet,ISegmentItem objSegmentItem,ISingleCollection<IGroup> objGroupList,int iRead)throws GroupMissingException
	{
		IGroup objGroup = null;
		if(objSegmentItem.getName()==EnumSegments.HL7_GROUP_SPECIMEN || objSegmentItem.getName()==EnumSegments.HL7_GROUP_RESULT)
		{
			if(!isGroupIdentified)
			{
				//Parse SPECIMEN Group and Result group 
				ISingleCollection<IGroup> objSpecimenResultGroupCollection = new SingleCollection<IGroup>();
				iRead = identifyAndPopulateGroups(objSegmentSet,objSegmentItem,objSpecimenResultGroupCollection,iRead);
				ICollectionIterator<IGroup> objSpecimenResultGroupIterator = objSpecimenResultGroupCollection.iterator();
				while(objSpecimenResultGroupIterator.hasNext())
				{
					IGroup objIdentifyPopulateGroups=objSpecimenResultGroupIterator.next();
					objGroupList.add(objIdentifyPopulateGroups);
				}
				isGroupIdentified = true;
			}
			return iRead;
		}

		//retrieving members of a group.
		IMap objGroupItemMessageMap = objSegmentItem.getGroupItemMessageMap();
		EnumSegmentCardinality enumCardinality = objSegmentItem.getSegmentCardinality();
		boolean isSegmentSetPopulated = false;
		do
		{
			/*
			 * In this loop it reads segment name from stream and compares it with ISegmentItem 
			 * and accordingly parses a segment.
			 * If some segment items are not present on stream then for next available segment , name is already read
			 * and in this case strSegmentName will not be null.  
			 */

			ISegment objSegment = objSegmentSet.getSegment(iRead);

			/*
			 * This if block make sures that whether available segments on stream belongs to this group or not.
			 * accordingly it parses the segments.
			 */
			if(!isGroupParsingRequired(objGroupItemMessageMap, objSegment.getName().getValue()))
			{
				if(!isSegmentSetPopulated && enumCardinality == EnumSegmentCardinality.MULTIPLE_COMPULSARY || enumCardinality == EnumSegmentCardinality.SINGLE_COMPULSARY)
					throw new GroupMissingException(objSegmentItem.getName().getValue());
				return iRead;
			}

			objGroup = new Group(objSegmentItem.getName(), objGroupItemMessageMap);
			int j = 1;
			ISegmentItem objTempSegmentItem = null;
			while(j <= objGroupItemMessageMap.size())
			{
				//Iterating through segment items of a group.
				objTempSegmentItem = objGroupItemMessageMap.getSegmentItem(j);

				if(objTempSegmentItem.isGroup())
				{
					ISingleCollection<IGroup> objTempGroupList = new SingleCollection<IGroup>();
					iRead = populateGroup(objSegmentSet,objTempSegmentItem,objTempGroupList,iRead);
					if(objTempSegmentItem.getName()== EnumSegments.HL7_GROUP_SPECIMEN ||objTempSegmentItem.getName()== EnumSegments.HL7_GROUP_RESULT )
					{
						ICollectionIterator<IGroup> objGroupiterator = objTempGroupList.iterator();
						while(objGroupiterator.hasNext())
						{
							IGroup objIdentifiedGroup = objGroupiterator.next();
							objGroup.addGroup(objIdentifiedGroup.getName(),objIdentifiedGroup);
						}
					}
					else
					{
						for(IGroup objTempGroup : objTempGroupList)
							objGroup.addGroup(objTempSegmentItem.getName(), objTempGroup);
					}
				}
				else
				{
					ISingleCollection<ISegment> objTempSegmentList = new SingleCollection<ISegment>();
					iRead = populateSegment(objSegmentSet,objTempSegmentItem,objTempSegmentList,iRead);
					for(ISegment objTempSegment : objTempSegmentList)
						objGroup.addSegment(objTempSegmentItem.getName(),objTempSegment);
				}

				j++;

				if(objSegmentSet.getSegment(iRead) == null)
				{
					objGroupList.add(objGroup);
					return iRead;
				}
			}
			objGroupList.add(objGroup);
			isSegmentSetPopulated = true;

		}while((EnumSegmentCardinality.MULTIPLE_COMPULSARY == enumCardinality) || (EnumSegmentCardinality.MULTIPLE_OPTIONAL == enumCardinality));

		return iRead;
	}
	/*
	 * Parses the segment from stream according to the cardinality specified in segment item
	 */
	private int populateSegment(ISegmentSet objSegmentSet,ISegmentItem objSegmentItem,ISingleCollection<ISegment> objResSegmentList,int iRead)throws SegmentMissingException
	{
		ISegment objSegment;

		/*
		 * In this loop it reads segment name from stream and compares it with ISegmentItem 
		 * and accordingly parses a segment.
		 * If some segment items are not present on stream then for next available segment , name is already read
		 * and in this case strSegmentName will not be null.  
		 */
		objSegment = objSegmentSet.getSegment(iRead);

		EnumSegmentCardinality enumCardinality = objSegmentItem.getSegmentCardinality();
		String strStructSegmentName = objSegmentItem.getName().getValue();
		strStructSegmentName = strStructSegmentName.substring(0,3);
		if(objSegment.getName().getValue().equals(strStructSegmentName))
		{
			if((EnumSegmentCardinality.SINGLE_COMPULSARY == enumCardinality) || (EnumSegmentCardinality.SINGLE_OPTIONAL == enumCardinality))
			{
				/*
				 * This block of code will be executed if segment is not repeatable 
				 */
				objResSegmentList.add(objSegment);
				iRead++;
			}
			else if((EnumSegmentCardinality.MULTIPLE_COMPULSARY == enumCardinality) || (EnumSegmentCardinality.MULTIPLE_OPTIONAL == enumCardinality))
			{
				/*
				 * This block of code will be executed for repeatable segments 
				 */
				while(true)
				{
					objResSegmentList.add(objSegment);
					iRead++;

					objSegment = objSegmentSet.getSegment(iRead);

					/*
					 * This loop will break if next segment on stream is not same.
					 * value of read segment will be persisted for next iteration
					 */
					if(objSegment == null || !(objSegment.getName().getValue().equals(strStructSegmentName)))
						break;
				}
			}
		}
		else if((EnumSegmentCardinality.MULTIPLE_COMPULSARY == enumCardinality) || (EnumSegmentCardinality.SINGLE_COMPULSARY == enumCardinality))
			throw new SegmentMissingException(objSegmentItem.getName().getValue());

		return iRead;
	}

	private boolean isGroupParsingRequired(IMap objGroupItemMessageMap,String strSegmentBuffer)
	{
		int i = 1;
		ISegmentItem objSegmentItem = null;
		while(i <= objGroupItemMessageMap.size())
		{
			objSegmentItem = objGroupItemMessageMap.getSegmentItem(i);

			if(objSegmentItem.isGroup())
			{
				IMap objMessageMapInGroup = objSegmentItem.getGroupItemMessageMap();
				return (isGroupParsingRequired(objMessageMapInGroup, strSegmentBuffer));
			}
			else
			{
				String strStructSegmentName = objSegmentItem.getName().getValue();
				strStructSegmentName = strStructSegmentName.substring(0,3);
				if(strStructSegmentName.equals(strSegmentBuffer))
					return true;
			}
			i++;
		}

		return false;
	}


	//parse SPECIMEN group
	//This method is wriiten for Identifying segments of Two groups Result and Specimen Group.
	//To avoid the confusion of reading OBX segment between Result and Specimen. 
	private int identifyAndPopulateGroups(ISegmentSet objSegmentSet,ISegmentItem objSegmentItem,ISingleCollection<IGroup> objSpecimenResultGroupCollection,int iRead)throws GroupMissingException
	{
		ISingleCollection<ISegment> objOBXSegmentCollection=null;
		ISingleCollection<IGroup> objContainerGroupCollection ;
		ISegment objSegment;

		objSegment = objSegmentSet.getSegment(iRead);
		try
		{

			if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_SPM.getValue())||objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_OBX.getValue()) || objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_SAC.getValue()) || 
					objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_INV.getValue())||objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_TCD.getValue())||objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_SID.getValue())||objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue()))
			{
				do
				{
					boolean isSegmentsProcessed = false;
					IGroup objSpecimenGroup=null;
					IGroup objResultGroup=null;
					objContainerGroupCollection =null;

					//SPM is Mandatory Segment which is parsed add to Specimen and If container Group is Present then 
					//checked for Specimen is Instantiated or not.if it is not then Throw Segment missing Exception for SPM.
					//Parse SPM segment
					if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_SPM.getValue()))
					{
						objSpecimenGroup = new Group(EnumSegments.HL7_GROUP_SPECIMEN, objSegmentItem.getGroupItemMessageMap());
						//Read segment name
						iRead++;
						objSpecimenGroup.addSegment(EnumSegments.HL7_SEG_SPM, objSegment);
						objSegment = objSegmentSet.getSegment(iRead);
						if(objSegment == null)
						{
							objSpecimenResultGroupCollection.add(objSpecimenGroup);
						}
					}
					//Parse OBX segment
					if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_OBX.getValue()))
					{
						objOBXSegmentCollection = new SingleCollection<ISegment>();
						Segment OBXSegment =null; 
						do
						{
							OBXSegment = (Segment)objSegmentSet.getSegment(iRead);
							objOBXSegmentCollection.add(OBXSegment);
							//Read segment name
							iRead++;
							objSegment = objSegmentSet.getSegment(iRead);
							if(objSegment == null)
							{
								//On End of Stream add the Segment OBX which is on Hold and Return
								objSpecimenGroup.addSegment(EnumSegments.HL7_SEG_OBX, OBXSegment);
								objSpecimenResultGroupCollection.add(objSpecimenGroup);
							}
						}while(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_OBX.getValue()));
					}

					//This checks for collection size of Container is greater then 
					//Container Groups is present if not throw segment missing exception.		
					//if Specimen group is present then add segments into Specimen Group.
					//Parse Container Group. 
					if(objContainerGroupCollection == null)
						objContainerGroupCollection = new SingleCollection<IGroup>();
					iRead = populateContainerGroup(objSegmentSet,objSegmentItem,objContainerGroupCollection,iRead);
					if(objContainerGroupCollection.size() > 0)
					{
						if(objSpecimenGroup == null)
							throw new SegmentMissingException(EnumSegments.HL7_SEG_SPM.getValue());
						for(ISegment objOBXSegment : objOBXSegmentCollection)
							objSpecimenGroup.addSegment(EnumSegments.HL7_SEG_OBX,objOBXSegment);
						for(IGroup objGroup:objContainerGroupCollection)
							objSpecimenGroup.addGroup(EnumSegments.HL7_GROUP_CONTAINER, objGroup);
						objOBXSegmentCollection = null;
						isSegmentsProcessed = true;
					}
					//Read segment name
					objSegment = objSegmentSet.getSegment(iRead);
					if(objSegment == null)
					{
						//On End Of stream Add All Segments of Specimen group,OBX Collection and Container segments.
						if(objOBXSegmentCollection != null)
						{
							for(ISegment objOBXSegment : objOBXSegmentCollection)
								objSpecimenGroup.addSegment(EnumSegments.HL7_SEG_OBX, objOBXSegment);
						}
						objSpecimenResultGroupCollection.add(objSpecimenGroup);
					}
					//If any Segment from Result group is not arrived then Call Proces Segment method 
					//and continue parsing of TCD.
					//Parse TCD segment
					if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_TCD.getValue()))
					{
						if(objResultGroup == null)
						{
							objResultGroup=processSegments(objSpecimenGroup, objOBXSegmentCollection, objSegmentItem);
							isSegmentsProcessed = true;
						}
						objResultGroup.addSegment(EnumSegments.HL7_SEG_TCD, objSegment);
						//Read segment name
						iRead++;
						objSegment = objSegmentSet.getSegment(iRead);
						if(objSegment == null)
						{
							objSpecimenResultGroupCollection.add(objResultGroup);
						}
					}
					//If any Segment from Result group is not arrived then Call Proces Segment method 
					//and continue parsing of SID.
					//Parse SID Segment
					if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_SID.getValue()))
					{
						if(objResultGroup == null)
						{
							objResultGroup=processSegments(objSpecimenGroup, objOBXSegmentCollection, objSegmentItem);
							isSegmentsProcessed = true;
						}
						do
						{
							objSegment = objSegmentSet.getSegment(iRead);
							objResultGroup.addSegment(EnumSegments.HL7_SEG_SID, objSegment);
							//Read segment name
							iRead++;
							objSegment = objSegmentSet.getSegment(iRead);
							if(objSegment == null)
							{
								objSpecimenResultGroupCollection.add(objResultGroup);
							}
						}while(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_SID.getValue()));
					}
					//If any Segment from Result group is not arrived then Call Proces Segment method 
					//and continue parsing of NTE.
					//Parse NTE Segment
					if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue()))
					{
						if(objResultGroup == null)
						{
							objResultGroup=processSegments(objSpecimenGroup, objOBXSegmentCollection, objSegmentItem);
							isSegmentsProcessed = true;
						}
						do
						{
							objSegment = objSegmentSet.getSegment(iRead);
							objResultGroup.addSegment(EnumSegments.HL7_SEG_NTE, objSegment);
							//Read segment name
							iRead++;
							objSegment = objSegmentSet.getSegment(iRead);
							if(objSegment == null)
							{
								objSpecimenResultGroupCollection.add(objResultGroup);
							}
						}while(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue()));
					}
					//This checks whehter continaer group and Result group segments are pasred then 
					//if Specimen group is instantiated then add OBX Segments collection in Specimen.
					//if Specimen is not instantiated and in OBX Segments collection is having only one segment
					//then add this OBX segment to Result.
					//if specimen group is null and Collection is having more than one OBX segment then throw 
					//exception of Segment missing for SPM.
					//else continue normal parsing.
					if(!isSegmentsProcessed)
					{
						if(objSpecimenGroup != null)
						{
							for(ISegment objOBXSegment : objOBXSegmentCollection)
								objSpecimenGroup.addSegment(EnumSegments.HL7_SEG_OBX, objOBXSegment);
							objSpecimenResultGroupCollection.add(objSpecimenGroup);
						}
						else if(objSpecimenGroup == null && objOBXSegmentCollection.size() == 1)
						{
							objResultGroup = new Group(EnumSegments.HL7_GROUP_RESULT, objSegmentItem.getGroupItemMessageMap());
							objResultGroup.addSegment(EnumSegments.HL7_SEG_OBX, objOBXSegmentCollection.elementAt(0));
							objSpecimenResultGroupCollection.add(objResultGroup);
						}
						else if(objSpecimenGroup == null && objOBXSegmentCollection.size() > 1)
							throw new SegmentMissingException(EnumSegments.HL7_SEG_SPM.getValue());
					}
					else
					{
						if(objSpecimenGroup != null)
							objSpecimenResultGroupCollection.add(objSpecimenGroup);
						else
							objSpecimenResultGroupCollection.add(objResultGroup);
					}

				}while(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_SPM.getValue())|| objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_OBX.getValue()) || objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_SAC.getValue()) ||
						objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_INV.getValue()) || objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_OBR.getValue()) || objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_ORC.getValue()) ||
						objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue()) || objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_TQ1.getValue()) || objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_TQ2.getValue()) ||
						objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_TCD.getValue()) || objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_SID.getValue()));
			}
		}

		catch(SegmentMissingException ex)
		{
			throw new ParseException(this.getClass().getSimpleName(),ex);
		}
		catch(InvalidSegmentException ex)
		{
			throw new ParseException(this.getClass().getSimpleName(),ex);
		}
		return iRead;		
	}
	//parse Container Group
	private int populateContainerGroup(ISegmentSet objSegmentSet,ISegmentItem objSegmentItem,ISingleCollection<IGroup> objGroupList,int iRead)throws GroupMissingException
	{
		ISegment objSegment=null;

		objSegment = objSegmentSet.getSegment(iRead);
		try
		{
			if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_SAC.getValue())||objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_INV.getValue()))
			{
				do
				{
					IGroup objContainerGroup = new Group(EnumSegments.HL7_GROUP_CONTAINER, objSegmentItem.getGroupItemMessageMap());

					//Parse SAC Segment
					if(!objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_SAC.getValue()))
						throw new SegmentMissingException(EnumSegments.HL7_SEG_SAC.getValue());
					else
					{
						objContainerGroup.addSegment(EnumSegments.HL7_SEG_SAC, objSegment);
						//Read segment name
						iRead++;
						objSegment = objSegmentSet.getSegment(iRead);
						if(objSegment == null)
						{
							objGroupList.add(objContainerGroup);
						}
					}

					//Parse INV Segment
					if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_INV.getValue()))
					{
						objSegment = objSegmentSet.getSegment(iRead);
						objContainerGroup.addSegment(EnumSegments.HL7_SEG_INV, objSegment);
						//Read segment name
						iRead++;
						objSegment = objSegmentSet.getSegment(iRead);
						if(objSegment == null)
						{
							objGroupList.add(objContainerGroup);
						}
					}
					objGroupList.add(objContainerGroup);
				}while(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_SAC.getValue())||objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_INV.getValue()));
			}
		}
		catch(SegmentMissingException ex)
		{
			throw new ParseException(this.getClass().getSimpleName(),ex);
		}
		catch(InvalidSegmentException ex)
		{
			throw new ParseException(this.getClass().getSimpleName(),ex);
		}
		return iRead;		
	}

	//This method is used to check if Any Segment from Result Group is arrived and Segment collection of OBX is 
	//null or zero then OBX segment is missing.
	//If OBX segment collection size is more than one then and It is not added into Specimen 
	//i.e it is null then SPM Segment is not fill into it.then throw segment missing exception for SPM.
	//else Add last OBX segment from collection from specimen group and add in this Result group. 
	private IGroup processSegments(IGroup objSpecimenGroup,ISingleCollection<ISegment> objOBXSegmentCollection, ISegmentItem objSegmentItem)
	{
		if(objOBXSegmentCollection == null || objOBXSegmentCollection.size() == 0)
			throw new SegmentMissingException(EnumSegments.HL7_SEG_OBX.getValue());

		IGroup objResultGroup = new Group(EnumSegments.HL7_GROUP_RESULT, objSegmentItem.getGroupItemMessageMap());
		if(objOBXSegmentCollection.size() > 1)
		{
			if(objSpecimenGroup == null)
				throw new SegmentMissingException(EnumSegments.HL7_SEG_SPM.getValue());
			objResultGroup.addSegment(EnumSegments.HL7_SEG_OBX,objOBXSegmentCollection.elementAt(objOBXSegmentCollection.size()-1));
			for(ISegment objOBXSegment : objOBXSegmentCollection)
				objSpecimenGroup.addSegment(EnumSegments.HL7_SEG_OBX, objOBXSegment);
		}
		else
		{
			objResultGroup.addSegment(EnumSegments.HL7_SEG_OBX,objOBXSegmentCollection.elementAt(0));
		}
		return objResultGroup;
	}
}