/*******************************************************************************
 *  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.hl7model.messages.medicalrecords;

import cdac.medinfo.sdk.hl7282.baselibs.ID;
import cdac.medinfo.sdk.hl7282.baselibs.ValueTables;
import cdac.medinfo.sdk.hl7282.baselibs.messages.Message;
import cdac.medinfo.sdk.hl7282.baselibs.segments.ConsentSeg;
import cdac.medinfo.sdk.hl7282.baselibs.segments.EVN;
import cdac.medinfo.sdk.hl7282.baselibs.segments.PID;
import cdac.medinfo.sdk.hl7282.baselibs.segments.PV1;
import cdac.medinfo.sdk.hl7282.baselibs.segments.SFT;
import cdac.medinfo.sdk.hl7282.baselibs.segments.TQ1;
import cdac.medinfo.sdk.hl7282.baselibs.segments.TXA;
import cdac.medinfo.sdk.hl7282.baselibs.segments.UAC;
import cdac.medinfo.sdk.hl7282.commoninterface.IGroup;
import cdac.medinfo.sdk.hl7282.commoninterface.ISegment;
import cdac.medinfo.sdk.hl7282.commoninterface.ISingleCollection;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidMessageException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidSegmentException;
import cdac.medinfo.sdk.hl7282.exceptions.SegmentMissingException;
import cdac.medinfo.sdk.hl7282.utils.EnumSegments;
import cdac.medinfo.sdk.hl7282.utils.HL7Constants;
import cdac.medinfo.sdk.hl7282.utils.IMap;

/**
 * MDM_T02 - Original Document Notification and Content(Event T02).
 * This even sent a notification of the creation of a document with the accompanying content.
 * <p>Represents a MDM_T02 message structure (see chapter 9.6.2). This structure contains the 
 * following elements: </p>
 * 0: MSH (Message Header Segment) <b>SINGLE_COMPULSARY</b><br>
 * 1: SFT (Software Segment) <b>MULTIPLE_OPTIONAL</b><br>
 * 2: UAC (User Authentication Credential) <b>SINGLE OPTIONAL</b><br/>
 * 3: EVN (Event Type Segment) <b>SINGLE_COMPULSARY</b><br>
 * 4: PID (Patient Identification Segment) <b>SINGLE_COMPULSARY</b><br>
 * 5: PV1 (Patient Visit Segment) <b>SINGLE_COMPULSARY</b><br>
 * 6: COMMON_ORDER (a Group of segment in MDM_T02 Message Structure) <b>MULTIPLE_OPTIONAL</b><br>
 * 7: TXA (Transcription Document Header Segment) <b>SINGLE_COMPULSARY</b><br>
 * 8: CON (Consent Segment) <b>MULTIPLE_OPTIONAL</b><br>
 * 9: OBSERVATION (a Group of segment in MDM_T02 Message Structure) <b>MULTIPLE_COMPULSARY</b><br>
 * This class provides mechanism to serialize, parse and to validate message against HL7 v 2.8.2 standards.
 * The class provides accessor & mutators methods for the elements mentioned above.
 * These elements can be easily populated & populated values can be easily retrieved.
 * The class has validate method which should be used to validate Message against HL7 v 2.8.2 standards.
 * It provides user with serialize method to serialize message object to file & 
 * parse method to obtain fully populated & validated Message object from file. 
 *
 * <p>Following is the step to create message<br>
 * Before that, respective Message Source and Message Recipient to be set on Message Factory.</p>
 * <p>MDM_T02 objT02 = (MDM_T02)MessageFactory.createInstance().createSendSupportedMessage("MDM", "T02");</p>
 */
public abstract class MDM_T02_Common extends Message
{
	/**
	 *  Constructs the ADT_A01 message.
	 */
	MDM_T02_Common(IMap objMessageMap, String strTriggerEvent, String strMessageCode, String strMessageStructure)
	{
		super(objMessageMap, strTriggerEvent, strMessageCode, strMessageStructure);
	}
	
	/**
	 * Retrieves Collection of Software Segments(SFT) present in this message.
	 * @return ISingleCollection<SFT> - Collection of Software Segments(SFT) present in this message.
	 */
	@SuppressWarnings("unchecked")
	public ISingleCollection<SFT> getSoftwareSegments()
	{
		ISingleCollection<?> objSegmentCollection = getSegment(EnumSegments.HL7_SEG_SFT);
		return (ISingleCollection<SFT>)objSegmentCollection;
	}

	/**
	 * Adds Software Segment(SFT) to this message.
	 * @param objSFT Specified Software Segment(SFT) to be added in this messageto this message.
	 */
	public void addSoftwareSegment(SFT objSFT)
	{
		addSegment(EnumSegments.HL7_SEG_SFT, objSFT);
	}

