package cdac.medinfo.sdk.hl7282.hl7net.sequence;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;

import cdac.medinfo.sdk.hl7282.baselibs.messagedictionary.MessageMapReader;
import cdac.medinfo.sdk.hl7282.baselibs.messages.ACK;
import cdac.medinfo.sdk.hl7282.baselibs.messages.IMessageRecepient;
import cdac.medinfo.sdk.hl7282.baselibs.messages.IMessageSource;
import cdac.medinfo.sdk.hl7282.baselibs.messages.Message;
import cdac.medinfo.sdk.hl7282.baselibs.messages.MessageFactory;
import cdac.medinfo.sdk.hl7282.baselibs.parserserializer.HL7Serializer;
import cdac.medinfo.sdk.hl7282.baselibs.segments.BHS;
import cdac.medinfo.sdk.hl7282.baselibs.segments.BTS;
import cdac.medinfo.sdk.hl7282.baselibs.segments.DB1;
import cdac.medinfo.sdk.hl7282.baselibs.segments.EVN;
import cdac.medinfo.sdk.hl7282.baselibs.segments.FHS;
import cdac.medinfo.sdk.hl7282.baselibs.segments.FTS;
import cdac.medinfo.sdk.hl7282.baselibs.segments.MSA;
import cdac.medinfo.sdk.hl7282.baselibs.segments.MSH;
import cdac.medinfo.sdk.hl7282.baselibs.segments.PD1;
import cdac.medinfo.sdk.hl7282.baselibs.segments.PDA;
import cdac.medinfo.sdk.hl7282.baselibs.segments.PID;
import cdac.medinfo.sdk.hl7282.baselibs.segments.PV1;
import cdac.medinfo.sdk.hl7282.baselibs.segments.PV2;
import cdac.medinfo.sdk.hl7282.baselibs.segments.ROL;
import cdac.medinfo.sdk.hl7282.baselibs.segments.SFT;
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.InvalidMessageException;
import cdac.medinfo.sdk.hl7282.exceptions.SerializeException;
import cdac.medinfo.sdk.hl7282.hl7model.messages.patientadministration.ADTRecepient;
import cdac.medinfo.sdk.hl7282.hl7model.messages.patientadministration.ADTSource;
import cdac.medinfo.sdk.hl7282.hl7model.messages.patientadministration.ADT_A02;
import cdac.medinfo.sdk.hl7282.hl7model.messages.query.QryRecepient;
import cdac.medinfo.sdk.hl7282.hl7model.messages.query.QrySource;
import cdac.medinfo.sdk.hl7282.hl7net.auxiliaryprotocol.batch.BatchAgent;
import cdac.medinfo.sdk.hl7282.hl7net.auxiliaryprotocol.batch.HL7Batch;
import cdac.medinfo.sdk.hl7282.hl7net.auxiliaryprotocol.batch.HL7BatchFile;
import cdac.medinfo.sdk.hl7282.hl7net.auxiliaryprotocol.fragment.FragmentAgent;
import cdac.medinfo.sdk.hl7282.hl7net.auxiliaryprotocol.sequence.InitiatingSequenceAgent;
import cdac.medinfo.sdk.hl7282.hl7net.auxiliaryprotocol.transactionhandler.TransactionHandler;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IBatchProcessor;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IFragmentProcessor;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IHL7Batch;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IHL7BatchFile;
import cdac.medinfo.sdk.hl7282.hl7net.commons.ISequenceAgent;
import cdac.medinfo.sdk.hl7282.hl7net.commons.ITransactionHandler;
import cdac.medinfo.sdk.hl7282.hl7net.localextension.Populate_Test;
import cdac.medinfo.sdk.hl7282.hl7net.upperlayer.IBatchReceiver;
import cdac.medinfo.sdk.hl7282.hl7net.upperlayer.IExceptionListener;
import cdac.medinfo.sdk.hl7282.hl7net.upperlayer.UserSession;
import cdac.medinfo.sdk.hl7282.utils.EnumMessageCode;
import cdac.medinfo.sdk.hl7282.utils.EnumMessageStructure;
import cdac.medinfo.sdk.hl7282.utils.EnumTriggerEvent;
import cdac.medinfo.sdk.hl7282.utils.HL7Config;
import cdac.medinfo.sdk.hl7282.utils.IMap;
import cdac.medinfo.sdk.hl7282.utils.IMessageMapReader;
import cdac.medinfo.sdk.hl7282.utils.SingleCollection;
import cdac.medinfo.sdk.hl7282.utils.StandardMethods;

