/*******************************************************************************
 *  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.util.Locale;

import cdac.medinfo.sdk.dcm30_04.enums.EnumMetaSOPClassUID;
import cdac.medinfo.sdk.dcm30_04.enums.EnumSOPClassUIDs;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.AssociationConstants;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumACKReason;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumCommunicationMode;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumLevelofDigitalSignatureSupport;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumLevelofSupport;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumRejectReason;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumRejectResult;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumRejectSource;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConfig;
import cdac.medinfo.sdk.dcm30_04.utils.DicomLogger;
import cdac.medinfo.sdk.dcm30_04.utils.ICollectionIterator;
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;

/**
 * This class generates Association RJ or AC according to the comparison of Default settings of Association and received Association Request.
 */
public class AssociationNegotiator  
{	
	/*
	 * It compares each AbstractSyntaxName of RQ with all AbstractSyntaxName of DefaultAssociation.
	 * If AbstractSyntaxName matches with AbstractSyntaxName of DefaultAssociation then for that AbstractSyntaxName
	 * then it compares each TransferSyntaxName of that RQ AbstractSyntaxName with all TransferSyntaxNames of matched TS of DefaultAssociation.
	 * and sets the very first matched TS in AssociationAC and returns.
	 * It performs this check for all RQPresentationContext from Collection.    
	 */

	/** object of agreed association. */
	AgreedAssociation objAgreedAssociation = null;

	/** object of Default Association */
	DefaultAssociation objDefaultAssociation = null;

	private static int idefMaxLength = 16384;

	/**
	 * Gets the agreed association.
	 * @return AgreedAssociation - instance of agreed association
	 */
	public AgreedAssociation getAgreedAssociation() 
	{
		return objAgreedAssociation;
	}

