/*******************************************************************************
 *  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.nget.scp;

import java.io.IOException;
import java.util.Locale;


import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataSet;
import cdac.medinfo.sdk.dcm30_04.enums.EnumCommandField;
import cdac.medinfo.sdk.dcm30_04.enums.EnumPDUType;
import cdac.medinfo.sdk.dcm30_04.enums.EnumSOPClassUIDs;
import cdac.medinfo.sdk.dcm30_04.exceptions.ParseException;
import cdac.medinfo.sdk.dcm30_04.exceptions.SerializeException;
import cdac.medinfo.sdk.dcm30_04.services.association.AAbort;
import cdac.medinfo.sdk.dcm30_04.services.association.AReleaseRQorRP;
import cdac.medinfo.sdk.dcm30_04.services.association.AssociateRQ;
import cdac.medinfo.sdk.dcm30_04.services.association.AssociationNegotiator;
import cdac.medinfo.sdk.dcm30_04.services.association.DefaultAssociation;
import cdac.medinfo.sdk.dcm30_04.services.association.IDIMSEAssociation;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumAbortReason;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumAbortSource;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumDataSink;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumDataSource;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumEarlyFailedStatus;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumImplOpsStatus;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumNGetStatus;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumServiceType;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.SourceException;
import cdac.medinfo.sdk.dcm30_04.services.dimsecommons.DIMSEResponse;
import cdac.medinfo.sdk.dcm30_04.services.dimsecommons.DicomMessage;
import cdac.medinfo.sdk.dcm30_04.services.dimsecommons.IDataSource;
import cdac.medinfo.sdk.dcm30_04.services.nget.commons.NGetRQ;
import cdac.medinfo.sdk.dcm30_04.services.nget.commons.NGetRSP;
import cdac.medinfo.sdk.dcm30_04.services.scp.commons.ClientSession;
import cdac.medinfo.sdk.dcm30_04.services.scp.commons.SCP;
import cdac.medinfo.sdk.dcm30_04.services.scp.commons.SCPSession;
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.ResourceBundleWrapper;
import cdac.medinfo.sdk.dcm30_04.utils.SingleCollection;

/**
 * NGetSCP works as a processor for NGet Service.
 * The N-GET service is used by a DIMSE-service-user to retrieve Attribute values from a peer DIMSEservice-user. 
 * It is a confirmed service.
 * It processes the arrived NGet Request and generates response.
 * It processes the request according to the Service Class SOP ClassUID specified in request.
 */
public class NGetSCP extends SCP 
{
	

	private final String N_GET = ResourceBundleWrapper.getValue("status", "NGet", new Locale(DicomConfig.createInstance().getLocale()));

	/** Paired collection containing  DataSource*/
	private IPairedCollection<EnumDataSource, IDataSource> objDataSourceCollection = null;
	
	private SCPSession objSCPSession = null;
	
	
	/**
	 * Instantiates object of NGet SCP.
	 */
	private NGetSCP()
	{
	}

	/**
	 * This is private constructor for NGet SCP. It is a singleton class.
	 * @param objClientSession object of client session.
	 */
	private NGetSCP(ClientSession objClientSession)
	{
		super(objClientSession);
	}

	/**
	 * This method returns new object of NGet SCP.
	 * @param objClientSession object of Client session.
	 * @return NGetSCP object of NGet SCP.
	 */
	public static NGetSCP createInstance(ClientSession objClientSession)
	{
		return (new NGetSCP(objClientSession));
	}

	/**
	 * Instantiates a new NGet SCP.
	 * @return NGetSCP  - instance of NGet SCP.
	 */
	public static NGetSCP createInstance()
	{
		return (new NGetSCP());
	}

