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

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
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.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.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.patientadministration.RSP_K21;
import cdac.medinfo.sdk.hl7282.hl7model.messages.patientadministration.RSP_K23;
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.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.IMLLP;
import cdac.medinfo.sdk.hl7282.hl7net.commons.ITransactionHandler;
import cdac.medinfo.sdk.hl7282.hl7net.communication.MLLP;
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.utils.EnumMessageCode;
import cdac.medinfo.sdk.hl7282.utils.EnumMessageStructure;
import cdac.medinfo.sdk.hl7282.utils.EnumSegments;
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 sample code demonstrates implementation of Server for Patient Adminstration System.
 * Server of Patient Adminstration System  has following capabilities
 * 1)Send Responses for Patienet Administration related queries. e.g Here QBP_Q21.
 * 2)Send Acknowledgements in response to event triggered by other Health Information Systems in 
 * the network provided it has knowledge of other systems. 
 * 3)Respond to Batch of HL7 Messages received from remote system.
 * 4)Respond to HL7 Batch files  received from remote system.
 */
public class PatientAdminServer implements IProcessHandler
{	
	int messageControlId = 0;
	ITransactionHandler objServerTransactionHandler = null;	
	IMLLP objIMLLP = null;
	MessageFactory objMessageFactory =null;
	int i = 1;
	String strMessageControlid =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:\\Server_Logs");
			
			System.out.print("Enter Ip address for HL7System server is started : ");
			InputStreamReader input = new InputStreamReader(System.in);
			BufferedReader reader = new BufferedReader(input);
			String strIp = reader.readLine();

			//Reading port for HL7System.
			System.out.print("Enter Port for HL7System server is Listening : ");
			String strPort = reader.readLine();
			int iPort = Integer.parseInt(strPort);

