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

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import cdac.medinfo.sdk.hl7282.baselibs.messages.AbstractQuery;
import cdac.medinfo.sdk.hl7282.baselibs.messages.Message;
import cdac.medinfo.sdk.hl7282.baselibs.messages.MessageFactory;
import cdac.medinfo.sdk.hl7282.commoninterface.ISegmentSet;
import cdac.medinfo.sdk.hl7282.commoninterface.ISingleCollection;
import cdac.medinfo.sdk.hl7282.exceptions.BatchException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidMessageException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidSequenceException;
import cdac.medinfo.sdk.hl7282.exceptions.MessageNotSupportedException;
import cdac.medinfo.sdk.hl7282.exceptions.ProcessFailedException;
import cdac.medinfo.sdk.hl7282.exceptions.SerializeException;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IBatchProcessor;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IConnectionListener;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IDataNotificationListener;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IExceptionReceiver;
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.IPublishAgent;
import cdac.medinfo.sdk.hl7282.hl7net.commons.ISequenceAgent;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IServerInteractiveContinuation;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IServerQueryCancellation;
import cdac.medinfo.sdk.hl7282.hl7net.commons.ITransactionHandler;
import cdac.medinfo.sdk.hl7282.hl7net.communication.AuxAgent;
import cdac.medinfo.sdk.hl7282.hl7net.communication.ServerSideMessageAgent;
import cdac.medinfo.sdk.hl7282.hl7net.communication.ServerSideNetworkAgent;
import cdac.medinfo.sdk.hl7282.utils.EnumErrorCode;
import cdac.medinfo.sdk.hl7282.utils.EnumSubscriptionType;
import cdac.medinfo.sdk.hl7282.utils.HL7Config;
import cdac.medinfo.sdk.hl7282.utils.HL7Logger;
import cdac.medinfo.sdk.hl7282.utils.ResourceBundleWrapper;
import cdac.medinfo.sdk.hl7282.utils.SingleCollection;


/**
 * ClientSession is instantiated when Client is Connected to Server end.
 * ClientSession is the entity that represents Client on Server side.
 */
public class ClientSession extends Thread implements IDataNotificationListener ,IExceptionReceiver  ,IConnectionListener
{
	

	/* The object client address. */
	private InetSocketAddress objClientAddress; 

	/* The Server Side Network Agent. */
	private ServerSideNetworkAgent objServerSideNetworkAgent;

	/* The Message agent. */
	private ServerSideMessageAgent objServerSideMessageAgent;

	/*The Message Handler */
	private MessageFactory objMessageFactory = null;

	/* The objIProcess handler. */
	private IProcessHandler objIProcessHandler = null;

	/* The accept connection.*/
	private boolean bAcceptConnection;

	/**The Fragment Processor */
	private IFragmentProcessor objFragmentProcessor = null;

	/** The MLLP Processor **/
	private IMLLP objIMLLP = null;

	/**The Batch Processor */
	private IBatchProcessor objBatchProcessor = null;

	/* The Client Status Listener. */
	private IClientStatusListener objIClientStatusListener;

	/* The Auxiliary Protocol Agent	 */
	private AuxAgent objAuxAgent;

	private int iReadMaxLength,iWriteMaxLength;

	/*The HL7 Message */
	private Message objHL7Message;

	/*The HL7 BatchFile Object*/
	private IHL7BatchFile objHL7BatchFile;

	/*HL7BatchCollection for Responses */
	private ISingleCollection<String> objHL7BatchCollection = null;

	/*Error code*/
	private EnumErrorCode errorCode;

	ExecutorService executor;

	/**The ISubscriptionListener */
	private ISubscriptionListener objISubscriptionListener = null;

	/**The Publish Processor */
	private IPublishAgent objPublishAgent = null;

	/**The Sequence Agent */
	private ISequenceAgent objSequenceAgent = null;

	/*The Query Cancellation agent*/
	private IServerQueryCancellation objServerSideQCNAgent = null;

	/*The Query Cancellation agent*/
	private IServerInteractiveContinuation objServerSideQICAgent = null;