/**
 * This class shows simple transaction of Sequence numbering which will be sued to keep 
 * track for the sending and receiving messages during communication.
 */
public class SequenceInitiatingSystem implements IExceptionListener,IBatchReceiver
{
	UserSession objUserSession = null;
	IBatchProcessor objIBatchProcessor = null;
	String strMessageControlID=null,strQueryId =null;
	int iMessageControlId =0,Count= 0;
	int i = 1;
	Populate_Test objPopulateTest = new Populate_Test();

	public static void main(String[] args) 
	{
		//Specifying logging directory.
		//User can specify any directory where log files will be created.
		//If no path is specified then logs will be generated in user's temp directory. 
		HL7Config.createInstance().enableLogging("E:\\SequenceInitiatingSystem_Logs");
		
		
		try
		{
			//Reading IP address for SequenceInitiatingSystem
			System.out.print("Enter IP Adderess of Sequence Responding System Running: ");
			InputStreamReader input = new InputStreamReader(System.in);
			BufferedReader reader = new BufferedReader(input);
			String strIP = reader.readLine();
			//Reading port for SequenceInitiatingSystem
			System.out.print("Enter Port on which Sequence Responding System is listening : ");
			String strPort =reader.readLine();
			int port = Integer.parseInt(strPort);

			//Initializing SequenceInitiatingSystem 
			SequenceInitiatingSystem objSequenceClient = new SequenceInitiatingSystem();
			objSequenceClient.initiate(strIP,port);
		}
		catch(Exception ioe)
		{
			ioe.printStackTrace();
		}
	}	

