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

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Locale;

import cdac.medinfo.sdk.hl7282.baselibs.segments.DSC;
import cdac.medinfo.sdk.hl7282.baselibs.segments.MSH;
import cdac.medinfo.sdk.hl7282.commoninterface.IDelimiter;
import cdac.medinfo.sdk.hl7282.commoninterface.ISingleCollection;
import cdac.medinfo.sdk.hl7282.exceptions.FragmentException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidSegmentException;
import cdac.medinfo.sdk.hl7282.exceptions.SerializeException;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IFragmentProcessor;
import cdac.medinfo.sdk.hl7282.hl7streams.HL7BufferStream;
import cdac.medinfo.sdk.hl7282.utils.EnumSegments;
import cdac.medinfo.sdk.hl7282.utils.HL7Config;
import cdac.medinfo.sdk.hl7282.utils.ResourceBundleWrapper;
import cdac.medinfo.sdk.hl7282.utils.SingleCollection;
import cdac.medinfo.sdk.hl7282.utils.StandardMethods;


/**
 * The Class FragmentAgent performs the fragmentation and defragmentation for HL7 messages
 * It Fragments the messages according to the max length allowed. 
 * and reassembles the Fragments in one message as and when required.
 */
public class FragmentAgent implements IFragmentProcessor 
{

	/** The maximum length. */
	private int maxLength;

	/**
	 * Instantiates a new fragment agent.
	 */
	public FragmentAgent()
	{
	}

	/**
	 * Sets maximum length for fragments.
	 * @param iLength value of maximum fragment length.
	 */
	public void setMaxLength(int iLength)
	{
		maxLength = iLength;
	}

	/**
	 * Concatenates the two HL7BufferStreams into the firststream.
	 * @param objHL7BufferStream1  - HL7BufferStream to which other stream will be concated. 
	 * @param objHL7BufferStream2  - HL7BufferStream which will be concated to other stream . 
	 */
	private void concatStreams(HL7BufferStream objHL7BufferStream1 ,HL7BufferStream objHL7BufferStream2)
	{
		ByteArrayInputStream objByteArrayInputStream1 = new ByteArrayInputStream(objHL7BufferStream1.toByteArray());
		ByteArrayInputStream objByteArrayInputStream2 = new ByteArrayInputStream(objHL7BufferStream2.toByteArray());
		objHL7BufferStream1.setInputStream(objByteArrayInputStream1);
		objHL7BufferStream2.setInputStream(objByteArrayInputStream2);

		byte []arrbyte = new byte[objHL7BufferStream2.available()];
		if(objHL7BufferStream2.available()!=0)
		{
			objHL7BufferStream2.readBuffer(arrbyte);
			objHL7BufferStream1.writeBuffer(arrbyte, 0, arrbyte.length);
		}
	}

	/**
	 * Serializes the DSC Segment and returns the stream for given Continuation Pointer value.
	 * @throws SerializeException when Serialization fails on the stream.
	 * @throws IOException Thrown when IO operation fails.  
	 */
	private HL7BufferStream writeDSC(String strContinuationPointer,IDelimiter objIDelimiter) throws SerializeException,IOException
	{
		DSC objDSC = new DSC();
		objDSC.setContinuationStyle("F");
		objDSC.setContinuationPointer(strContinuationPointer);

		HL7BufferStream objContinuationStream = new HL7BufferStream();
		ByteArrayOutputStream objArrayOutputStream = new ByteArrayOutputStream();
		objContinuationStream.setOutputStream(objArrayOutputStream);
		objDSC.serialize(objContinuationStream, objIDelimiter);
		return objContinuationStream;
	}

	/**
	 * Serializes the MSH Segment and returns the stream for given Continuation Pointer value. 
	 * @throws SerializeException when Serialization fails on the stream.
	 * @throws IOException Thrown when IO operation fails.  
	 */
	private HL7BufferStream writeMSH(String strContinuationPointer,MSH objFirstMSH) throws SerializeException, IOException
	{
		objFirstMSH.setContinuationPointer(strContinuationPointer);

		HL7BufferStream objContinuationStream = new HL7BufferStream();
		ByteArrayOutputStream objArrayOutputStream = new ByteArrayOutputStream();
		objContinuationStream.setOutputStream(objArrayOutputStream);
		objFirstMSH.serialize(objContinuationStream, objFirstMSH.getDelimiter());
		return objContinuationStream;
	}