	/*
	 * This method provides Association AC and RJ.
	 * It retrieves RQAbsSyntaxName and pass it to method which returns AgreedPresContext.
	 * Through iteration to all PresentationContexts it generates collection of AgreedPresContext.
	 * Using this collection it generates either collection of ACPresContext or AssociationRJ.
	 * Generates AssociateRJ If :-
	 * 						1. No AbsSyntaxName of RQ matches with AbsSyntaxName of DefaultAssociation.
	 * 						2. AbsSyntaxName matched but no TransferSyntax for that AbstractSyntaxName matched.
	 * else it generates AssociateAC
	 */
	/**
	 * This method negotiate the association request with Default association configuration set on SCP side.
	 * If none of presentation context provided in RQ are matched then reject will be generated else ACK will be generated.
	 * If association already exist then current association configuration will be modified according to new request.
	 * If default association is not configured properly on SCP then it generates null.
	 * @param objDefAssociation object of default association
	 * @param objAssociateRQ object of association request.
	 * @return IDIMSEAssociation - instance of DIMSE Association.
	 */
	public IDIMSEAssociation negotiate(DefaultAssociation objDefAssociation, AssociateRQ objAssociateRQ)
	{
		this.objDefaultAssociation = objDefAssociation;

		/*
		 * Null will be returned if Default Association is not configured properly.
		 */
		if(objDefaultAssociation == null || objDefaultAssociation.getRQPresContextCollection() == null)
		{
			if (DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.DefaultAssociationNotPopulated",new Locale(DicomConfig.createInstance().getLocale())));
			}
			return null;
		}

		String  strDefCallingAE = objDefaultAssociation.getCallingAETitle();
		String strRQCalledAE = objAssociateRQ.getCalledEntityTitle();

		/*
		 * This If block is checking whether Called AE title in Association RQ is same as specified in DefaultAssociation for this SCP or not.
		 * If AE title is not correct then RJ will be generated.
		 */
		if(strDefCallingAE == null || strRQCalledAE == null || !strDefCallingAE.trim().equalsIgnoreCase(strRQCalledAE.trim()))
		{
			AssociateRJ objAssociateRJ = new AssociateRJ();
			objAssociateRJ.setReason(EnumRejectReason.CALLED_AE_TITLE_NOT_RECOGNIZED);
			objAssociateRJ.setResult(EnumRejectResult.REJECT_PERMANENT);
			objAssociateRJ.setSource(EnumRejectSource.SERVICE_USER);
			if (DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.CalledAETitleNotMatched",new Locale(DicomConfig.createInstance().getLocale())));
			}
			return objAssociateRJ;
		}

		ISingleCollection<AgreedPresContext> objAgrPresContextCollection = new SingleCollection<AgreedPresContext>();
		IPairedCollection<Byte,RQPresContext> objRQPresContextCollection = objAssociateRQ.getRQPresContextCollection();

		/*
		 * Generating association rejection if presentation context collection is not present.
		 */
		if(objRQPresContextCollection == null)
		{
			if (DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.InvalidAssociationRQ",new Locale(DicomConfig.createInstance().getLocale())));
			}
			AssociateRJ objAssociateRJ = new AssociateRJ();
			objAssociateRJ.setReason(EnumRejectReason.APPLICATION_CONTEXT_NOT_SUPPORTED);
			objAssociateRJ.setResult(EnumRejectResult.REJECT_PERMANENT);
			objAssociateRJ.setSource(EnumRejectSource.SERVICE_USER);

			return objAssociateRJ;
		}

		Object []arrObjPresContextID = objRQPresContextCollection.getKeys();
		Byte []arrBytePresContextIDKeys = new Byte[arrObjPresContextID.length];
		int length = 0;
		while(length < arrObjPresContextID.length)
		{
			arrBytePresContextIDKeys[length] = (Byte.parseByte(arrObjPresContextID[length].toString()));
			length++;
		}

		AgreedPresContext objAgreedPresContext = null;
		int i = 0;
		for(RQPresContext objRQPresContext : objRQPresContextCollection)
		{
			String strRQAbsSyntax = objRQPresContext.getAbstractSyntaxName();
			ISingleCollection<String> RQTSCollection = objRQPresContext.getTSCollection();

			objAgreedPresContext = getAssocPresContext(strRQAbsSyntax, RQTSCollection);

			/*
			 * Returned objAgreedPresContext will be null if sent AbstractSyntax are not matched.
			 * Entry for unmatched AbsSyntax does not contribute in the ACK. 
			 */
			if(objAgreedPresContext != null)
			{
				objAgreedPresContext.setAbstractSyntaxName(strRQAbsSyntax);
				objAgreedPresContext.setPresContextId(arrBytePresContextIDKeys[i]);
				objAgrPresContextCollection.add(objAgreedPresContext);

				EnumMetaSOPClassUID enumMetaSOPClassUID = getMetaSOPClassUID(strRQAbsSyntax);

				if(enumMetaSOPClassUID != null)
				{
					String strReplacedUID = "";
					ISingleCollection<String> objAllSOPClassUID = getAllSOPClassUID(enumMetaSOPClassUID);
					if(objAllSOPClassUID != null && objAllSOPClassUID.size() != 0)
					{
						for( String strSOPClassUID : objAllSOPClassUID)
						{
							strReplacedUID += strSOPClassUID + ":";
							AgreedPresContext objTempAgreedPresContext = new AgreedPresContext();
							objTempAgreedPresContext.setAbstractSyntaxName(strSOPClassUID);
							objTempAgreedPresContext.setPresContextId(arrBytePresContextIDKeys[i]);
							objTempAgreedPresContext.setReason(objAgreedPresContext.getReason());
							objTempAgreedPresContext.setTS(objAgreedPresContext.getTS());
							objAgrPresContextCollection.add(objTempAgreedPresContext);
						}

						if (DicomConfig.createInstance().isLoggingOn())
						{
							DicomLogger objDicomLogger = DicomLogger.getLogger();
							objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.NegotiationDoneOnMetaSOPClassUID",new Locale(DicomConfig.createInstance().getLocale())) + enumMetaSOPClassUID.getValue());
							objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.SupportedUIDForMetaValue",new Locale(DicomConfig.createInstance().getLocale())) + strReplacedUID);
						}
					}

				}

			}
			else
			{
				if (DicomConfig.createInstance().isLoggingOn())
				{
					DicomLogger objDicomLogger = DicomLogger.getLogger();
					objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.AbstractSyntaxNotSupported",new Locale(DicomConfig.createInstance().getLocale())) + strRQAbsSyntax);
				}
			}
			i++;
		}

		/*
		 * If no AbstarctSyntax are matched between RQ and Default then this collection will be null.
		 * In this case RJ command will be generated. 
		 */
		if(objAgrPresContextCollection.size() < 1)
		{
			if (DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.RequestWithUnsupportedAbstractSyntax",new Locale(DicomConfig.createInstance().getLocale())));
			}
			AssociateRJ objAssociateRJ = new AssociateRJ();
			objAssociateRJ.setReason(EnumRejectReason.APPLICATION_CONTEXT_NOT_SUPPORTED);
			objAssociateRJ.setResult(EnumRejectResult.REJECT_PERMANENT);
			objAssociateRJ.setSource(EnumRejectSource.SERVICE_USER);

			return objAssociateRJ;
		}

		IPairedCollection<Byte,ACPresContext> objACPresCollection = getPresContextCollection(objAgrPresContextCollection);

		/*
		 * If AbstarctSynatx are matched but no TransferSyntax are matched for any of those matched AbsSyntax.
		 * Then this ACcollection will be null and RJ will be generated.
		 * Else ACK will be populated.
		 */
		if(objACPresCollection == null)
		{
			if (DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.RequestWithUnsupportedTransferSyntax",new Locale(DicomConfig.createInstance().getLocale())));
			}
			AssociateRJ objAssociateRJ = new AssociateRJ();
			objAssociateRJ.setReason(EnumRejectReason.NO_REASON);
			objAssociateRJ.setResult(EnumRejectResult.REJECT_PERMANENT);
			objAssociateRJ.setSource(EnumRejectSource.SERVICE_PROVIDER_ACSE);

			return objAssociateRJ;
		}
		else
		{
			AssociateAC objAssociateAC = new AssociateAC();
			objAgreedAssociation = new AgreedAssociation();

			objAssociateAC.setProtocolversion(objAssociateRQ.getProtocolversion());
			objAssociateAC.setCalledEntityTitle(objAssociateRQ.getCalledEntityTitle());
			objAssociateAC.setCallingEntityTitle(objAssociateRQ.getCallingEntityTitle());

			objAgreedAssociation.setProtocolVersion(objAssociateRQ.getProtocolversion());
			objAgreedAssociation.setCalledAETitle(objAssociateRQ.getCalledEntityTitle());
			objAgreedAssociation.setCallingAETitle(objAssociateRQ.getCallingEntityTitle());

			objAssociateAC.setAppContextName(objAssociateRQ.getAppContextName());
			objAgreedAssociation.setAppContextName(objAssociateRQ.getAppContextName());

			//Setting UserInfo
			UserInfo objACUserInfo = new UserInfo();
			UserInfo objAgreedUserInfo = new UserInfo();
			RQUserInfo objRQUserInfo = objAssociateRQ.getRQUserInfo();

			if(objRQUserInfo != null)
			{
				/*
				 * In max length field each entity sends it's own capacity.So while generating ACK length should be taken from DefaultSettings. 
				 */
				UserInfo objDefUserInfo = objDefaultAssociation.getRQUserInfo();

				/*
				 * If DefaultAssociation given by user does not contain userinfo or max length then it generates DefAssociation from file and sets it.
				 */
				if(objDefUserInfo == null || objDefUserInfo.getmaxlength() <= 0)
				{
					objACUserInfo.setmaxlength(idefMaxLength);
					objAgreedUserInfo.setmaxlength(idefMaxLength);
				}
				else
				{
					objACUserInfo.setmaxlength(objDefUserInfo.getmaxlength());
					objAgreedUserInfo.setmaxlength(objDefUserInfo.getmaxlength());
				}

				//This if-else sets max write length from def settings if not given in RQ.
				if(objRQUserInfo.getmaxlength() <= 0 )
				{
					if(objDefUserInfo.getmaxlength() <= 0)
						objAgreedAssociation.setWriteMaxLength(idefMaxLength);
					else
						objAgreedAssociation.setWriteMaxLength(objDefUserInfo.getmaxlength());
				}
				else 
					objAgreedAssociation.setWriteMaxLength(objRQUserInfo.getmaxlength());

				//This statement sets max read length from default settings.
				objAgreedAssociation.setReadMaxLength(objDefUserInfo.getmaxlength());

				objACUserInfo.setImplclassUID(objRQUserInfo.getImplclassUID());
				objACUserInfo.setImplVersionName(objRQUserInfo.getImplVersionName());
				objAgreedUserInfo.setImplclassUID(objRQUserInfo.getImplclassUID());
				objAgreedUserInfo.setImplVersionName(objRQUserInfo.getImplVersionName());

				//If in request mode is synchronous
				if(objRQUserInfo.getMaxNumOpsInvoked() == 1 && objRQUserInfo.getMaxNumOpsPerformed() == 1)
				{
					objAgreedAssociation.setCommMode(EnumCommunicationMode.SYNCHRONOUS);
					objACUserInfo.setSynchronousMode();
					objAgreedUserInfo.setSynchronousMode();
				}
				else
				{
					if(objDefUserInfo == null || (objDefUserInfo.getMaxNumOpsInvoked() == 1 && objDefUserInfo.getMaxNumOpsPerformed() == 1))
					{
						objACUserInfo.setSynchronousMode();
						objAgreedUserInfo.setSynchronousMode();
						objAgreedAssociation.setCommMode(EnumCommunicationMode.SYNCHRONOUS);
					}
					else if(objDefUserInfo.getMaxNumOpsInvoked() == 0 && objDefUserInfo.getMaxNumOpsPerformed() == 0)
					{
						objACUserInfo.setMaxNumOpsInvoked(objRQUserInfo.getMaxNumOpsInvoked());
						objACUserInfo.setMaxNumOpsPerformed(objRQUserInfo.getMaxNumOpsPerformed());
						objAgreedUserInfo.setMaxNumOpsInvoked(objRQUserInfo.getMaxNumOpsPerformed());
						objAgreedUserInfo.setMaxNumOpsPerformed(objRQUserInfo.getMaxNumOpsInvoked());
						if(objRQUserInfo.getMaxNumOpsInvoked() == 0 && objRQUserInfo.getMaxNumOpsPerformed() == 0)
							objAgreedAssociation.setCommMode(EnumCommunicationMode.UNBOUNDED_ASYNCHRONOUS);
						else
							objAgreedAssociation.setCommMode(EnumCommunicationMode.BOUNDED_ASYNCHRONOUS);
					}
					else
					{
						short shDefOpsPerformed = objDefUserInfo.getMaxNumOpsPerformed();
						short shDefOpsInvoked = objDefUserInfo.getMaxNumOpsInvoked();
						short shRQOpsPerformed = objRQUserInfo.getMaxNumOpsPerformed();
						short shRQOpsInvoked = objRQUserInfo.getMaxNumOpsInvoked();

						objAgreedAssociation.setCommMode(EnumCommunicationMode.BOUNDED_ASYNCHRONOUS);
						/*
						 * In this If-Else block it is checking for invoking and performing capacity according to the received RQ.
						 * invoking capacity of SCP will be decided according to the performing capacity of SCU.
						 * invoking capacity of SCU will be decided according to the performing capacity of SCP.
						 * Less values will be taken from RQ and Default Association.  
						 */ 
						if(shDefOpsPerformed <= shRQOpsInvoked && shDefOpsPerformed != 0)
						{
							objACUserInfo.setMaxNumOpsInvoked(shDefOpsPerformed);
							objAgreedUserInfo.setMaxNumOpsPerformed(shDefOpsPerformed);
						}
						else if(shRQOpsInvoked != 0)
						{
							objACUserInfo.setMaxNumOpsInvoked(shRQOpsInvoked);
							objAgreedUserInfo.setMaxNumOpsPerformed(shRQOpsInvoked);
						}
						else
						{
							objACUserInfo.setMaxNumOpsInvoked(shDefOpsPerformed);
							objAgreedUserInfo.setMaxNumOpsPerformed(shDefOpsPerformed);
						}

						if(shDefOpsInvoked <= shRQOpsPerformed && shDefOpsInvoked != 0)
						{
							objACUserInfo.setMaxNumOpsPerformed(shDefOpsInvoked);
							objAgreedUserInfo.setMaxNumOpsInvoked(shDefOpsInvoked);
						}
						else if(shRQOpsPerformed != 0)
						{
							objACUserInfo.setMaxNumOpsPerformed(shRQOpsPerformed);
							objAgreedUserInfo.setMaxNumOpsInvoked(shRQOpsPerformed);
						}
						else
						{
							objACUserInfo.setMaxNumOpsPerformed(shDefOpsInvoked);
							objAgreedUserInfo.setMaxNumOpsInvoked(shDefOpsInvoked);
						}
					}
				}
				objACUserInfo.setSCUSCPRole(objRQUserInfo.getSCUSCPRole());
				objACUserInfo.setSCUSCPRoleSelectionSOPclassuid(objRQUserInfo.getSCUSCPRoleSelectionSOPclassuid());
				objAgreedUserInfo.setSCUSCPRole(objRQUserInfo.getSCUSCPRole());
				objAgreedUserInfo.setSCUSCPRoleSelectionSOPclassuid(objRQUserInfo.getSCUSCPRoleSelectionSOPclassuid());

				//objRQUserInfo request user info
				//objDefUserInfo  Default Association userinfo
				//objAgreedUserInfo Agreed User Info

				//	if extended negotiation item is present in both i.e. in RQ and Default Association
				if(null != objRQUserInfo.getSOPClassExtendedNegotiationSubItemFields() && null != objDefUserInfo.getSOPClassExtendedNegotiationSubItemFields()
						&& objRQUserInfo.getSOPClassExtendedNegotiationSubItemFields().size() > 0
						&& objDefUserInfo.getSOPClassExtendedNegotiationSubItemFields().size() > 0 )
				{	

					ISingleCollection<SOPClassExtendedNegotiationSubItemFields> objSupportedItems = null;
					ISingleCollection<SOPClassExtendedNegotiationSubItemFields>  objRQItems = objRQUserInfo.getSOPClassExtendedNegotiationSubItemFields();
					ISingleCollection<SOPClassExtendedNegotiationSubItemFields>  objDefItems = objDefUserInfo.getSOPClassExtendedNegotiationSubItemFields();					

					for (SOPClassExtendedNegotiationSubItemFields objRQItem : objRQItems)
					{
						boolean bUIDMatchingStatus = false;
						for (SOPClassExtendedNegotiationSubItemFields  objDefItem : objDefItems) 
						{							
							if(objRQItem.getSOPClassExtNegotiationSOPclassuid().equals(objDefItem.getSOPClassExtNegotiationSOPclassuid()))
							{
								if(objSupportedItems == null)
									objSupportedItems = new SingleCollection<SOPClassExtendedNegotiationSubItemFields>();

								objSupportedItems.add(objDefItem);
								
								bUIDMatchingStatus = true;
							}
						}
						if(!bUIDMatchingStatus)
						{
							if (DicomConfig.createInstance().isLoggingOn())
							{
								DicomLogger objDicomLogger = DicomLogger.getLogger();
								objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.ExtItemNegotiationNotSupportedForUID",new Locale(DicomConfig.createInstance().getLocale())) + objRQItem.getSOPClassExtNegotiationSOPclassuid());
							}
						}
					}		

					if(objSupportedItems  != null)
					{
						objACUserInfo.setSOPClassExtendedNegotiationSubItemFields(objSupportedItems);
						ICollectionIterator<SOPClassExtendedNegotiationSubItemFields>iterator = objSupportedItems.iterator();
						if(iterator.hasNext())
						{
							SOPClassExtendedNegotiationSubItemFields subItem = iterator.next();
							String strExtSOPClass = subItem.getSOPClassExtNegotiationSOPclassuid();
							
							EnumLevelofSupport enumLevelofSupport = subItem.getLevelofSupport();
							EnumLevelofDigitalSignatureSupport enumLevelOfDigitalSigSupport = subItem.getLevelofDigitalSignatureSupport();
							
							if (DicomConfig.createInstance().isLoggingOn())
							{
								DicomLogger objDicomLogger = DicomLogger.getLogger();
								objDicomLogger.log(ResourceBundleWrapper.getValue("status", "LevelOfSupport",new Locale(DicomConfig.createInstance().getLocale())) + enumLevelofSupport.name());
																
								objDicomLogger.log(ResourceBundleWrapper.getValue("status", "LevelOfSupportInfo",new Locale(DicomConfig.createInstance().getLocale())));
							}							
							
							if(enumLevelofSupport.equals(EnumLevelofSupport.LEVEL2))
							{
								if (DicomConfig.createInstance().isLoggingOn())
								{
									DicomLogger objDicomLogger = DicomLogger.getLogger();
									objDicomLogger.log(ResourceBundleWrapper.getValue("status", "AssociationNegotiation.ExtendedNegotiationInfo",new Locale(DicomConfig.createInstance().getLocale()))
											+ strExtSOPClass);

									if(enumLevelOfDigitalSigSupport.equals(EnumLevelofDigitalSignatureSupport.LEVEL0))
									{
										objDicomLogger.log(ResourceBundleWrapper.getValue("status", "DSPLevel_0",new Locale(DicomConfig.createInstance().getLocale())));
									}
									else if(enumLevelOfDigitalSigSupport.equals(EnumLevelofDigitalSignatureSupport.LEVEL1))
									{
										objDicomLogger.log(ResourceBundleWrapper.getValue("status", "DSPLevel_1",new Locale(DicomConfig.createInstance().getLocale())));
									}
									else if(enumLevelOfDigitalSigSupport.equals(EnumLevelofDigitalSignatureSupport.LEVEL2))
									{
										objDicomLogger.log(ResourceBundleWrapper.getValue("status", "DSPLevel_2",new Locale(DicomConfig.createInstance().getLocale())));
									}
									else if(enumLevelOfDigitalSigSupport.equals(EnumLevelofDigitalSignatureSupport.LEVEL3))
									{
										objDicomLogger.log(ResourceBundleWrapper.getValue("status", "DSPLevel_3",new Locale(DicomConfig.createInstance().getLocale())));
									}
								}
							}
						}
						objAgreedUserInfo.setSOPClassExtendedNegotiationSubItemFields(objSupportedItems);
					}
					else
					{
						if (DicomConfig.createInstance().isLoggingOn())
						{
							DicomLogger objDicomLogger = DicomLogger.getLogger();
							objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.ExtItemNegotiationUnsupportedForALL",new Locale(DicomConfig.createInstance().getLocale())));
						}
					}
				}
				else if(null != objRQUserInfo.getSOPClassExtendedNegotiationSubItemFields() && objRQUserInfo.getSOPClassExtendedNegotiationSubItemFields().size() > 0)
				{
					if (DicomConfig.createInstance().isLoggingOn())
					{
						DicomLogger objDicomLogger = DicomLogger.getLogger();
						objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.ExtItemNegotiationNotRequested",new Locale(DicomConfig.createInstance().getLocale())));
					}
				}
				else
				{
					if (DicomConfig.createInstance().isLoggingOn())
					{
						DicomLogger objDicomLogger = DicomLogger.getLogger();
						objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.ExtItemNegotiationNotSupported",new Locale(DicomConfig.createInstance().getLocale())));
					}					
				}
				objAssociateAC.setUserInfo(objACUserInfo);
			}
			else
			{
				objAgreedAssociation.setCommMode(EnumCommunicationMode.SYNCHRONOUS);
				/*
				 * If DefaultAssociation given by user does not contain userinfo or max length then it generates DefAssociation from file and sets it.
				 */
				UserInfo objDefUserInfo = objDefaultAssociation.getRQUserInfo();
				{
					if(objDefUserInfo == null || objDefUserInfo.getmaxlength() <= 0)
					{
						objACUserInfo.setmaxlength(idefMaxLength);
						objAgreedUserInfo.setmaxlength(idefMaxLength);
						objAgreedAssociation.setWriteMaxLength(idefMaxLength);
					}
					else
					{
						objACUserInfo.setmaxlength(objDefUserInfo.getmaxlength());
						objAgreedUserInfo.setmaxlength(objDefUserInfo.getmaxlength());
						objAgreedAssociation.setWriteMaxLength(objDefUserInfo.getmaxlength());
					}
				}
			}
			objAgreedAssociation.setUserInfo(objAgreedUserInfo);
			{
				int iNegotiatedMaxLength = objAgreedUserInfo.getmaxlength();
				EnumCommunicationMode enumNegotiatedComMode = objAgreedAssociation.getCommMode();
				if (DicomConfig.createInstance().isLoggingOn())
				{
					DicomLogger objDicomLogger = DicomLogger.getLogger();
					objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.MaxLengthNegotiated",new Locale(DicomConfig.createInstance().getLocale())) + iNegotiatedMaxLength);

					if(enumNegotiatedComMode == EnumCommunicationMode.SYNCHRONOUS)
						objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.SynchronousCommunicationModeNegotiated",new Locale(DicomConfig.createInstance().getLocale())));
					else 
						objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.AsynchronousCommunicationModeNegotiated",new Locale(DicomConfig.createInstance().getLocale())));
				}
			}

			objAssociateAC.setACPresContextCollection(objACPresCollection);
			objAgreedAssociation.setAgreedPresContextCollection(objAgrPresContextCollection);

			return objAssociateAC;
		}
	}

