/*******************************************************************************
 *  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.orderentry;

import cdac.medinfo.sdk.hl7282.baselibs.ID;
import cdac.medinfo.sdk.hl7282.baselibs.ValueTables;
import cdac.medinfo.sdk.hl7282.baselibs.messages.Group;
import cdac.medinfo.sdk.hl7282.baselibs.messages.Message;
import cdac.medinfo.sdk.hl7282.baselibs.segments.NTE;
import cdac.medinfo.sdk.hl7282.baselibs.segments.SFT;
import cdac.medinfo.sdk.hl7282.baselibs.segments.TQ1;
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;

/**
 * OMD_O03 - Dietary OrderDiet (Event O03).
 * This message needs to receive specific information, the most important being the diet order itself.
 * <p>Represents a OMD_O03 message structure (see chapter 4.7.1 ) 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>MULTIPLE_OPTIONAL</b><br>
 * 4: PATIENT (a Group in OMD_O03 message) <b>SINGLE_OPTIONAL</b><br>
 * 5: ORDER_DIET (a Group in OMD_O03 message) <b>MULTIPLE_COMPULSARY</b><br>
 * 6: ORDER_TRAY (a Group in OMD_O03 message) <b>MULTIPLE_OPTIONAL</b><br>
 * This class provides mechanism to validate message against HL7 v 2.5 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.5 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>OMD_O03 objO03 = (OMD_O03)MessageFactory.createInstance().createSendSupportedMessage("OMD", "O03");</p>
 */
public final class OMD_O03 extends Message 
{

	boolean isGroupIdentified = false;
	
	private IMap objMessageMap = null;

