/*******************************************************************************
 *  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.IOException;
import java.util.ArrayList;
import java.util.Locale;

import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import cdac.medinfo.sdk.hl7282.baselibs.segments.MSH;
import cdac.medinfo.sdk.hl7282.commoninterface.ICollectionIterator;
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.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.utils.EnumSegmentCardinality;
import cdac.medinfo.sdk.hl7282.utils.EnumSegments;
import cdac.medinfo.sdk.hl7282.utils.HL7Config;
import cdac.medinfo.sdk.hl7282.utils.HL7Constants;
import cdac.medinfo.sdk.hl7282.utils.HL7Logger;
import cdac.medinfo.sdk.hl7282.utils.IMap;
import cdac.medinfo.sdk.hl7282.utils.IMultiValuedPairedCollection;
import cdac.medinfo.sdk.hl7282.utils.ISegmentItem;
import cdac.medinfo.sdk.hl7282.utils.MultiValuedPairedCollection;
import cdac.medinfo.sdk.hl7282.utils.ResourceBundleWrapper;
import cdac.medinfo.sdk.hl7282.utils.SingleCollection;

class DataValue
{
	

	private IMultiValuedPairedCollection<ISegmentKey, ISegment> objSegmentList = null;	

	private IMultiValuedPairedCollection<ISegmentKey, IGroup> objGroupList = null;

	Message objMessage = null;

	IMap objMessageMap = null;

	protected DataValue(IMap objMessageMap)
	{
		this.objMessageMap = objMessageMap;
		objSegmentList = new MultiValuedPairedCollection<ISegmentKey,ISegment>();
		objGroupList = new MultiValuedPairedCollection<ISegmentKey, IGroup>();
	}

	protected int addSegment(ISegmentKey enumSegments,ISingleCollection<ISegment> objSegmentCollection)
	{
		for(ISegment objSegment : objSegmentCollection)
			addSegment(enumSegments, objSegment);
		return HL7Constants.HL7_FUNCTION_SUCCESS;
	}

	/**
	 * Adds a segment to list with provided key
	 * @param enumSegments Specified enumSegments specifying key for list 
	 * @param objSegment Specified segment 
	 * @return int - shows status as success or failure
	 */
	protected int addSegment(ISegmentKey enumSegments,ISegment objSegment)
	{
		String strSegmentKey = enumSegments.getValue().substring(0, 3);
		String strSegmentNameTobeAdded = objSegment.getName().getValue().substring(0, 3);
		if(strSegmentKey.equals(strSegmentNameTobeAdded))
		{
			objSegmentList.put(enumSegments,objSegment);
			return HL7Constants.HL7_FUNCTION_SUCCESS;
		}
		else
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger.getLogger().log(ResourceBundleWrapper.getValue("exceptions", "InvalidSegmentKey", new Locale(HL7Config.createInstance().getLocale())) + enumSegments.getValue());
			}
			throw new InvalidSegmentException(ResourceBundleWrapper.getValue("exceptions","InvalidSegmentKey",new Locale(HL7Config.createInstance().getLocale()))+ enumSegments.getValue());
		}
	}

	protected void setSegment(ISegmentKey enumSegmentName, ISegment objSegment)
	{
		removeSegment(enumSegmentName);
		addSegment(enumSegmentName, objSegment);
	}

	/**
	 * Adds a group collection to list.
	 * @param objGroupCollection collection of groups
	 */
	protected int addGroup(ISegmentKey enumGroups,ISingleCollection<IGroup> objGroupCollection)
	{
		for(IGroup objGroup : objGroupCollection)
			addGroup(enumGroups,objGroup);
		return HL7Constants.HL7_FUNCTION_SUCCESS;
	}

	protected int addGroup(ISegmentKey enumGroups, IGroup objGroup)
	{
		String strGroupKey = enumGroups.getValue();
		String strGroupNameTobeAdded = objGroup.getName().getValue();
		if(strGroupKey.equals(strGroupNameTobeAdded))
		{
			objGroupList.put(enumGroups, objGroup);
			return HL7Constants.HL7_FUNCTION_SUCCESS;
		}
		else
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger.getLogger().log(ResourceBundleWrapper.getValue("exceptions", "InvalidSegmentKey", new Locale(HL7Config.createInstance().getLocale())) + enumGroups.getValue());
			}
			throw new InvalidSegmentException(ResourceBundleWrapper.getValue("exceptions","InvalidSegmentKey",new Locale(HL7Config.createInstance().getLocale()))+ enumGroups.getValue());
		}
	}

	protected void setGroup(ISegmentKey enumGroupName, IGroup objGroup)
	{
		removeGroup(enumGroupName);
		addGroup(enumGroupName, objGroup);
	}

	protected ISingleCollection<IGroup> getGroup(ISegmentKey enumGroup)
	{
		return objGroupList.get(enumGroup);
	}

	protected ISingleCollection<ISegment> getSegment(ISegmentKey enumSegments)
	{
		return objSegmentList.get(enumSegments);
	}

	protected boolean removeSegment(ISegmentKey enumSegments)
	{
		if(objSegmentList.contains(enumSegments))
		{
			objSegmentList.remove(enumSegments);
			return true;
		}
		else
			return false;
	}

	protected boolean removeGroup(ISegmentKey enumSegments)
	{
		if(objGroupList.contains(enumSegments))
		{
			objGroupList.remove(enumSegments);
			return true;
		}
		else
			return false;
	}

	protected boolean contains(ISegmentKey enumSegments)
	{
		if(objSegmentList.contains(enumSegments))
			return true;
		else if(objGroupList.contains(enumSegments))
			return true;
		else
			return false;
	}


	protected ISingleCollection<ISegment> getSegmentCollection()
	{
		ISingleCollection<ISegment> objSegmentCollection = null;

		if(validate())
		{
			objSegmentCollection = new SingleCollection<ISegment>();
			int i = 1;
			ISegmentItem objSegmentItem = null;
			while(i <= objMessageMap.size())
			{
				objSegmentItem = objMessageMap.getSegmentItem(i);

				if(objSegmentItem.isGroup())
				{
					/*
					 * For groups it is retrieving group collection and iterating through all groups if 
					 * group is repeatable.
					 */
					ISingleCollection<IGroup> objSingleCollectionGroup = getGroup(objSegmentItem.getName());
					if(objSingleCollectionGroup != null)
					{
						for(IGroup objGroup : objSingleCollectionGroup)
						{
							fillGroup(objSegmentCollection, objSegmentItem, objGroup);
						}
					}
				}
				else
				{
					if(EnumSegments.HL7_SEG_MSH.equals(objSegmentItem.getName()))
						fillHeader(objSegmentCollection);
					else
						fillSegments(getSegment(objSegmentItem.getName()), objSegmentCollection);
				}
				i++;
			}
		}
		return objSegmentCollection;
	}

	/*
	 * Serializes the segments of a group.
	 */
	protected void fillGroup(ISingleCollection<ISegment> objSegmentCollection,ISegmentItem objSegmentItem,IGroup objGroup)
	{
		ISegmentKey enumGroupName = objSegmentItem.getName();

		if(EnumSegments.HL7_GROUP_TRIPLE_DOT.getValue().equals(enumGroupName.getValue()) || EnumSegments.HL7_GROUP_OBR_ETC.getValue().equals(enumGroupName.getValue()))
		{
			ISingleCollection<ISegment> objGroupSegments = objGroup.getSegmentSet().getList();

			for(ISegment objISegment : objGroupSegments)
			{
				objSegmentCollection.add(objISegment);
			}
		}
		else
		{
			IMap objMessageMapInGroup =  objSegmentItem.getGroupItemMessageMap();
			int i = 1;

			ISegmentItem objTempSegmentItem = null;
			while(i <= objMessageMapInGroup.size())
			{
				objTempSegmentItem = objMessageMapInGroup.getSegmentItem(i);
				if(objTempSegmentItem.isGroup())
				{
					/*
					 * For sub groups it is recursively calling parsing of group.
					 */
					ISingleCollection<IGroup> objSingleCollectionGroup =  objGroup.getGroup(objTempSegmentItem.getName());
					if(objSingleCollectionGroup != null)
					{
						for(IGroup objTempGroup : objSingleCollectionGroup)
						{
							fillGroup(objSegmentCollection, objTempSegmentItem, objTempGroup);
						}
					}
				}
				else
				{
					fillSegments(objGroup.getSegment(objTempSegmentItem.getName()),objSegmentCollection);
				}
				i++;
			}
		}
	}

	/**
	 * This method fills Message Header in provided SegmentSet
	 * @param objSegmentSet instance of SegmentSet
	 * @throws InvalidMessageException If message validation fails.
	 */
	protected void fillHeader(ISingleCollection<ISegment> objSegmentCollection)throws InvalidMessageException
	{
		//Serialize MSH
		ISingleCollection<ISegment> objHeaderList = getSegment(EnumSegments.HL7_SEG_MSH);

		if(objHeaderList == null || objHeaderList.size() != 1)
			throw new InvalidMessageException(ResourceBundleWrapper.getValue("exceptions","MissingHeader",new Locale(HL7Config.createInstance().getLocale())));

		objSegmentCollection.add(objHeaderList.elementAt(0));
	}

	/**
	 * This method Accepts Single Collection of Segments & adds them one by one to provided SegmentSet
	 * @param objSegmentCollection list of segments.
	 * @param objSegmentSet instance of empty SegmentSet.
	 */
	protected void fillSegments(ISingleCollection<ISegment> objSegmentCollection, ISingleCollection<ISegment> objSegmentCollectionToFill)
	{
		if(objSegmentCollection != null)
		{
			for(ISegment objSegment : objSegmentCollection)
			{
				if(objSegment!=null)
					objSegmentCollectionToFill.add(objSegment);
			}
		}
	}


	protected void populate(ISegmentSet objSegmentSet)
	{
		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.
		 */
		ISegment objTempSegment = objSegmentSet.getSegment(iRead);
		if(objTempSegment != null)
			throw new InvalidSegmentSetException(ResourceBundleWrapper.getValue("exceptions", "NonMemberSegment", new Locale(HL7Config.createInstance().getLocale())) + objTempSegment.getName().getValue());
	}

	private int populateGroup(ISegmentSet objSegmentSet,ISegmentItem objSegmentItem,ISingleCollection<IGroup> objGroupList,int iRead)throws GroupMissingException
	{
		IGroup objGroup = null;

		//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);
			ISegmentKey enumGroupName =  objSegmentItem.getName();
			if(EnumSegments.HL7_GROUP_TRIPLE_DOT.getValue().equals(enumGroupName.getValue()))
			{
				objGroup = new Group(objSegmentItem.getName(), objGroupItemMessageMap);
				iRead = populateTripleDotGroup(objSegmentSet, objGroup, iRead);
				objGroupList.add(objGroup);
				return iRead;
			}
			else if(EnumSegments.HL7_GROUP_OBR_ETC.getValue().equals(enumGroupName.getValue()))
			{
				objGroup = new Group(objSegmentItem.getName(), objGroupItemMessageMap);
				iRead = populateOBRetcGroup(objSegmentSet, objGroup, iRead);
				objGroupList.add(objGroup);
				return iRead;
			}
			else
			{
				/*
				 * 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(),objSegmentSet, iRead))
				{
					if(!isSegmentSetPopulated )
					{
						GroupMissingException groupMissingException = new GroupMissingException(objSegmentItem.getName().getValue());
						
						if(enumCardinality == EnumSegmentCardinality.MULTIPLE_COMPULSARY || enumCardinality == EnumSegmentCardinality.SINGLE_COMPULSARY)
						{
							if(HL7Config.createInstance().isLoggingOn())
							{
								HL7Logger.getLogger().log(groupMissingException.getMessage());
							}
							throw groupMissingException;
						}

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

	/*
	 * Checks whether group parsing is required or not.
	 */
	private boolean isGroupParsingRequired(IMap objGroupItemMessageMap,String strSegmentBuffer ,ISegmentSet objSegmentSet, int iRead)
	{
		int i = 1;
		ISegmentItem objSegmentItem = null;
		while(i <= objGroupItemMessageMap.size())
		{
			objSegmentItem = objGroupItemMessageMap.getSegmentItem(i);

			if(objSegmentItem.isGroup())
			{
				ISegmentKey enumGroupName =  objSegmentItem.getName();
				if(EnumSegments.HL7_GROUP_TRIPLE_DOT.getValue().equals(enumGroupName.getValue()))
				{
					if(!(EnumSegments.HL7_SEG_MSH.getValue().equals(strSegmentBuffer) || EnumSegments.HL7_SEG_DSC.getValue().equals(strSegmentBuffer)))
						return true;
				}
				else if(EnumSegments.HL7_GROUP_OBR_ETC.getValue().equals(enumGroupName.getValue()))
				{
					if(EnumSegments.HL7_SEG_OBR.getValue().equals(strSegmentBuffer) || EnumSegments.HL7_SEG_RQD.getValue().equals(strSegmentBuffer) || 
							EnumSegments.HL7_SEG_RQ1.getValue().equals(strSegmentBuffer) || EnumSegments.HL7_SEG_RXO.getValue().equals(strSegmentBuffer) ||
							EnumSegments.HL7_SEG_ODS.getValue().equals(strSegmentBuffer) || EnumSegments.HL7_SEG_ODT.getValue().equals(strSegmentBuffer))
					{
						return true;
					}
				}

				IMap objMessageMapInGroup = objSegmentItem.getGroupItemMessageMap();
				boolean bStatus = isGroupParsingRequired(objMessageMapInGroup, strSegmentBuffer,objSegmentSet,iRead);
				if(bStatus)
					return bStatus;
			}
			else
			{
				String strStructSegmentName = objSegmentItem.getName().getValue();
				strStructSegmentName = strStructSegmentName.substring(0,3);
				if(strStructSegmentName.equals(strSegmentBuffer))
				{
					return isSegmentInGroup(objGroupItemMessageMap, strSegmentBuffer, objSegmentSet, iRead);
				}
					//return true;
			}
			i++;
		}

		return false;
	}
	/*
	 * This method checks for the first segment in group is compulsory or not.
	 * If first segment is compulsory, then it checks that whether this compulsory segment is present in SegmentSet(segment set after parsing) or not.
	 * If compulsory segment present in SegmentSet then it returns true, otherwise returns false.
	 * if first segment is optional, then it returns true. 
	 */
	private boolean isSegmentInGroup(IMap objGroupItemMessageMap,String strSegmentBuffer ,ISegmentSet objSegmentSet, int iRead)
	{
			//selects first item in GroupItemMessageMap	
			String strSegmentItem= objGroupItemMessageMap.getSegmentItem(1).getName().getValue();
			strSegmentItem=strSegmentItem.substring(0,3);
			EnumSegmentCardinality enumCardinality=objGroupItemMessageMap.getSegmentItem(1).getSegmentCardinality();
			
			//checks the cardinality of first selected item.
			if(enumCardinality == EnumSegmentCardinality.SINGLE_COMPULSARY || enumCardinality == EnumSegmentCardinality.MULTIPLE_COMPULSARY )
			{
				//Forms ArrayList of only segments which are read from SegmentSet
				ArrayList<String> objSegmentSetList=new ArrayList<String>();
				for(int k=0; k<=iRead; k++)
				{
					String strSegment = objSegmentSet.getSegment(k).getName().getValue(); 
					objSegmentSetList.add(strSegment);
				}
				//Checks whether first selected item is present in List of read segments
				if(objSegmentSetList.contains(strSegmentItem))
				{
					return true;
				}
				
			}
			else
			{
				return true;
			}
		return false;
	}

	/*
	 * 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 
		{
			SegmentMissingException segmentMissingException = new SegmentMissingException(objSegmentItem.getName().getValue());
			if((EnumSegmentCardinality.MULTIPLE_COMPULSARY == enumCardinality) || (EnumSegmentCardinality.SINGLE_COMPULSARY == enumCardinality))
			{
				if(HL7Config.createInstance().isLoggingOn())
				{
					HL7Logger.getLogger().log(segmentMissingException.getMessage());
				}
				throw segmentMissingException;
			}
		}

		return iRead;
	}

	/*
	 * Populates Segments of [...] group.
	 */
	private int populateTripleDotGroup(ISegmentSet objSegmentSet,IGroup objTripleDotGroup,int iRead)
	{
		ISegment objSegment = null;

		do
		{
			objSegment = objSegmentSet.getSegment(iRead);
			if(objSegment == null || objSegment.getName() == EnumSegments.HL7_SEG_MSH || objSegment.getName() == EnumSegments.HL7_SEG_DSC)
			{
				return iRead;
			}
			else
			{
				objTripleDotGroup.addSegment(objSegment.getName(), objSegment);
				iRead++;
			}
		}while(true);
	}

	/*
	 * Populates Segments of "OBR etc." group.
	 */
	private int populateOBRetcGroup(ISegmentSet objSegmentSet,IGroup objOBRetcGroup,int iRead)
	{
		ISegment objSegment = null;

		do
		{
			objSegment = objSegmentSet.getSegment(iRead);
			if(objSegment != null && (objSegment.getName() == EnumSegments.HL7_SEG_OBR || objSegment.getName() == EnumSegments.HL7_SEG_RQD || 
					objSegment.getName() == EnumSegments.HL7_SEG_RQ1 || objSegment.getName() == EnumSegments.HL7_SEG_RXO ||
					objSegment.getName() == EnumSegments.HL7_SEG_ODS || objSegment.getName() == EnumSegments.HL7_SEG_ODT))
			{
				objOBRetcGroup.addSegment(objSegment.getName(), objSegment);
				iRead++;
			}
			else
			{
				return iRead;
			}
		}while(true);
	}


	protected boolean validate()
	{
		ISegmentItem objSegmentItem = null;

		/*
		 * Iterating through the segment structure list and validating 
		 * that whether all mandatory groups or segments are present or not. 
		 */
		int i = 1;
		while(i <= objMessageMap.size())
		{
			objSegmentItem = objMessageMap.getSegmentItem(i);

			if(objSegmentItem.isGroup())
			{
				ISingleCollection<IGroup> objSingleCollectionGroup = getGroup(objSegmentItem.getName());
				if(objSingleCollectionGroup != null)
				{
					if(objSingleCollectionGroup.size() > 1)
					{
						EnumSegmentCardinality enumSegmentCardinality = objSegmentItem.getSegmentCardinality();
						InvalidMessageException invalidMessageException = new InvalidMessageException(ResourceBundleWrapper.getValue("exceptions","MultiplicityConstraint",new Locale(HL7Config.createInstance().getLocale()))+objSegmentItem.getName().getValue());
						if(enumSegmentCardinality == EnumSegmentCardinality.SINGLE_COMPULSARY || enumSegmentCardinality == EnumSegmentCardinality.MULTIPLE_COMPULSARY)
						{
							throw  invalidMessageException;
						}
						else
						{
							if(HL7Config.createInstance().isLoggingOn())
							{
								HL7Logger.getLogger().log(invalidMessageException.getMessage());
							}
						}
						
					}
					for(IGroup objGroup : objSingleCollectionGroup)
					{
						validateGroup(objSegmentItem, objGroup);
					}
				}
				else
				{
					EnumSegmentCardinality enumSegmentCardinality = objSegmentItem.getSegmentCardinality();
					GroupMissingException groupMissingException =  new GroupMissingException(objSegmentItem.getName().getValue());
					if(enumSegmentCardinality == EnumSegmentCardinality.SINGLE_COMPULSARY || enumSegmentCardinality == EnumSegmentCardinality.MULTIPLE_COMPULSARY)
					{
						if(HL7Config.createInstance().isLoggingOn())
						{
							HL7Logger.getLogger().log(groupMissingException.getMessage());
						}
						throw groupMissingException;
					}
				}
			}
			else
			{
				ISingleCollection<ISegment> objSegmentCollection = getSegment(objSegmentItem.getName());
				validateSegments(objSegmentCollection,objSegmentItem.getName(),objSegmentItem.getSegmentCardinality());
			}
			i++;
		}
		return true;
	}


	private boolean validateGroup(ISegmentItem objSegmentItem,IGroup objGroup)
	{
		ISegmentKey enumGroupName = objSegmentItem.getName();

		if(!(EnumSegments.HL7_GROUP_TRIPLE_DOT.getValue().equals(enumGroupName.getValue()) || EnumSegments.HL7_GROUP_OBR_ETC.getValue().equals(enumGroupName.getValue())))
		{
			IMap objMessageMapInGroup = objSegmentItem.getGroupItemMessageMap();
			int i = 1;

			ISegmentItem objTempSegmentItem = null;
			while(i <= objMessageMapInGroup.size())
			{
				objTempSegmentItem = objMessageMapInGroup.getSegmentItem(i);
				if(objTempSegmentItem.isGroup())
				{
					/*
					 * For validation of sub group same method will be called in recursive manner. 
					 */
					ISingleCollection<IGroup> objSingleCollectionGroup =  objGroup.getGroup(objTempSegmentItem.getName());
					if(objSingleCollectionGroup != null)
					{
						if(objSingleCollectionGroup.size() > 1)
						{
							EnumSegmentCardinality enumSegmentCardinality = objTempSegmentItem.getSegmentCardinality();
							InvalidMessageException invalidMessageException = new InvalidMessageException(ResourceBundleWrapper.getValue("exceptions","MultiplicityConstraint",new Locale(HL7Config.createInstance().getLocale()))+objTempSegmentItem.getName().getValue()); 
							if(enumSegmentCardinality == EnumSegmentCardinality.SINGLE_COMPULSARY || enumSegmentCardinality == EnumSegmentCardinality.MULTIPLE_COMPULSARY)
							{
								if(HL7Config.createInstance().isLoggingOn())
								{
									HL7Logger.getLogger().log(invalidMessageException.getMessage());
								}
								throw  invalidMessageException;	
							}
							else
							{
								if(HL7Config.createInstance().isLoggingOn())
								{
									HL7Logger.getLogger().log(invalidMessageException.getMessage());
								}
							}
						}

						for(IGroup objTempGroup : objSingleCollectionGroup)
						{
							validateGroup(objTempSegmentItem, objTempGroup);
						}
					}
					else
					{
						EnumSegmentCardinality enumSegmentCardinality = objTempSegmentItem.getSegmentCardinality();
						GroupMissingException groupMissingException = new GroupMissingException(objTempSegmentItem.getName().getValue());
						if(enumSegmentCardinality == EnumSegmentCardinality.SINGLE_COMPULSARY || enumSegmentCardinality == EnumSegmentCardinality.MULTIPLE_COMPULSARY)
						{
							if(HL7Config.createInstance().isLoggingOn())
							{
								HL7Logger.getLogger().log(groupMissingException.getMessage());
							}
							throw groupMissingException;
						}
					}
				}
				else
				{
					ISingleCollection<ISegment> objSingleCollection = objGroup.getSegment(objTempSegmentItem.getName());
					validateSegments(objSingleCollection,objTempSegmentItem.getName(),objTempSegmentItem.getSegmentCardinality());
				}
				i++;
			}
		}
		return true;
	}

	/**
	 * This method Accepts Single Collection of Segments & validates them one by one. 
	 * @param objISingleCollection Collection of Segments to be parsed.
	 * @param ISegmentKey Enum representing name of segment to be validated.
	 * @param enumSegmentCardinality Enum representing cardinality and optionality of segment.
	 */
	private void validateSegments(ISingleCollection<ISegment> objISingleCollection,ISegmentKey enumSegments,EnumSegmentCardinality enumSegmentCardinality)
	{
		switch(enumSegmentCardinality)
		{
		case  SINGLE_COMPULSARY : 
			if(objISingleCollection == null)
			{
				SegmentMissingException segmentMissingException =new SegmentMissingException(enumSegments.getValue());

					throw segmentMissingException;
			}
			else if(objISingleCollection.size() != 1)
			{
				InvalidMessageException invalidMessageException= new InvalidMessageException(ResourceBundleWrapper.getValue("exceptions","MultiplicityConstraint",new Locale(HL7Config.createInstance().getLocale()))+enumSegments.getValue());
				
					throw invalidMessageException;
			}
			break;
		case  SINGLE_OPTIONAL :
			if(objISingleCollection != null)
			{
				if(objISingleCollection.size() != 1)
				{
					InvalidMessageException invalidMessageException = new InvalidMessageException(ResourceBundleWrapper.getValue("exceptions","MultiplicityConstraint",new Locale(HL7Config.createInstance().getLocale()))+enumSegments.getValue());
					
						if(HL7Config.createInstance().isLoggingOn())
						{
							HL7Logger.getLogger().log(invalidMessageException.getMessage());
						}
				}
			}
			break;
		case  MULTIPLE_COMPULSARY :
			if(objISingleCollection == null)
			{
				SegmentMissingException segmentMissingException =new SegmentMissingException(enumSegments.getValue());
			
				throw segmentMissingException;
			
			}
			break;
		case  MULTIPLE_OPTIONAL : 
			break;
		}

		if(objISingleCollection != null)
		{
			for(ISegment objISegment : objISingleCollection)
			{
				if(objISegment!=null)
					objISegment.validate();
				else
					throw new InvalidSegmentException("NULL: " +enumSegments.getValue());
			}
		}
	}


	protected void dumpMessage(IHL7Stream objIHL7Stream)throws IOException
	{
		ISegmentItem objSegmentItem = null;
		MSH objMSH = (MSH)getSegment(EnumSegments.HL7_SEG_MSH).elementAt(0);
		IDelimiter objDelimiter = objMSH.getDelimiter();
		int i = 1;
		int iLevel = 0;
		
		objIHL7Stream.writeString("MESSAGE NAME :: " + objMSH.getMessageType().getMessageStructure().getValue().toString() + "\n\n");

		/*
		 * Iterating through Segment structure list
		 * Parses the segment or group according to the segment item.
		 */
		while(i <= objMessageMap.size())
		{
			//Get Structure of Messages from message Map
			objSegmentItem = objMessageMap.getSegmentItem(i);

			ISegmentKey objSegmentItemName = objSegmentItem.getName(); 

			if(objSegmentItem.isGroup())
			{
				//Get Filled Group List from Segment List
				ISingleCollection<IGroup> objGroupList = getGroup(objSegmentItemName);
				if(objGroupList != null)
				{
					for(IGroup objGroup : objGroupList)
					{
						String strSegmentKey = objSegmentItem.getName().getValue();
						objIHL7Stream.writeString("GROUP :: " + strSegmentKey + "\n\n");
						dumpGroup(objSegmentItem, objIHL7Stream, objGroup, objDelimiter,iLevel);
						objIHL7Stream.writeString("GROUP END :: " + strSegmentKey + "\n\n\n");
					}
				}
			}
			else
			{
				ISingleCollection<ISegment> objSegmentCollection = getSegment(objSegmentItemName);
				if(objSegmentCollection != null)
				{
					for(ISegment objSegment : objSegmentCollection)
					{
						objSegment.dumpSegment(objIHL7Stream,objDelimiter, iLevel);
						objIHL7Stream.writeString("\n\n");
					}
				}
			}
			i++;
		}
	}

	/**
	 * Serializes group information in the form of Attribute Name and its Value.
	 * @param objSegmentItem Object of ISegmentItem
	 * @param objHL7Stream Object of IHL7Stream
	 * @param objGroup Object of Group
	 * @param objDelimiter Object of IDelimiter
	 * @param iLevel integer shows level of an attribute.
	 * @throws DumpException Signals that dump operation is failed
	 * @throws IOException Signals that an I/O exception of some sort has occurred.
	 */
	private void dumpGroup(ISegmentItem objSegmentItem,IHL7Stream objHL7Stream,IGroup objGroup,IDelimiter objDelimiter,int iLevel) throws DumpException,IOException
	{
		//Get Structure of Group from map
		IMap objGroupMap = objSegmentItem.getGroupItemMessageMap();
		ISegmentItem objTempSegmentItem = null;
		int i = 1;
		iLevel = iLevel + 1;

		/*
		 * Iterating through Segment structure list
		 * Parses the segment or group according to the segment item.
		 */
		while(i <= objGroupMap.size())
		{
			objTempSegmentItem = objGroupMap.getSegmentItem(i);

			ISegmentKey objSegmentItemName = objTempSegmentItem.getName(); 

			if(objTempSegmentItem.isGroup())
			{
				ISingleCollection<IGroup> objGroupList = objGroup.getGroup(objSegmentItemName);
				if(objGroupList != null)
				{
					for(IGroup objTempGroup : objGroupList)
					{
						objHL7Stream.writeChar('\n');
						String strSegmentKey = objTempSegmentItem.getName().getValue();
						for(int j = 0 ; j <= iLevel ; j++)
						{
							objHL7Stream.writeChar('\t');
						}
						objHL7Stream.writeString("GROUP :: " + strSegmentKey + "\n\n");
						dumpGroup(objTempSegmentItem, objHL7Stream, objTempGroup, objDelimiter,iLevel);
						for(int j = 0 ; j <= iLevel ; j++)
						{
							objHL7Stream.writeChar('\t');
						}
						objHL7Stream.writeString("GROUP END :: " + strSegmentKey + "\n\n");
					}
				}
			}
			else
			{
				ISingleCollection<ISegment> objSegmentCollection = objGroup.getSegment(objSegmentItemName);
				if(objSegmentCollection != null)
				{
					for(ISegment objSegment : objSegmentCollection)
					{
						for(int j = 0 ; j <= iLevel; j++)
						{
							objHL7Stream.writeChar('\t');
						}
						objSegment.dumpSegment(objHL7Stream, objDelimiter, iLevel + 1);
						objHL7Stream.writeString("\n\n");
					}
				}
			}
			i++;
		}
	}


	protected int removeSegment(ISegmentKey enumSearchSegment, boolean isForcefullyRemove) 
	{
		int iResult = HL7Constants.HL7_FUNCTION_SUCCESS;
		if(objMessageMap.isFirstLevelMember(enumSearchSegment))
		{
			//			if(objMessageValue.objSegmentList != null && objMessageValue.objSegmentList.contains(enumSearchSegment))
			if(contains(enumSearchSegment))
			{
				ISingleCollection<ISegment> objSegCollection = getSegment(enumSearchSegment);
				removeSegment(enumSearchSegment);

				try
				{
					this.validate();
				}
				catch(InvalidMessageException ime)
				{
					if(isForcefullyRemove)
					{
						if(HL7Config.createInstance().isLoggingOn())
						{
							HL7Logger.getLogger().log(ResourceBundleWrapper.getValue("warnings", "SegmentRemovedFourcefully", new Locale(HL7Config.createInstance().getLocale())) + enumSearchSegment.getValue());
						}
						iResult = HL7Constants.HL7_FUNCTION_SUCCESS;
					}
					else
					{
						for(ISegment objSegment : objSegCollection)
						{
							addSegment(enumSearchSegment, objSegment);
						}

						if(HL7Config.createInstance().isLoggingOn())
						{
							HL7Logger.getLogger().log(ResourceBundleWrapper.getValue("warnings", "CanNotFourcefullyRemoveSegmentOrGroup", new Locale(HL7Config.createInstance().getLocale())) + enumSearchSegment.getValue());
						}
						iResult = HL7Constants.HL7_FUNCTION_FAIL;
					}
				}
			}
			else
			{
				if(HL7Config.createInstance().isLoggingOn())
				{
					HL7Logger.getLogger().log(ResourceBundleWrapper.getValue("warnings", "NullSegmentListOrSegmentNotPresent", new Locale(HL7Config.createInstance().getLocale())) + enumSearchSegment.getValue());
				}
				iResult = HL7Constants.HL7_FUNCTION_FAIL;
			}
		}
		else
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger.getLogger().log(ResourceBundleWrapper.getValue("warnings", "NotFirstLevelMember", new Locale(HL7Config.createInstance().getLocale())) + enumSearchSegment.getValue());
			}
			iResult = HL7Constants.HL7_FUNCTION_FAIL;
		}
		return iResult;
	}
	
	
	protected IMap getGroupMap(ISegmentKey enumGroupName)
	{
		return this.objMessageMap.getGroupMap(enumGroupName);
	}
	

	protected int sizeOfGroup()
	{
		return objGroupList.getAllValues().size();
	}

	protected ICollectionIterator<IGroup> iterator()
	{
		return objGroupList.iterator();
	}
	
	int writeToXML(XMLEventWriter eventWriter) throws IOException, XMLStreamException
	{
		ISegmentItem objSegmentItem = null;
		int i = 1;

		
		/*
		 * Iterating through Segment structure list
		 * Parses the segment or group according to the segment item.
		 */
		while(i <= objMessageMap.size())
		{
			//Get Structure of Messages from message Map
			objSegmentItem = objMessageMap.getSegmentItem(i);

			ISegmentKey objSegmentItemName = objSegmentItem.getName(); 

			XMLEventFactory eventFactory = XMLEventFactory.newInstance();
			XMLEvent end = eventFactory.createDTD("\n");
			XMLEvent tab = eventFactory.createDTD("\t");
			
			
			if(objSegmentItem.isGroup())
			{
				//Get Filled Group List from Segment List
				ISingleCollection<IGroup> objGroupList = getGroup(objSegmentItemName);
				if(objGroupList != null)
				{
					for(IGroup objGroup : objGroupList)
					{
						String strSegmentKey = objSegmentItem.getName().getValue();
						// Create Start node
						StartElement groupStartElement = eventFactory.createStartElement("", "", "Group");
						eventWriter.add(tab);
						eventWriter.add(groupStartElement);
						
						Attribute attribName = eventFactory.createAttribute("Name",  strSegmentKey);
						eventWriter.add(attribName);
						
						objGroup.writeToXML(eventWriter);
						
						// Create End node
						EndElement groupEndElement = eventFactory.createEndElement("", "","Group");
						eventWriter.add(groupEndElement);
						eventWriter.add(end);
					}
				}
			}
			else
			{
				ISingleCollection<ISegment> objSegmentCollection = getSegment(objSegmentItemName);
				if(objSegmentCollection != null)
				{
					for(ISegment objSegment : objSegmentCollection)
					{
						objSegment.writeToXML(eventWriter);
					}
				}
			}
			i++;
		}
		return HL7Constants.HL7_FUNCTION_SUCCESS;	
	}	
	
	/**
	 * Checks if Segment is group or not and serialize accordingly
	 * @param eventWriter contains start element
	 * @param messageType contains message Structure
	 * @return int to indicate success
	 * @throws XMLStreamException signals that an unexpected error occurred while parsing XML
	 * @throws IOException Signals that an I/O exception of some sort has occurred.
	 */
	int writeToXML_v2(XMLEventWriter eventWriter,String messageType) throws IOException, XMLStreamException
	{
		ISegmentItem objSegmentItem = null;
		int i = 1;

		/*
		 * Iterating through Segment structure list
		 * Parses the segment or group according to the segment item.
		 */
		while(i <= objMessageMap.size())
		{
			//Get Structure of Messages from message Map
			objSegmentItem = objMessageMap.getSegmentItem(i);

			ISegmentKey objSegmentItemName = objSegmentItem.getName(); 

			XMLEventFactory eventFactory = XMLEventFactory.newInstance();
			XMLEvent end = eventFactory.createDTD("\n");
			XMLEvent tab = eventFactory.createDTD("\t");
			
			
			if(objSegmentItem.isGroup())
			{
				//Get Filled Group List from Segment List
				ISingleCollection<IGroup> objGroupList = getGroup(objSegmentItemName);
				if(objGroupList != null)
				{
					for(IGroup objGroup : objGroupList)
					{
						String strSegmentKey = objSegmentItem.getName().getValue();
						// Create Start node
						StartElement groupStartElement = eventFactory.createStartElement("", "", messageType+"."+strSegmentKey);
						eventWriter.add(tab);
						eventWriter.add(groupStartElement);
						
						objGroup.writeToXML_v2(eventWriter,messageType);
						
						// Create End node
						EndElement groupEndElement = eventFactory.createEndElement("", "", messageType+"."+strSegmentKey);
						eventWriter.add(groupEndElement);
						eventWriter.add(end);
					}
				}
			}
			else
			{
				ISingleCollection<ISegment> objSegmentCollection = getSegment(objSegmentItemName);
				if(objSegmentCollection != null)
				{
					for(ISegment objSegment : objSegmentCollection)
					{
						objSegment.writeToXML_v2(eventWriter);
					}
				}
			}
			i++;
		}
		return HL7Constants.HL7_FUNCTION_SUCCESS;	
	}	
}