	/**
	 * Adds collection of Software Segment(SFT) to this message.
	 * @param objSFTSegmentCollection Collection of Software Segments to add in this message
	 */
	@SuppressWarnings("unchecked")
	public void addSoftwareSegment(ISingleCollection<SFT>  objSFTSegmentCollection)
	{
		ISingleCollection<?> objSegmentCollection =  objSFTSegmentCollection;
		addSegment(EnumSegments.HL7_SEG_SFT, (ISingleCollection<ISegment>)objSegmentCollection);
	}

	/**
	 * Retrieves User Authentication Credential(UAC) segment present in this message
	 * @return UAC - Specified UAC Segment
	 */
	public UAC getUserAuthenticationCredentialSegment()
	{
		ISingleCollection<ISegment> objSingleCollection = getSegment(EnumSegments.HL7_SEG_UAC);
		if(objSingleCollection != null)
			return (UAC)objSingleCollection.elementAt(0);
		return null;
	}

	/**
	 * Sets value of User Authentication Credential segment for this message,overwrites if already present.
	 * @param objUAC Specified UAC Segment
	 */
	public void setUserAuthenticationCredentialSegment(UAC objUAC)
	{
		setSegment(EnumSegments.HL7_SEG_UAC,objUAC);
	}


	/**
	 * Retrieves Event Type(EVN) segment present in this message
	 * @return EVN - the EVN Segment present in this message or null if unavailable.
	 */
	public EVN getEventTypeSegment()
	{
		ISingleCollection<ISegment> objSingleCollection = getSegment(EnumSegments.HL7_SEG_EVN);
		if(objSingleCollection != null)
			return (EVN)objSingleCollection.elementAt(0);
		return null;
	}

	/**
	 * Sets value of Event Type segment for this message,overwrites if already present.
	 * @param objEVN Specified EVN Segment 
	 */
	public void setEventTypeSegment(EVN objEVN)
	{
		setSegment(EnumSegments.HL7_SEG_EVN,objEVN);
	}

	/**
	 * Retrieves Patient Identification(PID) segment present in this message.
	 * @return PID - the Patient Identification Segment present in this message or null if unavailable.
	 */
	public PID getPatientIdentificationSegment()
	{
		ISingleCollection<ISegment> objSingleCollection = getSegment(EnumSegments.HL7_SEG_PID);
		if(objSingleCollection != null)
			return (PID)objSingleCollection.elementAt(0);
		return null;
	}

	/**
	 * Sets value of PID segment for this message,overwrites if already present.
	 * @param objPID Specified PID Segment 
	 */
	public void setPatientIdentificationSegment(PID objPID)
	{
		setSegment(EnumSegments.HL7_SEG_PID,objPID);
	}

	/**
	 * Retrieves Patient Visit(PV1) segment present in this message.
	 * @return PV1  - Patient Visit Segment present in this message or null if unavailable.
	 */
	public PV1 getPatientVisitSegment()
	{
		ISingleCollection<ISegment> objSingleCollection = getSegment(EnumSegments.HL7_SEG_PV1);
		if(objSingleCollection != null)
			return (PV1)objSingleCollection.elementAt(0);
		return null;
	}

	/**
	 * Sets value of PV1  segment for this message,overwrites if already present.
	 * @param objPV1  Specified PV1  Segment
	 */
	public void setPatientVisitSegment(PV1  objPV1)
	{
		setSegment(EnumSegments.HL7_SEG_PV1, objPV1);
	}

	/**
	 * Retrieves Collection of Common Order GROUP present in this message.
	 * @return ISingleCollection<Group> - Collection of Common Order Group present in this Message.
	 */
	public ISingleCollection<IGroup> getCommonOrderGroups()
	{
		return getGroup(EnumSegments.HL7_GROUP_COMMON_ORDER);
	}

	/**
	 * Adds Collection of Common Order GROUP to this message.
	 * @param objSingleCollection Specified  Common Order GROUP Collection.
	 */
	public void addCommonOrderGroups(ISingleCollection<IGroup> objSingleCollection)
	{
		addGroup(EnumSegments.HL7_GROUP_COMMON_ORDER, objSingleCollection);
	}

	/**
	 * Adds Common Order GROUP to this message.
	 * @param objGroup Specified Common Order GROUP. 
	 */
	public void addCommonOrderGroup(IGroup objGroup)
	{
		addGroup(EnumSegments.HL7_GROUP_COMMON_ORDER, objGroup);
	}

	/**
	 * Retrieves Document Notification(TXA) segment present in this message.
	 * @return TXA  - Document Notification Segment present in this message or null if unavailable.
	 */
	public TXA getDocumentNotificationSegment()
	{
		ISingleCollection<ISegment> objSingleCollection = getSegment(EnumSegments.HL7_SEG_TXA);
		if(objSingleCollection != null)
			return (TXA)objSingleCollection.elementAt(0);
		return null;
	}