	/**
	 * Constructs the OMD_O03 message.
	 */
	OMD_O03(IMap objMessageMap)
	{
		super(objMessageMap, EnumMessageCode.OMD.getValue(), EnumTriggerEvent.O03.getValue(), EnumMessageStructure.OMD_O03.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 Collection of Notes and Comments  Segments(NTE) present in this message.
	 * @return ISingleCollection<NTE> - Collection of Notes and Comments  Segments(NTE) present in this message.
	 */
	@SuppressWarnings("unchecked")
	public ISingleCollection<NTE> getNotesAndCommentsSegments()
	{
		ISingleCollection<?> objSegmentCollection = getSegment(EnumSegments.HL7_SEG_NTE);
		return (ISingleCollection<NTE>)objSegmentCollection;
	}

	/**
	 * Adds Notes and Comments  Segment(NTE) to this message.
	 * @param objNTE Specified Notes and Comments  Segment(NTE)to add in this message.
	 */
	public void addNotesAndCommentsSegment(NTE objNTE)
	{
		addSegment(EnumSegments.HL7_SEG_NTE, objNTE);
	}

	/**
	 * Adds collection of Notes and Comments  Segment(NTE) to this message.
	 * @param objNTESegmentCollection Collection of Notes and Comments  Segments to add in this message
	 */
	@SuppressWarnings("unchecked")
	public void addNotesAndCommentsSegment(ISingleCollection<NTE>  objNTESegmentCollection)
	{
		ISingleCollection<?> objSegmentCollection =  objNTESegmentCollection;
		addSegment(EnumSegments.HL7_SEG_NTE, (ISingleCollection<ISegment>)objSegmentCollection);
	}

	/**
	 * Retrieves Patient Group present in this message.
	 * @return Group - 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);
	}

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

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

	/**
	 * Adds ORDER_DIET GROUP to this message.
	 * @param objGroup Specified ORDER_DIET GROUP. 
	 */
	public void addOrderDietGroup(IGroup objGroup)
	{
		addGroup(EnumSegments.HL7_GROUP_ORDER_DIET , objGroup);
	}

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

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

	/**
	 * Adds ORDER_TRAY GROUP to this message.
	 * @param objGroup Specified ORDER_TRAY GROUP. 
	 */
	public void addOrderTrayGroup(IGroup objGroup)
	{
		addGroup(EnumSegments.HL7_GROUP_ORDER_TRAY , objGroup);
	}

	/**
	 * 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);
					if(objSegmentItem.getName()== EnumSegments.HL7_GROUP_ORDER_TRAY ||objSegmentItem.getName()== EnumSegments.HL7_GROUP_ORDER_DIET)
					{
						ICollectionIterator<IGroup> objGroupiterator = objGroupList.iterator();
						while(objGroupiterator.hasNext())
						{
							IGroup objIdentifiedGroup = objGroupiterator.next();
							addGroup(objIdentifiedGroup.getName(),objIdentifiedGroup);
						}
					}
					else
					{
						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_ORDER_DIET || objSegmentItem.getName()==EnumSegments.HL7_GROUP_ORDER_TRAY)
		{
			if(!isGroupIdentified)
			{
				//Parse Order Diet and Order Tray Group. 
				ISingleCollection<IGroup> objOrderDietTrayGroupCollection = new SingleCollection<IGroup>();
				iRead = identifyAndPopulateGroups(objSegmentSet,objSegmentItem,objOrderDietTrayGroupCollection,iRead);
				ICollectionIterator<IGroup> objSpecimenResultGroupIterator = objOrderDietTrayGroupCollection.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_ORDER_TRAY ||objTempSegmentItem.getName()== EnumSegments.HL7_GROUP_ORDER_DIET)
					{
						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;
	}


	/*
	 * Identify Parse Order Diet and Order Tray Group.
	 * Parsing of Order Diet and Order Tray are doing in same method because while parsing Order Diet Group, it is confused
	 * while parse ORC segment. To distinguished this ORC segment confusion we wrote this method.
	 */
	private int identifyAndPopulateGroups(ISegmentSet objSegmentSet,ISegmentItem objSegmentItem,ISingleCollection<IGroup> objOrderDiet_TrayGroupCollection,int iRead)throws GroupMissingException
	{
		boolean isOrderDietGroupArrived = false;
		boolean isOrderTrayGroupArrived = false;
		ISegment objORCSegment = null;
		ISegment objSegment = null;
		try
		{

			do
			{
				ISingleCollection<IGroup> objTimingGroupCollection = null;
				ISingleCollection<IGroup> objDietGroupCollection = null;

				objSegment = objSegmentSet.getSegment(iRead);
				//ORC segment is Taken separate because it is Single and Mandatory and it is 
				//not fixed to add in which group so after parsing group in other segments 
				//decision is taken to add in which group. 
				boolean isSegmentsProcessed = false;
				IGroup objOrderTrayGroup = null;
				IGroup objOrderDietGroup = null;	
				//Parse ORC segment
				if (!objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_ORC.getValue()))
					throw new SegmentMissingException(EnumSegments.HL7_SEG_ORC.getValue());
				else
				{
					objORCSegment = null;
					objORCSegment = objSegmentSet.getSegment(iRead);
					//Read segment name
					iRead++;
					objSegment = objSegmentSet.getSegment(iRead);
					if(objSegment == null)
					{
						//On End Of stream if OrderTray Group is arrived then add 
						//all ORC segment in OrderTray.If not then add to OrderDiet Group. 
						if(isOrderTrayGroupArrived)
						{
							objOrderTrayGroup = new Group(EnumSegments.HL7_GROUP_ORDER_TRAY, objSegmentItem.getGroupItemMessageMap());
							objOrderTrayGroup.addSegment(EnumSegments.HL7_SEG_ORC,objORCSegment);
							objOrderDiet_TrayGroupCollection.add(objOrderTrayGroup);
						}
						else
						{
							objOrderDietGroup = new Group(EnumSegments.HL7_GROUP_ORDER_DIET, objSegmentItem.getGroupItemMessageMap());
							objOrderDietGroup.addSegment(EnumSegments.HL7_SEG_ORC,objORCSegment);
							objOrderDiet_TrayGroupCollection.add(objOrderDietGroup);
						}
						return iRead;
					}
				}

				//Read segment name
				objSegment = objSegmentSet.getSegment(iRead);
				if(objSegment==null)
					return iRead;

				//Parse Timing Group
				if(objTimingGroupCollection==null)
					objTimingGroupCollection = new SingleCollection<IGroup>();
				iRead = populateTimingGroup(objSegmentSet,objSegmentItem,objTimingGroupCollection,iRead);

				//Read segment name
				//If segment is null then 
				objSegment = objSegmentSet.getSegment(iRead);
				if(objSegment!=null)
				{
					//Parse Diet Group
					if(objDietGroupCollection == null) 
						objDietGroupCollection = new SingleCollection<IGroup>();
					iRead = populateDietGroup(objSegmentSet,objSegmentItem,objDietGroupCollection,iRead);
				}

				/*
				 * Here Timing Diet and Diet Group are optional. So parsed Timing Diet and Diet Group.
				 * Check for size of collection of Diet Group.
				 */
				if(objDietGroupCollection.size() > 0)
				{
					objOrderDietGroup = new Group(EnumSegments.HL7_GROUP_ORDER_DIET, objSegmentItem.getGroupItemMessageMap());
					objOrderDietGroup.addSegment(EnumSegments.HL7_SEG_ORC,objORCSegment);
					modifyTimingGroupName(objTimingGroupCollection, EnumSegments.HL7_GROUP_TIMING_DIET);
					for(IGroup objTiminigGroup : objTimingGroupCollection)
						objOrderDietGroup.addGroup(EnumSegments.HL7_GROUP_TIMING_DIET, objTiminigGroup);
					for(IGroup objDietGroup : objDietGroupCollection)
						objOrderDietGroup.addGroup(EnumSegments.HL7_GROUP_DIET,objDietGroup);
					isOrderDietGroupArrived = true;
					isSegmentsProcessed = true;
					objORCSegment = null;
				}				

				//Read next Segment
				objSegment = objSegmentSet.getSegment(iRead);

				if(objSegment!=null)
				{
					//If ODT segment in OrderTray Group arrives before OrderDiet group then raise exception. 
					if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_ODT.getValue()))
					{
						if(objORCSegment == null)
							throw new SegmentMissingException(EnumSegments.HL7_SEG_ORC.getValue());
						else
						{
							if (isOrderDietGroupArrived == false)
								throw new GroupMissingException(EnumSegments.HL7_GROUP_ORDER_DIET.getValue());
							else
								isOrderTrayGroupArrived = true;
							objOrderTrayGroup = new Group(EnumSegments.HL7_GROUP_ORDER_TRAY, objSegmentItem.getGroupItemMessageMap());
							do
							{
								objOrderTrayGroup.addSegment(EnumSegments.HL7_SEG_ODT, objSegment);
								//Read segment name
								iRead++;
								objSegment = objSegmentSet.getSegment(iRead);
								if(objSegment ==null)
									break;
							}while(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_ODT.getValue()));
						}					
					}
				}

				if(objSegment!=null)
				{
					//Parse NTE segment
					if (objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue()))
					{
						if(!isOrderTrayGroupArrived)
							throw new GroupMissingException(EnumSegments.HL7_GROUP_ORDER_TRAY.getValue());
						do
						{
							objOrderTrayGroup.addSegment(EnumSegments.HL7_SEG_NTE,objSegment);
							//Read segment name
							iRead++;
							objSegment = objSegmentSet.getSegment(iRead);
							if(objSegment == null)
								break;
						} while (objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue()));
					}
				}
				//If Segment other than ORC and Timing is not present then 
				//The Decision is made according to arrival of group. If OrderTray group is arrived 
				//then add in OrderTray group or default add to OrderDiet Group.
				//else Normal Parsing carried Out. 
				if(!isSegmentsProcessed)
				{
					if(isOrderTrayGroupArrived)
					{
						objOrderTrayGroup.addSegment(EnumSegments.HL7_SEG_ORC,objORCSegment);
						modifyTimingGroupName(objTimingGroupCollection, EnumSegments.HL7_GROUP_TIMING_TRAY);
						for(IGroup objTiminigGroup : objTimingGroupCollection)
							objOrderTrayGroup.addGroup(EnumSegments.HL7_GROUP_TIMING_TRAY, objTiminigGroup);
						objOrderDiet_TrayGroupCollection.add(objOrderTrayGroup);
						if(objSegment == null)
							return iRead;
					}
					else
					{
						isOrderDietGroupArrived = true;
						objOrderDietGroup = new Group(EnumSegments.HL7_GROUP_ORDER_DIET, objSegmentItem.getGroupItemMessageMap());
						objOrderDietGroup.addSegment(EnumSegments.HL7_SEG_ORC,objORCSegment);
						modifyTimingGroupName(objTimingGroupCollection, EnumSegments.HL7_GROUP_TIMING_DIET);
						for(IGroup objTiminigGroup : objTimingGroupCollection)
							objOrderDietGroup.addGroup(EnumSegments.HL7_GROUP_TIMING_DIET, objTiminigGroup);
						objOrderDiet_TrayGroupCollection.add(objOrderDietGroup);
						if(objSegment == null)
							return iRead;
					}
				}
				else
				{
					if(objOrderDietGroup !=null)
					{
						objOrderDiet_TrayGroupCollection.add(objOrderDietGroup);
						if(objSegment==null)
							return iRead;
					}
					else
						objOrderDiet_TrayGroupCollection.add(objOrderTrayGroup);
				}

			}while (objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_ORC.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_ODS.getValue())||
					objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_ODT.getValue())||
					objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue())||
					objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_OBX.getValue()));
		}
		catch(SegmentMissingException ex)
		{
			throw new ParseException(this.getClass().getSimpleName(),ex);
		}
		catch(InvalidSegmentException ex)
		{
			throw new ParseException(this.getClass().getSimpleName(),ex);
		}
		return iRead;
	}

	//parse TIMING group
	private int populateTimingGroup(ISegmentSet objSegmentSet,ISegmentItem objSegmentItem,ISingleCollection<IGroup> objTimingGroupCollection,int iRead)throws GroupMissingException
	{
		ISegment objSegment;
		boolean bTQ1Repeat = false;//To check the repetition of segment TQ1
		try
		{
			objSegment = objSegmentSet.getSegment(iRead); 

			//--- TIMING begin
			if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_TQ1.getValue()) || objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_TQ2.getValue()))
			{
				do
				{
					IGroup objTimingGroup = new Group(EnumSegments.HL7_GROUP_TIMING, objSegmentItem.getGroupItemMessageMap());

					//Parse TQ1 segment
					if(!objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_TQ1.getValue()))
						throw new SegmentMissingException(EnumSegments.HL7_SEG_TQ1.getValue());
					else
					{
						if(bTQ1Repeat == true)
						{	
							ValueTables objValueTables = ValueTables.createInstance();
							ID objID = ((TQ1)objSegment).getConjunction();
							if(objID != null)
							{
								if(objValueTables.isValuePresent(427, (String)objID.getValue()) == HL7Constants.HL7_VALUE_NOT_FOUND)
									throw new InvalidSegmentException(EnumSegments.HL7_SEG_TQ1.getValue());
							}
						}
						objTimingGroup.addSegment(EnumSegments.HL7_SEG_TQ1, objSegment);
						//Read segment name
						iRead++;
						objSegment = objSegmentSet.getSegment(iRead); 
						if(objSegment == null)
						{
							objTimingGroupCollection.add(objTimingGroup);
							return iRead;
						}
					}

					//Parse TQ2 segment
					if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_TQ2.getValue()))
					{
						do
						{
							objTimingGroup.addSegment(EnumSegments.HL7_SEG_TQ2, objSegment);
							//Read segment name
							iRead++;
							objSegment = objSegmentSet.getSegment(iRead);
							if(objSegment == null)
							{
								objTimingGroupCollection.add(objTimingGroup);
								return iRead;
							}
						}while(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_TQ2.getValue()));
					}
					bTQ1Repeat = true;
					objTimingGroupCollection.add(objTimingGroup);

				}while(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_TQ1.getValue()) || objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_TQ2.getValue()));
			}//--- TIMING_DIET end
		}
		catch(SegmentMissingException ex)
		{
			throw new ParseException(this.getClass().getSimpleName(),ex);
		}
		catch(InvalidSegmentException ex)
		{
			throw new ParseException(this.getClass().getSimpleName(),ex);
		}
		return iRead;		
	}


	//Populate Diet Group
	private int populateDietGroup(ISegmentSet objSegmentSet,ISegmentItem objSegmentItem,ISingleCollection<IGroup> objDietGroupCollection,int iRead)throws GroupMissingException
	{
		ISegment objSegment;
		try
		{
			objSegment = objSegmentSet.getSegment(iRead);

			if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_ODS.getValue())||
					objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue())||
					objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_OBX.getValue()))
			{
				IGroup objDietGroup = new Group(EnumSegments.HL7_GROUP_DIET, objSegmentItem.getGroupItemMessageMap());

				//Parse ODS segment
				if (!objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_ODS.getValue()))
					throw new SegmentMissingException(EnumSegments.HL7_SEG_ODS.getValue());
				do
				{
					objDietGroup.addSegment(EnumSegments.HL7_SEG_ODS,objSegment);
					//Read segment name
					iRead++;
					objSegment = objSegmentSet.getSegment(iRead);
					if(objSegment == null)
					{
						objDietGroupCollection.add(objDietGroup);
						return iRead;
					}
				}while(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_ODS.getValue()));

				//Parse NTE segment
				if (objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue()))
				{
					do
					{
						objDietGroup.addSegment(EnumSegments.HL7_SEG_NTE,objSegment);
						//Read segment name
						iRead++;
						objSegment = objSegmentSet.getSegment(iRead);
						if(objSegment == null)
						{
							objDietGroupCollection.add(objDietGroup);
							return iRead;
						}
					} while (objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue()));
				}
				//parse Observation Group
				ISingleCollection<IGroup> objObservationGroupCollection = new SingleCollection<IGroup>(); 
				iRead = populateObservationGroup(objSegmentSet,objSegmentItem,objObservationGroupCollection,iRead);
				for(IGroup objGroup:objObservationGroupCollection)
					objDietGroup.addGroup(EnumSegments.HL7_GROUP_OBSERVATION,objGroup);
				objDietGroupCollection.add(objDietGroup);
			}
		}
		catch(SegmentMissingException ex)
		{
			throw new ParseException(this.getClass().getSimpleName(),ex);
		}
		catch(InvalidSegmentException ex)
		{
			throw new ParseException(this.getClass().getSimpleName(),ex);
		}
		return iRead;
	}