	/* Interactive Query receiver.*/
	private IInteractiveQueryReciever objInteractiveQueryReciever;

	/* Canceled Query Receiver.*/
	private ICanceledQueryReceiver objCanceledQueryReceiver;

	/* Server Side Transaction Handler*/
	private ITransactionHandler objServerSideTransactionHandler = null; 


	/**
	 * Retrieves ISubscriptionListener from ClientSession.
	 * @return ISubscriptionListener - instance of ISubscriptionListener
	 */
	public ISubscriptionListener getISubscriptionListener() 
	{
		return objISubscriptionListener;
	}

	/**
	 * Sets instance of ISubscriptionListener on ClientSession.
	 * @param ISubscriptionListener - instance of ISubscriptionListener
	 */
	public void setISubscriptionListener(ISubscriptionListener objISubscriptionListener) 
	{
		this.objISubscriptionListener = objISubscriptionListener;
	}	

	/**
	 * Retrieves IPublishAgent from ClientSession.
	 * @return IPublishAgent - instance of PublishAgent.
	 */
	public IPublishAgent getPublishAgent() {
		return objPublishAgent;
	}
	/**
	 * Retrieves IServerInteractiveContinuation from ClientSession.
	 * @return IServerInteractiveContinuation - instance of IServerInteractiveContinuation.
	 */
	public IServerInteractiveContinuation getServerSideQICAgent() {
		return objServerSideQICAgent;
	}

	/**
	 * Retrieves ISequenceAgent from ClientSession.
	 * @return ISequenceAgent - instance of ISequenceAgent.
	 */
	public ISequenceAgent getSequenceAgent()
	{
		return objSequenceAgent;
	}

	/**
	 * Sets the Interactive Continuation Agent. 
	 * @param objServerSideQICAgent the ServerSideQICAgent to set.
	 */
	public void setServerSideQICAgent(IServerInteractiveContinuation objServerSideQICAgent) {
		this.objServerSideQICAgent = objServerSideQICAgent;
	}
	/**
	 * Sets instance of PublishAgent on ClientSession.
	 * @param objPublishAgent - instance of PublishAgent.
	 */
	public void setPublishAgent(IPublishAgent objPublishAgent) 
	{
		this.objPublishAgent = objPublishAgent;
	}
	/**
	 * This will set the Interactive Query Receiver.
	 * @param objInteractiveQueryReciever - implementor of IInteractiveQueryReciever.
	 */
	public void setInteractiveQueryReciever(IInteractiveQueryReciever objInteractiveQueryReciever)
	{
		this.objInteractiveQueryReciever = objInteractiveQueryReciever;
	}
	/**
	 * This will set the ICanceledQueryReceiver on Client session.
	 * @param objCanceledQueryReceiver - implementor of ICanceledQueryReceiver
	 */
	public void setICanceledQueryReceiver(ICanceledQueryReceiver objCanceledQueryReceiver)
	{
		this.objCanceledQueryReceiver = objCanceledQueryReceiver;
	}
	/**
	 * This will set the ITransactionHandler on Client session.
	 * @param objServerSideTransactionHandler - implementor of ITransactionHandler
	 */
	public void setServerSideTransactionHandler(ITransactionHandler objServerSideTransactionHandler)
	{
		this.objServerSideTransactionHandler = objServerSideTransactionHandler;
	}

	/**
	 * Initializes a new instance of the ClientSession class.
	 */
	public ClientSession()
	{}

	/**
	 * Instantiates ClientSession with specified ProcessHandler and Socket object.
	 * @param objIProcessHandler specified Process Handler
	 * @param socket given socket object
	 */
	public ClientSession(IProcessHandler objIProcessHandler, Socket socket) throws IOException
	{
		super("ClientSession");
		this.objIProcessHandler = objIProcessHandler;
		objClientAddress = new InetSocketAddress(socket.getInetAddress(),socket.getPort());

		objServerSideNetworkAgent = new ServerSideNetworkAgent(socket);
		objServerSideNetworkAgent.setConnectionListener((IConnectionListener)this);
		objServerSideMessageAgent  = new ServerSideMessageAgent();
		executor = Executors.newCachedThreadPool();

		objHL7BatchCollection = new SingleCollection<String>();
	}

