/*******************************************************************************
 *  Copyright 2017-2018 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.dcm30_04.services.association;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Locale;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;

import cdac.medinfo.sdk.dcm30_04.enums.EnumSOPClassUIDs;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumServiceType;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConfig;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConstants;
import cdac.medinfo.sdk.dcm30_04.utils.DicomLogger;
import cdac.medinfo.sdk.dcm30_04.utils.IPairedCollection;
import cdac.medinfo.sdk.dcm30_04.utils.ISingleCollection;
import cdac.medinfo.sdk.dcm30_04.utils.PairedCollection;
import cdac.medinfo.sdk.dcm30_04.utils.ResourceBundleWrapper;
import cdac.medinfo.sdk.dcm30_04.utils.SingleCollection;

/**
 * The Class implements the Default settings of Association.
 * Default Association can be populated either manually or with default values provided by SDK
 * DefaultAssociation specifies the configuration which would be referred while creating association.
 */
public class DefaultAssociation  
{
	
	/** short value of Dicom protocol version. */
	private short shProtocolVersion = -1;

	/** Destination DICOM Application Name. */
	private String strCalledAETitle;

	/** Source DICOM Application Name from Default association XML file. */
	private String strCallingAETitle;

	/** Application context name. */
	private String strAppContextName = null;

	/** The RQ presentation context Single collection. */
	private ISingleCollection<RQPresContext> objRQPresContextCollection = new SingleCollection<RQPresContext>();

	private ISingleCollection<String> objDefTransferSyntaxList = null;

	/** Object of user info. */ 
	private RQUserInfo objRQUserInfo = null;

	private boolean isLoaded = false;

	private final short shDefProcVersion = (short)1;
	private final String strDefAppContextName = "1.2.840.10008.3.1.1.1"; 
	private final int iDefMaxLength = 16384;
	private final String strDefImplClassUID = "1.2.40.0.13.1.1";
	private final String strDefImplVersionName = "SDK";
	private final short shDefMaxOpsInvoked = (short)1;
	private final short shDefMaxOpsPerformed = (short)1;

	/**
	 * Constructor to initialize object using given parameters.
	 * @param calledAETitle String value of Destination DICOM Application Name
	 * @param callingAETitle String value of source DICOM Application Name
	 * @param ProcVersion short value of Dicom Protocol version.
	 * @param strAppContextName String value of Application context name.
	 * @param objRQPresContextCollection collection of Presentation Context
	 * @param objRQUserInfo object of User Info.
	 */ 
	public DefaultAssociation(String calledAETitle, String callingAETitle, short ProcVersion, String strAppContextName, ISingleCollection<RQPresContext> objRQPresContextCollection, RQUserInfo objRQUserInfo)
	{
		shProtocolVersion = ProcVersion;
		strCalledAETitle = calledAETitle;
		strCallingAETitle = callingAETitle;
		this.strAppContextName = strAppContextName;
		this.objRQPresContextCollection = objRQPresContextCollection;
		this.objRQUserInfo = objRQUserInfo;
		isLoaded = true;
	}

	/**
	 * Constructor to initialize object using given calling AE Title.
	 * @param strAETitle value of calling AE Title
	 */
	public DefaultAssociation(String strCallingAETitle)
	{
		this.strCallingAETitle = strCallingAETitle;
	}

	/**
	 * Retrieves list of supported transfer syntax for Abstract Syntax.
	 * @return ISingleCollection<String> collection of supported transfer syntax
	 */
	public ISingleCollection<String> getDefTransferSyntaxList() 
	{
		return objDefTransferSyntaxList;
	}

	/**
	 * Sets list of supported transfer syntax for Abstract Syntax.
	 * @param objDefTransferSyntaxList collection of supported transfer syntax
	 */
	public void setDefTransferSyntaxList(ISingleCollection<String> objDefTransferSyntaxList) 
	{
		this.objDefTransferSyntaxList = objDefTransferSyntaxList;
	}

	/**
	 * Adds the Transfer Syntax value in List
	 * @param strTS value of Transfer Syntax
	 */
	public void addDefTransferSyntax(String strTS)
	{
		if(objDefTransferSyntaxList == null)
			objDefTransferSyntaxList = new SingleCollection<String>();
		objDefTransferSyntaxList.add(strTS);
	}

	/**
	 * Returns the status of DefaultAssociation whether it is loaded or not.
	 * @return boolean - true , if association is loaded
	 * 					 false, if association is not loaded
	 */
	public boolean isAssociationLoaded()
	{
		return isLoaded;
	}

