package cdac.medinfo.sdk.hl7282.hl7net.auxiliaryprotocol.interactivecontinuation;

import java.util.Locale;

import cdac.medinfo.sdk.hl7282.baselibs.segments.DSC;
import cdac.medinfo.sdk.hl7282.baselibs.segments.MSA;
import cdac.medinfo.sdk.hl7282.baselibs.segments.MSH;
import cdac.medinfo.sdk.hl7282.baselibs.segments.QAK;
import cdac.medinfo.sdk.hl7282.baselibs.segments.QID;
import cdac.medinfo.sdk.hl7282.baselibs.segments.QPD;
import cdac.medinfo.sdk.hl7282.baselibs.segments.RCP;
import cdac.medinfo.sdk.hl7282.baselibs.segments.SegmentSet;
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.hl7net.commons.IClientInteractiveContinuation;
import cdac.medinfo.sdk.hl7282.utils.EnumMessageCode;
import cdac.medinfo.sdk.hl7282.utils.EnumSegments;
import cdac.medinfo.sdk.hl7282.utils.HL7Config;
import cdac.medinfo.sdk.hl7282.utils.HL7Logger;
import cdac.medinfo.sdk.hl7282.utils.IMultiValuedPairedCollection;
import cdac.medinfo.sdk.hl7282.utils.IPairedCollection;
import cdac.medinfo.sdk.hl7282.utils.MultiValuedPairedCollection;
import cdac.medinfo.sdk.hl7282.utils.PairedCollection;
import cdac.medinfo.sdk.hl7282.utils.ResourceBundleWrapper;
import cdac.medinfo.sdk.hl7282.utils.SingleCollection;

/**
 * This class provides the re-structure of the Message, if user has set the Agent.
 * This agent class tracks all the messages.
 */
public class ClientSideQICAgent implements IClientInteractiveContinuation
{
	//Used to store Query ID and Continuation Pointer
	private IPairedCollection<String, String> objQueryTagCollection = null;

	//used to retreive the segments for Interactive query. 
	private IMultiValuedPairedCollection<String, ISegment> objQueryTagSegmentsCollection = null;

	//Used to store Message Control ID
	private ISingleCollection<String> objMSHControlIDCollection = null;

	//Used to store Query ID and Message Control ID
	private IMultiValuedPairedCollection<String, String> objMultiValuedPairedContinuationCollection = null;

	//Used to store Query ID and its status
	private IPairedCollection<String, String> objSinglePairStatus = null;