	/**
	 * This method will Start the Server on given IP and  at the port specified.
	 * @param strIP IP at which Server has to be started.
	 * @param strAETitle value of AE Title for service provider.
	 * @param port at which Server has to be started.
	 * @throws IOException throws IOException if IO operation fails.
	 */
	public void start(String strAETitle,String strIP, int port) throws IOException
	{
		objSCPSession = new SCPSession();
		objSCPSession.setIProcessHandler(this);
		EnumServiceType[] arrenumServiceType = new EnumServiceType[20];

		arrenumServiceType[0] = EnumServiceType.NGET_GPPPS;
		arrenumServiceType[1] = EnumServiceType.NGET_MPPS;
		arrenumServiceType[2] = EnumServiceType.NGET_BASIC_COLOR_PRINT_MANAGEMENT;
		arrenumServiceType[3] = EnumServiceType.NGET_BASIC_GRAYSCALE_PRINT_MANAGEMENT;
		arrenumServiceType[4] = EnumServiceType.NGET_PRINT_JOB;
		arrenumServiceType[5] = EnumServiceType.NGET_PRINTER_CONFIGURATION_RETRIEVAL;
		arrenumServiceType[6] = EnumServiceType.NGET_PULLPRINT;
		arrenumServiceType[7] = EnumServiceType.NGET_PRINT_QUEUE_MANAGEMENT;
		arrenumServiceType[8] = EnumServiceType.NGET_DETACHED_RESULT_MANAGEMENT;
		arrenumServiceType[9] = EnumServiceType.NGET_DETACHED_INTERPRETATION_MANAGEMENT;
		arrenumServiceType[10] = EnumServiceType.NGET_DETACHED_RESULT_MANAGEMENT_META_SOP;
		arrenumServiceType[11] = EnumServiceType.NGET_MEDIA_CREATION;
		arrenumServiceType[12] = EnumServiceType.NEVENTREPORT_DETACHED_PATIENT_MANAGEMENT;
		arrenumServiceType[13] = EnumServiceType.NEVENTREPORT_DETACHED_VISIT_MANAGEMENT;
		arrenumServiceType[14] = EnumServiceType.NEVENTREPORT_DETACHED_PATIENT_MANAGEMENT_META_SOP;
		arrenumServiceType[15] = EnumServiceType.NGET_STUDY_MANAGEMENT_META;
		arrenumServiceType[16] = EnumServiceType.NGET_RT_CONVENTIONAL_MACHINE_VERIFICATION;
		arrenumServiceType[17] = EnumServiceType.NGET_RT_ION_MACHINE_VERIFICATION;
		arrenumServiceType[18] = EnumServiceType.NGET_UPS;
		arrenumServiceType[19] = EnumServiceType.NGET_DISPLAY_SYSTEM_MANAGEMENT;

		objSCPSession.selectServices(arrenumServiceType);
		objSCPSession.start(strAETitle,strIP,port);
	}

	/**
	 * This method will Start the Server on the local host at the port specified.
	 * @param strAETitle value of AE Title for service provider.
	 * @param port port at which Server has to be started at local host.
	 * @throws IOException throws IOException if IO operation fails.
	 */
	public void start(String strAETitle,int port) throws IOException
	{
		objSCPSession = new SCPSession();
		objSCPSession.setIProcessHandler(this);

		EnumServiceType[] arrenumServiceType = new EnumServiceType[20];
		arrenumServiceType[0] = EnumServiceType.NGET_GPPPS;
		arrenumServiceType[1] = EnumServiceType.NGET_MPPS;
		arrenumServiceType[2] = EnumServiceType.NGET_BASIC_COLOR_PRINT_MANAGEMENT;
		arrenumServiceType[3] = EnumServiceType.NGET_BASIC_GRAYSCALE_PRINT_MANAGEMENT;
		arrenumServiceType[4] = EnumServiceType.NGET_PRINT_JOB;
		arrenumServiceType[5] = EnumServiceType.NGET_PRINTER_CONFIGURATION_RETRIEVAL;
		arrenumServiceType[6] = EnumServiceType.NGET_PULLPRINT;
		arrenumServiceType[7] = EnumServiceType.NGET_PRINT_QUEUE_MANAGEMENT;
		arrenumServiceType[8] = EnumServiceType.NGET_DETACHED_RESULT_MANAGEMENT;
		arrenumServiceType[9] = EnumServiceType.NGET_DETACHED_INTERPRETATION_MANAGEMENT;
		arrenumServiceType[10] = EnumServiceType.NGET_DETACHED_RESULT_MANAGEMENT_META_SOP;
		arrenumServiceType[11] = EnumServiceType.NGET_MEDIA_CREATION;
		arrenumServiceType[12] = EnumServiceType.NEVENTREPORT_DETACHED_PATIENT_MANAGEMENT;
		arrenumServiceType[13] = EnumServiceType.NEVENTREPORT_DETACHED_VISIT_MANAGEMENT;
		arrenumServiceType[14] = EnumServiceType.NEVENTREPORT_DETACHED_PATIENT_MANAGEMENT_META_SOP;
		arrenumServiceType[15] = EnumServiceType.NGET_STUDY_MANAGEMENT_META;
		arrenumServiceType[16] = EnumServiceType.NGET_RT_CONVENTIONAL_MACHINE_VERIFICATION;
		arrenumServiceType[17] = EnumServiceType.NGET_RT_ION_MACHINE_VERIFICATION;
		arrenumServiceType[18] = EnumServiceType.NGET_UPS;
		arrenumServiceType[19] = EnumServiceType.NGET_DISPLAY_SYSTEM_MANAGEMENT;

		objSCPSession.selectServices(arrenumServiceType);
		objSCPSession.start(strAETitle,port);
	}