	/**
	 * Sets the Fragment Processor
	 * @param objIFragmentProcessor the objIFragmentProcessor to set
	 */
	public void setFragmentProcessor(IFragmentProcessor objIFragmentProcessor) 
	{
		objFragmentProcessor = objIFragmentProcessor;
	}

	/**
	 * Sets the MLLP Processor.
	 * @param objIMLLP the MLLP processor to set.
	 */
	public void setMLLPProcessor(IMLLP objIMLLP)
	{
		this.objIMLLP = objIMLLP;
	}

	/**
	 * Sets the Sequence Agent.
	 * @param objSequenceAgent  instance of Sequence Agent.
	 */
	public void setSequenceAgent(ISequenceAgent objSequenceAgent)
	{
		this.objSequenceAgent = objSequenceAgent;
	}

	/**
	 * Sets the Batch Processor
	 * @param objIBatchProcessor the objIBatchProcessor to set
	 */
	public void setBatchProcessor(IBatchProcessor objIBatchProcessor) 
	{
		this.objBatchProcessor = objIBatchProcessor;
	} 

	
	/**
	 * Retrieves Error Code after receiving Message Coming from Remote end
	 * @return EnumErrorCode  error code
	 */
	public EnumErrorCode getErrorCode()
	{
		return this.errorCode; 
	}

	/**
	 * Sets Error Code specified in parameter iErrorCode.
	 * @param enumErrorCode Error Code to set
	 */
	void setErrorCode(EnumErrorCode enumErrorCode)
	{
		errorCode = enumErrorCode;
	}

	/**
	 * Retrieves Message from ClientSession
	 * @return Message Message object
	 */
	public Message getMessage()
	{
		return objHL7Message;
	}

	/**
	 * Retrieves HL7BatchFile from ClientSession
	 * @return IHL7BatchFile Specifies object of batch file.
	 */
	public IHL7BatchFile getBatchFile()
	{
		return objHL7BatchFile;
	}

	/**
	 * Sets Message on ClientSession
	 * @param objMessage Message object 
	 */
	void setMessage(Message objMessage)
	{
		objHL7Message = objMessage;
	}

	/**
	 * Sets MessageFactory on ClientSession.
	 * @param objMessageFactory Specifies object of Message Factory.
	 */
	public void setMessageFactory(MessageFactory objMessageFactory)
	{
		this.objMessageFactory = objMessageFactory;
	}

	/**
	 * Retrieves MessageFactory from ClientSession. 
	 * @return MessageFactory Specifies object of Message Factory.
	 */
	public MessageFactory getMessageFactory()
	{
		return  this.objMessageFactory;
	}

	/**
	 * Adds the client status listener.
	 * @param objIClientStatusListener Specifies Object of Client Status Listener.
	 */
	public void addClientStatusListener(IClientStatusListener objIClientStatusListener)
	{
		this.objIClientStatusListener = objIClientStatusListener;
	}

	/**
	 * Retrieves the client address of ClientSession.
	 * @return InetSocketAddress The client address
	 */
	public InetSocketAddress getClientAddress() 
	{
		return objClientAddress;
	}

	/**
	 * Sets the client address of ClientSession.
	 * @param objClientAddress The new client address
	 */
	public void setClientAddress(InetSocketAddress objClientAddress)
	{
		this.objClientAddress = objClientAddress;
	}

	/**
	 * Checks if is connection accepted.
	 * @return boolean true if is connection accepted.
	 */
	public boolean isConnectionAccepted() 
	{
		return bAcceptConnection;
	}