	/**
	 * Fragments The HL7BufferStream and returns Fragmented HL7BufferStream.
	 * It adds required segments in fragments specified by HL7 standard.
	 * @param objHL7BufferStream HL7BufferStream to fragment.
	 * @return HL7BufferStream - Fragmented HL7BufferStream .
	 * @throws IOException Thrown when IO operation fails.  
	 */
	public ISingleCollection<HL7BufferStream> fragment(HL7BufferStream objHL7BufferStream) throws IOException
	{
		ISingleCollection<HL7BufferStream> objBufferStreamCollection = new SingleCollection<HL7BufferStream>();

		ByteArrayInputStream objByteArrayInputStream = null;
		
		byte []inputBuffer = objHL7BufferStream.toByteArray();
		ByteArrayInputStream objArrayInputStream = new ByteArrayInputStream(inputBuffer);
		objHL7BufferStream.setInputStream(objArrayInputStream);

		final int iDSCSegmentLength = 43;
		final int iADDSegmentHeaderLength = 5;
		final int iADDSegmentTrailerLength = 4;
		final int iContinuationPointerLength = 36;
		boolean bFirstFragment = true;
		boolean bAddSegmentTrailerRequired = false;
		String strContinuationPointer = null;

		if(objHL7BufferStream.available() <= maxLength)
		{
			/*
			 * This block of code executes if fragmentation is not required and available data is less than maximum length.
			 */
			objBufferStreamCollection.add(objHL7BufferStream);
			return objBufferStreamCollection;
		}
		HL7BufferStream objFragmentedHL7Stream = new HL7BufferStream();
		ByteArrayOutputStream tempOutStream = new ByteArrayOutputStream();
		objFragmentedHL7Stream.setOutputStream(tempOutStream);

		MSH firstMSH = new MSH();
		IDelimiter objIDelimiter = null;
		try 
		{
			StringBuffer strBuffValue = new StringBuffer();
			objHL7BufferStream.readString(strBuffValue, 3);
			String strStream = "";
			char cReadChar;
			do
			{
				cReadChar=objHL7BufferStream.readChar();
				strStream=strStream+cReadChar;
			}while(cReadChar != '\r');
			firstMSH.parse(strStream, objIDelimiter);
			objIDelimiter = firstMSH.getDelimiter();
			{
				firstMSH.serialize(objFragmentedHL7Stream, objIDelimiter);
			}
			/*
			 * Minimum allowed length specifies the sum of length of all additional segments and length of unique continuation pointer value.
			 * Max length must be greater than minimum allowed length. 
			 */
			int iMinAllowedLength = tempOutStream.size()+ iContinuationPointerLength + iDSCSegmentLength + iADDSegmentHeaderLength + iADDSegmentTrailerLength;
			if(iMinAllowedLength >= maxLength)
			{
				throw new FragmentException(ResourceBundleWrapper.getValue("exceptions","Fragment_MinimumMaxLength",new Locale(HL7Config.createInstance().getLocale())));
			}

			byte[] bCuttingFragment = null;
			byte[] oneFragment = null;
			int iFragmentSize = maxLength - tempOutStream.size()-iDSCSegmentLength - iADDSegmentHeaderLength;
			int iMSHFragmentLength = tempOutStream.size() + iContinuationPointerLength;
			while(objHL7BufferStream.available() != 0)
			{
				/*
				 * This If block serializes the additional MSH and ADD segments if required.
				 * CuttingFragment specifies the extra data if any name of segment is being cut during arbitrary fragment selection.
				 * It modifies the remaining fragment length accordingly. 
				 */
				if(!bFirstFragment)
				{
					concatStreams(objFragmentedHL7Stream,writeMSH(strContinuationPointer,firstMSH));
					strContinuationPointer = null;
					iFragmentSize = maxLength - iMSHFragmentLength - iDSCSegmentLength - iADDSegmentHeaderLength;

					if(bAddSegmentTrailerRequired)
					{
						byte[] bContinuationOfSegment = ("ADD|").getBytes();
						objFragmentedHL7Stream.writeBuffer(bContinuationOfSegment, 0, bContinuationOfSegment.length);
						iFragmentSize -= iADDSegmentTrailerLength;
						bAddSegmentTrailerRequired = false;
					}

					if(bCuttingFragment != null)
					{
						objFragmentedHL7Stream.writeBuffer(bCuttingFragment, 0, bCuttingFragment.length);
						iFragmentSize -= bCuttingFragment.length;
						bCuttingFragment = null;
					}
				}
				// If Available bytes are less than size of Fragment then write them as they are.
				if(objHL7BufferStream.available() <= iFragmentSize)
				{
					// Fragment size should of Available bytes only.
					iFragmentSize = objHL7BufferStream.available();

					// Write it as it is and return 
					oneFragment = new byte[iFragmentSize];
					objHL7BufferStream.readBuffer(oneFragment);
					objFragmentedHL7Stream.writeBuffer(oneFragment, 0, oneFragment.length);
					objByteArrayInputStream = new ByteArrayInputStream(objFragmentedHL7Stream.toByteArray());
					objFragmentedHL7Stream.setInputStream(objByteArrayInputStream);
					objBufferStreamCollection.add(objFragmentedHL7Stream);
					break;
				}
				else
				{
					bFirstFragment = false;
					if(strContinuationPointer == null)
						strContinuationPointer = StandardMethods.generateUUID();
					oneFragment = new byte[iFragmentSize];
					objHL7BufferStream.readBuffer(oneFragment);

					// Checking for \r i.e 13 at second and third last position is required to 
					// assure that the Fragmenter will not break  the segment name.

					if(oneFragment[oneFragment.length -3] == 13) // Third Last position
					{
						bCuttingFragment = new byte[2];
						bCuttingFragment[0] = oneFragment[oneFragment.length -2];
						bCuttingFragment[1] = oneFragment[oneFragment.length -1];
						objFragmentedHL7Stream.writeBuffer(oneFragment, 0, iFragmentSize - bCuttingFragment.length);
						concatStreams(objFragmentedHL7Stream, writeDSC(strContinuationPointer, firstMSH.getDelimiter()));
						objByteArrayInputStream = new ByteArrayInputStream(objFragmentedHL7Stream.toByteArray());
						objFragmentedHL7Stream.setInputStream(objByteArrayInputStream);
						objBufferStreamCollection.add(objFragmentedHL7Stream);
						objFragmentedHL7Stream.close();
						objFragmentedHL7Stream = new HL7BufferStream();
						tempOutStream = new ByteArrayOutputStream();
						objFragmentedHL7Stream.setOutputStream(tempOutStream);
					}
					else if(oneFragment[oneFragment.length -2] == 13) // Second Last position
					{
						bCuttingFragment = new byte[1];
						bCuttingFragment[0] = oneFragment[oneFragment.length -1];
						objFragmentedHL7Stream.writeBuffer(oneFragment, 0, iFragmentSize - bCuttingFragment.length);
						concatStreams(objFragmentedHL7Stream, writeDSC(strContinuationPointer, firstMSH.getDelimiter()));
						objByteArrayInputStream = new ByteArrayInputStream(objFragmentedHL7Stream.toByteArray());
						objFragmentedHL7Stream.setInputStream(objByteArrayInputStream);
						objBufferStreamCollection.add(objFragmentedHL7Stream);
						objFragmentedHL7Stream.close();
						objFragmentedHL7Stream = new HL7BufferStream();
						tempOutStream = new ByteArrayOutputStream();
						objFragmentedHL7Stream.setOutputStream(tempOutStream);
					}

					// Checking for \r i.e 13 at last position is required to decide the insertion of ADD
					// the block below executes when the segment is required to be broken 
					// and to continue itself in the next message.
					else if(oneFragment[oneFragment.length -1] != 13) // Last position
					{
						/*
						 * This If-Else block is checking whether ADD segment is required or not.
						 * It reads the ADD segment length bytes separately and checks wheather ADD segment is required or not.
						 * It checks that can we make perfect fragment if we does not reduce ADD segment length from fragment length.  
						 */
						byte []objTempArray = new byte[iADDSegmentHeaderLength];
						objHL7BufferStream.mark(iADDSegmentHeaderLength);
						objHL7BufferStream.readBuffer(objTempArray);
						if(oneFragment[oneFragment.length -1] != 13)
						{
							objHL7BufferStream.reset();
							objTempArray = null;
							objFragmentedHL7Stream.writeBuffer(oneFragment, 0, iFragmentSize);
							byte[] bContinuationOfMessage = ("\rADD\r").getBytes();
							objFragmentedHL7Stream.writeBuffer(bContinuationOfMessage, 0, bContinuationOfMessage.length);
							concatStreams(objFragmentedHL7Stream, writeDSC(strContinuationPointer, firstMSH.getDelimiter()));
							objByteArrayInputStream = new ByteArrayInputStream(objFragmentedHL7Stream.toByteArray());
							objFragmentedHL7Stream.setInputStream(objByteArrayInputStream);
							objBufferStreamCollection.add(objFragmentedHL7Stream);
							objFragmentedHL7Stream.close();
							objFragmentedHL7Stream = new HL7BufferStream();
							tempOutStream = new ByteArrayOutputStream();
							objFragmentedHL7Stream.setOutputStream(tempOutStream);
							bAddSegmentTrailerRequired = true;
						}
						else
						{
							objFragmentedHL7Stream.writeBuffer(oneFragment, 0, iFragmentSize);
							objFragmentedHL7Stream.writeBuffer(objTempArray, 0, objTempArray.length);
							concatStreams(objFragmentedHL7Stream, writeDSC(strContinuationPointer, firstMSH.getDelimiter()));
							objByteArrayInputStream = new ByteArrayInputStream(objFragmentedHL7Stream.toByteArray());
							objFragmentedHL7Stream.setInputStream(objByteArrayInputStream);
							objBufferStreamCollection.add(objFragmentedHL7Stream);
							objFragmentedHL7Stream.close();
							objFragmentedHL7Stream = new HL7BufferStream();
							tempOutStream = new ByteArrayOutputStream();
							objFragmentedHL7Stream.setOutputStream(tempOutStream);
						}
					}
					else 
					{
						// this will execute when segment is not required to be broken
						// but Message is required to be broken.
						objFragmentedHL7Stream.writeBuffer(oneFragment, 0, iFragmentSize);
						concatStreams(objFragmentedHL7Stream, writeDSC(strContinuationPointer, firstMSH.getDelimiter()));
						objByteArrayInputStream = new ByteArrayInputStream(objFragmentedHL7Stream.toByteArray());
						objFragmentedHL7Stream.setInputStream(objByteArrayInputStream);
						objBufferStreamCollection.add(objFragmentedHL7Stream);
						objFragmentedHL7Stream.close();
						objFragmentedHL7Stream = new HL7BufferStream();
						tempOutStream = new ByteArrayOutputStream();
						objFragmentedHL7Stream.setOutputStream(tempOutStream);
					}
				}
			}
			
			return objBufferStreamCollection;
		} 
		catch (InvalidSegmentException ise) 
		{
			throw new FragmentException(ResourceBundleWrapper.getValue("exceptions", "InvalidSegment", new Locale(HL7Config.createInstance().getLocale())));
		}
		catch (SerializeException se)
		{
			throw new FragmentException(ResourceBundleWrapper.getValue("exceptions", "SerializeFailed", new Locale(HL7Config.createInstance().getLocale())));
		}
	}