//	parse OBSERVATION  group
	private int populateObservationGroup(ISegmentSet objSegmentSet,ISegmentItem objSegmentItem,ISingleCollection<IGroup> objObservationGroupCollection,int iRead)throws GroupMissingException
	{
		ISegment objSegment;
		try
		{
			objSegment = objSegmentSet.getSegment(iRead);

			//----OBSERVATION GROUP begin
			if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_OBX.getValue()) || objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue()))
			{
				do
				{
					IGroup objObservationGroup = new Group(EnumSegments.HL7_GROUP_OBSERVATION, objSegmentItem.getGroupItemMessageMap());

					//Parse OBX segment
					if(!objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_OBX.getValue()))
						throw new SegmentMissingException(EnumSegments.HL7_SEG_OBX.getValue());
					else
					{
						objObservationGroup.addSegment(EnumSegments.HL7_SEG_OBX, objSegment);
						//Read segment name
						iRead++;
						objSegment = objSegmentSet.getSegment(iRead);
						if(objSegment== null)
						{
							objObservationGroupCollection.add(objObservationGroup);
							return iRead;
						}
					}

					//Parse NTE segment
					if(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue()))
					{
						do
						{
							objObservationGroup.addSegment(EnumSegments.HL7_SEG_NTE, objSegment);
							//Read segment name
							iRead++;
							objSegment = objSegmentSet.getSegment(iRead);
							if(objSegment == null)
							{
								objObservationGroupCollection.add(objObservationGroup);
								return iRead;
							}
						}while(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue()));
					}
					objObservationGroupCollection.add(objObservationGroup);
				}while(objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_OBX.getValue()) || objSegment.getName().getValue().equals(EnumSegments.HL7_SEG_NTE.getValue()));
			}//----OBSERVATION GROUP end
		}
		catch(SegmentMissingException ex)
		{
			throw new ParseException(this.getClass().getSimpleName(),ex);
		}
		catch(InvalidSegmentException ex)
		{
			throw new ParseException(this.getClass().getSimpleName(),ex);
		}
		return iRead;		
	}
	/*
	 * At the timing of Parsing it is not decided that Timing Diet Group is Parsing or Timning Tray.
	 * So After Parsing decision is made and group name is modified and added in respective group. 
	 */
	private void modifyTimingGroupName(ISingleCollection<IGroup> objTimingGroup,EnumSegments objName)
	{
		for(IGroup objGroup:objTimingGroup)
		{
			objGroup.setName(objName);
		}
	}
}