	/**
	 * Accept connection.
	 * @param acceptConnection The accept connection
	 */
	public void acceptConnection(boolean acceptConnection)
	{
		if(acceptConnection == true)
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();
				objHL7Logger.log(ResourceBundleWrapper.getValue("status", "ClientConnected",new Locale(HL7Config.createInstance().getLocale())));
			}
		}

		bAcceptConnection = acceptConnection;
	}

	/**
	 * Retrieves Enumeration of specified Message Structure.  
	 * @return String Enumeration of Message Structure.
	 */
	public String getMessageStructure()
	{
		return objHL7Message.getMessageStructure();
	}

	/**
	 * Retrieves Enumeration of specified Trigger Event.  
	 * @return String Enumeration of Trigger Event. 
	 */
	public String getTiggerEvent()
	{
		return objHL7Message.getTriggerEvent();
	}

	/**
	 * Sets max length limit of reading and writing. 
	 * @param iReadLength value of Reading max length.
	 * @param iWriteLength value of Writing max length.
	 */
	public void setReadWriteLength(int iReadLength,int iWriteLength)
	{
		if(iReadLength>0 && iWriteLength>0)
		{
			iReadMaxLength = iReadLength;
			iWriteMaxLength = iWriteLength;	
		}
		else
		{
			if(iReadLength>=0)
			{
				iReadMaxLength = iReadLength;
			}
			if(iWriteLength>=0)
			{
				iWriteMaxLength = iWriteLength;	
			}
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();
				String strLogMessage = ResourceBundleWrapper.getValue("warnings","Fragment_InvalidReadersWritersLength",new Locale(HL7Config.createInstance().getLocale()));
				objHL7Logger.log(strLogMessage);
			}
		}
	}

	/**
	 * Invoked when exception occurs.
	 * @param ex Exception occurred 
	 * @param byte[] buffer-buffer byte array containing data for which exception is thrown. 
	 */
	public void onException(Exception ex, byte[] buffer) 
	{
		if (HL7Config.createInstance().isLoggingOn())
		{
			HL7Logger.getLogger().log(ex.getMessage());
		}
		if(objIProcessHandler != null)
			objIProcessHandler.onException(ex, buffer);
	} 

	/**
	 * Invoked when exception is fetched while processing Message.
	 * @param ex Exception fetched.
	 * @param buffer data for which error is fetched.
	 * 
	 */
	public void onException(Exception ex,ISegmentSet objISegmentSet) 
	{
		if (HL7Config.createInstance().isLoggingOn())
		{
			HL7Logger.getLogger().log(ex.getMessage());
		}
		if(objIProcessHandler != null)
			objIProcessHandler.onException(ex, objISegmentSet);
	} 
	/**
	 * Initializes the communication and registers with all Agents and Listeners.
	 */
	public void initComm() 
	{
		objAuxAgent = new AuxAgent();

		if(objFragmentProcessor != null)
			objFragmentProcessor.setMaxLength(iWriteMaxLength);

		objAuxAgent.setFragmentProcessor(objFragmentProcessor);
		objAuxAgent.setMLLPProcessor(objIMLLP);
		objAuxAgent.setNetworkAgent(objServerSideNetworkAgent);
		objAuxAgent.setDataProcessor(objServerSideMessageAgent);
		objServerSideMessageAgent.setServerSideQCNAgent(objServerSideQCNAgent);
		objServerSideMessageAgent.setServerSideQICAgent(objServerSideQICAgent);
		objServerSideMessageAgent.setPublishAgent(objPublishAgent);
		objServerSideMessageAgent.setSequenceAgent(objSequenceAgent);
		objServerSideMessageAgent.setBatchProcessor(objBatchProcessor);
		objAuxAgent.setExceptionReceiver(this);

		objServerSideNetworkAgent.addBufferReceiver(objAuxAgent);
		objServerSideMessageAgent.addDataNotoficationListener(this);

		if(objMessageFactory == null)
			objMessageFactory = MessageFactory.createInstance();
		objServerSideMessageAgent.setMessageFactory(objMessageFactory);
		objServerSideMessageAgent.setBufferSender(objAuxAgent);

		objAuxAgent.setWriteLength(iWriteMaxLength);
		objServerSideNetworkAgent.setReadWriteLength(iReadMaxLength,iWriteMaxLength);
		objServerSideNetworkAgent.setConnectionListener((IConnectionListener)this);
		objServerSideNetworkAgent.start();
	}


	/**
	 * Sends specified Message to Network Agent.
	 * @param objHL7Message Specified message to be sent.
	 */
	public boolean sendMessage(Message objHL7Message) throws IOException
	{
		if(objHL7Message != null)
		{
			try 
			{
				if(isMessageSupported(objHL7Message))
				{
					if(objHL7Message.validate())
					{
						if(HL7Config.createInstance().isLoggingOn())
						{
							StatusLogger.logMessage(objHL7Message);
						}

						// This Server Side Transaction handler will process the 
						// sending message and keeps data for it.
						// internally it performs all validation for all Messages according to Mode set. 
						if(objServerSideTransactionHandler!=null)
							objServerSideTransactionHandler.processMessageTransaction(objHL7Message);

						objServerSideMessageAgent.sendMessage(objHL7Message);
						return true;
					}
				}
				throw new InvalidMessageException(ResourceBundleWrapper.getValue("exceptions","MessageNotRegistered",new Locale(HL7Config.createInstance().getLocale()+ objHL7Message.getMessageStructure())));
			}
			catch (ProcessFailedException pfe) 
			{
				if(HL7Config.createInstance().isLoggingOn())
				{
					HL7Logger objHL7Logger = HL7Logger.getLogger();
					objHL7Logger.log(pfe);
				}
				throw pfe;
			}
			catch (InvalidMessageException ime) 
			{
				if(HL7Config.createInstance().isLoggingOn())
				{
					HL7Logger objHL7Logger = HL7Logger.getLogger();
					objHL7Logger.log(ime);
				}
				throw ime;
			}
			catch (MessageNotSupportedException mse) 
			{
				if(HL7Config.createInstance().isLoggingOn())
				{
					HL7Logger objHL7Logger = HL7Logger.getLogger();
					objHL7Logger.log(mse);
				}
				throw mse;
			}
			catch (InvalidSequenceException ise)
			{
				if(HL7Config.createInstance().isLoggingOn())
				{
					HL7Logger objHL7Logger = HL7Logger.getLogger();
					objHL7Logger.log(ise);
				}
				throw ise;
			}
		}
		else
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();
				String strLogMessage = ResourceBundleWrapper.getValue("exceptions","NullMessage",new Locale(HL7Config.createInstance().getLocale()));
				objHL7Logger.log(strLogMessage);
			}
			return false;
		}
	}


	/**
	 * Sends specified Batch to Network Agent.
	 * @param objHL7Batch Received Batch .
	 * @throws IOException Thrown when Message Validation fails.
	 */
	public boolean sendBatch(IHL7BatchFile objHL7BatchFile) throws IOException
	{
		if(objHL7BatchFile != null)
		{
			try 
			{
				if(objHL7BatchFile.validate())
				{
					if(HL7Config.createInstance().isLoggingOn())
					{
						StatusLogger.logBatchFile(objHL7BatchFile);
					}

					// This Server Side Transaction handler will process the 
					// sending Batch File and keeps data for it.
					// internally it performs all validation for all Batches according to Mode set.
					if(objServerSideTransactionHandler!=null)
						objServerSideTransactionHandler.processBatchTransaction(objHL7BatchFile);

					if(objBatchProcessor == null)
						throw new BatchException(ResourceBundleWrapper.getValue("exceptions","Batch_BatchNotSupported",new Locale(HL7Config.createInstance().getLocale())));

					objServerSideMessageAgent.sendBatch(objHL7BatchFile);
				}
			}
			catch (BatchException e) 
			{
				if(HL7Config.createInstance().isLoggingOn())
				{
					HL7Logger objHL7Logger = HL7Logger.getLogger();
					objHL7Logger.log(e);
				}
				return false;
			}
		}
		else
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();
				String strLogMessage = ResourceBundleWrapper.getValue("exceptions","NullMessage",new Locale(HL7Config.createInstance().getLocale()));
				objHL7Logger.log(strLogMessage);
			}
			return false;
		}
		return true;
	}

	/**
	 * Executes client session thread.
	 */
	public void run()
	{
		initComm();
	}

	/**
	 * Invoked when Message is received from Remote end.
	 * @param objMessage Received Message
	 * @param enumErrorCode Received error code
	 * @throws IOException Thrown when IO operation fails.
	 * @throws SerializeException Thrown when serialization of Message fails.
	 */
	public void notifyData(Message objMessage,EnumErrorCode enumErrorCode) throws IOException
	{
		setErrorCode(enumErrorCode);
		if(enumErrorCode == EnumErrorCode.MESSAGE_ACCEPTED)
		{
			setMessage(objMessage);
			if(HL7Config.createInstance().isLoggingOn())
			{
				StatusLogger.logMessage(objMessage);
			}

			// This Sever Side Transaction handler will process the 
			// Receiving message and keeps data for it.
			// internally it performs all validation for all Messages according to Mode set.
			if(objServerSideTransactionHandler!=null)
				objServerSideTransactionHandler.processMessageTransaction(objMessage);
			//This will check that any Subscription Agent is not null and message is subscription message or Unsubsription type.  
			if(objISubscriptionListener!=null)
			{
				if(EnumSubscriptionType.MODIFIED_SUBSCRIPTION == objMessage.getSubscriptionType() ||EnumSubscriptionType.NEW_SUBSCRIPTION == objMessage.getSubscriptionType())
				{
					objISubscriptionListener.onSubscriptionMessageReceive(this);
					return;
				}
				else if(EnumSubscriptionType.CANCEL_SUBSCRIPTION == objMessage.getSubscriptionType())
				{
					objISubscriptionListener.onSubscriptionCancel(this);
					return;
				}
			}
			if(objInteractiveQueryReciever!=null)
			{
				if(objMessage.isQuery())
				{
					AbstractQuery objAbstractquery = (AbstractQuery)objMessage;
					if(objAbstractquery.isInteractiveQuery())
					{
						objInteractiveQueryReciever.onInteractiveQueryReceive(this);
						return;
					}
				}
			}
			if(objCanceledQueryReceiver!=null)
			{
				if(objMessage.isQuery())
				{
					AbstractQuery objAbstractquery = (AbstractQuery)objMessage;
					if(objAbstractquery.isCanceledQuery())
					{
						objCanceledQueryReceiver.onQueryCancellationReceive(this);
						return;
					}
				}
			}
			objIProcessHandler.OnMessageReceive(this);
		}
		else
			objIProcessHandler.OnErrorReceive(this);
	}

	/**
	 * Invoked when HL7BatchFile is received from Remote end.
	 * @param objHL7BatchFile Received HL7BatchFile
	 */
	public void notifyData(IHL7BatchFile objHL7BatchFile)
	{
		this.objHL7BatchFile = objHL7BatchFile;
		if(HL7Config.createInstance().isLoggingOn())
		{
			StatusLogger.logBatchFile(objHL7BatchFile);
		}

		ISingleCollection<IHL7Batch> objBatchCollection = objHL7BatchFile.getHL7BatchCollection();

		// This Server Side Transaction handler will process the 
		// Received Batch File and Removed data for batch for which data is already received.
		// internally it performs all validation for all Batches according to Mode set.
		if(objServerSideTransactionHandler!=null)
			objServerSideTransactionHandler.processBatchTransaction(objHL7BatchFile);

		//If any Interactive query is is present then if Interactive Query Receiver 
		//is present then this will be given to this IInteractiveQueryReciever interface and this message will 
		//be removed from MessageCollection. 
		if(objInteractiveQueryReciever!=null)
		{
			for(IHL7Batch objHL7Batch : objBatchCollection)
			{
				ISingleCollection<Message> objMessageCollection = objHL7Batch.getMessageCollection();
				for(Message objMessage: objMessageCollection)
				{
					if(objMessage.isQuery())
					{
						AbstractQuery objAbstractquery = (AbstractQuery)objMessage;
						if(objAbstractquery.isInteractiveQuery())
						{
							objCanceledQueryReceiver.onQueryCancellationReceive(this);
							objMessageCollection.remove(objMessage);
						}
					}
				}
			}
		}
		//If any cancellation query is coming or canceled query is present then if Canceled Query receiver 
		//is present then this will be given to this interface and this message will 
		//be removed from MessageCollection. 
		if(objCanceledQueryReceiver!=null)
		{
			for(IHL7Batch objHL7Batch : objBatchCollection)
			{
				ISingleCollection<Message> objMessageCollection = objHL7Batch.getMessageCollection();
				for(Message objMessage: objMessageCollection)
				{
					if(objMessage.isQuery())
					{
						AbstractQuery objAbstractquery = (AbstractQuery)objMessage;
						if(objAbstractquery.isCanceledQuery())
						{
							objCanceledQueryReceiver.onQueryCancellationReceive(this);
							objMessageCollection.remove(objMessage);
						}
					}
				}
			}
		}
		for(IHL7Batch objHL7Batch : objBatchCollection)
		{
			if(objHL7BatchCollection.contains(objHL7Batch.getBatchControlID())) // batch id is present means the arrived batch is of ACK.
			{
				objHL7BatchCollection.remove(objHL7Batch.getBatchControlID());
			}
			else
			{
				objHL7BatchCollection.add(objHL7Batch.getBatchControlID());
			}
		}
		objIProcessHandler.OnBatchFileReceive(this);
	}

	/**
	 * This function is automatically invoked when connection is closed by remote end.
	 */
	public void closeConnection() 
	{
		try 
		{
			close();
		} 
		catch (IOException e) 
		{
		}
	}

	/**
	 * Close.
	 * @return true, if successful
	 */
	public boolean close() throws IOException
	{
		synchronized (this) 
		{
			notify();	
		}
		if(objIClientStatusListener != null)
			objIClientStatusListener.updateStatus(this);

		objIProcessHandler.onClose(this);
		{
			objClientAddress = null;
			objMessageFactory = null;
			objIProcessHandler = null;
			objFragmentProcessor = null;
			objIMLLP = null;
			objBatchProcessor = null;
			objHL7Message = null;
			objHL7BatchFile = null;
			objHL7BatchCollection = null;
			errorCode = null;
			objISubscriptionListener = null;
			objPublishAgent = null;
			objSequenceAgent = null;
			objServerSideQCNAgent = null;
			objServerSideQICAgent = null;
			objInteractiveQueryReciever = null;
			objCanceledQueryReceiver = null;
			objServerSideTransactionHandler = null;
			executor = null;
			if(objServerSideNetworkAgent != null)
				objServerSideNetworkAgent.close();
			objServerSideNetworkAgent = null;
			if(objServerSideMessageAgent != null)
				objServerSideMessageAgent.close();
			objServerSideMessageAgent = null;
			if(objAuxAgent != null)
				objAuxAgent.close();
			objAuxAgent = null;
			objIClientStatusListener = null;

			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();
				objHL7Logger.log(ResourceBundleWrapper.getValue("status", "ClientSessionClosed",new Locale(HL7Config.createInstance().getLocale())));
			}
		}
		return true;
	}
	/**
	 * This will set the Query Cancellation agent on ClientSideMessageAgent
	 * @param objQCNAgent Specifies Object of Server Side Query Cancellation agent.
	 */
	public void setServerSideQCNAgent(IServerQueryCancellation objServerSideQCNAgent)
	{
		this.objServerSideQCNAgent = objServerSideQCNAgent;
	}
	//This method will be used to check that sending message is of library if not message  
	//is registered with local extension agent.
	private boolean isMessageSupported(Message messageToSend)
	{
		Message objMessage = objMessageFactory.createSendSupportedMessage(messageToSend.getMessageCode().toString(), messageToSend.getTriggerEvent().toString());
		if(messageToSend.getClass().getName() == objMessage.getClass().getName())
			return true;
		else if(objMessageFactory.getLocalExtensionAgent()!=null)
		{
			String strMsgQualifiedName = objMessageFactory.getLocalExtensionAgent().getMsgQualifiedName(messageToSend.getMessageCode().toString(), messageToSend.getTriggerEvent().toString());
			if(strMsgQualifiedName!=null)
				return strMsgQualifiedName.equals(objMessage.getClass().getName());
		}

		return false;
	}
}