	/**
	 * Defragments The HL7BufferStream and returns Defragmented HL7BufferStream.
	 * Removes additional segments from fragments and generates the original HL7 Message.
	 * @param objHL7BufferStream HL7BufferStream to defragment.
	 * @return HL7BufferStream - Defragmented HL7BufferStream .
	 * @throws IOException Thrown when IO operation fails.
	 */
	public HL7BufferStream defragment(HL7BufferStream objHL7BufferStream) throws IOException
	{

		/*
		 * ContinuationPointerCollection is used to unite message .
		 */
		ISingleCollection<String> objContinuationPointerCollection = new SingleCollection<String>();

		ISingleCollection<HL7BufferStream> objDefragmentedStreamCollection = new SingleCollection<HL7BufferStream>();


		HL7BufferStream objDefragmentedHL7Stream = new HL7BufferStream();
		ByteArrayOutputStream objByteArrayOutputStream = new ByteArrayOutputStream();
		objDefragmentedHL7Stream.setOutputStream(objByteArrayOutputStream);

		ByteArrayInputStream objByteArrayInputStream = null;

		IDelimiter objIDelimiter = null;
		try
		{
			// Read Segments , greater than 3 condition assures that there are at least 3 chars so that read name of segment.
			while(objHL7BufferStream.available()!=0) //&& objHL7BufferStream.available()>3)
			{
				char cReadChar;
				StringBuffer objStringBuffer = new StringBuffer(3);
				objHL7BufferStream.mark(3);
				objHL7BufferStream.readString(objStringBuffer, 3);

				/*
				 * Previously there was switch case in place of if-else loop, to handle batches of Locally
				 * Extended messages. Previously it gives IllegalArguementException while searching for an
				 * enum in EnumSegments class for Locally Extended segment. Either way to handle it was to
				 * catch it in catch block as IllegalArguementException or replace the switch case with
				 * if-else loop to handle Locally Extended segment.
				 */
				if(EnumSegments.HL7_SEG_MSH.getValue().equalsIgnoreCase(objStringBuffer.toString()))
				{
					//case HL7_SEG_MSH:
					// The Delimiter checking is required to ensure that you can parse DSC segment when required.
					// Also if it is null, then MSH must be the beginning of new message.
					if(objIDelimiter != null)
					{
						MSH objMSH = new MSH();
						{
							String strStream = "";
							do
							{
								cReadChar=objHL7BufferStream.readChar();
								strStream=strStream+cReadChar;
							}while(cReadChar != '\r');
							objMSH.parse(strStream, objIDelimiter);
						}
						// objContinuationPointerCollection's size is zero, then definitely the MSH is begining of new Message. 
						if(objContinuationPointerCollection.size() != 0)
						{
							String strContinuationPointer = (String)objMSH.getContinuationPointer().getValue();
							if(strContinuationPointer !=null)
							{
								if(objContinuationPointerCollection.contains(strContinuationPointer))
								{
									objContinuationPointerCollection.remove(strContinuationPointer);
								}
								else
								{

									objByteArrayInputStream = new ByteArrayInputStream(objDefragmentedHL7Stream.toByteArray());
									objDefragmentedHL7Stream.setInputStream(objByteArrayInputStream);
									if(objDefragmentedHL7Stream.available()!=0)
									{
										objDefragmentedHL7Stream.close();
										objDefragmentedStreamCollection.add(objDefragmentedHL7Stream);
										objByteArrayOutputStream = new ByteArrayOutputStream();
										objDefragmentedHL7Stream = new HL7BufferStream();
										objDefragmentedHL7Stream.setOutputStream(objByteArrayOutputStream);
									}

									objMSH.serialize(objDefragmentedHL7Stream, objIDelimiter);
								}
							}
							else
							{
								objMSH.serialize(objDefragmentedHL7Stream, objIDelimiter);
							}
						}
						else
						{
							objByteArrayInputStream = new ByteArrayInputStream(objDefragmentedHL7Stream.toByteArray());
							objDefragmentedHL7Stream.setInputStream(objByteArrayInputStream);
							if(objDefragmentedHL7Stream.available()!=0)
							{
								objDefragmentedHL7Stream.close();
								objDefragmentedStreamCollection.add(objDefragmentedHL7Stream);
								objByteArrayOutputStream = new ByteArrayOutputStream();
								objDefragmentedHL7Stream = new HL7BufferStream();
								objDefragmentedHL7Stream.setOutputStream(objByteArrayOutputStream);
							}

							// A new message 
							objMSH.serialize(objDefragmentedHL7Stream, objIDelimiter);
						}
					}
					else
					{
						MSH objTempMSH = new MSH();
						objHL7BufferStream.mark(1);
						String strStream = "";
						do
						{
							cReadChar=objHL7BufferStream.readChar();
							strStream=strStream+cReadChar;
						}while(cReadChar != '\r');
						objTempMSH.parse(strStream, objIDelimiter);
						objIDelimiter = objTempMSH.getDelimiter();
						objTempMSH.serialize(objDefragmentedHL7Stream, objIDelimiter);
					}
				}
				else if(EnumSegments.HL7_SEG_DSC.getValue().equalsIgnoreCase(objStringBuffer.toString()))
				{
					DSC objDSC = new DSC();
					String strStream = "";
					do
					{
						cReadChar=objHL7BufferStream.readChar();
						strStream=strStream+cReadChar;
					}while(cReadChar != '\r');
					objDSC.parse(strStream, objIDelimiter);
					if("F".equals(objDSC.getContinuationStyle().getValue()))
						objContinuationPointerCollection.add((String)objDSC.getContinuationPointer().getValue());
					else
						objDSC.serialize(objDefragmentedHL7Stream, objIDelimiter);
				}

				//case HL7_SEG_ADD:
				else if(EnumSegments.HL7_SEG_ADD.getValue().equalsIgnoreCase(objStringBuffer.toString()))
				{
					// code for concating with previous segment
					objHL7BufferStream.mark(1);
					cReadChar = objHL7BufferStream.readChar();
					// if cReadChar is | then we are not required to write it.
					// in othercase cReadChar will be \r then we should reset it.
					if(cReadChar == '\r')
						continue;
					else if(cReadChar == '|')
					{
						cReadChar = objHL7BufferStream.readChar();
						while(cReadChar != '\r' && objHL7BufferStream.available()!=0)
						{
							objDefragmentedHL7Stream.writeChar(cReadChar);
							cReadChar = objHL7BufferStream.readChar();
						}

						// The logic below decides when to write \r after ADD Segment
						if(objHL7BufferStream.available()>3)
						{
							objStringBuffer = new StringBuffer(3);
							objHL7BufferStream.mark(3);
							objHL7BufferStream.readString(objStringBuffer, 3);
							String tempString = objStringBuffer.toString();
							if(!(tempString.equalsIgnoreCase(EnumSegments.HL7_SEG_ADD.getValue())|| tempString.equalsIgnoreCase(EnumSegments.HL7_SEG_DSC.getValue())))
							{
								objDefragmentedHL7Stream.writeChar(cReadChar);
							}
							objHL7BufferStream.reset();
						}
						else
						{
							objDefragmentedHL7Stream.writeChar(cReadChar);
							if(objHL7BufferStream.available()!=0)
							{
								byte []b = new byte[objHL7BufferStream.available()];
								objHL7BufferStream.readBuffer(b);
								objDefragmentedHL7Stream.writeBuffer(b, 0, b.length);
							}
						}
					}
				}
				else
				{
					//objDefragmentedHL7Stream.writeString(enumSegments.getValue());
					objDefragmentedHL7Stream.writeString(objStringBuffer.toString());
					cReadChar = objHL7BufferStream.readChar();
					while(cReadChar != '\r' && objHL7BufferStream.available()!=0)
					{
						objDefragmentedHL7Stream.writeChar(cReadChar);
						cReadChar = objHL7BufferStream.readChar();
					}

					// The logic below decides when to write \r after Segment
					if(objHL7BufferStream.available()>3)
					{
						objStringBuffer = new StringBuffer(3);
						objHL7BufferStream.mark(3);
						objHL7BufferStream.readString(objStringBuffer, 3);
						String tempString = objStringBuffer.toString();
						if(!(tempString.equalsIgnoreCase(EnumSegments.HL7_SEG_ADD.getValue())))
						{
							objDefragmentedHL7Stream.writeChar(cReadChar);
						}
						objHL7BufferStream.reset();
					}
					else
					{
						objDefragmentedHL7Stream.writeChar(cReadChar);
						if(objHL7BufferStream.available()!=0)
						{
							byte []b = new byte[objHL7BufferStream.available()];
							objHL7BufferStream.readBuffer(b);
							objDefragmentedHL7Stream.writeBuffer(b, 0, b.length);
						}
					}
				}
			}

			objByteArrayInputStream = new ByteArrayInputStream(objDefragmentedHL7Stream.toByteArray());
			objDefragmentedHL7Stream.setInputStream(objByteArrayInputStream);
			if(objDefragmentedHL7Stream.available()!=0)
			{
				objDefragmentedHL7Stream.close();
				objDefragmentedStreamCollection.add(objDefragmentedHL7Stream);
				objByteArrayOutputStream = new ByteArrayOutputStream();
				objDefragmentedHL7Stream = new HL7BufferStream();
				objDefragmentedHL7Stream.setOutputStream(objByteArrayOutputStream);
			}

			objByteArrayOutputStream = new ByteArrayOutputStream();
			objDefragmentedHL7Stream = new HL7BufferStream();
			objDefragmentedHL7Stream.setOutputStream(objByteArrayOutputStream);
			for(HL7BufferStream objBufferStream: objDefragmentedStreamCollection)
			{
				byte []b = new byte[objBufferStream.available()];
				objBufferStream.readBuffer(b);

				objDefragmentedHL7Stream.writeBuffer(b, 0, b.length);
			}

			objByteArrayInputStream = new ByteArrayInputStream(objDefragmentedHL7Stream.toByteArray());
			objDefragmentedHL7Stream.setInputStream(objByteArrayInputStream);
			objDefragmentedHL7Stream.close();
		} 
		catch (InvalidSegmentException ise) 
		{
			throw new FragmentException(ResourceBundleWrapper.getValue("exceptions", "InvalidSegment", new Locale(HL7Config.createInstance().getLocale())));
		}
		catch (SerializeException se)
		{
			throw new FragmentException(ResourceBundleWrapper.getValue("exceptions", "SerializeFailed", new Locale(HL7Config.createInstance().getLocale())));
		}
		return objDefragmentedHL7Stream;
	}
}
