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



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.DSC;
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.QID;
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.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.QBP_Q23;
import cdac.medinfo.sdk.hl7282.hl7model.messages.query.QCN_J01;
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.interactivecontinuation.ClientSideQICAgent;
import cdac.medinfo.sdk.hl7282.hl7net.auxiliaryprotocol.transactionhandler.TransactionHandler;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IBatchProcessor;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IClientInteractiveContinuation;
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.ITransactionHandler;
import cdac.medinfo.sdk.hl7282.hl7net.localextension.PopulateDataType;
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.IInteractiveResponseReciever;
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.EnumSegments;
import cdac.medinfo.sdk.hl7282.utils.EnumTriggerEvent;
import cdac.medinfo.sdk.hl7282.utils.HL7Config;
import cdac.medinfo.sdk.hl7282.utils.HL7Constants;
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 acts as Initiating system which sends Different Query.(Specially meant for Interactive query)
 * And receives the response related to it.
 */

public class QICInitiatingSystem implements IInteractiveResponseReciever,IBatchReceiver,IExceptionListener
{
	UserSession objUserSession = null;
	IInteractiveResponseReciever objInteractiveResponseReciever = null;
	IBatchProcessor objIBatchProcessor = null;
	IClientInteractiveContinuation objClientSideQICAgent =null;
	DSC objDSCSegment =null;
	int i = 1;
	int Count= 0,Response=0;
	int iMessageControlId =0;
	String strMessageControlID=null;
	String strcounter;
	MessageFactory objMessageFactory =null;
	Populate_Test objPopulateTest = null;

	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:\\QICInitiatingSystem_Logs");


			//Reading IP address for QICInitiatingSystem
			System.out.print("Enter IP Adderess of QICInitiatingSystem: ");
			InputStreamReader input = new InputStreamReader(System.in);
			BufferedReader reader = new BufferedReader(input);
			String strIP = reader.readLine();
			
			//Reading port for QICInitiatingSystem
			System.out.print("Enter Port on which QICInitiatingSystem is listening : ");
			String strPort =reader.readLine();
			int port = Integer.parseInt(strPort);

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

	/**
	 * Initiates this System as QICInitiatingSystem.
	 * 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 
		{
			objPopulateTest = new Populate_Test();
			System.out.println("Creating user session for QICInitiatingSystem");

			//Creating user session for QICInitiatingSystem
			objUserSession  = new UserSession();
			
			
			//Create MessageFactory to provide support for creation and population of messages.
			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);


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

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

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

			/*
			 * Returns received HL7BatchFile .
			 * User can provide his own implementation by implementing interface IBatchReceiver.
			 */
			objUserSession.setIBatchReceiver((IBatchReceiver)this);

			/*
			 * This will register the IInteractiveResponseReciever on user session.
			 */
			objUserSession.setIInteractiveResponseReciever(this);

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

			//Sets the wait period for which each User Session should wait till it 
			//receives ACK/response for triggered event/Query.
			//if user not sets then by default set to 20 sec.
			objUserSession.setWaitPeriod(6000);

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

			/*
			 * Instantiating IClientInteractiveContinuation which handles Interactive Continuation query.
			 * User can provide his own implementation for handling Interactive Continuation query
			 * by implementing IClientInteractiveContinuation interface.
			 */
			objClientSideQICAgent = new  ClientSideQICAgent(); 
			
			/*
			 * This will set the Client Side QIC agent on User Session
			 */
			objUserSession.setClientSideQICAgent(objClientSideQICAgent);
			
			
			/*
			 * 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.
			 * if user wants verification for each message within transaction then user has to set it else not.
			 */
			ITransactionHandler objClientSideTransactionHandler = new TransactionHandler();
			
			//Setting this transaction handler on User session.
			objUserSession.setClientSideTransactionHandler(objClientSideTransactionHandler);
			
			//Connecting this client with QICRespondingSystem
			objUserSession.connect(strIP, port);


