/*******************************************************************************
 *  Copyright 2017 Centre for Development of Advanced Computing(C-DAC), Pune
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *    http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *******************************************************************************/
package cdac.medinfo.sdk.hl7282.baselibs.messages;

import java.util.Locale;

import cdac.medinfo.sdk.hl7282.baselibs.messagedictionary.MessageMapReader;
import cdac.medinfo.sdk.hl7282.baselibs.segments.MSH;
import cdac.medinfo.sdk.hl7282.baselibs.segments.Segment;
import cdac.medinfo.sdk.hl7282.commoninterface.ISegment;
import cdac.medinfo.sdk.hl7282.commoninterface.ISegmentSet;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidMessageException;
import cdac.medinfo.sdk.hl7282.exceptions.MessageNotSupportedException;
import cdac.medinfo.sdk.hl7282.exceptions.SegmentNotSupportedException;
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.IMap;
import cdac.medinfo.sdk.hl7282.utils.IPairedCollection;
import cdac.medinfo.sdk.hl7282.utils.PairedCollection;
import cdac.medinfo.sdk.hl7282.utils.ResourceBundleWrapper;

/**
 * LocalExtensionAgent works as a factory for locally extended messages and segments. 
 * Locally extended message or segment should follow the structure constraints defined by HL7 standard.
 * Every modified, extended or a new z-message or z-segment must register itself with LocalExtensionAgent.
 */
public final class LocalExtensionAgent
{
	

	private IPairedCollection<String,String> objRegisteredMsgList = new PairedCollection<String,String>();
	
	private IPairedCollection<String,String> objRegisteredSegmentList = new PairedCollection<String, String>();

	/**
	 * Retrieves the Fully Qualified Name of Locally Extended Message.
	 * @param strMsgCode value of message code
	 * @param strTriggerEvent value of trigger event
	 * @return String - value of Fully Qualified Name for Locally Extended Message
	 */
	public String getMsgQualifiedName(String strMsgCode,String strTriggerEvent) 
	{
		String strKey = strMsgCode + strTriggerEvent;
		return objRegisteredMsgList.get(strKey);
	}
	
	/**
	 * Retrieves the Fully Qualified Name of Locally Extended Segment.
	 * @param strSegmentName name of segment
	 * @return String - value of Fully Qualified Name for Locally Extended Message
	 */
	public String getSegmentQualifiedName(String strSegmentName) 
	{
		return objRegisteredSegmentList.get(strSegmentName);
	}

	/**
	 * Checks whether message with provided code and trigger event is registered as locally extended message.
	 * @param strMsgCode value of message code
	 * @param strTriggerEvent value of trigger event
	 * @return boolean - shows status as success or failure 
	 */
	public boolean isMsgLocallyRegistered(String strMsgCode,String strTriggerEvent) 
	{
		String strKey = strMsgCode + strTriggerEvent;
		return objRegisteredMsgList.contains(strKey);
	}
	
	/**
	 * Checks whether segment is registered as locally extended segment.
	 * @param strSegmentName name of segment
	 * @return boolean - shows status as success or failure 
	 */
	public boolean isSegmentLocallyRegistered(String strSegmentName) 
	{
		return objRegisteredSegmentList.contains(strSegmentName);
	}

	/**
	 * Registers message with provided code and trigger event as locally extended message.
	 * @param strMsgCode value of message code
	 * @param strTriggerEvent value of trigger event
	 * @param strQualifiedName value of Fully Qualified Name for Locally Extended Message
	 */
	public void registerMessage(String strMsgCode,String strTriggerEvent, String strQualifiedName)
	{
		String strKey = strMsgCode + strTriggerEvent;
		objRegisteredMsgList.put(strKey, strQualifiedName);
	}
	
	/**
	 * Registers segment with provided segment name as locally extended segment.
	 * @param strSegmentName name of segment
	 * @param strQualifiedName value of Fully Qualified Name for Locally Extended Segment
	 */
	public void registerSegment(String strSegmentName, String strQualifiedName)
	{
		objRegisteredSegmentList.put(strSegmentName, strQualifiedName);
	}
	