	/**
	 * Instantiates QICAgent
	 */
	public ClientSideQICAgent()
	{
		objQueryTagCollection = new PairedCollection<String, String>();
		objMSHControlIDCollection = new SingleCollection<String>();
		objMultiValuedPairedContinuationCollection = new MultiValuedPairedCollection<String, String>();
		objSinglePairStatus = new PairedCollection<String, String>();
		objQueryTagSegmentsCollection = new MultiValuedPairedCollection<String, ISegment>();
	}
	public ISegmentSet generateCancellationQuery(String strQueryId)
	{
		if(strQueryId!=null && objQueryTagSegmentsCollection.contains(strQueryId))
		{
			QPD objQPDSegment =null;
			QID objQIDSegment =null;
			ISingleCollection<ISegment> objSegmentCollection = objQueryTagSegmentsCollection.get(strQueryId);
			if(objSegmentCollection!= null && objSegmentCollection.size()>0)
			{
				ISegmentSet objSegmentSetPopulated = new SegmentSet();
				int iSegSetSize = objSegmentCollection.size();
				boolean isMessageHeaderSet = false;
				for(int iRead=0;iRead<iSegSetSize;iRead++)
				{
					ISegment objSegment = null;
					objSegment = objSegmentCollection.elementAt(iRead);
					if(objSegment == null)
						throw new InvalidMessageException(this.getClass().getSimpleName());
					EnumSegments enumSegments = (EnumSegments) objSegment.getName();
					switch(enumSegments)
					{
					case HL7_SEG_MSH:
						if(false == isMessageHeaderSet)
						{
							MSH objMSHSegment = (MSH)objSegment;
							MSH objMSH = new MSH();
							objMSH.setFieldSeparator(objMSHSegment.getFieldSeparator());
							objMSH.setEncodingChars(objMSHSegment.getEncodingChar());
							objMSH.setDateTimeOfMessage(objMSHSegment.getDateTimeOfMessage());
							objMSH.setMessageType("QCN","J01","QCN_J01");
							objMSH.setMessageControlID(objMSHSegment.getMessageControlID());
							objMSH.setProcessingID(objMSHSegment.getProcessingID());
							objMSH.setVersionID(objMSHSegment.getVersionID());
							
							objSegmentSetPopulated.addSegment(objMSH);
							isMessageHeaderSet = true;
						}
						else
							throw new InvalidMessageException(this.getClass().getSimpleName());
						break;
					case HL7_SEG_QPD:
						objQPDSegment = (QPD)objSegment;
						objQIDSegment = new QID();
						objQIDSegment.setQueryTag(strQueryId);
						objQIDSegment.setMessageQueryName(objQPDSegment.getMessageQueryName());
						objSegmentSetPopulated.addSegment(objQIDSegment);
						break;
					}
				}
				return objSegmentSetPopulated;
			}
		}
		else if(HL7Config.createInstance().isLoggingOn())
		{
			HL7Logger objHL7Logger = HL7Logger.getLogger();
			String strLogMessage = ResourceBundleWrapper.getValue("exceptions","QIC_QueryIdNotPresent",new Locale(HL7Config.createInstance().getLocale()));
			objHL7Logger.log(strLogMessage);
		}
		return null;
	}

	/**
	 * Retrieves the number of query fired for specified Query ID.
	 * @param strQueryID Query ID of an Query
	 * @return ISegmentSet object of Segment Set. 
	 */
	public ISegmentSet generateInteractiveQuery(String strQueryId) 
	{
		if(strQueryId!=null && objQueryTagSegmentsCollection.contains(strQueryId))
		{
			ISingleCollection<ISegment> objSegmentCollection = objQueryTagSegmentsCollection.get(strQueryId);
			if(objSegmentCollection!= null && objSegmentCollection.size()>0)
			{
				DSC objDSC =null;
				ISegmentSet objSegmentSetPopulated = new SegmentSet();
				int iSegSetSize = objSegmentCollection.size();
				boolean isMessageHeaderSet = false;
				for(int iRead=0;iRead<iSegSetSize;iRead++)
				{
					ISegment objSegment = null;
					objSegment = objSegmentCollection.elementAt(iRead);
					if(objSegment == null)
						throw new InvalidMessageException(this.getClass().getSimpleName());
					EnumSegments enumSegments = (EnumSegments) objSegment.getName();
					switch(enumSegments)
					{
					case HL7_SEG_MSH:
						if(false == isMessageHeaderSet)
						{
							objSegmentSetPopulated.addSegment(objSegment);
							isMessageHeaderSet = true;
						}
						else
							throw new InvalidMessageException(this.getClass().getSimpleName());
						break;
					case HL7_SEG_DSC:
						objDSC = (DSC)objSegment;
						if(objQueryTagCollection.get(strQueryId)!=null)
							objDSC.setContinuationPointer(objQueryTagCollection.get(strQueryId));
						else 
						{
							if(HL7Config.createInstance().isLoggingOn())
							{
								HL7Logger objHL7Logger = HL7Logger.getLogger();
								String strLogMessage = ResourceBundleWrapper.getValue("exceptions","QIC_ContinuationPointerNull",new Locale(HL7Config.createInstance().getLocale()+strQueryId));
								objHL7Logger.log(strLogMessage);
							}
						}
						objDSC.setContinuationStyle("I");
						objSegmentSetPopulated.addSegment(objDSC);
						break;
					default:
						objSegmentSetPopulated.addSegment(objSegment);
					}
				}
				if(objDSC==null)
				{
					objDSC = new DSC();
					if(objQueryTagCollection.get(strQueryId)!=null)
						objDSC.setContinuationPointer(objQueryTagCollection.get(strQueryId));
					else
						objDSC.setContinuationPointer("77");
					objDSC.setContinuationStyle("I");
					objSegmentSetPopulated.addSegment(objDSC);
				}
				return objSegmentSetPopulated;
			}
		}else if(HL7Config.createInstance().isLoggingOn())
		{
			HL7Logger objHL7Logger = HL7Logger.getLogger();
			String strLogMessage = ResourceBundleWrapper.getValue("exceptions","QIC_QueryIdNotPresent",new Locale(HL7Config.createInstance().getLocale()));
			objHL7Logger.log(strLogMessage);
		}
		return null;
	}