	/*
	 * This method compares passed AbstarctSyntaxName with DefaultAssociation and then if matched then compares for TransferSyntax.
	 * It returns AgreedPresContext(sets TSName and Reason)if matched else returns null.
	 */
	private AgreedPresContext getAssocPresContext(String strRQAbsSyntax, ISingleCollection<String> RQTSCollection)
	{
		ISingleCollection<RQPresContext> DefPresContextCollection = objDefaultAssociation.getRQPresContextCollection();
		AgreedPresContext objAgreedPresContext = null;
		/*
		 * This loop compares sent AbstarctSynatx with all entries of DefaultAssociation.
		 */
		for(RQPresContext objDefRQPresContext : DefPresContextCollection)
		{
			String strDefAbsSyntax = objDefRQPresContext.getAbstractSyntaxName();
			// If AbstarctSyntax matches then for that AbsSyntax it compares each entry of RQ TS collection with all Default TS Collection. 
			if(strRQAbsSyntax.equals(strDefAbsSyntax))
			{
				objAgreedPresContext = new AgreedPresContext();
				String strRQTS = null;
				for(String strTempRQTS : RQTSCollection)
				{
					strRQTS = strTempRQTS;
					for(String strDEFTS : objDefRQPresContext.getTSCollection())
					{
						// If RQ and Def TS matches then TS and acceptance reason will be set.
						if(strRQTS.equals(strDEFTS))
						{
							objAgreedPresContext.setReason(AssociationConstants.ASSOCIATION_REASON_ACCEPTANCE);
							objAgreedPresContext.setTS(strRQTS);
							return objAgreedPresContext;
						}
					}
				}
				//This case occurs AbsSyntax match but no TS match for that AbsSyntax.
				objAgreedPresContext.setReason(AssociationConstants.ASSOCIATION_REASON_TRANSFER_SYNTAX_NOT_SUPPORTED);
				objAgreedPresContext.setTS(strRQTS);
				if (DicomConfig.createInstance().isLoggingOn())
				{
					DicomLogger objDicomLogger = DicomLogger.getLogger();
					objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "AssociationNegotiation.TransferSyntaxNotSupported",new Locale(DicomConfig.createInstance().getLocale())) + strRQAbsSyntax);
				}
			}
		}
		return objAgreedPresContext;
	}

	/*
	 * This method generates collection of ACPresContext and modifies collection of AgreedPresContext
	 * if any one AbsSyntaxName and TSName is matched 
	 * else it sends null.
	 */
	private IPairedCollection<Byte,ACPresContext> getPresContextCollection(ISingleCollection<AgreedPresContext> objAgreedSingleCollection)
	{
		IPairedCollection<Byte,ACPresContext> objTempACSingleCollection = new PairedCollection<Byte, ACPresContext>();

		ICollectionIterator<AgreedPresContext> objAgreedCollectionIterator = objAgreedSingleCollection.iterator();
		AgreedPresContext objAgreedContext = null;
		boolean rejectStatus = true;
		// For all AgreedPresContext it generates ACPresContext and adds it in AC Collection.
		while(objAgreedCollectionIterator.hasNext())
		{
			objAgreedContext = objAgreedCollectionIterator.next();

			ACPresContext objTempACPresContext = new ACPresContext();
			//objTempACPresContext.setPresContextId(objAgreedContext.getPresContextId());
			objTempACPresContext.setReason(getEnumACKReason(objAgreedContext.getReason()));
			objTempACPresContext.setTransferSyntaxName(objAgreedContext.getTS());
			objTempACSingleCollection.put(objAgreedContext.getPresContextId(), objTempACPresContext);

			if(objAgreedContext.getReason() == AssociationConstants.ASSOCIATION_REASON_TRANSFER_SYNTAX_NOT_SUPPORTED)
			{

				// If AbstarctSyntax is matched but no TS is found then this will contribute in ACK but not in Agreed Collection.
				objAgreedSingleCollection.remove(objAgreedContext);
				objAgreedCollectionIterator = objAgreedSingleCollection.iterator();
			}
			//If for min one AbsSyntax TS is found then ACK will be generated.
			if(objAgreedContext.getReason() == AssociationConstants.ASSOCIATION_REASON_ACCEPTANCE)
				rejectStatus = false;
		}


		if(rejectStatus == true)
			return null;
		else
			return objTempACSingleCollection;
	}

	/* this private function takes the byte value and returns enum value of EnumACKReason*/
	private EnumACKReason getEnumACKReason(byte byteReason)
	{
		EnumACKReason enumReason = null;
		switch(byteReason)
		{
		case (byte)0:
			enumReason = EnumACKReason.ACCEPTANCE;
		break;
		case (byte)1:
			enumReason = EnumACKReason.USER_REJECTION;
		break;
		case (byte)2:
			enumReason = EnumACKReason.NO_REASON;
		break;
		case (byte)3:
			enumReason = EnumACKReason.ABSTRACT_SYNTAX_NOT_SUPPORTED;
		break;
		case (byte)4:
			enumReason = EnumACKReason.TRANSFER_SYNTAX_NOT_SUPPORTED;
		}
		return enumReason;
	}

	/*
	 * This function returns all the SOP class related to enumMetaSOP class provided.
	 */
	private ISingleCollection<String> getAllSOPClassUID(EnumMetaSOPClassUID enumMetaSOPClassUID)
	{
		ISingleCollection<String> objSingleCollection = new SingleCollection<String>();
		switch(enumMetaSOPClassUID)
		{
		case BASIC_GRAYSCALE_PRINT_MANAGEMENT_META_SOP_CLASS :
			objSingleCollection.add(EnumSOPClassUIDs.BASICFILMSESSIONIOD.getValue());
			objSingleCollection.add(EnumSOPClassUIDs.BASICFILMBOXIOD.getValue());
			objSingleCollection.add(EnumSOPClassUIDs.BASICGRAYSCALEIMAGEBOX.getValue());
			objSingleCollection.add(EnumSOPClassUIDs.PRINTERIOD.getValue());
			return objSingleCollection;
		case BASIC_COLOR_PRINT_MANAGEMENT_META_SOP_CLASS :
			objSingleCollection.add(EnumSOPClassUIDs.BASICFILMSESSIONIOD.getValue());
			objSingleCollection.add(EnumSOPClassUIDs.BASICFILMBOXIOD.getValue());
			objSingleCollection.add(EnumSOPClassUIDs.BASICCOLORIMAGEBOX.getValue());
			objSingleCollection.add(EnumSOPClassUIDs.PRINTERIOD.getValue());
			return objSingleCollection;
		case PULL_STORED_PRINT_MANAGEMENT_META_SOP_CLASS :
			objSingleCollection.add(EnumSOPClassUIDs.PULLPRINTREQUESTIOD.getValue());
			objSingleCollection.add(EnumSOPClassUIDs.PRINTERIOD.getValue());
			return objSingleCollection;
		case STUDY_MANAGEMENT_META_SOP_CLASS :
			objSingleCollection.add(EnumSOPClassUIDs.DETACHEDSTUDYMANAGEMENTIOD.getValue());
			objSingleCollection.add(EnumSOPClassUIDs.STUDYCOMPONENTMANAGEMENT.getValue());
			return objSingleCollection;
		case DETACHED_RESULT_MANAGEMENT_META_SOP_CLASS:
			objSingleCollection.add(EnumSOPClassUIDs.DETACHEDRESULTSMANAGEMENTIOD.getValue());
			objSingleCollection.add(EnumSOPClassUIDs.DETACHEDINTERPRETATIONMANAGEMENTIOD.getValue());
			return objSingleCollection;
		case DETACHED_PATIENT_MANAGEMENT_META_SOP_CLASS:
			objSingleCollection.add(EnumSOPClassUIDs.DETACHEDPATIENTMANAGEMENTIOD.getValue());
			objSingleCollection.add(EnumSOPClassUIDs.DETACHEDVISITMANAGEMENTIOD.getValue());
			return objSingleCollection;
		}
		return null;
	}

	/*
	 * This function returns the Enum value of Meta SOP Class corresponding to strMetaSOPClass.
	 */
	private EnumMetaSOPClassUID getMetaSOPClassUID(String strMetaSOPClassUID)
	{
		if(strMetaSOPClassUID.equals(EnumMetaSOPClassUID.BASIC_COLOR_PRINT_MANAGEMENT_META_SOP_CLASS.getValue()))
			return EnumMetaSOPClassUID.BASIC_COLOR_PRINT_MANAGEMENT_META_SOP_CLASS;
		if(strMetaSOPClassUID.equals(EnumMetaSOPClassUID.BASIC_GRAYSCALE_PRINT_MANAGEMENT_META_SOP_CLASS.getValue()))
			return EnumMetaSOPClassUID.BASIC_GRAYSCALE_PRINT_MANAGEMENT_META_SOP_CLASS;
		if(strMetaSOPClassUID.equals(EnumMetaSOPClassUID.PULL_STORED_PRINT_MANAGEMENT_META_SOP_CLASS.getValue()))
			return EnumMetaSOPClassUID.PULL_STORED_PRINT_MANAGEMENT_META_SOP_CLASS;
		if(strMetaSOPClassUID.equals(EnumMetaSOPClassUID.STUDY_MANAGEMENT_META_SOP_CLASS.getValue()))
			return EnumMetaSOPClassUID.STUDY_MANAGEMENT_META_SOP_CLASS;
		if(strMetaSOPClassUID.equals(EnumMetaSOPClassUID.DETACHED_RESULT_MANAGEMENT_META_SOP_CLASS.getValue()))
			return EnumMetaSOPClassUID.DETACHED_RESULT_MANAGEMENT_META_SOP_CLASS;
		if(strMetaSOPClassUID.equals(EnumMetaSOPClassUID.DETACHED_PATIENT_MANAGEMENT_META_SOP_CLASS.getValue()))
			return EnumMetaSOPClassUID.DETACHED_PATIENT_MANAGEMENT_META_SOP_CLASS;
		return null;
	}
}