			//////////Starting First interactive Session///////////
			System.out.println("Starting first interactive Session..................");
			System.out.println("Creating and Sending First Interactive Query QBP^Q23.");
			objUserSession.sendMessage(getFirstQBPQ23());

			//Waiting for Response to receiver for first query.
			Thread.sleep(9999);
			
			ISingleCollection<Message> responseCollection = objUserSession.getRecievedMessagesCollection("MSG1241020");
			if (responseCollection != null)
			{
				if(responseCollection.size() != 0)
				{
					for (Message response :responseCollection)
					{
						if(response.getMessageStructure().equals(EnumMessageStructure.ACK.getValue()))
						{
							System.out.println("ACK Received for:: " + response.getTriggerEvent());
						}
					}
				}
				else
					System.out.println("Response Collection is empty");
			}
			else
				System.out.println("No ACK is recieved");

			/*
			 * This is Utility provided by the agent by which user can generate second interactive Query through
			 * Segment Set which will be generated by agent for which already interactive query was send.
			 * if no query is triggered then this will not return the segment set. 

			    This will return the Segment set for the interactive Query send.
			    String strCounter = Query id value for previously send interactive query.
				ISegmentSet objSegmentSet = objClientSideQICAgent.generateInteractiveQuery(strCounter);

				//This will generate Interactive query.
				QBP_Q23 objQBP_Q23 = generateInteractiveQuery(objSegmentSet);

			 */


			/*
			 *	If from response DSCSegment of response was received then store the value and generate 
			 *	second interactive query.
			 *	RSP_K23 response can not be understood that it is deferred or Immediate so it is added in response collection. 
			 */
			
			//Waiting for Response to receiver for query.
			Thread.sleep(9999);
			ISingleCollection<Message> responseCollection11 = objUserSession.getRecievedMessagesCollection("MSG1241020");
			if (responseCollection11 != null)
			{
				if(responseCollection11.size() != 0)
				{
					for (Message response :responseCollection11)
					{
						if(response.getMessageStructure().equals(EnumMessageStructure.RSP_K23.getValue()))
						{
							ISingleCollection<ISegment> objDSCSegmentCollection = response.getSegment(EnumSegments.HL7_SEG_DSC);
							if(objDSCSegmentCollection!=null)
							{
								objDSCSegment = (DSC)objDSCSegmentCollection.elementAt(0);
							}
						}
					}
				}
				else
					System.out.println("Response Collection is empty");
			}
			else
				System.out.println("No response is received");

			//generating second interactive query from the First interactive query.
			QBP_Q23 objQBP_Q23 = getSecondQBPQ23(objDSCSegment);	
			
			//Sending this second Interactive query.
			System.out.println("Creating and Sending Second Interactive Query QBP^Q23.");
			objUserSession.sendMessage(objQBP_Q23);
			//Waiting for Response to receiver for first query.
			Thread.sleep(9999);
			ISingleCollection<Message> ACKCollection = objUserSession.getRecievedMessagesCollection("MSG1241021");
			if (ACKCollection != null)
			{
				if(ACKCollection.size() != 0)
				{
					for (Message objack :ACKCollection)
					{
						if(objack.getMessageStructure().equals(EnumMessageStructure.ACK.getValue()))
						{
							System.out.println("ACK Received for:: " + objack.getTriggerEvent());
						}
					}
				}
				else
					System.out.println("ACK Collection is empty");
			}
			else
				System.out.println("No ACK is recieved");

			System.out.println("first interactive Session Ended..................");
			
			///////Starting Second interactive Session////////
			