	/**
	 * Retrieves the Data Source List.
	 * @return IPairedCollection<EnumDataSource, IDataSource> - collection of Data Source.
	 */
	public IPairedCollection<EnumDataSource, IDataSource> getDataSourceList() 
	{
		return objDataSourceCollection;
	}

	/**
	 * Sets the Data Source List.
	 * @param objSourceList collection of Data Source.
	 */
	public void setDataSourceList(IPairedCollection<EnumDataSource, IDataSource> objSourceList) 
	{
		this.objDataSourceCollection = objSourceList;
	}

	/**
	 * This will give the response of NGet Request.
	 * It processes the request according to the Service Class SOP ClassUID specified in request.
	 * @param objDicomMessage  object of DIMSE request.
	 * @return DIMSEResponse - instance of service response .
	 * @throws IOException if IO operation fails.
	 */
	public DIMSEResponse processRequest(DicomMessage objDicomMessage) 
	{
		/*
		 * NGet Response to be send
		 */
		NGetRSP objNGetRSP = null;

		/*
		 * NGet Request
		 */
		NGetRQ objNGetRQ  = null;

		/*
		 * Get the early failed status.
		 */
		EnumEarlyFailedStatus enumEarlyFailedStatus = objClientSession.getEarlyFailedStatus();

		try
		{
			//check for null value of object of DicomMessage, if DicomMessage is null or if DICOMMsgValidationStatus is false, then Processing failure RSP will be send.
			if(objDicomMessage == null || objClientSession.getDICOMMsgValidationStatus() == false)
			{
				if(DicomConfig.createInstance().isLoggingOn())
				{
					DicomLogger objDicomLogger = DicomLogger.getLogger();
					objDicomLogger.log(ResourceBundleWrapper.getValue("warning","InvalidDicomMessage",new Locale(DicomConfig.createInstance().getLocale())));
				}

				objNGetRQ = new NGetRQ(objDicomMessage);

				objNGetRSP = new NGetRSP();
				objNGetRSP.setCommandField(EnumCommandField.N_GET_RSP);
				objNGetRSP.setAffectedSOPClassUID(objNGetRQ.getRequestedSOPClassUID());
				objNGetRSP.setMessageIDBeingRespondedTo(objNGetRQ.getMessageID());
				//constant should be used here.
				objNGetRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_NOT_PRESENT);
				objNGetRSP.setStatus(EnumNGetStatus.PROCESSING_FAILURE);
				return objNGetRSP;
			}
			
			/*
			 * Construct NGet RQ from Dicom Message.
			 */
			objNGetRQ = new NGetRQ(objDicomMessage);

			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("status", "ProcessingServiceRequest", new Locale(DicomConfig.createInstance().getLocale())) + objDicomMessage.getCommandType().name());
				objDicomLogger.log(ResourceBundleWrapper.getValue("status", "AbstractSyntaxName", new Locale(DicomConfig.createInstance().getLocale())) + objNGetRQ.getRequestedSOPClassUID());
				if(objNGetRQ.getDataSet() != null)
					objDicomLogger.log(ResourceBundleWrapper.getValue("status", "TransferSyntax", new Locale(DicomConfig.createInstance().getLocale())) + objNGetRQ.getDataSet().getTransferSyntax().getTransferSyntaxUID());
				objDicomLogger.log(ResourceBundleWrapper.getValue("status", "NGetRetrieveSOPInstance", new Locale(DicomConfig.createInstance().getLocale())) + objNGetRQ.getRequestedSOPInstanceUID());
			}


			if(objClientSession.getAssociationStatus())
			{
				objNGetRSP = new NGetRSP();
				//IDataSet objIDataSet = objNGetRQ.getDataSet();
				ISingleCollection<IDataSet> objResDatasetList = new SingleCollection<IDataSet>();

				//Initializing RSP 
				objNGetRSP.setAffectedSOPClassUID(objNGetRQ.getRequestedSOPClassUID());
				objNGetRSP.setCommandField(EnumCommandField.N_GET_RSP);
				objNGetRSP.setMessageIDBeingRespondedTo(objNGetRQ.getMessageID());
				//If SOP Instance UID is not given in Request then new value needs to be generated and send it in Response. 
				objNGetRSP.setAffectedSOPInstanceUID(objNGetRQ.getRequestedSOPInstanceUID());

				/*
				 * This if-else block checks for the status of arrived RQ and takes corresponding action.
				 */
				if(enumEarlyFailedStatus == EnumEarlyFailedStatus.NO_EARLY_FAILED)
				{
					IDataSource objIDataSource = null;
					String strAffectedSOPUid = objNGetRQ.getRequestedSOPClassUID();
					if(objDataSourceCollection != null)
					{
//						Retrieving appropriate DataSink according to service class 
						if(strAffectedSOPUid.equals(EnumSOPClassUIDs.MPPSRETRIEVE.getValue()))
							objIDataSource = objDataSourceCollection.get(EnumDataSource.MPPS);
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.GPPPS.getValue()))
							objIDataSource = objDataSourceCollection.get(EnumDataSource.GPPPS);
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.PRINTJOBIOD.getValue()) || strAffectedSOPUid.equals(EnumSOPClassUIDs.PRINTERIOD.getValue()) || strAffectedSOPUid.equals(EnumSOPClassUIDs.PRINTERCONFIGURATIONRETRIEVAL.getValue()) || strAffectedSOPUid.equals(EnumSOPClassUIDs.PRINTQUEUEMANAGEMENTIOD.getValue()))
							objIDataSource = objDataSourceCollection.get(EnumDataSource.PRINT_MANAGEMENT);
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDINTERPRETATIONMANAGEMENTIOD.getValue())|| strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDRESULTSMANAGEMENTIOD.getValue()))
							objIDataSource = objDataSourceCollection.get(EnumDataSource.RESULT_MANAGEMENT);
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.MEDIACREATIONMANAGEMENT.getValue()))
							objIDataSource = objDataSourceCollection.get(EnumDataSource.MEDIA_CREATION);
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDVISITMANAGEMENTIOD.getValue())|| strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDPATIENTMANAGEMENTIOD.getValue()))
							objIDataSource = objDataSourceCollection.get(EnumDataSource.PATIENT_MANAGEMENT);
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.STUDYCOMPONENTMANAGEMENT.getValue()) || strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDSTUDYMANAGEMENTIOD.getValue()))
							objIDataSource = objDataSourceCollection.get(EnumDataSource.STUDY_MANAGEMENT_META);
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.DCM_UPS_PULL.getValue())||strAffectedSOPUid.equals(EnumSOPClassUIDs.DCM_UPS_PUSH.getValue())||strAffectedSOPUid.equals(EnumSOPClassUIDs.DCM_UPS_WATCH.getValue()))
							objIDataSource = objDataSourceCollection.get(EnumDataSource.UPSPULL);
			
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.RTCONVENTIONALMACHINEVERIFICATIONINFORMATIONOBJECTDEFINITION.getValue())
								||strAffectedSOPUid.equals(EnumSOPClassUIDs.RTIONMACHINEVERIFICATIONINFORMATIONOBJECTDEFINITION.getValue())
								)
							objIDataSource = objDataSourceCollection.get(EnumDataSource.RTMACHINEVERIFICATION);

						
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.DISPLAYSYSTEMIOD.getValue()))
							objIDataSource = objDataSourceCollection.get(EnumDataSource.DISPLAY_SYSTEM_MANAGEMENT);
						if(objIDataSource != null)
						{
							short shShort = objIDataSource.retrieve(objDicomMessage, objResDatasetList);
							objNGetRSP.setStatus(shShort);
							if(shShort == EnumImplOpsStatus.SUCCESS.getValue() || shShort == EnumImplOpsStatus.FAILURE_NO_SUCH_OBJECT_INSTANCE.getValue())
							{
								if(objResDatasetList.size() != 0)
								{
									IDataSet objDataSet = objResDatasetList.elementAt(0);
									if(objDataSet.isEmpty())
									{
										objNGetRSP.setStatus(EnumNGetStatus.PROCESSING_FAILURE);
										objNGetRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_NOT_PRESENT);
										if(DicomConfig.createInstance().isLoggingOn())
										{
											DicomLogger objDicomLogger = DicomLogger.getLogger();
											objDicomLogger.log(ResourceBundleWrapper.getValue("warning","NoMatchFound",new Locale(DicomConfig.createInstance().getLocale()))+ N_GET);
										}
									}
									else
									{
										objNGetRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_PRESENT);
										objNGetRSP.setDataSet(objResDatasetList.elementAt(0));
									}
								}
								else
								{
									objNGetRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_NOT_PRESENT);
									if(DicomConfig.createInstance().isLoggingOn())
									{
										DicomLogger objDicomLogger = DicomLogger.getLogger();
										objDicomLogger.log(ResourceBundleWrapper.getValue("warning","NoMatchFound",new Locale(DicomConfig.createInstance().getLocale()))+ N_GET);
									}
								}
							}
							else
							{
								objNGetRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_NOT_PRESENT);
								if(DicomConfig.createInstance().isLoggingOn())
								{
									DicomLogger objDicomLogger = DicomLogger.getLogger();
									objDicomLogger.log(ResourceBundleWrapper.getValue("warning","ImplOperationFailure",new Locale(DicomConfig.createInstance().getLocale()))+ N_GET);
								}
							}
						}
						else
						{
							objNGetRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_NOT_PRESENT);
							objNGetRSP.setStatus(EnumNGetStatus.PROCESSING_FAILURE);
							if(DicomConfig.createInstance().isLoggingOn())
							{
								DicomLogger objDicomLogger = DicomLogger.getLogger();
								objDicomLogger.log(ResourceBundleWrapper.getValue("warning","ImplementationNotProvided",new Locale(DicomConfig.createInstance().getLocale()))+ N_GET);
							}
						}
					}
					else
					{
						objNGetRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_NOT_PRESENT);
						objNGetRSP.setStatus(EnumNGetStatus.PROCESSING_FAILURE);
						if(DicomConfig.createInstance().isLoggingOn())
						{
							DicomLogger objDicomLogger = DicomLogger.getLogger();
							objDicomLogger.log(ResourceBundleWrapper.getValue("warning","ImplementationNotProvided",new Locale(DicomConfig.createInstance().getLocale()))+ N_GET);
						}
					}

				}
				else
				{
					objNGetRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_NOT_PRESENT);
					objNGetRSP.setStatus(EnumNGetStatus.SOP_CLASS_NOT_SUPPORTED);
					if(DicomConfig.createInstance().isLoggingOn())
					{
						DicomLogger objDicomLogger = DicomLogger.getLogger();
						objDicomLogger.log(ResourceBundleWrapper.getValue("warning","EarlyFailedResponse",new Locale(DicomConfig.createInstance().getLocale()))+ N_GET);
					}
				}

				int iGroupLength = objNGetRSP.getCommandSet().getCommandSetLength();
				objNGetRSP.setCommandGroupLength(iGroupLength);
			}
			else
			{
				if(DicomConfig.createInstance().isLoggingOn())
				{
					DicomLogger objDicomLogger = DicomLogger.getLogger();
					objDicomLogger.log(ResourceBundleWrapper.getValue("status","AssociationDoesNotExists",new Locale(DicomConfig.createInstance().getLocale()))+ N_GET);
				}
			}
		}
		catch(ParseException pe)
		{
			if(DicomConfig.createInstance().isLoggingOn() )
			{
				String strService = ResourceBundleWrapper.getValue("exceptions", "NGet", new Locale(DicomConfig.createInstance().getLocale()));
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log( strService + pe.getMessage());
			}			

			objNGetRSP = getNGetResponse(objDicomMessage,EnumNGetStatus.UNABLE_TO_PROCESS_PARSING_FAILED);
		}
		catch(SerializeException pe)
		{
			if(DicomConfig.createInstance().isLoggingOn() )
			{
				String strService = ResourceBundleWrapper.getValue("exceptions", "NGet", new Locale(DicomConfig.createInstance().getLocale()));
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log( strService + pe.getMessage());
			}			

			objNGetRSP = getNGetResponse(objDicomMessage,EnumNGetStatus.INVALID_ATTRIBUTE_VALUE);
		}
		catch(SourceException se)
		{
			if(DicomConfig.createInstance().isLoggingOn() )
			{
				String strService = ResourceBundleWrapper.getValue("exceptions", "NGet", new Locale(DicomConfig.createInstance().getLocale()));
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log( strService + se.getMessage());
			}	
			
			objNGetRSP = getNGetResponse(objDicomMessage,EnumNGetStatus.PROCESSING_FAILURE);
		}
		catch(IOException ioe)
		{
			if(DicomConfig.createInstance().isLoggingOn() )
			{
				String strService = ResourceBundleWrapper.getValue("exceptions", "NGet", new Locale(DicomConfig.createInstance().getLocale()));
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log( strService + ioe.getMessage());
			}	
			
			objNGetRSP = getNGetResponse(objDicomMessage,EnumNGetStatus.PROCESSING_FAILURE);
		}

		if(DicomConfig.createInstance().isLoggingOn())
		{
			DicomLogger objDicomLogger = DicomLogger.getLogger();
			objDicomLogger.log(objDicomMessage.getCommandType() + " " +ResourceBundleWrapper.getValue("status","RSPStatus",new Locale(DicomConfig.createInstance().getLocale()))+ Integer.toHexString(objNGetRSP.getStatus()));
		}
		return objNGetRSP;
	}

	/**
	 * This method notifies when Association Request, Associate Release Request or Abort is received from one of the 
	 * SCUs. 
	 * If Association Request is received,
	 * 		Create Association Negotiator which negotiates the request with the existing default association and
	 * 		creates an AgreedAssociation which contains the negotiated association values. AssociationNegotiator
	 * 		returns the Association Response as AssociateAC when request is accepted or returns AssociateRQ is 
	 * 		none of the values in Association Request are acceptable.
	 * 	If Associate Release Request is received,
	 * 		Create a Associate Release Response and send the response to the SCU using the respective ClientSession.
	 *  If Abort is received,
	 *  	An Abort response must be generated and sent using ClientSession.
	 *    
	 * @param objClientSession instance of Client Session.
	 */
	public void onAssociateReceive(ClientSession objClientSession) 
	{
		//Get Association RQ
		IDIMSEAssociation objIDIMSEAssociation = objClientSession.getDIMSEAssociation();
		EnumPDUType enumPDUType = objIDIMSEAssociation.getPDUType();
		IDIMSEAssociation objAssociationRSP = null;

		//For Association Request negotiate with Default Association and generate Response
		switch(enumPDUType)
		{
		case ASSOCIATE_RQ :
		{
			boolean associationStatus = objClientSession.getAssociationStatus();
			if(associationStatus)
			{
				if(DicomConfig.createInstance().isLoggingOn())
				{
					DicomLogger objDicomLogger = DicomLogger.getLogger();
					objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions", "Association.UnexpectedAssociationRQ",new Locale(DicomConfig.createInstance().getLocale())));
				}
				/*
				 * If association already exists then abort will be generated
				 */
				AAbort objAAbort = new AAbort();
				objAAbort.setReason(EnumAbortReason.UNEXPECTED_PDU);
				objAAbort.setSource(EnumAbortSource.SERVICE_PROVIDER);

				objClientSession.sendAssociationMessage(objAAbort);
				return;
			}
			AssociateRQ objAssociateRQ = (AssociateRQ)objIDIMSEAssociation;

			//get the default association from Client Session.
			DefaultAssociation objDefaultAssociation = objClientSession.getDefaultAssociation();

			//Negotiate and get Response
			AssociationNegotiator objAssNegotiator = new AssociationNegotiator();
			objAssociationRSP = objAssNegotiator.negotiate(objDefaultAssociation, objAssociateRQ);

			//set agreed association in client Session.
			objClientSession.setAgreedAssociation(objAssNegotiator.getAgreedAssociation());

			//Send Response
			objClientSession.sendAssociationMessage(objAssociationRSP);
			break;
		}
		case RELEASE_RQ :
		{
			System.out.println("Release Request Received");

			//Create Release Response.
			objAssociationRSP = new AReleaseRQorRP(EnumPDUType.RELEASE_RP);

			//Send Release Response.
			objClientSession.sendAssociationMessage(objAssociationRSP);
			System.out.println("Release Response sent");

			//Close Client Session
			objClientSession.close();
			break;
		}
		case ABORT :
		{
			System.out.println("Abort has arrived Stop Porcessing -- Association is no longer available");
			break;
		}
		}
	}


	/**
	 * Closes client Session.
	 * @param objClientSession Object of Client Session.
	 */
	public void onClose(ClientSession objClientSession) 
	{
		objClientSession.close();
	}

	/**
	 * This method notifies when a new client connection is requested. A decision is to be made whether to
	 * accept or reject the connection.  
	 * @param objClientSession  object of client session.
	 */
	public void onConnect(ClientSession objClientSession)
	{
		objClientSession.acceptConnection(true);
	}

	/**
	 * This method notifies when a service related request is received.
	 * For Example,
	 * If C-Store-RQ is received,
	 * 		Create CStoreSCP using the ClientSession.
	 * 		Process the request using the CStoreSCP which actually serializes the DataSet on a Stream and returns
	 * 		a CStoreRSP containing the response.
	 * 		Send this response using the ClientSession.
	 * @see objClientSession Object of Client Session.
	 */
	public void onServiceRequest(ClientSession objClientSession)
	{
		/*
		 * Create CSTORE_SCP get RQ from ClientSession call processRequest and pass C_STORE_RQ on CSTORE_SCP
		 * receive response from CSTORE_SCP sendResponse using ClientSession
		 */
		DIMSEResponse objDResponse = null;
		try
		{
			objDResponse = processRequest(objClientSession.getDicomMessage());

			/*
			 * Send the service message through Client Session.
			 */
			if(objDResponse != null)
				objClientSession.sendServiceMessage(objDResponse);
		}catch(Exception ioe)
		{
			if(DicomConfig.createInstance().isLoggingOn())
				DicomLogger.getLogger().log(ioe);
			
			objClientSession.close();
			
		}  
	}
	
	/**
	 * Stops accepting new connection's from SCU's and continue to process 
	 * already connected clients.
	 */
	public void stopListening()
	{
		if(null != objSCPSession)
			objSCPSession.stopListening();
	}
	
	/**
	 * Closes all the currently executing sessions gracefully.	 
	 */
	public void close()
	{
		if(null != objSCPSession)
			objSCPSession.close();
		else
		{	
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "NoSCPSessionRunning",new Locale(DicomConfig.createInstance().getLocale())));
			}
		}
	}
	
	/**
	 * Gives NGet Response.
	 * @param objDicomMessage Object of DicomMessage.
	 * @param enumStatus Enum for EnumNGetStatus which specifies status of response.
	 * @return objNGetRSP - Object of NGetRSP.
	 */
	private NGetRSP getNGetResponse(DicomMessage objDicomMessage, EnumNGetStatus enumStatus)
	{
		NGetRSP objNGetRSP = new NGetRSP();
		
		objNGetRSP.setAffectedSOPClassUID(objDicomMessage.getCommandSet().getValue(0x0000, 0x0003).toString());
		objNGetRSP.setCommandField(EnumCommandField.N_GET_RSP);
		objNGetRSP.setMessageIDBeingRespondedTo(Short.parseShort(objDicomMessage.getCommandSet().getValue(0x0000,0x0110).toString()));
		
		objNGetRSP.setCommandDataSetType((short)0x0101);										
		objNGetRSP.setAffectedSOPInstanceUID(objDicomMessage.getCommandSet().getValue(0x0000, 0x1001).toString());
		objNGetRSP.setStatus(enumStatus);
		objNGetRSP.setCommandGroupLength(objNGetRSP.getCommandSet().getCommandSetLength());
		
		return objNGetRSP;
	}
}