	/**
	 * Sets value of TXA  segment for this message,overwrites if already present.
	 * @param objTXA  Specified Document Notification  Segment
	 */
	public void setDocumentNotificationSegment(TXA  objTXA)
	{
		setSegment(EnumSegments.HL7_SEG_TXA, objTXA);
	}

	/**
	 * Retrieves Collection of Consent Segments(CON) present in this message.
	 * @return ISingleCollection<SFT> - Collection of Consent Segments(CON) present in this message.
	 */
	@SuppressWarnings("unchecked")
	public ISingleCollection<ConsentSeg> getConsentSegments()
	{
		ISingleCollection<?> objSegmentCollection = getSegment(EnumSegments.HL7_SEG_CON);
		return (ISingleCollection<ConsentSeg>)objSegmentCollection;
	}

	/**
	 * Adds Consent Segment(CON) to this message.
	 * @param objCON Specified Consent Segment(CON) to be added in this messageto this message.
	 */
	public void addConsentSegment(ConsentSeg objCON)
	{
		addSegment(EnumSegments.HL7_SEG_CON, objCON);
	}

	/**
	 * Adds collection of Consent Segment(CON) to this message.
	 * @param objCONSegmentCollection Collection of Consent Segments to add in this message
	 */
	@SuppressWarnings("unchecked")
	public void addConsentSegment(ISingleCollection<ConsentSeg>  objCONSegmentCollection)
	{
		ISingleCollection<?> objSegmentCollection =  objCONSegmentCollection;
		addSegment(EnumSegments.HL7_SEG_CON, (ISingleCollection<ISegment>)objSegmentCollection);
	}
	
	/**
	 * Retrieves Collection of Observation GROUP present in this message.
	 * @return ISingleCollection<Group> - Collection of Observation Group present in this Message.
	 */
	public ISingleCollection<IGroup> getObservationGroups()
	{
		return getGroup(EnumSegments.HL7_GROUP_OBSERVATION);
	}

	/**
	 * Adds Collection of Observation GROUP to this message.
	 * @param objSingleCollection Specified  Observation GROUP Collection.
	 */
	public void addObservationGroups(ISingleCollection<IGroup> objSingleCollection)
	{
		addGroup(EnumSegments.HL7_GROUP_OBSERVATION, objSingleCollection);
	}

	/**
	 * Adds  Observation GROUP to this message.
	 * @param objGroup Specified  Observation GROUP. 
	 */
	public void addObservationGroup(IGroup objGroup)
	{
		addGroup(EnumSegments.HL7_GROUP_OBSERVATION, objGroup);
	}
	
	/**
	 * Validates HL7 Message objects.
	 * Validate method checks for validation each and every segments in side the Message object against specified message Structure. 
	 * Validation chcecks include optionality of segment or group.
	 * It also verifies the number of times segment or group is repeated in message.
	 * e.g 
	 * {PSH} while parsing PSH segment validate method will check for its mandatory presence.
	 * [PSH] in this case validate will throw exception if one tries to enter more than one PSH segment.    
	 * @return boolean - true If Message and all segments in side it are validated properly 
	 *				   - false If segment in side it is not validated properly and if compulsory segments are not present.
	 * @throws InvalidMessageException Thrown when Message validation fails.
	 */

	public boolean validate() throws InvalidMessageException
	{
		try
		{
			super.validate();
			
			/*
			 * This block of code checks for TQ1 repeatability constraint 
			 */
			ISingleCollection<IGroup> objCOMMON_ORDER_List = getCommonOrderGroups();
			if(objCOMMON_ORDER_List != null)
			{
				for(IGroup objCOMMON_ORDER : objCOMMON_ORDER_List)
				{
					ISingleCollection<IGroup> objTIMING_List = objCOMMON_ORDER.getGroup(EnumSegments.HL7_GROUP_TIMING);
					if(objTIMING_List != null)
					{
						for(IGroup objTiming : objTIMING_List)
						{		
							ISingleCollection<ISegment> objTQ1List = objTiming.getSegment(EnumSegments.HL7_SEG_TQ1);
							if(objTQ1List != null && objTQ1List.size() > 1)
							{
								int i = 1;
								while(i < objTQ1List.size())
								{
									ISegment objSegment = objTQ1List.elementAt(i);
									ValueTables objValueTables = ValueTables.createInstance();
									ID objID = ((TQ1)objSegment).getConjunction();
									if(objID != null)
									{
										if(objValueTables.isValuePresent(427, (String)objID.getValue()) == HL7Constants.HL7_VALUE_NOT_FOUND)
											throw new InvalidSegmentException(EnumSegments.HL7_SEG_TQ1.getValue());
									}
								}
							}
						}
					}
				}
			}

		}
		catch(InvalidSegmentException ex)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex); 
		}
		catch(SegmentMissingException ex)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex);
		}
		return true;
	}
}
