/*******************************************************************************
 *  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.io.IOException;

import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamException;

import cdac.medinfo.sdk.hl7282.baselibs.segments.SegmentSet;
import cdac.medinfo.sdk.hl7282.commoninterface.IGroup;
import cdac.medinfo.sdk.hl7282.commoninterface.ISegment;
import cdac.medinfo.sdk.hl7282.commoninterface.ISegmentKey;
import cdac.medinfo.sdk.hl7282.commoninterface.ISegmentSet;
import cdac.medinfo.sdk.hl7282.commoninterface.ISingleCollection;
import cdac.medinfo.sdk.hl7282.exceptions.GroupMissingException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidMessageException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidSegmentException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidSegmentSetException;
import cdac.medinfo.sdk.hl7282.exceptions.SegmentMissingException;
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;

/**
 * Group class contains multi valued paired collection of segments and groups of HL7 message group.
 * This class provides mechanism to serialize, parse and validate message against HL7 v 2.8.2 standard.
 * The class provides accessors & mutators for the elements mentioned above.
 * These elements can be easily populated & populated values can be easily retrieved.
 * The class has validate method which validates Message against HL7 v 2.8.2 standard.
 * It provides user with serialize method to serialize message object to any stream & 
 * parse method to obtain fully populated & validated Message object from any stream.
 */
public class Group implements IGroup
{
	

	protected ISegmentKey enumGroups = null;
	protected IMap objMessageMap = null;
	private DataValue objDataValue = null;

	/**
	 * This constructor creates object of MultiValuedPairedCollection and initializes the Group Name with enumGroups.
	 * @param enumGroups Name of the Group.
	 */
	public Group(ISegmentKey enumGroups, IMap objMessageMap)
	{
		this.enumGroups = enumGroups;
		this.objMessageMap = objMessageMap;
		objDataValue = new DataValue(objMessageMap);
	}

	/**
	 *Gives collection of group of name enumGroup as passed by user.
	 * @param enumGroup Name of group.
	 * @return ISingleCollection<Group> - Collection of Group .
	 */
	public ISingleCollection<IGroup> getGroup(ISegmentKey enumGroup)
	{
		return objDataValue.getGroup(enumGroup);
	}

	/**
	 * Return SegmentKey.
	 * @return SegmentKey.
	 */
	public ISegmentKey getName()
	{
		return enumGroups;
	}

	/**
	 * Sets the name of group.
	 * @param enumName - object of enumName.
	 */
	public void setName(ISegmentKey enumName)
	{
		this.enumGroups = enumName;
	}

	/**
	 * Gives collection of segment of name enumSegments as passed by user.
	 * @param enumSegments Name of Segment.
	 * @return ISingleCollection<Segment> - Collection of Segments .
	 */
	public ISingleCollection<ISegment> getSegment(ISegmentKey enumSegments)
	{
		return objDataValue.getSegment(enumSegments);
	}

	/**
	 * Adds the group in Collection of data value.
	 * @param enumSegments - object of enumSegment .
	 * @param objGroup - object of Group.
	 */
	public void addGroup(ISegmentKey enumSegments ,IGroup objGroup)
	{
		objDataValue.addGroup(enumSegments, objGroup);
	}

	/**
	 * Adds segment in multi valued paired collection.
	 * @param enumSegments Key that is to be added to collection
	 * @param objSegment Value of the key.
	 */
	public void addSegment(ISegmentKey enumSegments , ISegment objSegment)
	{
		objDataValue.addSegment(enumSegments, objSegment);
	}

	/**
	 * Retrieves all segments present in this group
	 * @return ISingleCollection<ISegment> - List of Segments
	 */
	public ISegmentSet getSegmentSet()
	{
		ISegmentSet objSegmentSet = new SegmentSet();

		ISingleCollection<ISegment> objSegmentCollection = objDataValue.getSegmentCollection();

		if(objSegmentCollection.size() != 0 || objSegmentCollection != null)
		{
			for(ISegment objSegment : objSegmentCollection)
			{
				objSegmentSet.addSegment(objSegment);
			}
		}

		return objSegmentSet;
	}
	
	/**
	 * Retrieves collection of ISegment.
	 * @return ISingleCollection<ISegment> -return collection of ISegment.
	 */
	public ISingleCollection<ISegment> getSegmentCollection()
	{
		return objDataValue.getSegmentCollection();
	}

	/**
	 * Populates the Segment Set.
	 * @return int - return HL7_FUNCTION_SUCCESS after success.
	 * @throws GroupMissingException
	 */
	public int populate(ISegmentSet objSegmentSet) throws GroupMissingException
	{
		try
		{
			objDataValue.populate(objSegmentSet);
		}
		catch(InvalidSegmentSetException ex)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex);
		}
		catch (SegmentMissingException ex)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex);
		}
		catch (GroupMissingException ex)
		{
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex);
		}
		return HL7Constants.HL7_FUNCTION_SUCCESS;
	}

	/**
	 * Validates HL7 Message objects.
	 * Validate method checks for validation each and every segments in side the Message object against specified message Structure. 
	 * Validation checks 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
		{
			objDataValue.validate();
		}
		catch(SegmentMissingException ex)
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();						
				objHL7Logger.log(ex.getMessage());
			}
			throw new InvalidMessageException(this.getClass().getSimpleName(), ex);
		}
		catch(GroupMissingException gme)
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();						
				objHL7Logger.log(gme.getMessage());
			}
			throw new InvalidMessageException(this.getClass().getSimpleName(), gme);
		}
		catch (InvalidSegmentException e) 
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();						
				objHL7Logger.log(e.getMessage());
			}
			throw new InvalidMessageException(this.getClass().getSimpleName(), e);
		}
		return true;
	}
	
	/**
	 * Retrieves Map of specified Group name
	 * @param enumGroupName Object of ISegmentKey provides name of Group.
	 * @return IMap - Object of IMap.
	 */
	public IMap getGroupMap(ISegmentKey enumGroupName)
	{
		return objDataValue.getGroupMap(enumGroupName);
	}

	/**
	 * Returns the size of the group
	 * @return int - return size.
	 */
	public int size()
	{
		return objDataValue.sizeOfGroup();
	}
	
	/**
	 * Used to write to XML 
	 * @param eventWriter object of eventWriter.
	 * @return int - return integer 
	 */
	public int writeToXML(XMLEventWriter eventWriter) throws IOException, XMLStreamException
	{
		return objDataValue.writeToXML(eventWriter);
	}
	/**
	 * Uses to write to XML 
	 * @param eventWriter object of eventWriter.
	 * @param messageType contains message Structure
	 * @return int - return integer 
	 * @throws IOException
	 * @throws XMLStreamException
	 */
	public int writeToXML_v2(XMLEventWriter eventWriter,String messageType) throws IOException, XMLStreamException
	{
		return objDataValue.writeToXML_v2(eventWriter,messageType);
	}
}