	/**
	 * Initiates this System as Initiating System.
	 * Connects the Client to Server on specific IP Address & port on which Server System is listening.
	 * @param strIP IP Address specified
	 * @param port port specified 
	 */
	public void initiate(String strIP,int port)
	{
		try 
		{
			//Creating user session for SequenceInitiatingSystem
			objUserSession = new UserSession();

			/*Creating Message Factory for SequenceInitiatingSystem
			 *MessageFactory class provides support for creation and population of messages.
			 *It creates and populates supported message according to the available list of Source and Recipient.
			 */
			MessageFactory objMessageFactory = MessageFactory.createInstance();

			/*   Query System */
			//Initiating Message Recipient for Query System.
			IMessageRecepient objQryRecepient = new QryRecepient();

			//Add the Message Recipient in MessageFactory.
			objMessageFactory.addMessageRecepient(objQryRecepient);

			//Initiating Message Source for Query System.
			IMessageSource objQrySource  = new QrySource();

			//Add MessageRecipient to MessageFactory.
			objMessageFactory.addMessageSource(objQrySource);

			//Patient Administration System 
			/*Instantiating Message Source for HL7System
			User can provide his own implementation of Message Source by implementing IMessageSource interface.*/ 
			IMessageSource objADTSource  = new ADTSource();

			/*Adding Patient Admin Source and Recipient on on Message Factory.
			By setting this source client becomes capable to create messages and receive responses of Patient Admin System
			Similarly user can set sources for different systems.
			sdk for HL7 provides sources for all systems specified by HL7.*/
			objMessageFactory.addMessageSource(objADTSource);

			/*Instantiating Message Recipient for HL7System
			User can provide his own implementation of Message Recipient by implementing IMessageReceipient interface.*/ 
			IMessageRecepient objADTRecepient = new ADTRecepient();

			/*Adding Patient Admin Recipient on Message Factory.
			By setting this recipient client becomes capable to create messages and send Events of Patient Admin System
			Similarly user can set recipients for different systems.
			SDK for HL7 provides sources for all systems specified by HL7.*/
			objMessageFactory.addMessageRecepient(objADTRecepient);

			/* 
			 * As per the given systems requirement Un-Comment required source and 
			 * Recipient for sending and receiving for supported messages.  

		    	Application management System 
			//Initiating Message Recipient for Application management System.
			IMessageRecepient objAppMgtRecepient = new AppMgtRecepient();

			//Add the Message Recipient in MessageFactory.
			objMessageFactory.addMessageRecepient(objAppMgtRecepient);

			//Initiating Message Source for Application management System.
			IMessageSource objAppMgtSource  = new AppMgtSource();

			//Add MessageRecipient to MessageFactory.
			objMessageFactory.addMessageSource(objAppMgtSource);


			   Clinical Laboratory Automation System 
			//Initiating Message Recipient for Clinical Laboratory Automation System.
			IMessageRecepient objClinicalLabAutoRecepient = new ClinicalLabAutoRecepient();

			//Add the Message Recipient in MessageFactory.
			objMessageFactory.addMessageRecepient(objClinicalLabAutoRecepient);

			//Initiating Message Source for Clinical Laboratory Automation System.
			IMessageSource objClinicalLabAutoSource  = new ClinicalLabAutoSource();

			//Add MessageRecipient to MessageFactory.
			objMessageFactory.addMessageSource(objClinicalLabAutoSource);


			   Financial Management System 
			//Initiating Message Recipient for Financial Management System.
			IMessageRecepient objFinancialRecepient = new FinancialRecepient();

			//Add the Message Recipient in MessageFactory.
			objMessageFactory.addMessageRecepient(objFinancialRecepient);

			//Initiating Message Source for Financial Management System.
			IMessageSource objFinancialSource  = new FinancialSource();

			//Add MessageRecipient to MessageFactory.
			objMessageFactory.addMessageSource(objFinancialSource);


			   Master Files System 
			//Initiating Message Recipient for Master Files System.
			IMessageRecepient objMasterFileRecepient = new MasterFileRecepient();

			//Add the Message Recipient in MessageFactory.
			objMessageFactory.addMessageRecepient(objMasterFileRecepient);

			//Initiating Message Source for Master Files System.
			IMessageSource objMasterFileSource  = new MasterFileSource();

			//Add MessageRecipient to MessageFactory.
			objMessageFactory.addMessageSource(objMasterFileSource);


			   Medical Records System 
			//Initiating Message Recipient for Medical Records System.
			IMessageRecepient objMediRecRecepient = new MediRecRecepient();

			//Add the Message Recipient in MessageFactory.
			objMessageFactory.addMessageRecepient(objMediRecRecepient);

			//Initiating Message Source for Medical Records System.
			IMessageSource objMediRecSource  = new MediRecSource();

			//Add MessageRecipient to MessageFactory.
			objMessageFactory.addMessageSource(objMediRecSource);


			  Observation Reporting System 
			//Initiating Message Recipient for Observation Reporting System.
			IMessageRecepient objObsvRepRecepient = new ObsvRepRecepient();

			//Add the Message Recipient in MessageFactory.
			objMessageFactory.addMessageRecepient(objObsvRepRecepient);

			//Initiating Message Source for Observation Reporting System.
			IMessageSource objObsvRepSource  = new ObsvRepSource();

			//Add MessageRecipient to MessageFactory.
			objMessageFactory.addMessageSource(objObsvRepSource);


			  Order Entry System 
			//Initiating Message Recipient for Order Entry System.
			IMessageRecepient objOrderEntryRecepient = new OrderEntryRecepient();

			//Add the Message Recipient in MessageFactory.
			objMessageFactory.addMessageRecepient(objOrderEntryRecepient);

			//Initiating Message Source for Order Entry System.
			IMessageSource objOrderEntrySource  = new OrderEntrySource();

			//Add MessageRecipient to MessageFactory.
			objMessageFactory.addMessageSource(objOrderEntrySource);


			  Patient Care System 
			//Initiating Message Recipient for Patient Care System.
			IMessageRecepient objPatientCareRecepient = new PatientCareRecepient();

			//Add the Message Recipient in MessageFactory.
			objMessageFactory.addMessageRecepient(objPatientCareRecepient);

			//Initiating Message Source for Patient Care System.
			IMessageSource objPatientCareSource  = new PatientCareSource();

			//Add MessageRecipient to MessageFactory.
			objMessageFactory.addMessageSource(objPatientCareSource);


			  Patient Referral System 
			//Initiating Message Recipient for Patient Referral System.
			IMessageRecepient objPatientRefRecepient = new PatientRefRecepient();

			//Add the Message Recipient in MessageFactory.
			objMessageFactory.addMessageRecepient(objPatientRefRecepient);

			//Initiating Message Source for Patient Referral System.
			IMessageSource objPatientRefSource  = new PatientRefSource();

			//Add MessageRecipient to MessageFactory.
			objMessageFactory.addMessageSource(objPatientRefSource);


			  Personnel Management System 
			//Initiating Message Recipient for Personnel Management System.
			IMessageRecepient objPersonalMgmtRecepient = new PersonalMgmtRecepient();

			//Add the Message Recipient in MessageFactory.
			objMessageFactory.addMessageRecepient(objPersonalMgmtRecepient);

			//Initiating Message Source for Personnel Management System.
			IMessageSource objPersonalMgmtSource  = new PersonalMgmtSource();

			//Add MessageRecipient to MessageFactory.
			objMessageFactory.addMessageSource(objPersonalMgmtSource);


			  Scheduling System 
			//Initiating Message Recipient for Scheduling System.
			IMessageRecepient objSchedulingRecepient = new SchedulingRecepient();

			//Add the Message Recipient in MessageFactory.
			objMessageFactory.addMessageRecepient(objSchedulingRecepient);

			//Initiating Message Source for Scheduling System.
			IMessageSource objSchedulingSource  = new SchedulingSource();

			//Add MessageRecipient to MessageFactory.
			objMessageFactory.addMessageSource(objSchedulingSource);*/

			//Sets Message Factory on User Session
			objUserSession.setMessageFactory(objMessageFactory);

			/*
			 * Instantiating IFragmentProcessor which handles the fragmentation and defragmention for larger messages.
			 * FragmentAgent is the default implementation provided by sdk for HL7.
			 * User can provide his own implementation for FragmentProcessor by implementing IFragmentProcessor interface.
			 */ 
			IFragmentProcessor objIFragmentProcessor = new FragmentAgent();

			//Setting Fragment processor on user session.
			objUserSession.setFragmentProcessor(objIFragmentProcessor);

			/*
			 * Instantiating IBatchProcessor which handles HL7Batch and HL7BatchFiles.
			 * BatchAgent is the default implementation provided by sdk for HL7.
			 * User can provide his own implementation for BatchProcessor by implementing IBatchProcessor interface.
			 */
			objIBatchProcessor = new BatchAgent();

			//Setting Batch Processor on user session
			objUserSession.setBatchProcessor(objIBatchProcessor);

			/*
			 * Instantiating SequenceAgent which keeps  record and provides the necessary Sequence Number to the Messages.
			 * it Checks and verifies whether the Sequence Number of Messages are correctly placed or not.
			 * User can provide his own implementation for SequenceAgent by implementing ISequenceAgent interface.
			 */
			ISequenceAgent objInitiatingSequenceAgnet = InitiatingSequenceAgent.createInstance();

			//Setting Sequence Agent on user session.
			objUserSession.setSequenceAgent(objInitiatingSequenceAgnet);

			/*
			 * User sets the Queue length.
			 * If not set by the user, then by default Sequence Agent set Queue length 1.  
			 */
			objInitiatingSequenceAgnet.setQueueLength(3);

			/*
			 * Sets the initial value in Queue.
			 * If not set by the user, then by default Sequence Agent set Initial Queue length 1. 
			 */
			objInitiatingSequenceAgnet.setInitialQueueValue(6);

			/*
			 * If user want to start Sequence numbering, then set TRUE value
			 * Else by default agent will set FALSE value. 
			 */
			objInitiatingSequenceAgnet.setStartSequencing(true);
			
			/*
			 * Instantiating TransactionHandler which keeps track for the sending and receiving message 
			 * and does verification of each message during transaction.
			 * User can provide his own implementation for ClientSideTransactionHandler by implementing ITransactionHandler interface.
			 */
			ITransactionHandler objClientSideTransactionHandler = new TransactionHandler();
			
			//Setting this transaction handler on User session.
			objUserSession.setClientSideTransactionHandler(objClientSideTransactionHandler);

			/*
			 * Setting the class implementing behavior of PatientAdminClient as listener in asynchronous mode,
			 * to process differed queries.
			 * User can provide his own implementation by implementing interface IAsynReceiver
			 */
			objUserSession.addExceptionListener(this);

			//Set the reading and writing max length for this client which will be used to decide whether to fragment or not.
			objUserSession.setReadWriteLength(2048,2048);

			//Setting time for which user Session should wait for ACK/Response to arrive from remote end.
			//If time frame is elapsed then user session resends Event/Query message.
			objUserSession.setWaitPeriod(60000);

			//Setting time period for which User Session will read on socket 
			//for given time period in milliseconds. 
			//The timeout must be > 0. A timeout of zero is interpreted as an infinite timeout. 
			objUserSession.setTimeOut(0);

			//This will register the IBatchReceiver on user session.
			objUserSession.setIBatchReceiver(this);

			//Connecting this client with SequenceInitiatingSystem
			objUserSession.connect(strIP, port);
			
			
			//Sending Deferred Query with Sequence number
			System.out.println("Sending QRY_Q01 with Deferred priority: ");
			//objUserSession.sendMessage(getQRYQ01("D"));
			ISingleCollection<Message> ackDeferredCollection = objUserSession.getRecievedMessagesCollection(strMessageControlID);
			if (ackDeferredCollection != null)
			{
				if(ackDeferredCollection.size() != 0)
				{
					for(Message objMessage :ackDeferredCollection)
					{
						if(objMessage.getMessageStructure().equals(EnumMessageStructure.ACK.getValue()))
						{
							ACK ackDefQueryMessage = (ACK)objMessage;
							System.out.println("ACK Received for Deffered Query :: " + ackDefQueryMessage.getTriggerEvent());
							fileSerialization(objMessage);
						}
					}
					/*
					 * To receive Deferred response User has to implement IAsyncReceiver
					 * Here this class implements IAsyncReceiver.
					 * The deferred response is received on onResponseReceive() method. 
					 */
					System.out.println("Total No of ACK received :: "+ ackDeferredCollection.size());
					System.out.println("Waiting for Deffered Query Response...");
				}
				else
					System.out.println("ACK Collection is empty");
			}
			
			Thread.sleep(9999);
			
			System.out.println("Sending QRY_Q01 with Immediate priority: ");
			/*
			 * User retrieves response from User Session object in following manner.
			 */
			//Waiting for response to retrieve from User Session.
			Thread.sleep(9999);
			ISingleCollection<Message> responseCollection = objUserSession.getRecievedMessagesCollection(strMessageControlID);
			if (responseCollection != null)
			{
				if(responseCollection.size() != 0)
				{
					for (Message response :responseCollection)
					{
						if(!response.getMessageStructure().equals(EnumMessageStructure.ACK.getValue()))
						{
							System.out.println("Response Received :: " + response.getMessageStructure());
							fileSerialization(response);
						}
					}
				}
				else
					System.out.println("Response Collection is empty");
			}
			else
				System.out.println("No response is recieved");
			
			objUserSession.sendMessage(getADTA02());
			/*
			 * User can get Acknowledgment received from remote Server .This is demonstrated below.
			 * Acknowledgment message received  contains acknowledgment for Transfer Patient Event (ADT_A02) sent.
			 * This is how we receive Acknowledgment for event.
			 */
			ISingleCollection<Message> ackCollection = objUserSession.getRecievedMessagesCollection(strMessageControlID);
			if(ackCollection != null)
			{
				for(Message objMessage :ackCollection)
				{
					if(objMessage.getMessageStructure().equals(EnumMessageStructure.ACK.getValue()))
					{
						ACK ackMessage = (ACK)objMessage;
						System.out.println("ACK Received for event :: " + ackMessage.getTriggerEvent());
						fileSerialization(objMessage);
					}
				}
			}
			else
				System.out.println("ACK Collection is empty");
			
			
			/* Sending batch file with ADT event.*/
			objUserSession.sendBatchFile(getADTEventBatchFile());
			ISingleCollection<IHL7BatchFile> objACKBatchCollectionForFile = objUserSession.getResponseBatches();
			if(objACKBatchCollectionForFile != null)
			{
				for(IHL7BatchFile objBatchFromFile : objACKBatchCollectionForFile)
				{
					ISingleCollection<IHL7Batch> objhl7batch = objBatchFromFile.getHL7BatchCollection();

					for(IHL7Batch objIHL7Batch :objhl7batch)
					{
						System.out.println("ACK Received for Batch :: " + objIHL7Batch.getBatchControlID());
					}
				}
			}
			else
				System.out.println("ACK Collection is empty");
			
		}
		catch (Exception e)
		{
			System.out.println("Error Ocured (Ending Client..)== > "+e.getMessage());
			e.printStackTrace();
		}
	}
	//This method will generate file and done serialization of message. 
	private void fileSerialization(Message objMessage)
	{
		try 
		{
			String strFilePath = "E:\\Server" + i++ + ".hl7"; 
			HL7Serializer objHL7Serializer = new HL7Serializer();

			objHL7Serializer.serialize(objMessage.getSegmentSet(),strFilePath);
			System.out.print("Message serialized successfully\n");
		}
		catch (SerializeException e) 
		{
		} 
		catch (InvalidMessageException e) 
		{
			e.printStackTrace();
		}
		catch (IOException e) 
		{
			e.printStackTrace();
		}
	}
	/**
	 * This method is invoked when deferred response is received.
	 */
	public void onResponseReceive(Message deferredResponse)
	{

		System.out.println("Deferred Response Received............ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");

		try
		{
			fileSerialization(deferredResponse);

			//After receiving deferredResponse user sends Acknowledgment in response to Deferred response.
			//generate Acknowledgment with respect to deferred response.
			ACK ackMessage = generateAcknowledgement(deferredResponse);

			//Send Acknowledgment for deferred response
			objUserSession.sendMessage(ackMessage);

			synchronized (this) 
			{
				notify();	
			}
		}catch(IOException ioe)
		{
			ioe.printStackTrace();
		}
		catch (InvalidMessageException e) 
		{
			e.printStackTrace();
		} 
	}