			System.out.println("Starting Second interactive Session.............");
			System.out.println("Creating and Sending first interactive Query");
			objUserSession.sendMessage(getFirstQBPQ23());
			//Waiting for Response to receiver for first query.
			Thread.sleep(9999);
			ISingleCollection<Message> responseCollection1 = objUserSession.getRecievedMessagesCollection("MSG1241022");
			if (responseCollection1 != null)
			{
				if(responseCollection1.size() != 0)
				{
					for (Message response :responseCollection1)
					{
						if(response.getMessageStructure().equals(EnumMessageStructure.ACK.getValue()))
						{
							System.out.println("ACK Received :: " + response.getTriggerEvent());
						}
					}
				}
				else
					System.out.println("Response Collection is empty");
			}
			else
				System.out.println("No response is recieved");

			//Generate Cancellation Query for the First Interactive Query.
			System.out.println("Generating Cancellation query for First part of Inetractive Query");
			System.out.println("Cancelling second interactive Session.....");
			objUserSession.sendMessage(getCancelQueryQBPQ23());

			Thread.sleep(9999);
			if(objDSCSegment!=null)
			{
				System.out.println("Generating Second part of first Interactive query.");
				if(objUserSession.sendMessage(getSecondQBPQ23()))
				{
					ISingleCollection<Message> responseCollection2 = objUserSession.getRecievedMessagesCollection("MSG1241023");
					if (responseCollection2 != null)
					{
						if(responseCollection2.size() != 0)
						{
							for (Message response :responseCollection2)
							{
								if(response.getMessageStructure().equals(EnumMessageStructure.ACK.getValue()))
								{
									System.out.println("ACK Received :: " + response.getTriggerEvent());
								}
							}
						}
						else
							System.out.println("Response Collection is empty");
					}
					else
						System.out.println("No response is recieved");
					System.out.println("Query Send Succesfully");
				}
				else
					System.out.println("Second interactive Query can not be send...");
			}
			