	/**
	 * Creates Empty Message with Message Structure & Trigger Event specified.
	 * @param strMessageCode specified message code
	 * @param strTriggerEvent specified trigger event
	 * @return Message - Generated Empty Message
	 */
	public Message createMessage(String strMessageCode,String strTriggerEvent)
	{
		Message objMessage = null;
		try
		{
			if(isMsgLocallyRegistered(strMessageCode,strTriggerEvent))
			{
				String strAssemblyInfo = getMsgQualifiedName(strMessageCode,strTriggerEvent);
				objMessage = (Message)Class.forName(strAssemblyInfo).newInstance();
			}
			else if(EnumMessageCode.ACK.getValue().equals(strMessageCode))
			{
				IMap objMsgMap = MessageMapReader.createInstance().getMessageMap(EnumMessageCode.ACK,null);
				objMessage = new ACK(strTriggerEvent,objMsgMap);
			}
		}
		catch(ClassNotFoundException cfe)
		{
			throw new MessageNotSupportedException(cfe.getMessage(),cfe);
		}
		catch(InstantiationException ie)
		{
			throw new MessageNotSupportedException(ie.getMessage(),ie);
		}
		catch(IllegalAccessException iae)
		{
			throw new MessageNotSupportedException(iae.getMessage(),iae);
		}
		
		return objMessage;
	}
	
	/**
	 * Creates empty Segment for specified locally extended segment.
	 * @param strSegmentName name of segment
	 * @return ISegment - Generated Empty Segment
	 */
	public ISegment createSegment(String strSegmentName)
	{
		ISegment objSegment = null;
		try
		{
			if(isSegmentLocallyRegistered(strSegmentName))
			{
				String strAssemblyInfo = getSegmentQualifiedName(strSegmentName);
				objSegment = (ISegment)Class.forName(strAssemblyInfo).newInstance();
			}
		}
		catch(ClassNotFoundException cfe)
		{
			throw new SegmentNotSupportedException(cfe.getMessage(),cfe);
		}
		catch(InstantiationException ie)
		{
			throw new SegmentNotSupportedException(ie.getMessage(),ie);
		}
		catch(IllegalAccessException iae)
		{
			throw new SegmentNotSupportedException(iae.getMessage(),iae);
		}
		return objSegment;
	}

	/**
	 * Populates a locally extended message.
	 * @param objSegmentSet instance of SegmentSet 
	 * @return Message - the Message present in this message or null if unavailable
	 * @throws MessageNotSupportedException if message is not supported or loading of class fails
	 * @throws InvalidMessageException If Message validation fails
	 */
	public Message populateMessage(ISegmentSet objSegmentSet) throws MessageNotSupportedException,InvalidMessageException
	{
		Segment objTempSegment = (Segment)objSegmentSet.getSegment(0);
		String strMsgCode = null;
		String strTriggerEvent = null;
		if(objTempSegment.getName().equals(EnumSegments.HL7_SEG_MSH))
		{
			MSH objMSH = (MSH)objTempSegment;
			strTriggerEvent = (String)objMSH.getMessageType().getTriggerEvent().getValue();
			strMsgCode = (String)objMSH.getMessageType().getMessageCode().getValue();
		}

		return (populateMessage(strMsgCode,strTriggerEvent, objSegmentSet));
	}

	/**
	 * Populates a locally extended message with provided message code and trigger event.
	 * @param strMessageCode value of message code
	 * @param strTriggerEvent value of trigger event
	 * @param objSegmentSet instance of SegmentSet 
	 * @return Message - the Message present in this message or null if unavailable
	 * @throws MessageNotSupportedException if message is not supported or loading of class fails
	 * @throws InvalidMessageException If Message validation fails
	 */
	public Message populateMessage(String strMessageCode,String strTriggerEvent,ISegmentSet objSegmentSet) throws MessageNotSupportedException, InvalidMessageException
	{
		Message objMessage = null;
		try
		{
			if(isMsgLocallyRegistered(strMessageCode,strTriggerEvent))
			{
				String strAssemblyInfo = getMsgQualifiedName(strMessageCode,strTriggerEvent);
				objMessage = (Message)Class.forName(strAssemblyInfo).newInstance();
				objMessage.populate(objSegmentSet);
			}
			else if(EnumMessageCode.ACK.getValue().equals(strMessageCode))
			{
				IMap objMsgMap = MessageMapReader.createInstance().getMessageMap(EnumMessageCode.ACK,null);
				objMessage = new ACK(strTriggerEvent,objMsgMap);
				objMessage.populate(objSegmentSet);
			}

			//Status logging
			if(HL7Config.createInstance().isLoggingOn())
			{
				String strSendingMessage = ResourceBundleWrapper.getValue("exceptions", "LocalExtensionPopulation",new Locale(HL7Config.createInstance().getLocale())) + strMessageCode + "_" + strTriggerEvent;
				HL7Logger objHLogger = HL7Logger.getLogger();
				objHLogger.log(strSendingMessage);
			}
		}
		catch(ClassNotFoundException cfe)
		{
			throw new MessageNotSupportedException(cfe.getMessage(),cfe);
		}
		catch(InstantiationException ie)
		{
			throw new MessageNotSupportedException(ie.getMessage(),ie);
		}
		catch(IllegalAccessException iae)
		{
			throw new MessageNotSupportedException(iae.getMessage(),iae);
		}
		return objMessage;
	}
}
