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



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

import cdac.medinfo.sdk.hl7282.baselibs.CWE;
import cdac.medinfo.sdk.hl7282.baselibs.CX;
import cdac.medinfo.sdk.hl7282.baselibs.MSG;
import cdac.medinfo.sdk.hl7282.baselibs.SI;
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.MSA;
import cdac.medinfo.sdk.hl7282.baselibs.segments.MSH;
import cdac.medinfo.sdk.hl7282.baselibs.segments.PID;
import cdac.medinfo.sdk.hl7282.baselibs.segments.QID;
import cdac.medinfo.sdk.hl7282.baselibs.segments.QPD;
import cdac.medinfo.sdk.hl7282.commoninterface.IDataType;
import cdac.medinfo.sdk.hl7282.commoninterface.IGroup;
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.observationreporting.ORU_R01;
import cdac.medinfo.sdk.hl7282.hl7model.messages.observationreporting.ObsvRepRecepient;
import cdac.medinfo.sdk.hl7282.hl7model.messages.observationreporting.ObsvRepSource;
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.fragment.FragmentAgent;
import cdac.medinfo.sdk.hl7282.hl7net.auxiliaryprotocol.pubsub.PublishAgent;
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.IPublishAgent;
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.ClientSession;
import cdac.medinfo.sdk.hl7282.hl7net.upperlayer.IExceptionListener;
import cdac.medinfo.sdk.hl7282.hl7net.upperlayer.IProcessHandler;
import cdac.medinfo.sdk.hl7282.hl7net.upperlayer.ISubscriptionListener;
import cdac.medinfo.sdk.hl7282.hl7net.upperlayer.ServerSession;
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.HL7Config;
import cdac.medinfo.sdk.hl7282.utils.HL7Constants;
import cdac.medinfo.sdk.hl7282.utils.HL7Logger;
import cdac.medinfo.sdk.hl7282.utils.IMap;
import cdac.medinfo.sdk.hl7282.utils.IMessageMapReader;
import cdac.medinfo.sdk.hl7282.utils.IPairedCollection;
import cdac.medinfo.sdk.hl7282.utils.PairedCollection;
import cdac.medinfo.sdk.hl7282.utils.StandardMethods;

/**
 * This class acts as Hub which will receive subscription message and Subscription cancellation message.
 * Publish message to subscriber when subscriber's requirement found. 
 *
 */
public class HL7System_Publisher implements ISubscriptionListener, IProcessHandler 
{
	/*Collection for MessageQuery name and subscription value as subscriber subscribed.*/
	private IPairedCollection<CWE,IDataType>	objUserParamaterColletion = null;
	
	/*Collection of Subscriber value and Client which has requested for it. */
	private IPairedCollection<IDataType,InetSocketAddress>	objSubscriberValueClientAddressColletion = null;

	/*Collection for conformance types*/
	private IPairedCollection<MSG, MSG> objConformanceMatchingMsgTypes;

	/*Reference of Publish agent for detection of Subscription and Cancellation Subscription message*/
	IPublishAgent objPublishAgent =null;
	
	/*Instance of MessageFactory for population and creation of messages.*/
	MessageFactory objMessageFactory =null;
	
	//Handler which does verification of messages.
	ITransactionHandler objServerTransactionHandler =null;
	
	String strMessageControlID=null,strQueryId =null;
	int iMessageControlId =0;
	Populate_Test objPopulate_Test = null;
	