			//Initializing PatientAdminServer  
			PatientAdminServer objPatientAdminServer = new PatientAdminServer();
			objPatientAdminServer.initiate(strIp,iPort);
		}
		catch(IOException ioe)
		{
			ioe.printStackTrace();
		}
	}	

	/**
	 * Starts Patient Admin Server 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 HL7System.
			ServerSession objServerSession = new ServerSession();

			//Create MessageFactory to provide support for creation and population of messages.
			objMessageFactory = MessageFactory.createInstance();

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

			/*   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);
		
			/*
			 * 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 HL7System.
			 * 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(1024,1024);

			//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 PatientAdmin Server 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 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();

		try
		{

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

			/*
			 * Check for Query Message received from other systems
			 */
			else if(receivedMessage.isQuery())
			{
				if(receivedMessage.getMessageStructure().equals(EnumMessageStructure.QCN_J01.getValue()))
				{
					System.out.println("QCN_J01 Arrived at Server.");
				}
				//Handle Query Message with deferred priority 
				else if(((AbstractQuery)receivedMessage).isDeferred())
				{
					String strFilePath = null;
					try
					{
						strFilePath = "E:\\Client" + i++ + ".hl7"; //reader.readLine();

						HL7Serializer objHL7Serializer = new HL7Serializer();
						objHL7Serializer.serialize(receivedMessage.getSegmentSet(), strFilePath);

						System.out.print("Message serialized successfully\n");
					}
					catch(IOException ioe)
					{
						ioe.printStackTrace();
					}

					ACK 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
				{
					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
			{
				ACK acknowledgement = generateAcknowledgement(receivedMessage);
				objClientSession.sendMessage(acknowledgement);
			}
		}
		catch(InvalidMessageException Ime)
		{
			Ime.printStackTrace();
		}
		catch(IOException ioe)
		{
			ioe.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 = "MSG90231"+messageControlId++;
		
		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 strRecievedMsgCntlID = objHL7Message.getHeader().getMessageControlID().getValue().toString();
		AbstractQuery objAbstractQuery = (AbstractQuery)objHL7Message;
		String strReceivedQueryId = objAbstractQuery.getQueryIdentifier().getValue().toString();
		switch(EnumMessageStructure.valueOf(strMessageStructure))
		{
		case QBP_Q21:
			if("MSG0011".equalsIgnoreCase(objHL7Message.getHeader().getMessageControlID().getValue().toString()))
			{
                //This will extract Query id from QPD segment and set it in QAK segment. 
                QPD objQPDSegment = (QPD)objHL7Message.getSegment(EnumSegments.HL7_SEG_QPD).elementAt(0);
                
				RSP_K23 objRSP_K23 = (RSP_K23)new ADTRecepient().createMessage(EnumMessageCode.RSP, EnumTriggerEvent.K23);

				//Populate RSP_K23 MSH (message header segment)
				MSH objNewMSH = objPopulateTest.populateMSH();
				objNewMSH.setMessageType(EnumMessageCode.RSP.getValue(), EnumTriggerEvent.K23.getValue(), EnumMessageStructure.RSP_K23.getValue());
				objRSP_K23.getHeader().setMessageControlID("MSG0011"+messageControlId++);
				objRSP_K23.setHeader(objNewMSH);

				MSA objMSA = objPopulateTest.populateMSA();
				objMSA.setMessageControlID(strRecievedMsgCntlID);
				objMSA.setExpectedSequenceNumber("1234567");
				objRSP_K23.setMessageAcknowledgmentSegment(objMSA);

				QAK objQAK = objPopulateTest.populateQAK();
				objQAK.setQueryTag(objQPDSegment.getQueryTag().getValue().toString());
				objRSP_K23.setQueryAcknowledgementSegment(objQAK);

				QPD objQPD = objPopulateTest.populateQPD();
				objRSP_K23.setQueryParameterDefinitionSegment(objQPD);

				DSC objDSC = objPopulateTest.populateDSC();
                objDSC.setContinuationStyle("I");
                objRSP_K23.setContinuationPointerSegment(objDSC);

				response = objRSP_K23;
			}
			else
			{
                //This will extract Query id from QPD segment and set it in QAK segment.
                QPD objQPDSegment = (QPD)objHL7Message.getSegment(EnumSegments.HL7_SEG_QPD).elementAt(0);
                
				RSP_K21 objRSP_K21 = (RSP_K21)new ADTRecepient().createMessage(EnumMessageCode.RSP, EnumTriggerEvent.K21);
				
				//Populate RSP_K21SH (message header segment)
				MSH objNewMSH = objPopulateTest.populateMSH();
				objNewMSH.setMessageType(EnumMessageCode.RSP.getValue(), EnumTriggerEvent.K21.getValue(), EnumMessageStructure.RSP_K21.getValue());
				//objNewMSH.setMessageControlID("MSG0002");
				objNewMSH.setMessageControlID("MSG0033"+messageControlId++);
				objRSP_K21.setHeader(objNewMSH);

				MSA objMSA = objPopulateTest.populateMSA();
				objMSA.setMessageControlID(strRecievedMsgCntlID);
				objMSA.setExpectedSequenceNumber("1234567");
				objRSP_K21.setMessageAcknowledgmentSegment(objMSA);

                QAK objQAK = objPopulateTest.populateQAK();
                objQAK.setQueryTag(objQPDSegment.getQueryTag().getValue().toString());
                objRSP_K21.setQueryAcknowledgementSegment(objQAK);

				QPD objQPD = objPopulateTest.populateQPD();
				objRSP_K21.setQueryParameterDefinitionSegment(objQPD);

				response = objRSP_K21;
			}
			break;

		}
		return response;
	}

	/**
	 * 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 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.
		 * if user wants verification for each message within transaction then user has to set it else not.
		 */
		objServerTransactionHandler = new TransactionHandler();
		
		objClientSession.setServerSideTransactionHandler(objServerTransactionHandler);
		
		//Set MLLP on Client Session.
		objIMLLP = new MLLP();
		objClientSession.setMLLPProcessor(objIMLLP);
		
		objClientSession.setMessageFactory(objMessageFactory);
		objClientSession.acceptConnection(true);
	}

	/**
	 * 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 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 deffered

				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 invoked when exception is fetched from remote end.
	 * @ex Exception fetched
	 * @buffer for which exception is received
	 */
	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 invoked when exception is fetched from remote end.
	 * @ex Exception fetched
	 * @objISegmentSet for which Segment Set is Received.
	 */
	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
		{
			System.out.println("Segment Set is : ");
			ISingleCollection<ISegment> objISegmentSetCollection = objISegmentSet.getList();
			if(objISegmentSetCollection!=null)
			{
				System.out.println("Received Segment Set Contains following Segments:");
				for(ISegment objISegment : objISegmentSetCollection)
				{
					System.out.println(objISegment.getName());
				}
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * Closes the Connection.
	 * @param objClientSession object of Client Session.
	 */
	public void onClose(ClientSession objClientSession) 
	{
		System.out.println("On Close");
	}
}
