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

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.SocketException;

import cdac.medinfo.sdk.hl7282.baselibs.messagedictionary.MessageMapReader;
import cdac.medinfo.sdk.hl7282.baselibs.messages.ACK;
import cdac.medinfo.sdk.hl7282.baselibs.messages.AbstractQuery;
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.DSC;
import cdac.medinfo.sdk.hl7282.baselibs.segments.DSP;
import cdac.medinfo.sdk.hl7282.baselibs.segments.ERR;
import cdac.medinfo.sdk.hl7282.baselibs.segments.MSA;
import cdac.medinfo.sdk.hl7282.baselibs.segments.MSH;
import cdac.medinfo.sdk.hl7282.baselibs.segments.QAK;
import cdac.medinfo.sdk.hl7282.baselibs.segments.QPD;
import cdac.medinfo.sdk.hl7282.baselibs.segments.RCP;
import cdac.medinfo.sdk.hl7282.baselibs.segments.SFT;
import cdac.medinfo.sdk.hl7282.baselibs.segments.Segment;
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.hl7model.messages.patientadministration.ADTRecepient;
import cdac.medinfo.sdk.hl7282.hl7model.messages.patientadministration.ADTSource;
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.ReceivingSequenceAgent;
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.ClientSession;
import cdac.medinfo.sdk.hl7282.hl7net.upperlayer.IProcessHandler;
import cdac.medinfo.sdk.hl7282.hl7net.upperlayer.ServerSession;
import cdac.medinfo.sdk.hl7282.hl7streams.HL7FileStream;
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 SequenceRespondingSystem implements IProcessHandler
{
	int i = 1;
	ISequenceAgent objReceivingSequenceAgent = null;
	MessageFactory objMessageFactory =null;
	String strMessageControlID=null;
	int iMessageControlId =0;
	ITransactionHandler objServerTransactionHandler =null;
	private Populate_Test objPopulateTest = new Populate_Test();

	public static void main(String[] args) 
	{
		try
		{
			//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:\\SequenceRespondingSystem_Logs");

			
			//Reading IP address for SequenceRespondingSystem
			System.out.print("Enter IP for SequenceRespondingSystem : ");
			InputStreamReader input = new InputStreamReader(System.in);
			BufferedReader reader = new BufferedReader(input);
			String strIP = reader.readLine();

			//Reading port for SequenceRespondingSystem
			System.out.print("Enter Port for SequenceRespondingSystem : ");
			String strPort =reader.readLine();
			int port = Integer.parseInt(strPort);	

			//Initializing SequenceRespondingSystem
			SequenceRespondingSystem objSequenceServer = new SequenceRespondingSystem();
			objSequenceServer.initiate(strIP,port);
		}
		catch(IOException ioe)
		{
			ioe.printStackTrace();
		}
	}	

	/**
	 * Starts ReceivingSystem at IPadderess & port provided.
	 * @param strIP  IP Address specified
	 * @param port port specified 
	 */
	public void initiate(String strIP,int port)
	{
		try 
		{
			//Creating server session for Sequence Responding System.
			ServerSession objServerSession = new ServerSession();

			/*Creating Message Factory for Sequence Responding System.
			 *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.
			 */
			objMessageFactory = MessageFactory.createInstance();

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

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

			/*Instantiating Message Source for HL7System
			User can provide his own implementation of Message Source by implementing IMessageSource interface.*/
			IMessageSource objQrySource  = new QrySource();

			/*Adding Query 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(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.  

			   Patient Administration System 
			//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);


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

			    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);*/

			/*
			 * 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 server session.
			objServerSession.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.
			 */
			IBatchProcessor objIBatchProcessor = new BatchAgent();

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

			/*
			 * Setting this class as ProcessHandler for SequenceRespondingSystem.
			 * IProcessHandler interface must be implemented ,so that server can receive messages from client side. 
			 */
			objServerSession.setIProcessHandler(this);	

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

			//Setting time period for which ClientSession 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. 
			objServerSession.setTimeOut(0);

			/*
			 * Starts the ServerSession for Sequence Responding System on given IP and port.
			 */
			objServerSession.start(strIP, port);
		}
		catch(ConnectException ce)
		{
			System.out.println(ce.getMessage());
		}
		catch(SocketException se)
		{
			System.out.println(se.getMessage());
		}
		catch (IOException e) 
		{
			e.printStackTrace();
		}
	}

	/**
	 * This method is implementation of IProcesshandler interface.
	 * This method is invoked when SDK receives file containing batch of HL7 messages from remote system.
	 * @param objClientSession session of remote end
	 */

	public void OnBatchFileReceive(ClientSession objClientSession) throws InvalidMessageException
	{
		try
		{
			IHL7BatchFile objHL7BatchFile = objClientSession.getBatchFile();

			for(IHL7Batch objHL7Batch : objHL7BatchFile.getHL7BatchCollection())
			{
				ISingleCollection<Message> objMessageCollection = objHL7Batch.getMessageCollection();
				ISingleCollection<Message> objAckCollection = new SingleCollection<Message>(); // set on batch and sent.
				ISingleCollection<Message> objMessage2ResponseCollection = new SingleCollection<Message>(); // used to respond deferred

				if(objMessageCollection.size() == 0)
				{
					BHS objBHS = new BHS();
					objBHS.setBatchControlID(objHL7Batch.getBatchControlID());
					objBHS.setBatchFieldSeparator('|');
					objBHS.setBatchEncodingChars('^','&','~','\\','#');

					BTS objBTS = new BTS();
					objBTS.setBatchMessageCount(""+objAckCollection.size());
					IHL7Batch objACKBatch = new HL7Batch(objBHS,objBTS,objAckCollection);
					System.out.println("Sending Batch.......");

					IHL7BatchFile objBatchFile = new HL7BatchFile();
					ISingleCollection<IHL7Batch> objIHL7BatchCollection = new SingleCollection<IHL7Batch>();
					objIHL7BatchCollection.add(objACKBatch);
					objBatchFile.setHL7BatchCollection(objIHL7BatchCollection);
					objClientSession.sendBatch(objBatchFile);
				}

				for(Message objMessage : objMessageCollection)
				{
					ACK acknowledgement = null;
					//ACK is fetched only when Deferred response is dispatched
					if(objMessage.getMessageStructure().equals(EnumMessageStructure.ACK.getValue()))  
					{
						System.out.println("ACK received for Deferred response By User Process Handler");
					}
					//check for query
					else if(objMessage.isQuery())
					{
						//Code block of Deferred Query 
						if(((AbstractQuery)objMessage).isDeferred())
						{
							acknowledgement = generateAcknowledgement(objMessage);
							objAckCollection.add(acknowledgement);

							Message response = generateResponse(objMessage);
							objMessage2ResponseCollection.add(response);

						}
						//Code block of Immediate Query
						else
						{
							Message response = generateResponse(objMessage);
							objAckCollection.add(response);
						}
					}
					//Code block of ACK
					else
					{
						acknowledgement = generateAcknowledgement(objMessage);

						objAckCollection.add(acknowledgement);
					}
				}

				// Ack for Batch
				if(objAckCollection.size()>0)
				{

					BHS objBHS = new BHS();
					objBHS.setBatchControlID(objHL7Batch.getBatchControlID());
					objBHS.setBatchFieldSeparator('|');
					objBHS.setBatchEncodingChars('^','&','~','\\','#');

					BTS objBTS = new BTS();
					objBTS.setBatchMessageCount(""+objAckCollection.size());
					IHL7Batch objACKBatch = new HL7Batch(objBHS,objBTS,objAckCollection);
					System.out.println("Sending Batch.......");

					IHL7BatchFile objBatchFile = new HL7BatchFile();
					ISingleCollection<IHL7Batch> objIHL7BatchCollection = new SingleCollection<IHL7Batch>();
					objIHL7BatchCollection.add(objACKBatch);
					objBatchFile.setHL7BatchCollection(objIHL7BatchCollection);
					objClientSession.sendBatch(objBatchFile);
				}

				// Responding Deferred..
				for(Message objMessage : objMessage2ResponseCollection)
				{
					objClientSession.sendMessage(objMessage);
				}

				synchronized (this)
				{
					System.out.println("Server : before wait");
					this.wait(6000);	
					System.out.println("Server : after wait");
				}
			}
		}
		catch(IOException ioe)
		{
			ioe.printStackTrace();
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * This method is implementation of IProcesshandler interface.
	 * This method is invoked when any user establishes connection with Server. 
	 * @param objClientSession session of remote end
	 */
	public void OnConnect(ClientSession objClientSession) 
	{
		/*
		 * 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.
		 */
		objReceivingSequenceAgent = ReceivingSequenceAgent.createInstance();

		//This will set the SequenceAgent on Client Session.
		objClientSession.setSequenceAgent(objReceivingSequenceAgent);

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

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

		/*
		 * If user want to start Sequence numbering, then set TRUE value
		 * Else by default agent will set FALSE value. 
		 */
		objReceivingSequenceAgent.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 ServerSideTransactionHandler by implementing ITransactionHandler interface.
		 */
		objServerTransactionHandler = new TransactionHandler();

		objClientSession.setServerSideTransactionHandler(objServerTransactionHandler);
		objClientSession.acceptConnection(true);
		//Setting MessageFactory
		objClientSession.setMessageFactory(objMessageFactory);

	}

	/**
	 * This method is implementation of IProcesshandler interface.
	 * This method is invoked when SDK is not able to parse received message.
	 * @param objClientSession 
	 */
	public void OnErrorReceive(ClientSession objClientSession)
	{
		//user code to generate error ACK to send 
		System.out.println("Error Occured"+objClientSession.getErrorCode());
	}

	/**
	 * This method is automatically invoked when Message is received from remote system.
	 * User provides the implementation of this method.
	 * Simulation of user code is demonstrated below.
	 * @param objClientSession instance of Client Session
	 */
	public void OnMessageReceive(ClientSession objClientSession) throws InvalidMessageException
	{
		Message receivedMessage = objClientSession.getMessage();

		//User code goes over here
		try
		{
			/*
			 * Sequence Responding System can receive following types of messages.
			 * 1)Any Event triggered by other Health Information System available in HL7 network.
			 * 2)Query message specific to Patient Administration system.
			 * 3)Acknowledgment for the Deferred response sent.  
			 */
			ACK acknowledgement = null;

			/* 
			 * When user send Deferred response,ACK message is received from remote client end.
			 * ACK is fetched only when Deferred response is dispatched.
			 * Following code block handles Acknowledgment received for deferred response sent.
			 */
			if (receivedMessage.getMessageStructure().equals(EnumMessageStructure.ACK.getValue()))  
			{
				System.out.println("ACK received for Deferred response by User Process Handler");
				fileSerialization(receivedMessage);
			}

			/*
			 * Check for Query Message received from other systems
			 */
			else if(receivedMessage.isQuery())
			{
				//Handle Query Message with deferred priority 
				if(((AbstractQuery)receivedMessage).isDeferred())
				{
					System.out.println("Query received with Deferred Priority");

					fileSerialization(receivedMessage);

					acknowledgement = generateAcknowledgement(receivedMessage);
					objClientSession.sendMessage(acknowledgement);

					Message response = generateResponse(receivedMessage);

					if(((AbstractQuery)receivedMessage).isBatchRSPExpected())
					{
						AbstractQuery receivedQuery = (AbstractQuery)receivedMessage;
						BHS objBHS = new BHS();
						objBHS.setBatchControlID(receivedMessage.getHeader().getMessageControlID().getValue().toString());
						objBHS.setBatchFieldSeparator('|');
						objBHS.setBatchEncodingChars('^','&','~','\\','#');

						BTS objBTS = new BTS();
						objBTS.setBatchMessageCount(""+1);

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

						IHL7Batch objBatchInResponse2Query = new HL7Batch(objBHS,objBTS,objMessageCollection);
						ISingleCollection<Segment> objQuerySegmentCollection = receivedQuery.getQuerySegments();
						if(objQuerySegmentCollection!=null)
						{

							if (objQuerySegmentCollection.elementAt(0) instanceof QPD)
							{
								QPD objQPD = (QPD) objQuerySegmentCollection.elementAt(0);

								RCP objRCP = null;

								if (objQuerySegmentCollection.elementAt(1) instanceof RCP) {
									objRCP = (RCP) objQuerySegmentCollection.elementAt(1);

								}
								objBatchInResponse2Query.setQuerySegments(objQPD,objRCP );
							}
						}
						IHL7BatchFile objBatchFile = new HL7BatchFile();
						ISingleCollection<IHL7Batch> objIHL7BatchCollection = new SingleCollection<IHL7Batch>();
						objIHL7BatchCollection.add(objBatchInResponse2Query);
						objBatchFile.setHL7BatchCollection(objIHL7BatchCollection);
						objClientSession.sendBatch(objBatchFile);
					}
					else
					{
						objClientSession.sendMessage(response);
					}
				}
				//Send response for Query Message with Immediate priority 
				else
				{
					System.out.println("Query received with Immediate Priority");

					Message response = generateResponse(receivedMessage);

					if(((AbstractQuery)receivedMessage).isBatchRSPExpected())
					{
						AbstractQuery receivedQuery = (AbstractQuery)receivedMessage;
						BHS objBHS = new BHS();
						objBHS.setBatchControlID(receivedMessage.getHeader().getMessageControlID().getValue().toString());
						objBHS.setBatchFieldSeparator('|');
						objBHS.setBatchEncodingChars('^','&','~','\\','#');

						BTS objBTS = new BTS();
						objBTS.setBatchMessageCount(""+1);

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

						IHL7Batch objBatchInResponse2Query = new HL7Batch(objBHS,objBTS,objMessageCollection);
						ISingleCollection<Segment> objQuerySegmentCollection = receivedQuery.getQuerySegments();
						if(objQuerySegmentCollection!=null)
						{

							if (objQuerySegmentCollection.elementAt(0) instanceof QPD)
							{
								QPD objQPD = (QPD) objQuerySegmentCollection.elementAt(0);

								RCP objRCP = null;

								if (objQuerySegmentCollection.elementAt(1) instanceof RCP) {
									objRCP = (RCP) objQuerySegmentCollection.elementAt(1);

								}
								objBatchInResponse2Query.setQuerySegments(objQPD,objRCP );
							}

						}
						IHL7BatchFile objBatchFile = new HL7BatchFile();
						ISingleCollection<IHL7Batch> objIHL7BatchCollection = new SingleCollection<IHL7Batch>();
						objIHL7BatchCollection.add(objBatchInResponse2Query);
						objBatchFile.setHL7BatchCollection(objIHL7BatchCollection);
						objClientSession.sendBatch(objBatchFile);
					}
					else
					{
						objClientSession.sendMessage(response);
					}
				}
			}
			//Send Acknowledgment when event is received from other system.
			else
			{
				System.out.println("Event Message received :: " + receivedMessage.getTriggerEvent());
				fileSerialization(receivedMessage);
				acknowledgement = generateAcknowledgement(receivedMessage);
				objClientSession.sendMessage(acknowledgement);
			}
		}
		catch(InvalidMessageException Ime)
		{
			Ime.printStackTrace();
		}
		catch(IOException ioe)
		{
			ioe.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,byte[] buffer) 
	{

		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) 
	{

		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 simulates user action to generate Acknowledgment for message received.
	 * This method is demonstrated for test purpose only.
	 * In the actual scenario, it is User's responsibility to generates acknowledgment & sends it on client Session. 
	 * @param objReceivedMessage specified Message object
	 * @return ACK instance of acknowledgment
	 */
	public ACK generateAcknowledgement(Message objReceivedMessage)
	{
		strMessageControlID = "MSG71Q3456" + 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 simulates user action to generate Response for Query message received.
	 * This method is demonstrated for test purpose only.
	 * In the actual scenario It is User's responsibility to generates response & sends it on client Session.
	 * In this method as we have demonstrated response generation for Query (QRY_A19) message.
	 * Response specific to QRY_A19 message is ADR_A19.
	 * Code block below first checks the priority of Query message.Afterwards it creates response message ADR_A19 by manually populating it.
	 * The values used for population of Response message are for test purpose only.   
	 * @param enumMessageStructures enum value of Message Structure
	 * @throws IOException if input/output operation fails
	 */
	Message generateResponse(Message objHL7Message) throws IOException
	{
		Message response = null;
		String strMessageStructure = objHL7Message.getMessageStructure();
		String strReceivedMsgID = objHL7Message.getHeader().getMessageControlID().getValue().toString();
		AbstractQuery objAbstractQuery = (AbstractQuery)objHL7Message;
		String strReceivedQueryId = objAbstractQuery.getQueryIdentifier().getValue().toString();
		switch(EnumMessageStructure.valueOf(strMessageStructure))
		{
		case QRY_Q01:
			if(((AbstractQuery)objHL7Message).isDeferred())
			{
				
				//Populate ADR_A19 MSH (message header segment)
				MSH objNewMSH = objPopulateTest.populateMSH();
				objNewMSH.setMessageType(EnumMessageCode.DSR.getValue(), EnumTriggerEvent.Q01.getValue(), EnumMessageStructure.DSR_Q01.getValue());
				objNewMSH.setMessageControlID(objHL7Message.getHeader().getMessageControlID().getValue().toString()+iMessageControlId++);
				objNewMSH.setSequenceNumber(objHL7Message.getHeader().getSequenceNumber());
			

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

				//MSA Segment
				MSA objMSA = objPopulateTest.populateMSA();
				objMSA.setMessageControlID(strReceivedMsgID);
				objMSA.setExpectedSequenceNumber("1234567");
			//	objDSR_Q01.setMessageAcknowledgmentSegment(objMSA);

				//ERR Segment
				ERR objERR = objPopulateTest.populateERR(); 
				//objDSR_Q01.setErrorSegment(objERR);

				//QAK Segment
				QAK objQAK = objPopulateTest.populateQAK(); 
				//objDSR_Q01.setQueryAcknowledgmentSegment(objQAK);

				//QRD segment
//				QRD objQRD = objPopulateTest.populateQRD();
//				objQRD.setQueryID(strReceivedQueryId);
//				objDSR_Q01.setQueryDefinitionSegment(objQRD);

				//QRF segment
//				QRF objQRF = objPopulateTest.populateQRF();
//				objDSR_Q01.setQueryFilterSegment(objQRF);

				DSP objDSP = objPopulateTest.populateDSP();
			//	objDSR_Q01.addDisplayDataSegment(objDSP);

				DSC objDSC = objPopulateTest.populateDSC();
				objDSC.setContinuationStyle("F");
			//	objDSR_Q01.setContinuationPointerSegment(objDSC);

			//	response = objDSR_Q01;
			}
			else
			{
				//Instantiate ADR_A19 message using ADTSource.
			//	DSR_Q01 objDSR_Q01 = (DSR_Q01)new QryRecepient().createMessage(EnumMessageCode.DSR, EnumTriggerEvent.Q01);

				//Populate ADR_A19 MSH (message header segment)
				MSH objNewMSH = objPopulateTest.populateMSH();
				objNewMSH.setMessageType(EnumMessageCode.DSR.getValue(), EnumTriggerEvent.Q01.getValue(), EnumMessageStructure.DSR_Q01.getValue());
				objNewMSH.setMessageControlID(objHL7Message.getHeader().getMessageControlID().getValue().toString()+iMessageControlId++);
				objNewMSH.setSequenceNumber(objHL7Message.getHeader().getSequenceNumber());
			//	objDSR_Q01.setHeader(objNewMSH);

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

				//MSA Segment
				MSA objMSA = objPopulateTest.populateMSA();
				objMSA.setMessageControlID(strReceivedMsgID);
				objMSA.setExpectedSequenceNumber("1234567");
				//objDSR_Q01.setMessageAcknowledgmentSegment(objMSA);

				//ERR Segment
				ERR objERR = objPopulateTest.populateERR(); 
				//objDSR_Q01.setErrorSegment(objERR);

				//QAK Segment
				QAK objQAK = objPopulateTest.populateQAK(); 
			//	objDSR_Q01.setQueryAcknowledgmentSegment(objQAK);

				//QRD segment
//				QRD objQRD = objPopulateTest.populateQRD();
//				objQRD.setQueryPriority("I");
//				objQRD.setQueryID(strReceivedQueryId);
//				objDSR_Q01.setQueryDefinitionSegment(objQRD);

				//QRF segment
//				QRF objQRF = objPopulateTest.populateQRF();
//				objDSR_Q01.setQueryFilterSegment(objQRF);

				DSP objDSP = objPopulateTest.populateDSP();
				//objDSR_Q01.addDisplayDataSegment(objDSP);

				DSC objDSC = objPopulateTest.populateDSC();
				objDSC.setContinuationStyle("F");
				//objDSR_Q01.setContinuationPointerSegment(objDSC);

				//response = objDSR_Q01;
			}
			break;
		}
		return response;
	}

	/*
	 * This method is used for Serialization of Message.
	 */
	private void fileSerialization(Message objMessage)
	{
		String strFilePath = null;
		HL7FileStream objIHL7Stream = new HL7FileStream();
		try
		{
			strFilePath = "E:\\Message" + i++ + ".hl7";
			File objFileWrite = new File(strFilePath);
			objFileWrite.createNewFile();
			FileOutputStream objFileOutputStream = new FileOutputStream(objFileWrite);	
			objIHL7Stream.setOutputStream(objFileOutputStream);
			ISegmentSet objSegmentSet = objMessage.getSegmentSet();
			HL7Serializer objHL7Serializer = new HL7Serializer();
			objHL7Serializer.serialize(objSegmentSet,objIHL7Stream);
			System.out.print("Message serialized successfully\n");
		}
		catch(IOException ioe)
		{
			ioe.printStackTrace();
		}
	}

	/*
	 *Closes the Connection  
	 */
	public void onClose(ClientSession objClientSession) 
	{
		System.out.println("On Close");
	}
}