	/**
	 * Keeps the logs of incoming Interactive Responses.
	 * @param objSegmentSet object of Segment Set received.
	 * @return boolean - true if response is interactive else false. 
	 */
	public boolean processReceive(ISegmentSet objSegmentSet) 
	{
		QPD objQuerySegment = null;
		DSC objDSCSegment = null;
		MSA objMSASegment = null;
		QAK objQAKSegment = null;
		String strQueryTag = null;
		String strContinuationPointer =null;

		ISingleCollection<ISegment> objSegmentCollection = objSegmentSet.getList();
		if(objSegmentCollection!= null && objSegmentCollection.size()>0)
		{
			int iSegSetSize = objSegmentCollection.size();
			boolean isMessageHeaderSet = false;
			for(int iRead=0;iRead<iSegSetSize;iRead++)
			{
				ISegment objSegment = null;
				objSegment = objSegmentCollection.elementAt(iRead);
				if(objSegment == null)
					throw new InvalidMessageException(this.getClass().getSimpleName());
				EnumSegments enumSegments = (EnumSegments) objSegment.getName();
				switch(enumSegments)
				{
				case HL7_SEG_MSH:
					if(false == isMessageHeaderSet)
						isMessageHeaderSet = true;
					else
						throw new InvalidMessageException(this.getClass().getSimpleName());
					break;
				case HL7_SEG_QPD:
					objQuerySegment= (QPD)objSegment;
					break;
				case HL7_SEG_MSA:
					objMSASegment = (MSA)objSegment;
					break;
				case HL7_SEG_DSC:
					objDSCSegment = (DSC)objSegment;
					break;
				case HL7_SEG_QAK:
					objQAKSegment = (QAK)objSegment;
				}
			}
		}
		if (objQAKSegment!=null && isDeferred((String)objQAKSegment.getQueryTag().getValue()))
		{
			if(null != objMSASegment && objMSHControlIDCollection.contains((String)(objMSASegment).getMessageControlID().getValue()))
			{
				if(objMSASegment.getMessageControlID() != null)
				{
					strQueryTag = (String)objQuerySegment.getQueryTag().getValue();
					if(objQueryTagCollection.contains(strQueryTag))
					{
						//Is response is the last response
						if(objDSCSegment == null)
						{
							objSinglePairStatus.put(strQueryTag, EnumSinglePairStatus.COMPLETED.getValue());
							objQueryTagCollection.remove(strQueryTag);
							ISingleCollection<String> objMsgControlIDtobeRemoved = new SingleCollection<String>();
							objMsgControlIDtobeRemoved = objMultiValuedPairedContinuationCollection.get(strQueryTag);
							for(String strMsgControlIDtobeRemoved : objMsgControlIDtobeRemoved)
								objMSHControlIDCollection.remove(strMsgControlIDtobeRemoved);
							objMultiValuedPairedContinuationCollection.remove(strQueryTag);
							objQueryTagSegmentsCollection.remove(strQueryTag); 
						}
						else if(EnumSegments.HL7_SEG_DSC.equals(objDSCSegment.getName()) && ((String)(objDSCSegment).getContinuationStyle().getValue()).equalsIgnoreCase("I"))
						{
							strContinuationPointer = ((String)(objDSCSegment).getContinuationPointer().getValue()); 
							objSinglePairStatus.put(strQueryTag, EnumSinglePairStatus.INPROGRESS.getValue());
							objQueryTagCollection.put(strQueryTag, strContinuationPointer);
						}

						//Status logging
						if(HL7Config.createInstance().isLoggingOn())
						{
							String strSendingMessage = ResourceBundleWrapper.getValue("exceptions", "QIC_RecievedMessage",new Locale(HL7Config.createInstance().getLocale())) + strQueryTag;
							HL7Logger objHLogger = HL7Logger.getLogger();
							objHLogger.log(strSendingMessage);
						}
						return true;
					}
				}
			}
		}
		return false;
	}