	/**
	 * Validates the Default Association for mandatory attributes.
	 * Generates log for missing or invalid attributes and throws exception accordingly.
	 * @return boolean - shows status as true or false.
	 * @throws InvalidAssociationPrimitiveException if any mandatory attribute is missing or invalid.
	 */
	public boolean validate()throws InvalidAssociationPrimitiveException
	{
		//Validating default parameters 
		if(shProtocolVersion == -1)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","AssociationException.MissingProtocolVersion",new Locale(DicomConfig.createInstance().getLocale())));
			}
			throw new InvalidAssociationPrimitiveException(ResourceBundleWrapper.getValue("exceptions","AssociationException.MissingProtocolVersion",new Locale(DicomConfig.createInstance().getLocale())));
		}
		else if(strCallingAETitle == null)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","AssociationException.MissingCallingAETitle",new Locale(DicomConfig.createInstance().getLocale())));
			}
			throw new InvalidAssociationPrimitiveException(ResourceBundleWrapper.getValue("exceptions","AssociationException.MissingCallingAETitle",new Locale(DicomConfig.createInstance().getLocale())));
		}
		else if(strAppContextName == null)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","AssociationException.MissingAppContextName",new Locale(DicomConfig.createInstance().getLocale())));
			}
			throw new InvalidAssociationPrimitiveException(ResourceBundleWrapper.getValue("exceptions","AssociationException.MissingAppContextName",new Locale(DicomConfig.createInstance().getLocale())));
		}

		if(objRQPresContextCollection == null)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","AssociationException.MissingPresContextList",new Locale(DicomConfig.createInstance().getLocale())));
			}
			throw new InvalidAssociationPrimitiveException(ResourceBundleWrapper.getValue("exceptions","AssociationException.MissingPresContextList",new Locale(DicomConfig.createInstance().getLocale())));
		}
		else
		{
			//Validating RQPresContext populated in list
			int ival = validate(objRQPresContextCollection);

			if(ival == 1 || ival == 2 || ival == 3)
			{
				if(DicomConfig.createInstance().isLoggingOn())
				{
					DicomLogger.getLogger().log(ResourceBundleWrapper.getValue("exceptions", "AssociationException.InvalidPresContext", new Locale(DicomConfig.createInstance().getLocale())));
				}
				throw new InvalidAssociationPrimitiveException(ResourceBundleWrapper.getValue("exceptions", "AssociationException.InvalidPresContext", new Locale(DicomConfig.createInstance().getLocale())));
			}
			else if(ival == 4)
			{
				{
					throw new InvalidAssociationPrimitiveException(ResourceBundleWrapper.getValue("exceptions","AssociationException.DefTSNotPresent",new Locale(DicomConfig.createInstance().getLocale())));
				}
			}
		}

		//validating UserInfo
		if(objRQUserInfo != null)
		{
			if(!objRQUserInfo.validate())
			{
				if(DicomConfig.createInstance().isLoggingOn())
				{
					DicomLogger objDicomLogger = DicomLogger.getLogger();
					objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","AssociationException.InvalidUserinfo",new Locale(DicomConfig.createInstance().getLocale())));
				}
				throw new InvalidAssociationPrimitiveException(ResourceBundleWrapper.getValue("exceptions","AssociationException.InvalidUserinfo",new Locale(DicomConfig.createInstance().getLocale())));
			}
		}

		return true;
	}

	/**
	 * Validates the Collection of Presentation context. Where if Associate RQ contains more than one Transfer
	 * Syntax for same Abstract Syntax, then default transfer syntax corresponding to that abstract syntax should
	 * be present. 
	 * @param objRQPresContextCollection Single Collection of RQPress Context 
	 * @return integer 1 - if AbstractSyntaxName is null.
	 * 				   2 - if collection of TS or its size is null.
	 * 				   3 - if collection of TS contains null value.
	 * 				   4 - if collection of TS does not contain default value of TS for corresponding AS.
	 * 				   0 - if validation is successful.
	 */
	private int validate(ISingleCollection<RQPresContext> objRQPresContextCollection)
	{
		ISingleCollection<String> collOfAlreadyCompairedAS = new SingleCollection<String>();

		int iSize = objRQPresContextCollection.size();

		for(int i = 0; i<iSize ; i++)
		{
			ISingleCollection<String> objTSCollection = new SingleCollection<String>();
			RQPresContext objRQPressCont = objRQPresContextCollection.elementAt(i);

			int ivalForsinglePressCon = objRQPressCont.validate();
			if(ivalForsinglePressCon != 0)
				return ivalForsinglePressCon;

			if(!collOfAlreadyCompairedAS.contains(objRQPressCont.getAbstractSyntaxName()))
			{
				String strAS = null;
				if(objRQPressCont != null)
				{
					strAS = objRQPressCont.getAbstractSyntaxName();

					collOfAlreadyCompairedAS.add(strAS);

					ISingleCollection<String> objTS = objRQPressCont.getTSCollection();
					for(String str : objTS)
					{
						if(!objTSCollection.contains(str))
							objTSCollection.add(str);
					}

					for(int j = i+1; j < iSize ; j++)
					{
						RQPresContext objPresContext = objRQPresContextCollection.get(j);
						int ivalForNextMatchecPressCon = objPresContext.validate();
						if(ivalForNextMatchecPressCon != 0)
							return ivalForNextMatchecPressCon;

						String strNextAS = objRQPresContextCollection.get(j).getAbstractSyntaxName();
						if(strAS.equalsIgnoreCase(strNextAS))
						{

							ISingleCollection<String> objTSColl = objRQPresContextCollection.get(j).getTSCollection();
							for(String strTS : objTSColl)
							{
								if(!objTSCollection.contains(strTS))
									objTSCollection.add(strTS);
							}
						}
					}
				}

				RQPresContext TempRQPressContext = new RQPresContext(strAS, objTSCollection);
				int ival = TempRQPressContext.validate(objTSCollection);
				return ival;
			}
		}
		return 0;
	}

	/**
	 * Populates the DefaultAsasociation with default values if default fields have not been initialized.
	 * Populates the supported RQPresContext list with given parameter. 
	 * @param objRQPresContextList collection of RQPresContext.
	 */
	public void loadAssociation(ISingleCollection<RQPresContext> objRQPresContextList)throws AssociationLoadFailedException
	{
		try
		{
			if(!isLoaded)
			{
				loadDefault();
				this.objRQPresContextCollection = objRQPresContextList;
				if(validate())
					isLoaded = true;
			}
		}
		catch(InvalidAssociationPrimitiveException iape)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","AssociationException.InvalidUserinfo",new Locale(DicomConfig.createInstance().getLocale())));
			}
			throw new AssociationLoadFailedException(iape.getMessage(),iape);
		}
	}

	/**
	 * Populates the DefaultAsasociation with default values if default fields have not been initialized.
	 * Populates the supported RQPresContext list only for provided SOP Class UIDs. 
	 * @param arrenumSOPClassUID array of SOP Class UID
	 */
	public void loadAssociation(EnumSOPClassUIDs []arrenumSOPClassUID)throws AssociationLoadFailedException
	{
		try
		{
			if(!isLoaded)
			{
				loadDefault();

				if(objRQPresContextCollection == null)
					objRQPresContextCollection = new SingleCollection<RQPresContext>();

				if(objDefTransferSyntaxList == null)
					populateTSList();

				RQPresContext objRQPresContext = null;
				for(int i = 0 ; i < arrenumSOPClassUID.length ; i++)
				{
					objRQPresContext = new RQPresContext();
					objRQPresContext.setAbstractSyntaxName(arrenumSOPClassUID[i].getValue());
					objRQPresContext.setTSCollection(objDefTransferSyntaxList);
					objRQPresContextCollection.add(objRQPresContext);
				}
				if(validate())
					isLoaded = true;
			}
		}
		catch(InvalidAssociationPrimitiveException iape)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","AssociationException.InvalidUserinfo",new Locale(DicomConfig.createInstance().getLocale())));
			}
			throw new AssociationLoadFailedException(iape.getMessage(),iape);
		}
	}

	/**
	 * Populates the DefaultAsasociation with default values if default fields have not been initialized.
	 * Populates the supported RQPresContext list for all SOP Class UIDs available in libs directory for given services 
	 * @param enumServiceTypes array of services type
	 */
	public void loadAssociation(EnumServiceType[] enumServiceTypes) throws AssociationLoadFailedException
	{
		try
		{
			if(!isLoaded)
			{
				loadDefault();

				if(objDefTransferSyntaxList == null)
					populateTSList();
				DefaultAssocXMLHandler objDefaultAssocXMLHandler = new DefaultAssocXMLHandler();
				objDefaultAssocXMLHandler.loadFromClasspath(enumServiceTypes);
				if(validate())
					isLoaded = true;
			}
		}
		catch(InvalidAssociationPrimitiveException iape)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","AssociationException.InvalidUserinfo",new Locale(DicomConfig.createInstance().getLocale())));
			}
			throw new AssociationLoadFailedException(iape.getMessage(),iape);
		}
	}

	/**
	 * Replaces the transfer syntax list for a RQPresContext which contains provided SOPClassUID as AbstractSyntax. 
	 * If programmer wants to specify a list of transfer syntaxes(which may be different from default support) for a particular abstratct syntax,
	 * he/she can make this change through this method.
	 * It returns true if modification made successfully else false.
	 * @param enumSOPClassUIDs enum value for abstract syntax
	 * @param objTSList list of transfer syntax
	 * @return boolean shows status as true or false.
	 */
	public boolean changeSupportedTS(EnumSOPClassUIDs enumSOPClassUIDs,ISingleCollection<String> objTSList)
	{
		if(objRQPresContextCollection != null && objRQPresContextCollection.size() != 0)
		{
			String strAbstractSyntax = null;
			for(RQPresContext objRQPresContext : objRQPresContextCollection)
			{
				strAbstractSyntax = objRQPresContext.getAbstractSyntaxName();

				if(enumSOPClassUIDs.getValue().equals(strAbstractSyntax))
				{
					objRQPresContext.setTSCollection(objTSList);
					return true;
				}
			}
		}

		if(DicomConfig.createInstance().isLoggingOn())
		{
			DicomLogger objDicomLogger = DicomLogger.getLogger();
			objDicomLogger.log(ResourceBundleWrapper.getValue("warning","AbstractSyntaxNotMatched",new Locale(DicomConfig.createInstance().getLocale()))+ enumSOPClassUIDs.getValue());
		}
		return false;
	}

	/**
	 * Retrieves the RQPresContext which contains provided SOPClassUID as AbstractSyntax. 
	 * @param enumSOPClassUIDs enum value for abstract syntax
	 * @return RQPresContext instance of RQPresContext
	 */
	public RQPresContext getRQPresContext(EnumSOPClassUIDs enumSOPClassUIDs)
	{
		if(objRQPresContextCollection != null && objRQPresContextCollection.size() != 0)
		{
			String strAbstractSyntax = null;
			for(RQPresContext objRQPresContext : objRQPresContextCollection)
			{
				strAbstractSyntax = objRQPresContext.getAbstractSyntaxName();

				if(enumSOPClassUIDs.getValue().equals(strAbstractSyntax))
				{
					return objRQPresContext;
				}
			}
		}

		if(DicomConfig.createInstance().isLoggingOn())
		{
			DicomLogger objDicomLogger = DicomLogger.getLogger();
			objDicomLogger.log(ResourceBundleWrapper.getValue("warning","AbstractSyntaxNotMatched",new Locale(DicomConfig.createInstance().getLocale()))+ enumSOPClassUIDs.getValue());
		}
		return null;
	}

	/**
	 * Adds the entry for RQPresContext
	 * @param objRQPresContext instance of RQPresContext
	 */
	public void addPresContext(RQPresContext objRQPresContext)
	{
		if(objRQPresContextCollection != null)
		{
			objRQPresContextCollection.add(objRQPresContext);
		}
	}

	/*
	 * Populates the default values for DefaultAssociation if values not initialized
	 */
	private void loadDefault()throws AssociationLoadFailedException
	{
		if(strCallingAETitle == null)
			throw new AssociationLoadFailedException(ResourceBundleWrapper.getValue("exceptions","AssociationException.NULLAETitle",new Locale(DicomConfig.createInstance().getLocale())));

		if(shProtocolVersion == -1)
			shProtocolVersion = shDefProcVersion;
		if(strAppContextName == null)
			strAppContextName = strDefAppContextName;
		if(objRQUserInfo == null)
			objRQUserInfo = new RQUserInfo();

		if(objRQUserInfo.getmaxlength() == -1)
			objRQUserInfo.setmaxlength(iDefMaxLength);
		if(objRQUserInfo.getImplclassUID() == null)
			objRQUserInfo.setImplclassUID(strDefImplClassUID);
		if(objRQUserInfo.getImplVersionName() == null)
			objRQUserInfo.setImplVersionName(strDefImplVersionName);
		if(objRQUserInfo.getMaxNumOpsPerformed() == -1)
			objRQUserInfo.setMaxNumOpsPerformed(shDefMaxOpsPerformed);
		if(objRQUserInfo.getMaxNumOpsInvoked() == -1)
			objRQUserInfo.setMaxNumOpsInvoked(shDefMaxOpsInvoked);
	}

	/**
	 * DefaultAssocXMLHandler Handler class provides java dependent implementation of XML File parsing mechanism.<br>
	 */
	private class DefaultAssocXMLHandler
	{
		private final String strAssociationInfoPath = "AssociationInfo.xml";

		DefaultAssocXMLHandler() 
		{

		}

		/**
		 * Loads the association as per specified service types.
		 * @param enumServiceTypes - if null , association is loaded for all service types.
		 * @throws AssociationLoadFailedException
		 */
		public void loadFromClasspath(EnumServiceType[] enumServiceTypes) throws AssociationLoadFailedException
		{
			String strServiceTypes = "";
			if(enumServiceTypes != null)
			{
				for(EnumServiceType enumServiceType : enumServiceTypes)
				{
					if(enumServiceType!=null)
					{
						strServiceTypes += enumServiceType.getValue();
					}
				}
			}

			try
			{
				URL objURL = DefaultAssocXMLHandler.class.getResource(strAssociationInfoPath);
				URLConnection objJarURLConnection = objURL.openConnection();

				//Creates instance of XMLEventReader for xml file
				XMLInputFactory inputFactory = XMLInputFactory.newInstance();
				XMLEventReader reader = 
					inputFactory.createXMLEventReader(objJarURLConnection.getInputStream());

				String strParentEventName = null;
				String strParentServiceMerge = null;
				boolean isAbsSyntaxSupported = false;
				//Traverses through the nodes of xml file using event based mechanism of STAX parser
				/*
				 * In this loop for N-Services and CFind service it stores name of that service and
				 * while parsing child service classes it concatenates parent service class and child service class.
				 * In service classes it compares for concatenated value to make sure that for any service 
				 * only required service classes are getting populated.
				 * EnumServiceType has been modified accordingly.
				 * Note :- For a service class tag name in xml file,in xmlnodes class and value in EnumServiceType
				 * should be same and in same char case.
				 */
				while(reader.hasNext())
				{
					XMLEvent event = reader.nextEvent(); 

					switch(event.getEventType())
					{
					//event type START_ELEMENT defines the start tag of the xml node in the file
					case XMLStreamConstants.START_ELEMENT:
					{
						//processes each node and retrieves the attribute or value of the node
						//if(strParentEventName == null)
						//strParentEventName = event.asStartElement().getName().getLocalPart();

						String strChileNode = event.asStartElement().getName().getLocalPart();
						switch(XmlNodes.valueOf(strChileNode))
						{
						case CECHO:
						case CSTORE:
						case CFIND:
						case CGET:
						case CMOVE:
						case NACTION:
						case NCREATE:
						case NDELETE:
						case NSET:
						case NGET:
						case NEVENTREPORT:
							strParentEventName = strChileNode;
							if(strServiceTypes.contains(strParentEventName)|| strServiceTypes.contains(EnumServiceType.ALL.getValue()))
							{
								isAbsSyntaxSupported = true;
								if(reader.hasNext())
									event = reader.nextEvent();
							}
							break;
						case DEFAULT:
						case MPPS:
						case GPPPS:
						case GPSPS:
						case STORAGECOMMITMENT:
						case IAN:
						case MWL:
						case GPWL:
						case BASICGRAYSCALEPRINTMANAGEMENT:
						case BASICCOLORPRINTMANAGEMENT:
						case PULLPRINT:
						case BASICPRINTIMAGEOVERLAY:
						case PRESENTATIONLUT:
						case AEL:
						case BASICANNOTATION :
						case DETACHEDRESULTMANAGEMENT :
						case DETACHEDINTERPRETATIONMANAGEMENT :
						case DETACHEDRESULTMANAGEMENTMETASOP :
						case PRINTJOB :
						case PRINTQUEUEMANAGEMENT :
						case PRINTERCONFIGURATIONRETRIEVAL :
						case MEDIACREATION :
						case DETACHEDPATIENTMANAGEMENT:
						case DETACHEDVISITMANAGEMENT:
						case STUDYMANAGEMENTMETA:
						case DETACHEDSTUDYMANAGEMENT:
						case STUDYCOMPONENTMANAGEMENT:
						case DETACHEDPATIENTMANAGEMENTMETASOP:
						case RELEVANTPATIENTINFOQRY:
						case SRS:
						case STUDYCONTENTNOTIFICATION:
						case COLORPALETTE:
						case ORMS:
						case SUBSTANCEADMINISTRATION:
						case DISPLAYSYSTEMMANAGEMENT:
						case HANGINGPROTOCOL:
						case IMPLANTTEMPLATE:
						case COMPOSITEINSTANCERETRIEVEWITHOUTBULKDATA:
						case IFLR:
						case UPS:
						case RTCONVENTIONALMACHINEVERIFICATION:
						case RTIONMACHINEVERIFICATION:
						case SPSS:
							strParentServiceMerge = strParentEventName + event.asStartElement().getName().getLocalPart();
							if(strServiceTypes.contains(strParentServiceMerge) || strServiceTypes.contains(EnumServiceType.ALL.getValue()))
							{
								isAbsSyntaxSupported = true;
								if(reader.hasNext())
									event = reader.nextEvent();
							}
							else
								isAbsSyntaxSupported = false;
							break;
						case AbstractSyntax:
							if(reader.hasNext() && isAbsSyntaxSupported)
							{
								if(objRQPresContextCollection == null)
									objRQPresContextCollection = new SingleCollection<RQPresContext>();
								event = reader.nextEvent();
								RQPresContext objRQTempPresContext = new RQPresContext();
//								if(DataTypeConvertor.isAbstractSyntaxSupported(event.asCharacters().getData()))
//								{
									objRQTempPresContext.setAbstractSyntaxName(event.asCharacters().getData());
									objRQTempPresContext.setTSCollection(objDefTransferSyntaxList);
									objRQPresContextCollection.add(objRQTempPresContext);
//								}
//								else
//								{
//									if(DicomConfig.createInstance().isLoggingOn())
//									{
//										DicomLogger.getLogger().log(ResourceBundleWrapper.getValue("warning", "AbstractSyntaxNotSupported", new Locale(DicomConfig.createInstance().getLocale())) + event.asCharacters().getData());
//									}
//								}
							}
							break;
						}
					}
					break;
					//event type END_ELEMENT defines the end tag of the xml node in the file
					case XMLStreamConstants.END_ELEMENT:
						break;
					}
				}
			}
			catch(XMLStreamException xse)
			{
				if(DicomConfig.createInstance().isLoggingOn())
					DicomLogger.getLogger().log(xse.getMessage());

				throw new AssociationLoadFailedException(ResourceBundleWrapper.getValue("exceptions","AssociationException.OperationFail",new Locale(DicomConfig.createInstance().getLocale())),xse);
			}
			catch(FileNotFoundException fnfe)
			{
				if(DicomConfig.createInstance().isLoggingOn())
					DicomLogger.getLogger().log(fnfe.getMessage());

				throw new AssociationLoadFailedException(ResourceBundleWrapper.getValue("exceptions","AssociationException.FileMissing",new Locale(DicomConfig.createInstance().getLocale())),fnfe);
			}
			catch(IOException ioe)
			{
				if(DicomConfig.createInstance().isLoggingOn())
					DicomLogger.getLogger().log(ioe.getMessage());

				throw new AssociationLoadFailedException(ResourceBundleWrapper.getValue("exceptions","AssociationException.OperationFail",new Locale(DicomConfig.createInstance().getLocale())),ioe);
			}
		}

	}


	/**
	 * Retrieves Destination DICOM Application Name .
	 * 
	 * @return String - value of Destination DICOM Application Name
	 */
	public String getCalledAETitle() 
	{
		return strCalledAETitle;
	}

	/**
	 * Sets Destination DICOM Application Name .
	 * 
	 * @param strCalledAETitle the Destination DICOM Application Name .
	 */
	public void setCalledAETitle(String strCalledAETitle) 
	{
		this.strCalledAETitle = strCalledAETitle;
	}

	/**
	 * Retrieves Source DICOM Application Name .
	 * 
	 * @return String - value of Source DICOM Application Name.
	 */
	public String getCallingAETitle() 
	{
		return strCallingAETitle;
	}

	/**
	 * Sets Source DICOM Application Name .
	 * 
	 * @param strCallingAETitle Source DICOM Application Name .
	 */
	public void setCallingAETitle(String strCallingAETitle) 
	{
		this.strCallingAETitle = strCallingAETitle;
	}

	/**
	 * Retrieves Application context name .
	 * 
	 * @return String - String value of Application context name.
	 */
	public String getAppContextName() 
	{
		return strAppContextName;
	}

	/**
	 * Sets Application context name .
	 * 
	 * @param strAppContextName String value of Application context name .
	 */
	public void setAppContextName(String strAppContextName) 
	{
		this.strAppContextName = strAppContextName;
	}

	/**
	 * This method gets the Presentation Context collection.
	 * 
	 * @return ISingleCollection<RQPresContext> - Presentation Context collection
	 */
	public ISingleCollection<RQPresContext> getRQPresContextCollection() 
	{
		return objRQPresContextCollection;
	}

	/**
	 * Sets the RQ presentation context collection.
	 * 
	 * @param objRQPresContext Presentation Context collection
	 */
	public void setRQPresContext(ISingleCollection<RQPresContext> objRQPresContext)
	{
		this.objRQPresContextCollection = objRQPresContext;
	}

	/**
	 * This method gets the User Info.
	 * 
	 * @return RQUserInfo - object of User info
	 */
	public RQUserInfo getRQUserInfo() 
	{
		return objRQUserInfo;
	}

	/**
	 * Sets the user info item.
	 * 
	 * @param objRQUserInfo the user info item.
	 */
	public void setUserInfoItem(RQUserInfo objRQUserInfo)
	{
		this.objRQUserInfo = objRQUserInfo;
	}

	/**
	 * Gets the protocol version.
	 * 
	 * @return short - value of the protocol version
	 */
	public short getProtocolVersion() 
	{
		return shProtocolVersion;
	}

	/**
	 * Sets the protocol version.
	 * 
	 * @param protocolVersion short value of protocol version.
	 */
	public void setProtocolVersion(short protocolVersion) 
	{
		shProtocolVersion = protocolVersion;
	}

	/**
	 * Retrieves the Association RQ.
	 * It validates the Default Association.
	 * If Default Association is not loaded or contains invalid entry then it generates exception.
	 * @param strRequestedAETitle AE title of service provider 
	 * @return AssociateRQ - the default association request.
	 * @throws AssociationLoadFailedException if DefaultAssociation is invalid or PresContextList size exceeds then 66.
	 */
	public AssociateRQ getAssociationRQ(String strRequestedAETitle)throws AssociationLoadFailedException
	{
		AssociateRQ objAssociateRQ = new AssociateRQ();
		byte bPresContextID = 1;
		byte increment = 2;
		IPairedCollection<Byte,RQPresContext> objRQPresContextPairedCollection = null;

		try
		{
			if(validate())
			{
				objAssociateRQ.setProtocolversion(shProtocolVersion);
				objAssociateRQ.setCalledEntityTitle(strRequestedAETitle);
				objAssociateRQ.setCallingEntityTitle(strCallingAETitle);
				objAssociateRQ.setAppContextName(strAppContextName);
				if(objRQPresContextCollection.size() > 90)
				{
					if(DicomConfig.createInstance().isLoggingOn())
					{
						DicomLogger objDicomLogger = DicomLogger.getLogger();
						objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","AssociationException.LimitExceeded",new Locale(DicomConfig.createInstance().getLocale()))+ EnumServiceType.CGET.getValue());
					}
					throw new AssociationLoadFailedException(ResourceBundleWrapper.getValue("exceptions", "AssociationException.LimitExceeded", new Locale(DicomConfig.createInstance().getLocale())));
				}
				else
				{
					objRQPresContextPairedCollection = new PairedCollection<Byte, RQPresContext>();
					for(RQPresContext objRQPresContext : objRQPresContextCollection)
					{
						//objRQPresContext.setPresContextId(bPresContextID);
						objRQPresContextPairedCollection.put(bPresContextID,objRQPresContext);
						bPresContextID = (byte)(bPresContextID + increment);
					}
				}
				objAssociateRQ.setRQPresContextCollection(objRQPresContextPairedCollection);
				objAssociateRQ.setRQUserInfo(objRQUserInfo);
			}
		}
		catch(InvalidAssociationPrimitiveException iape)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","AssociationException.OperationFail",new Locale(DicomConfig.createInstance().getLocale())));
			}
			throw new AssociationLoadFailedException(ResourceBundleWrapper.getValue("exceptions","AssociationException.OperationFail",new Locale(DicomConfig.createInstance().getLocale())));
		}

		return objAssociateRQ;
	}

	/**
	 * Retrieves the Association RQ.
	 * If Default Association is not loaded then it will be loaded for default values and  
	 * It generates RequestPresentationContext list by provided collection of SOPClass UID and Transfer Syntax.  
	 * @param strRequestedAETitle AE title of service provider 
	 * @param objPresContextList collection of Abstact syntax and corresponding transfer syntax list.
	 * @return AssociateRQ - the default association request.
	 */
	public AssociateRQ getAssociationRQ(String strRequestedAETitle,IPairedCollection<EnumSOPClassUIDs,ISingleCollection<String>> objPresContextList)throws AssociationLoadFailedException
	{
		AssociateRQ objAssociateRQ = new AssociateRQ();
		byte bPresContextID = 1;
		byte increment = 2;
		IPairedCollection<Byte,RQPresContext> objRQPresContextPairedCollection = null;

		if(!isLoaded)
			loadDefault();

		objAssociateRQ.setProtocolversion(shProtocolVersion);
		objAssociateRQ.setCalledEntityTitle(strRequestedAETitle);
		objAssociateRQ.setCallingEntityTitle(strCallingAETitle);
		objAssociateRQ.setAppContextName(strAppContextName);
		if(objPresContextList.size() > 64)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","AssociationException.LimitExceeded",new Locale(DicomConfig.createInstance().getLocale()))+ EnumServiceType.CGET.getValue());
			}
			throw new AssociationLoadFailedException(ResourceBundleWrapper.getValue("exceptions", "AssociationException.LimitExceeded", new Locale(DicomConfig.createInstance().getLocale())));
		}
		else
		{
			objRQPresContextPairedCollection = new PairedCollection<Byte, RQPresContext>();

			Object []arrobjAbsSyntax = objPresContextList.getKeys();
			EnumSOPClassUIDs []arrenumAbsSyntax = new EnumSOPClassUIDs[arrobjAbsSyntax.length];
			int length = 0;
			while(length < arrobjAbsSyntax.length)
			{
				arrenumAbsSyntax[length] = (EnumSOPClassUIDs)(arrobjAbsSyntax[length]);
				length++;
			}

			RQPresContext objRQPresContext = null;
			for(int i = 0 ; i < arrenumAbsSyntax.length ; i++)
			{
				objRQPresContext = new RQPresContext();
				objRQPresContext.setAbstractSyntaxName(arrenumAbsSyntax[i].getValue());
				objRQPresContext.setTSCollection(objPresContextList.get(arrenumAbsSyntax[i]));
				objRQPresContextPairedCollection.put(bPresContextID,objRQPresContext);
				bPresContextID = (byte)(bPresContextID + increment);
			}
		}
		objAssociateRQ.setRQPresContextCollection(objRQPresContextPairedCollection);
		objAssociateRQ.setRQUserInfo(objRQUserInfo);
		return objAssociateRQ;
	}

	/*
	 * Populates the transfer syntax list with all supported values.
	 */
	private void populateTSList()
	{
		objDefTransferSyntaxList = new SingleCollection<String>();
		objDefTransferSyntaxList.add(DicomConstants.TS_DEFAULT_IMPLICIT_VR_LITTLE_ENDIAN);
		objDefTransferSyntaxList.add(DicomConstants.TS_EXPLICIT_VR_LITTLE_ENDIAN);
		objDefTransferSyntaxList.add(DicomConstants.TS_JPEG_2000_PART2_MULTI_COMPONENT_IMAGECOMPRESSION_LOSSLESS_ONLY);
		objDefTransferSyntaxList.add(DicomConstants.TS_DEFLATED_EXPLICIT_VR_LITTLE_ENDIAN);
		objDefTransferSyntaxList.add(DicomConstants.TS_EXPLICIT_VR_BIG_ENDIAN);
		objDefTransferSyntaxList.add(DicomConstants.TS_LOSSY_JPEG_8_BIT);
		objDefTransferSyntaxList.add(DicomConstants.TS_LOSSY_JPEG_12_BIT);
		objDefTransferSyntaxList.add(DicomConstants.TS_JPEG_LOSSLESS_NON_HIERARCHICAL_PROCESS_14);
		objDefTransferSyntaxList.add(DicomConstants.TS_DEFAULT_LOSSLESS_JPEG);
		objDefTransferSyntaxList.add(DicomConstants.TS_JPEG_LS_LOSSLESS);
		objDefTransferSyntaxList.add(DicomConstants.TS_JPEG_LS_LOSSY_NEAR_LOSSLESS);
		objDefTransferSyntaxList.add(DicomConstants.TS_JPEG_2000_LOSSLESS_ONLY);
		objDefTransferSyntaxList.add(DicomConstants.TS_JPEG_2000);
		objDefTransferSyntaxList.add(DicomConstants.TS_JPEG_2000_PART2_MULTI_COMPONENT_IMAGECOMPRESSION);
		objDefTransferSyntaxList.add(DicomConstants.TS_JPIP_REFERENCED);
		objDefTransferSyntaxList.add(DicomConstants.TS_JPIP_REFERENCED_DEFLATE);
		objDefTransferSyntaxList.add(DicomConstants.TS_RLE_LOSSLESS);
		objDefTransferSyntaxList.add(DicomConstants.TS_MPEG_MAIN_PROFILE);
		objDefTransferSyntaxList.add(DicomConstants.TS_MPEG2_MAIN_PROFILE_HIGH_LEVEL);
		objDefTransferSyntaxList.add(DicomConstants.TS_MPEG_4_AVC_H264_HIGH_PROFILE_LEVEL_4_1);
		objDefTransferSyntaxList.add(DicomConstants.TS_MPEG_4_AVC_H264_BD_COMPATIBLE_HIGHPROFILE_LEVEL_4_1);
		objDefTransferSyntaxList.add(DicomConstants.TS_MPEG_4_AVC_H264_HIGH_PROFILE_LEVEL_4_2_FOR_2D_VIDEO);
		objDefTransferSyntaxList.add(DicomConstants.TS_MPEG_4_AVC_H264_HIGH_PROFILE_LEVEL_4_2_FOR_3D_VIDEO);
		objDefTransferSyntaxList.add(DicomConstants.TS_MPEG_4_AVC_H264_STEREO_HIGH_PROFILE_LEVEL_4_2);
		objDefTransferSyntaxList.add(DicomConstants.TS_RFC_2557_MIME_ENCAPSULATION);
		objDefTransferSyntaxList.add(DicomConstants.TS_XML_ENCODING);
	}
}