	/**
	 * This method simulates user action to generate Acknowledgment from message received.
	 * This method is demonstrated for test purpose only.
	 * In the actual scenario,it is the responsibility of user to generates Acknowledgment & sends it on remote end. 
	 * @param objReceivedMessage specified Message object
	 * @return ACK message object
	 */
	public ACK generateAcknowledgement(Message objReceivedMessage)
	{
		strMessageControlID = "MSG7656" + iMessageControlId++;
		
		IMessageMapReader objMessageMapReader = MessageMapReader.createInstance();
		IMap objMessageMap = objMessageMapReader.getMessageMap(EnumMessageCode.ACK, null);

		ACK ackMessage = new ACK(StandardMethods.getEnumTriggerEvent(objReceivedMessage.getTriggerEvent()), objMessageMap);
		MSH objMSH = objPopulateTest.populateMSH();
		objMSH.setMessageType(EnumMessageCode.ACK.getValue(), objReceivedMessage.getTriggerEvent(),EnumMessageStructure.ACK.getValue());
		objMSH.setMessageControlID(strMessageControlID);
		ackMessage.setHeader(objMSH);
		
		//MSA Segment
		MSA objMSA = new MSA();
		objMSA.setAcknowledgmentCode("AA");
		objMSA.setMessageControlID(objReceivedMessage.getHeader().getMessageControlID().getValue().toString());
		objMSA.setExpectedSequenceNumber("1234567");
		ackMessage.setMessageAcknowledgmentSegment(objMSA);
		return ackMessage;
	}