	/**
	 * Keeps the logs of outgoing Interactive Queries. 
	 * @param objSegmentSet object of Segment Set received.
	 */
	public void processSend(ISegmentSet objSegmentSet) 
	{
		MSH objMSHSegment = null;
		RCP objRCPSegment =null;
		DSC objDSCSegment =null;
		QPD objQPDSegment = null;
		String strQueryID = null;
		ISingleCollection<ISegment> objSegmentCollection = objSegmentSet.getList();
		if(objSegmentCollection!= null && objSegmentCollection.size()>0)
		{
			int iSegSetSize = objSegmentCollection.size();
			boolean isMessageHeaderSet = false;
			for(int iRead=0;iRead<iSegSetSize;iRead++)
			{
				ISegment objSegment = null;
				objSegment = objSegmentCollection.elementAt(iRead);
				if(objSegment == null)
					throw new InvalidMessageException(this.getClass().getSimpleName());
				EnumSegments enumSegments = (EnumSegments) objSegment.getName();
				switch(enumSegments)
				{
				case HL7_SEG_MSH:
					if(false == isMessageHeaderSet)
					{
						objMSHSegment = (MSH)objSegment;
						isMessageHeaderSet = true;
					}
					else
						throw new InvalidMessageException(this.getClass().getSimpleName());
					break;
				case HL7_SEG_QPD:
					objQPDSegment= (QPD)objSegment;
					strQueryID =(String)objQPDSegment.getQueryTag().getValue();
					break;
				case HL7_SEG_QID:
					QID objQIDSegment = (QID)objSegment;
					strQueryID =(String)objQIDSegment.getQueryTag().getValue();
					break;
				case HL7_SEG_RCP:
					objRCPSegment = (RCP)objSegment;
					break;
				case HL7_SEG_DSC:
					objDSCSegment = (DSC)objSegment;
					break;
				}
			}
		}
		if(((String)objMSHSegment.getMessageType().getMessageCode().getValue()).equalsIgnoreCase(EnumMessageCode.QCN.getValue()))
		{
			if(objQueryTagCollection.contains(strQueryID))
			{
				setStatus(strQueryID, -1);
				objQueryTagCollection.remove(strQueryID);
				ISingleCollection<String> objMsgControlIDtobeRemoved = new SingleCollection<String>();
				objMsgControlIDtobeRemoved = objMultiValuedPairedContinuationCollection.get(strQueryID);
				for(String strMsgControlIDtobeRemoved : objMsgControlIDtobeRemoved)
				{
					objMSHControlIDCollection.remove(strMsgControlIDtobeRemoved);
				}
				objMultiValuedPairedContinuationCollection.remove(strQueryID);
				if(HL7Config.createInstance().isLoggingOn())
				{
					HL7Logger objHL7Logger = HL7Logger.getLogger();
					String strLogMessage = ResourceBundleWrapper.getValue("exceptions","QC_QueryCancelled",new Locale(HL7Config.createInstance().getLocale()))+strQueryID;
					objHL7Logger.log(strLogMessage);
				}

				//Status logging
				if(HL7Config.createInstance().isLoggingOn())
				{
					String strRecievedMessage = ResourceBundleWrapper.getValue("exceptions", "QIC_SendingMessage",new Locale(HL7Config.createInstance().getLocale())) + strQueryID;
					HL7Logger objHLogger = HL7Logger.getLogger();
					objHLogger.log(strRecievedMessage);
				}
			}
		}
		else if(getStatus(strQueryID))
		{
			if(objRCPSegment != null)
			{
				if(null != ((RCP)objRCPSegment).getQuantityLimitedRequest())
				{
					if(objDSCSegment == null)
					{
						objQueryTagCollection.put((String)objQPDSegment.getQueryTag().getValue(), null);
						objQueryTagSegmentsCollection.put(strQueryID, objSegmentCollection);	
					}
					else
					{
						String strContinuationPointer = ((String)(objDSCSegment).getContinuationPointer().getValue());
						objSinglePairStatus.put((String)objQPDSegment.getQueryTag().getValue(), EnumSinglePairStatus.INPROGRESS.getValue());
						objQueryTagCollection.put((String)objQPDSegment.getQueryTag().getValue(), strContinuationPointer);
					}
					objMSHControlIDCollection.add((String)objMSHSegment.getMessageControlID().getValue());
					objMultiValuedPairedContinuationCollection.put((String)objQPDSegment.getQueryTag().getValue(), (String)objMSHSegment.getMessageControlID().getValue());

					//Status logging
					if(HL7Config.createInstance().isLoggingOn())
					{
						String strRecievedMessage = ResourceBundleWrapper.getValue("exceptions", "QIC_SendingMessage",new Locale(HL7Config.createInstance().getLocale())) + strQueryID;
						HL7Logger objHLogger = HL7Logger.getLogger();
						objHLogger.log(strRecievedMessage);
					}
				}
			}
		}
		else
			throw new InvalidMessageException(ResourceBundleWrapper.getValue("exceptions", "QIC_InteractiveQueryAlreadyEnded", new Locale(HL7Config.createInstance().getLocale()))+strQueryID);
	}