	/**
	 * @param args
	 */
	public static void main(String[] args) 
	{

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

			//Reading port for HL7System_Publisher
			System.out.print("Enter Port on which HL7System_Publisher is listening : ");
			String strPort = reader.readLine();
			int port = Integer.parseInt(strPort);

			HL7System_Publisher objHL7System_Publisher = new HL7System_Publisher();
			objHL7System_Publisher.initiate(strIP,port);	
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * Initiating HL7System_Publisher.
	 */
	public void initiate(String strIP,int port)
	{

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

			
			objPopulate_Test = new Populate_Test();

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


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

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

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

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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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

			  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 fragmentaion 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 Server session
			objServerSession.setBatchProcessor(objIBatchProcessor);
			
			
			/*
			 * Setting this class as ProcessHandler for PatientAdminServer.
			 * 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 whehther 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);

			/*
			 * As per the conformance statement it is decided that for particular
			 * messageType subscription will be received in this case QSB_Q16. 
			 */
			//Creating MessageType by which subscriber will subscribe   
			MSG objSubscribeMSG =  PopulateDataType.populateMSG(HL7Constants.HL7_SUBCOMPONENT);	
			objSubscribeMSG.setMessageCode("QSB");
			objSubscribeMSG.setTriggerEvent("Q16");
			objSubscribeMSG.setMessageStructure("QSB_Q16");

			/*
			 * As per the conformance statement it is decided that for particular
			 * messageType messages will be published in this case ORU_R01. 
			 */
			//Creating MessageType by which Publisher will match for publishing.
			MSG objPublishMSG =PopulateDataType.populateMSG(HL7Constants.HL7_SUBCOMPONENT);		
			objPublishMSG.setMessageCode("ORU");
			objPublishMSG.setTriggerEvent("R01");
			objPublishMSG.setMessageStructure("ORU_R01");

			/*
			 * Storing messageType value in collection.
			 */
			//Adding it in collection.
			objConformanceMatchingMsgTypes = new PairedCollection<MSG, MSG>();
			objConformanceMatchingMsgTypes.put(objPublishMSG , objSubscribeMSG);

			//Starting ServerSession on specified port.
			System.out.println("Initiating HL7System_Publisher .");
			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 invoked when Cancellation of Subscription Message is received.
	 */
	public void onSubscriptionCancel(ClientSession objClientSession) 
	{
		try
		{
			System.out.println("Subscription cancellation message Received From Client : " + objClientSession.getClientAddress().toString());

			Message SubscriptionCancellationMessage = objClientSession.getMessage();		

			//Generate ACk for Subscription Cancellation message.
			ACK acknowledgement = generateAcknowledgement(SubscriptionCancellationMessage);


			//Removing the subscription details.
			ISingleCollection<ISegment> objQIDCollection = SubscriptionCancellationMessage.getSegment(EnumSegments.HL7_SEG_QID);
			if(objQIDCollection!= null)
			{
				QID objQID = (QID)objQIDCollection.elementAt(0);
				CWE objCE  = objQID.getMessageQueryName();
				CX objCXToDelete =null;
				
				Object[] objArray = objUserParamaterColletion.getKeys();
				
				for(Object obj:objArray)
				{
					CWE objCECompare = (CWE)obj; 
					if(objCECompare.equals(objCE))
					{
						objCXToDelete = (CX)objUserParamaterColletion.get(objCECompare);
						objUserParamaterColletion.remove(objCECompare);
						if(objSubscriberValueClientAddressColletion!=null)
						{
							objSubscriberValueClientAddressColletion.remove(objCXToDelete);
						}
					}
				}
			}

			//Sending Ack for Subscription Cancellation message.
			objClientSession.sendMessage(acknowledgement); 
		}
		catch (IOException e)
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHLogger = HL7Logger.getLogger();
				objHLogger.log(e);
			}		
		}
	}