	/**
	 * This method is invoked when exception is fetched from remote end.
	 * @param ex - Exception fetched
	 * @param buffer - for which exception is received
	 */
	public void onException(Exception ex,byte[] buffer) 
	{
		synchronized (this)
		{
			this.notify();
		}
		System.out.println("In On Exception Receiced : Printing Stacktrace...");
		System.out.println(ex.getMessage());
		System.out.println("End of Stacktrace....");
		try
		{
			System.out.println("Byte Buffer is : ");
			ByteArrayOutputStream objByteArrayOutputStream = new ByteArrayOutputStream();
			objByteArrayOutputStream.write(buffer);

			objByteArrayOutputStream.writeTo(System.out);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * This method is implementation of IProcesshandler interface.
	 * This method is invoked when SDK receives erroneous binary data.
	 * @param buffer Binary data for which exception is generated
	 * @param ex Type of Exception raised
	 */
	public void onException(Exception ex,ISegmentSet objISegmentSet) 
	{
		synchronized (this)
		{
			this.notify();
		}
		System.out.println("In On Exception Receiced : Printing Stacktrace...");
		System.out.println(ex.getMessage());
		System.out.println("End of Stacktrace....");
		try
		{
			ISingleCollection<ISegment>  objCollection = objISegmentSet.getList();
			for(ISegment objISegment : objCollection)
			{
				System.out.println(objISegment.getName());
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}



	/*
	 * This method populates ADT_A02 message.
	 */
	private ADT_A02 getADTA02()
	{
		strMessageControlID =  "MSG124102"+iMessageControlId++;
		/* 
		 * To create new Message ADTRecepient is required.
		 * 1)Initialize  ADTRecepient
		 * 2)Specify Trigger event & message structure of message to be created.
		 */
		ADTRecepient objPatientAdminRecipient = new ADTRecepient();
		ADT_A02 objADT_A02 = (ADT_A02)objPatientAdminRecipient.createMessage(EnumMessageCode.ADT, EnumTriggerEvent.A02);

		//Populate MSH Segment
		MSH objMSH = objPopulateTest.populateMSH();
		objMSH.setMessageType("ADT","A02","ADT_A02");
		objMSH.setMessageControlID(strMessageControlID);
		objMSH.setSequenceNumber("8988");
		objADT_A02.setHeader(objMSH);

		//Populate SFT segment
		SFT objSFT = objPopulateTest.populateSFT();
		objADT_A02.addSoftwareSegment(objSFT);

		//Populate EVN Segment
		EVN objEVN = objPopulateTest.populateEVN();
		objADT_A02.setEventTypeSegment(objEVN);

		//Populate PID Segment
		PID objPID = objPopulateTest.populatePID();
		objADT_A02.setPatientIdentificationSegment(objPID);

		//Populate PD1 segment
		PD1 objPD1 = objPopulateTest.populatePD1();
		objADT_A02.setAdditinalDemographicsSegment(objPD1);

		//Populate ROL segment
		ROL objROL_Patient = objPopulateTest.populateROL();
		objADT_A02.addPatientIdentificationRoleSegment(objROL_Patient);

		//Populate PV1 segment
		PV1 objPV1 = objPopulateTest.populatePV1();
		objADT_A02.setPatientVisitSegment(objPV1);

		//Populate PV2 segment
		PV2 objPV2 = objPopulateTest.populatePV2();
		objADT_A02.setPatientVisitAdditionalInfoSegment(objPV2);

		//Populate ROL SEGMENT
		ROL objROL_PatientVisit = objPopulateTest.populateROL();
		objADT_A02.addPatientVisitRoleSegment(objROL_PatientVisit);

		//Populate DB1 segment
		DB1 objDB1 = objPopulateTest.populateDB1();
		objADT_A02.addDisabilityInformationSegment(objDB1);

		//Populate PDA SEGMENT
		PDA objPDA = objPopulateTest.populatePDA();
		objADT_A02.setPatientDeathandAutopsySegment(objPDA);

		return objADT_A02;
	}

	/*
	 * This method creates Batch file containing batch of ADT_A02 and QRY_A19 message.
	 */
	private IHL7BatchFile getADTEventBatchFile()
	{
		FHS objBatchFileHeader  = new FHS();
		objBatchFileHeader.setFieldSeparator('|');
		objBatchFileHeader.setEncodingChars('^','&','~','\\','#');
		objBatchFileHeader.setFileControlID("BatchFile89888");

		BHS objBatchHeader  = new BHS();
		objBatchHeader.setBatchFieldSeparator('|');
		objBatchHeader.setBatchEncodingChars('^','&','~','\\','#');
		objBatchHeader.setBatchControlID("BatchF343434");

		ISingleCollection<Message> objMessageCollection = new SingleCollection<Message>();
		objMessageCollection.add(getADTA02());

		BTS objBatchTrailer = new BTS();
		objBatchTrailer.addBatchTotals("1");

		IHL7Batch objBatch = new HL7Batch(objBatchHeader,objBatchTrailer,objMessageCollection);

		ISingleCollection<IHL7Batch> objBatchCollection = new SingleCollection<IHL7Batch>();
		objBatchCollection.add(objBatch);

		FTS objBatchFileTrailer = new FTS();
		objBatchFileTrailer.setFileBatchCount("1");

		IHL7BatchFile objBatchFile = new HL7BatchFile(objBatchFileHeader,objBatchFileTrailer,objBatchCollection);

		return objBatchFile;
	}

	/**
	 * This method is invoked when deferred response received is in Batch.
	 */
	public void onBatchReceive(IHL7BatchFile objBatchFile) 
	{
		ISingleCollection<IHL7Batch> objBatchCollection = objBatchFile.getHL7BatchCollection();
		for(IHL7Batch objBatch : objBatchCollection)
		{
			if(objBatch.isResponseBatch() && objBatch.isDeferred())
			{
				acknowledgeResponseBatch(objBatch);
			}
			else
				System.out.println("ACK received for BatchID  " + objBatch.getBatchControlID());
		}
	}

	private void acknowledgeResponseBatch(IHL7Batch objHL7Batch)
	{
		System.out.println("Deferred Response Received is Batch............ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
		try 
		{

			//After receiving deferredResponse user sends Acknowledgment in response to Deferred response.
			//generate Acknowledgment with respect to deferred response.
			FHS objBatchFileHeader  = new FHS();
			objBatchFileHeader.setFieldSeparator('|');
			objBatchFileHeader.setEncodingChars('^','&','~','\\','#');
			objBatchFileHeader.setFileControlID("BatchFileE121we");

			BHS objBatchHeader  = new BHS();
			objBatchHeader.setBatchFieldSeparator('|');
			objBatchHeader.setBatchEncodingChars('^','&','~','\\','#');
			objBatchHeader.setBatchControlID("BatchE121we");//Generate it using UUID jar.

			ISingleCollection<Message> objMessageCollection = generateAcknowledgement(objHL7Batch.getMessageCollection());

			int iMessageCount = 0;
			if(objMessageCollection != null )
				iMessageCount = objMessageCollection.size();

			BTS objBatchTrailer = new BTS();
			objBatchTrailer.addBatchTotals(""+iMessageCount);

			IHL7Batch objBatch = new HL7Batch(objBatchHeader,objBatchTrailer,objMessageCollection);


			ISingleCollection<IHL7Batch> objIHL7BatchCollection = new SingleCollection<IHL7Batch>();
			objIHL7BatchCollection.add(objBatch);


			FTS objBatchFileTrailer = new FTS();
			objBatchFileTrailer.setFileBatchCount("1");

			IHL7BatchFile objBatchFile = new HL7BatchFile(objBatchFileHeader,objBatchFileTrailer,objIHL7BatchCollection);


			//Send Acknowledgment for deferred response
			System.out.println("Sending ACK for Deferred Response Batch..");

			objUserSession.sendBatchFile(objBatchFile);

			System.out.println("ACK for Deferred Response Batch is Sent ..");
			synchronized (this) 
			{
				notify();	
			}
		}
		catch (InvalidMessageException e) 
		{
			e.printStackTrace();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * This method simulates user action to generate Acknowledgment from message received in the batch.
	 * This method is demonstrated for test purpose only.
	 * In the actual scenario,it is the responsibility of user to generates Acknowledgment & sends it on remote end. 
	 * @param objMessageCollection specified Message collection from 
	 * @return objACKCollection - collection of ACK messages.
	 */
	public ISingleCollection<Message> generateAcknowledgement(ISingleCollection<Message> objMessageCollection)
	{
		ISingleCollection<Message> objACKCollection = new SingleCollection<Message>();
		for(Message objMessage :objMessageCollection)
		{
			objACKCollection.add(generateAcknowledgement(objMessage));
		}
		return objACKCollection;
	}
}