			System.out.println("Ending Client..");

		}
		catch (Exception e)
		{
			System.out.println("Error Ocured (Ending Client..)== > "+e.getMessage());
			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)
	{
		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(objReceivedMessage.getHeader().getMessageControlID().getValue().toString() + "1");
		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 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;
	}
	/**
	 * This method is invoked when exception is fetched from remote end.
	 * @ex Exception fetched
	 * @buffer for which exception is received</param>
	 */
	public void onException(Exception ex,byte[] buffer) 
	{
		synchronized (this)
		{
			this.notify();
		}
		System.out.println("In On Exception Received : 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 Received : 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();
		}
	}


	private QBP_Q23 getFirstQBPQ23()
	{
		strcounter = "Q102"+Count++;

		//Deferred Query
		ADTSource objADTSource = new ADTSource();
		QBP_Q23 objQBP_Q23 =(QBP_Q23)objADTSource.createMessage(EnumMessageCode.QBP, EnumTriggerEvent.Q23);


		//MSH Segment
		MSH objMSH = objPopulateTest.populateMSH();
		objMSH.setMessageControlID("MSG124102" + iMessageControlId++);//ST
		objMSH.setMessageType("QBP","Q23","QBP_Q21");
		objQBP_Q23.setHeader(objMSH);

		QPD objQPD = objPopulateTest.populateQPD();
        objQPD.setQueryTag(strcounter);//ST
		objQBP_Q23.setQueryParameterDefinitionSegment(objQPD);

		//RCP segment
		RCP objRCP = objPopulateTest.populateRCP();
		objQBP_Q23.setResponseControlParametersSegment(objRCP);

		return objQBP_Q23;
	}

	private QBP_Q23 getSecondQBPQ23()
	{
		//Deferred Query
		ADTSource objADTSource = new ADTSource();
		QBP_Q23 objQBP_Q23 =(QBP_Q23)objADTSource.createMessage(EnumMessageCode.QBP, EnumTriggerEvent.Q23);


		//MSH Segment
		MSH objMSH = objPopulateTest.populateMSH();
		objMSH.setMessageControlID("MSG124102" + iMessageControlId++);//ST
		objMSH.setMessageType("QBP","Q23","QBP_Q21");
		objQBP_Q23.setHeader(objMSH);

		QPD objQPD = objPopulateTest.populateQPD();
        objQPD.setQueryTag(strcounter);//ST
		objQBP_Q23.setQueryParameterDefinitionSegment(objQPD);

		//RCP segment
		RCP objRCP = objPopulateTest.populateRCP();
		objQBP_Q23.setResponseControlParametersSegment(objRCP);

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

		return objQBP_Q23;
	}

	private QBP_Q23 getSecondQBPQ23(DSC objResponseDSC)
	{
		
		//Deferred Query
		ADTSource objADTSource = new ADTSource();
		QBP_Q23 objQBP_Q23 =(QBP_Q23)objADTSource.createMessage(EnumMessageCode.QBP, EnumTriggerEvent.Q23);

		//MSH Segment
		MSH objMSH = objPopulateTest.populateMSH();
		objMSH.setMessageControlID("MSG124102" + iMessageControlId++);//ST
		objMSH.setMessageType("QBP","Q23","QBP_Q21");
		objQBP_Q23.setHeader(objMSH);

		QPD objQPD = objPopulateTest.populateQPD();
        objQPD.setQueryTag(strcounter);//ST
		objQBP_Q23.setQueryParameterDefinitionSegment(objQPD);

		//RCP segment
		RCP objRCP = objPopulateTest.populateRCP();
		objQBP_Q23.setResponseControlParametersSegment(objRCP);

		objQBP_Q23.setContinuationPointerSegment(objResponseDSC);

		return objQBP_Q23;
	}
	/**
	 * This will generate cancellation query for QRY_A19.
	 * @return QCN_J01 cancellation query.
	 */
	private QCN_J01 getCancelQueryQBPQ23()
	{
		strMessageControlID =  "MSG13560"+iMessageControlId++;
		
		QrySource objQrySource = new QrySource();
		QCN_J01 objQCN_J01 = (QCN_J01)objQrySource.createMessage(EnumMessageCode.QCN, EnumTriggerEvent.J01);

		//Populate MSH Segment
		MSH objMSH = objPopulateTest.populateMSH();
		objMSH.setMessageType("QCN","J01","QCN_J01");
		objMSH.setMessageControlID(strMessageControlID);
		objQCN_J01.setHeader(objMSH);

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

		//Populate QID Segment
		QID objQID = objPopulateTest.populateQID();
		objQID.setQueryTag(strcounter);
		objQID.setMessageQueryName(PopulateDataType.populateCWE(HL7Constants.HL7_SUBCOMPONENT));
		objQCN_J01.setQueryIdentificationSegment(objQID);

		return objQCN_J01;
	}

	/**
	 * This method will automatically invoke when Interactive Response is received. 
	 */
	public void onInteractiveResponseReceive(Message deferredResponse) 
	{
		Response++;
		System.out.println("on InteractiveResponseReceive ............ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"+ Response);

		String strFilePath = null;
		try
		{
			strFilePath = "E:\\Server" + i++ + ".hl7"; 
			HL7Serializer objHL7Serializer = new HL7Serializer();
			objHL7Serializer.serialize(deferredResponse.getSegmentSet(),strFilePath);
			System.out.print("Message serialized successfully\n");
			
			ISingleCollection<ISegment> objDSCCollection = deferredResponse.getSegment(EnumSegments.HL7_SEG_DSC);
			if(objDSCCollection!=null)
				objDSCSegment = (DSC)objDSCCollection.elementAt(0);

			//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 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("BatchFile3265");

			BHS objBatchHeader  = new BHS();
			objBatchHeader.setBatchFieldSeparator('|');
			objBatchHeader.setBatchEncodingChars('^','&','~','\\','#');
			objBatchHeader.setBatchControlID(objHL7Batch.getBatchControlID());//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 will invoke when canceled response is received at Client Side.
	 */
	public void onResponseCancellationReceive(Message objMessage)
	{
		System.out.println("onResponseCancellationReceive recieved canceled response"+objMessage.getMessageStructure());
	}
}