	/**
	 * This method is invoked when Subscription Message is received.
	 */
	public void onSubscriptionMessageReceive(ClientSession objClientSession)
	{
		try
		{

			System.out.println(" Subscription Message Received From Client : " + objClientSession.getClientAddress().toString());
			Message objSubScriptionMessage = objClientSession.getMessage();

			//Generate ACK for Subscription Cancellation message.
			ACK acknowledgement = generateAcknowledgement(objSubScriptionMessage);

			//Sending ACK for Subscription message.
			objClientSession.sendMessage(acknowledgement);

			MSG objMSG = objSubScriptionMessage.getHeader().getMessageType();

			Object[] keys = objConformanceMatchingMsgTypes.getKeys();
			for (int i = 0; i < keys.length; i++)
			{
				MSG objLocalMSG =  objConformanceMatchingMsgTypes.get((MSG)keys[i]);

				//checked that received message is subscription message then in this case check with 
				//conformance statement from the collection.
				if(objLocalMSG.equals(objMSG))
				{
					CWE objCE = null;
					//Taking out the MessageQuery name from message
					ISingleCollection<ISegment> objQPDCollection = objSubScriptionMessage.getSegment(EnumSegments.HL7_SEG_QPD);
					if(objQPDCollection!=null);
					{
						QPD objQPD = (QPD)objQPDCollection.elementAt(0);
						objCE = objQPD.getMessageQueryName();
					}	
					
					// Parse QPD and get the Parameters for Subscription and set same parameters on PublishAgent for Client Message Received.
					// For more information refer to 5.5.4 QPD - Query Parameter Definition 3rd Field
					// this Collection can be Used for customizing publishing message.
					IDataType objRceivedDataType = objSubScriptionMessage.getQueryUserParameters().elementAt(0);
					CX objCX = PopulateDataType.populateCX(HL7Constants.HL7_SUBCOMPONENT);
					SI objSI = PopulateDataType.populateSI(HL7Constants.HL7_COMPONENT);
					try 
					{
						objCX.copy(objRceivedDataType,objSubScriptionMessage.getHeader().getDelimiter());

					}
					catch (IOException e) 
					{
						e.printStackTrace();
					}
					//Add the requirement from subscriber for particular values.
					if(objUserParamaterColletion==null)
						objUserParamaterColletion = new PairedCollection<CWE,IDataType>();

					objUserParamaterColletion.put(objCE, objCX);
					
					//Add subscriber requirement and client address.
					if(objSubscriberValueClientAddressColletion==null)
						objSubscriberValueClientAddressColletion = new PairedCollection<IDataType,InetSocketAddress>();
					
					//This will add the subscriber requirement and its address.
					//if any requirement found with subscriber requirement then it will send to this address. 
					InetSocketAddress objInetSocketAddress = objClientSession.getClientAddress();
					objSubscriberValueClientAddressColletion.put(objCX,objInetSocketAddress);
				}
			}

		}catch(Exception 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) 
	{
		try
		{
			System.out.println("Message Receive to HL7System_Publisher.....");

			// On receiving any message to central archieve, Programmer can identify the User parameters
			// from message and can publish the message to all Subscriber.
			// e.g.If any user subscribes for any ORU_R01 message then publish that message to that subscriber.

			Message objMessage = objClientSession.getMessage();

			//Checked that Received Message is ORU_R01.
			//if yes then for publishing of it has create new User Session and send this message to 
			//Server which is Running on particular ip and st specified port which is 
			//already negotiated and known to client.
			if (objMessage.getMessageStructure().equals(EnumMessageStructure.ACK.getValue()))  
			{
				System.out.println("ACK received for Deferred response by User Process Handler");
			}
			else if(objMessage.isQuery())
			{ 
				System.out.println(" Query received by IProcessHandler");
			}
			else 
			{	
				//This will generate the Acknowledgment for the Reciebed Message.
				ACK acknowledgement = generateAcknowledgement(objMessage);

				//Sending ACK for Recieved Message.
				objClientSession.sendMessage(acknowledgement);
			}
			if(objMessage instanceof ORU_R01 )
			{
				if(isPublishMessage(objMessage))
				{
					System.out.println("Recieved Message with Subscriber Requirement.....");

					//Publishing ORU_R01 by creating separate User Session.
					//This  class which will create separate thread for Publishing message.
					HL7System_PublisherUserSession objHL7System_PublisherUserSession = new HL7System_PublisherUserSession();
					objHL7System_PublisherUserSession.setMessage(objMessage);
					objHL7System_PublisherUserSession.start();
				}
			}
		}
		catch (Exception e)
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHLogger = HL7Logger.getLogger();
				objHLogger.log(e);
			}			
		}
	}
	/*
	 * This will check that received message is received which matches with the subscriber requirement.
	 * if it matches then send to all subscriber for those who have subscribed.
	 * return boolean true if it is to be published else false.    
	 */
	private boolean isPublishMessage(Message objMessage)
	{
		ORU_R01 objORU_R01 = (ORU_R01)objMessage;

		IGroup objPatienResultGroup  =   objORU_R01.getPatientResultGroups().elementAt(0);
		IGroup objPatientGroup = objPatienResultGroup.getGroup(EnumSegments.HL7_GROUP_PATIENT).elementAt(0);
		PID objPatientIdentificationSegment = (PID)objPatientGroup.getSegment(EnumSegments.HL7_SEG_PID).elementAt(0);
		ISingleCollection<CX> objSI = objPatientIdentificationSegment.getPatientIdentifierList();
		if(objUserParamaterColletion!=null)
		{
			if(objSubscriberValueClientAddressColletion!=null)
			{
				Object[] objectArray = objSubscriberValueClientAddressColletion.getKeys();
				for(Object obj : objectArray)
				{
					CX objNewSI = (CX)obj;
					if(objNewSI.getIDNumber().equals(objSI.elementAt(0).getIDNumber()))
						return true;
				}
				if(objSubscriberValueClientAddressColletion.contains(objSI.elementAt(0)))
						return true;
			}
		}

		return false;
	}
	/**
	 * This method is invoked when deferred response received is in BatchFile.
	 */
	public void OnBatchFileReceive(ClientSession objClientSession)
	{
		System.out.println("OnBatchFileReceive");

	}
	/**
	 * This method is invoked when deferred response received is in Batch.
	 */
	public void OnConnect(ClientSession objClientSession)
	{
		/*
		 * Setting this class as SubscriptionListener for HL7System_Publisher.
		 * ISubscriptionListener interface must be implemented ,so that server can receive messages from client side. 
		 */
		objClientSession.setISubscriptionListener(this);
		/*
		 * Instantiating IPublishAgent which handles Subscription message and UnSubscription message 
		 * PublishAgent is the default implementation provided by SDK for HL7.
		 * User can provide his own implementation for PublishAgent by implementing IPublishAgent interface.
		 */
		objPublishAgent = new PublishAgent();
		/*
		 *Setting publish agent on Client session.   
		 */
		objClientSession.setPublishAgent(objPublishAgent);
		
		/*
		 * 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();
		
		/*
		 * Setting this MessageFactory on Client Session
		 */
		objClientSession.setMessageFactory(objMessageFactory);
		
		//Setting this Server Side transaction handler.
		objClientSession.setServerSideTransactionHandler(objServerTransactionHandler);
		
		System.out.println("On Connect");
		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)
	{
		System.out.println("OnErrorReceive");

	}
	/**
	 * 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 simulates user action to generate Acknowledgement for message received.
	 * This method is demonstrated for test purpose only.
	 * In the actual scenerion, it is User's responsibility to generates acknowledgement & sends it on client Session. 
	 * @param objReceivedMessage specified Message object
	 * @return ACK instance of acknowledgement
	 */
	public ACK generateAcknowledgement(Message objReceivedMessage)
	{
		strMessageControlID = "MSQ32336" + iMessageControlId++; 
		
		IMessageMapReader objMessageMapReader = MessageMapReader.createInstance();
		IMap objMessageMap = objMessageMapReader.getMessageMap(EnumMessageCode.ACK, null);

		ACK ackMessage = new ACK(StandardMethods.getEnumTriggerEvent(objReceivedMessage.getTriggerEvent()), objMessageMap);
		MSH objMSH = objPopulate_Test.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 class internally creates thread which will Publish message to Server present at other end.
	 * Specified that Server is running on specified port and ip which is already negotiated and it is as per 
	 * conformance statement.
	 * This class internally creates UserSession and Publishes the Message for which subscriber is subscribed for.   
	 * 
	 */
	public class HL7System_PublisherUserSession extends Thread  implements IExceptionListener
	{
		//object of Message to be send to Publish. 
		Message message;
		UserSession objUserSession = null;
		int i=0;
		//Handler which does verification of messages.
		ITransactionHandler objClientTransactionHandler =null;

		/**
		 * This will set the Publish Message. 
		 * @param message object of Publish Message.
		 */
		public void setMessage(Message message)
		{
			this.message = message;
		}
		public void run() 
		{
			publishMessage(message);
		}
		/**
		 * This method creates the UserSesion internally and Publishes Message. 
		 * @param objMessage object of Message to be send. 
		 */
		public void publishMessage(Message objMessage)
		{
			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("C:\\Client_Logs");

				
				//Creating User Session for HL7System_PublisherUserSession.
				objUserSession  = new UserSession();

				//Create MessageFactory to provide support for creation and population of messages.
				MessageFactory 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);

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

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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


				/*
				 * Instantiating IFragmentProcessor which handles the fragmentaion 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);
				
				/*
				 * 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.
				 */
				objClientTransactionHandler = new TransactionHandler();
				
				//Setting ClientTransactionHandler on User Session.
				objUserSession.setClientSideTransactionHandler(objClientTransactionHandler);

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

				//Reading IP address for HL7System_PublisherUserSession is running.
				System.out.println("Enter Ip of HL7System_Subscriber Server running: ");
				InputStreamReader input = new InputStreamReader(System.in);
				BufferedReader reader = new BufferedReader(input);
				String strIP = reader.readLine();

				//Reading port for HL7System_Subscriber.
				System.out.println("Enter Port on which HL7System_Subscriber is listening for publish message: ");
				String strPort = reader.readLine();
				int port = Integer.parseInt(strPort);

				//Connecting this client with HL7System_Subscriber.
				objUserSession.connect(strIP, port);

				//Publishing ORU_R01 message. 
				objUserSession.sendMessage(objMessage);

				//Checked that Server at other end received Publish message by MessageControl id of the Publish Message. 
				ISingleCollection<Message> objMessageCollection = objUserSession.getRecievedMessagesCollection((String)objMessage.getHeader().getMessageControlID().getValue());
				if(objMessageCollection!=null)
				{
					for(Message objMsg: objMessageCollection)
					{
						if(objMsg.getMessageStructure().equals(EnumMessageStructure.ACK.getValue()))
							System.out.println("ACK Recieved for "+objMsg.getTriggerEvent());
					}
				}
			}
			catch (Exception e)
			{
				if(HL7Config.createInstance().isLoggingOn())
				{
					HL7Logger objHLogger = HL7Logger.getLogger();
					objHLogger.log(e);
				}			
			}
		}
		/**
		 * 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();
			}

		}
		/**
		 * This method is invoked when deferred response is received.
		 */
		public void onResponseReceive(Message deferredResponse) 
		{
			System.out.println("Deferred Response Received............ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");

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

				//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();
			} 
		}
	}
	/**
	 * Closes the Connection.
	 * @param objClientSession object of Client Session.
	 */
	public void onClose(ClientSession objClientSession) 
	{
		System.out.println("On Close");
	}
	/**
	 * 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();
		}

	}
}