	// Sets Status for specified query.
	// @param queryToSend Specified interactive query.
	// @param Status Define Status of Queries.
	private void setStatus(String strQueryID, int Status)
	{
		String enumStatus = null;
		if (strQueryID != null)
		{
			if(Status == -1)
				enumStatus = EnumSinglePairStatus.CANCELLED.getValue();
			else if(Status == 0)
				enumStatus = EnumSinglePairStatus.INPROGRESS.getValue();
			else
				enumStatus = EnumSinglePairStatus.COMPLETED.getValue();
			objSinglePairStatus.put(strQueryID, enumStatus);
		}
	}

	//Retrieves Status of specified interactive query.
	//@param messageToSend Specified the Interactive Query.
	//@return boolean - return true, if interactive query is Inprogress else false, if interactive query is Canceled or Completed.
	//@throws InvalidMessageException - Thrown When Message Populated is Invalid.
	private boolean getStatus(String strQueryID)
	{
		if(objSinglePairStatus.contains(strQueryID))
		{
			String StatusValue = objSinglePairStatus.get(strQueryID);
			if(StatusValue == EnumSinglePairStatus.INPROGRESS.getValue())
				return true;
			else if(StatusValue == EnumSinglePairStatus.COMPLETED.getValue() || StatusValue == EnumSinglePairStatus.CANCELLED.getValue())
				return false;
		}
		return true;
	}


	//Check whether specified Query ID of an Query is Deferred or not. 
	//@param strQueryID Query ID of an Query
	//@return boolean - true, if Query is deferred else false.
	private boolean isDeferred(String strQueryID)
	{
		if(objQueryTagCollection.contains(strQueryID))
			return true;
		return false;
	}
	/**
	 * This will return the String array from the Collection which are unprocessed. 
	 * @return ISingleCollection<String> Collection of Response Id which are NotCompleted status. 
	 */
	public ISingleCollection<String> getUnprocessedQueries() 
	{
		ISingleCollection<String> objQueryCollection =null;
		Object[] objStringCollection =  objQueryTagCollection.getKeys();
		if(objStringCollection!=null)
		{
			objQueryCollection = new SingleCollection<String>();
			for(Object objectString:objStringCollection)
			{
				String strqueryID =(String)objectString;
				objQueryCollection.add(strqueryID);
			}
		}
		return objQueryCollection;
	}
}