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

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


import cdac.medinfo.sdk.dcm30_04.baselibs.ImplicitDE;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataElement;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataSet;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDictionaryReader;
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.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.EnumNEventReportStatus;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumNGetStatus;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumSCEventTypeID;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumServiceType;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.ICommandElement;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.SinkException;
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.IDataSink;
import cdac.medinfo.sdk.dcm30_04.services.dimsecommons.IDataSource;
import cdac.medinfo.sdk.dcm30_04.services.neventreport.commons.NEventReportRQ;
import cdac.medinfo.sdk.dcm30_04.services.neventreport.commons.NEventReportRSP;
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.ISingleCollection;
import cdac.medinfo.sdk.dcm30_04.utils.SingleCollection;

import cdac.medinfo.sdk.dcm30_04.utils.IPairedCollection;
import cdac.medinfo.sdk.dcm30_04.utils.ResourceBundleWrapper;

/**
 * NEventReportSCP works as a processor for NEventReport Service.
 * The N-EVENT-REPORT service is used by a DIMSE-service-user to report an event to a peer DIMSEservice-user.
 * It is a confirmed service.
 * It processes the arrived NEventReport Request and generates response accordingly.
 * It process the event notification operation generated by a Service Class User.
 */
public class NEventReportSCP extends SCP 
{
	

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

	/* Paired collection containing  DataSink*/
	private IPairedCollection<EnumDataSink, IDataSink> objDataSinkCollection = null;

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

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

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

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

	/**
	 * 
	 * @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[16];
		arrenumServiceType[0] = EnumServiceType.NEVENTREPORT_MPPS;
		arrenumServiceType[1] = EnumServiceType.NEVENTREPORT_STORAGE_COMMITMENT;
		arrenumServiceType[2] = EnumServiceType.NEVENTREPORT_BASIC_COLOR_PRINT_MANAGEMENT;
		arrenumServiceType[3] = EnumServiceType.NEVENTREPORT_BASIC_GRAYSCALE_PRINT_MANAGEMENT;
		arrenumServiceType[4] = EnumServiceType.NEVENTREPORT_PRINT_JOB;
		arrenumServiceType[5] = EnumServiceType.NEVENTREPORT_PULLPRINT;
		arrenumServiceType[6] = EnumServiceType.NEVENTREPORT_PRINT_QUEUE_MANAGEMENT;

		arrenumServiceType[7] = EnumServiceType.NEVENTREPORT_DETACHED_RESULT_MANAGEMENT;
		arrenumServiceType[8] = EnumServiceType.NEVENTREPORT_DETACHED_INTERPRETATION_MANAGEMENT;
		arrenumServiceType[9] = EnumServiceType.NEVENTREPORT_DETACHED_RESULT_MANAGEMENT_META_SOP;

		arrenumServiceType[10] = EnumServiceType.NEVENTREPORT_DETACHED_PATIENT_MANAGEMENT;
		arrenumServiceType[11] = EnumServiceType.NEVENTREPORT_DETACHED_VISIT_MANAGEMENT;
		arrenumServiceType[12] = EnumServiceType.NEVENTREPORT_DETACHED_PATIENT_MANAGEMENT_META_SOP;
		arrenumServiceType[13] = EnumServiceType.NEVENTREPORT_DETACHED_STUDY_MANAGEMENT;
		
		arrenumServiceType[14] = EnumServiceType.NEVENTREPORT_RT_CONVENTIONAL_MACHINE_VERIFICATION;
		arrenumServiceType[15] = EnumServiceType.NEVENTREPORT_RT_ION_MACHINE_VERIFICATION;


		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[16];
		arrenumServiceType[0] = EnumServiceType.NEVENTREPORT_MPPS;
		arrenumServiceType[1] = EnumServiceType.NEVENTREPORT_STORAGE_COMMITMENT;
		arrenumServiceType[2] = EnumServiceType.NEVENTREPORT_BASIC_COLOR_PRINT_MANAGEMENT;
		arrenumServiceType[3] = EnumServiceType.NEVENTREPORT_BASIC_GRAYSCALE_PRINT_MANAGEMENT;
		arrenumServiceType[4] = EnumServiceType.NEVENTREPORT_PRINT_JOB;
		arrenumServiceType[5] = EnumServiceType.NEVENTREPORT_PULLPRINT;
		arrenumServiceType[6] = EnumServiceType.NEVENTREPORT_PRINT_QUEUE_MANAGEMENT;

		arrenumServiceType[7] = EnumServiceType.NEVENTREPORT_DETACHED_RESULT_MANAGEMENT;
		arrenumServiceType[8] = EnumServiceType.NEVENTREPORT_DETACHED_INTERPRETATION_MANAGEMENT;
		arrenumServiceType[9] = EnumServiceType.NEVENTREPORT_DETACHED_RESULT_MANAGEMENT_META_SOP;

		arrenumServiceType[10] = EnumServiceType.NEVENTREPORT_DETACHED_PATIENT_MANAGEMENT;
		arrenumServiceType[11] = EnumServiceType.NEVENTREPORT_DETACHED_VISIT_MANAGEMENT;
		arrenumServiceType[12] = EnumServiceType.NEVENTREPORT_DETACHED_PATIENT_MANAGEMENT_META_SOP;
		arrenumServiceType[13] = EnumServiceType.NEVENTREPORT_DETACHED_STUDY_MANAGEMENT;
		arrenumServiceType[14] = EnumServiceType.NEVENTREPORT_RT_CONVENTIONAL_MACHINE_VERIFICATION;
		arrenumServiceType[15] = EnumServiceType.NEVENTREPORT_RT_ION_MACHINE_VERIFICATION;

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

	/**
	 * Retrieves the DataSink List
	 * @return IPairedCollection<EnumDataSink, IDataSink> - collection of DataSink
	 */
	public IPairedCollection<EnumDataSink, IDataSink> getDataSinkList() 
	{
		return objDataSinkCollection;
	}

	/**
	 * Sets the Data Sink List.
	 * @param objSinkCollection collection of Data Sink.
	 */
	public void setDataSinkList(IPairedCollection<EnumDataSink, IDataSink> objSinkCollection) 
	{
		this.objDataSinkCollection = objSinkCollection;
	}
	/**
	 * 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 NEventReport Request.
	 * @param objDicomMessage  object of DIMSE request.
	 * @return DIMSEResponse - instance of service response. 
	 * @throws IOException if IO operation fails.
	 */
	public DIMSEResponse processRequest(DicomMessage objDicomMessage) 
	{
		/*
		 * NEventReport Response to be send
		 */
		NEventReportRSP objNEventReportRSP = null;

		/*
		 * NEventReport Request
		 */
		NEventReportRQ objNEventReportRQ  = 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())));
				}

				objNEventReportRQ = new NEventReportRQ(objDicomMessage);

				objNEventReportRSP = new NEventReportRSP();
				objNEventReportRSP.setCommandField(EnumCommandField.N_EVENT_REPORT_RSP);
				objNEventReportRSP.setAffectedSOPClassUID(objNEventReportRQ.getAffectedSOPClassUID());
				objNEventReportRSP.setMessageIDBeingRespondedTo(objNEventReportRQ.getMessageID());
				//constant should be used here.
				objNEventReportRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_NOT_PRESENT);
				objNEventReportRSP.setStatus(EnumNEventReportStatus.PROCESSING_FAILURE);
				return objNEventReportRSP;
			}

			/*
			 * Construct NEventReport RQ from Dicom Message.
			 */
			objNEventReportRQ = new NEventReportRQ(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())) + objNEventReportRQ.getAffectedSOPClassUID());
				if(objNEventReportRQ.getDataSet() != null)
					objDicomLogger.log(ResourceBundleWrapper.getValue("status", "TransferSyntax", new Locale(DicomConfig.createInstance().getLocale())) + objNEventReportRQ.getDataSet().getTransferSyntax().getTransferSyntaxUID());
			}

			if(objClientSession.getAssociationStatus())
			{
				objNEventReportRSP = new NEventReportRSP();
				ISingleCollection<IDataSet> objResDatasetList = new SingleCollection<IDataSet>();

				//Initializing RSP 
				objNEventReportRSP.setAffectedSOPClassUID(objNEventReportRQ.getAffectedSOPClassUID());
				objNEventReportRSP.setCommandField(EnumCommandField.N_EVENT_REPORT_RSP);
				objNEventReportRSP.setMessageIDBeingRespondedTo(objNEventReportRQ.getMessageID());
				//Constant should be used here.
			//	objNEventReportRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_NOT_PRESENT);
				//If SOP Instance UID is not given in Request then new value needs to be generated and send it in Response. 
				objNEventReportRSP.setAffectedSOPInstanceUID(objNEventReportRQ.getAffectedSOPInstanceUID());

				/*
				 * This if-else block checks for the status of arrived RQ and takes corresponding action.
				 */
				if(enumEarlyFailedStatus == EnumEarlyFailedStatus.NO_EARLY_FAILED)
				{
					IDataSink objIDataSink = null;
					IDataSource objIDataSource = null;
					String strAffectedSOPUid = objNEventReportRQ.getAffectedSOPClassUID();
					
					if(!strAffectedSOPUid.equals("1.2.840.10008.5.1.4.34.8") && (!strAffectedSOPUid.equals("1.2.840.10008.5.1.4.34.9"))&& (!strAffectedSOPUid.equals("1.2.840.10008.5.1.4.34.6.4")))
					{
						if(objDataSinkCollection != null)
						{
							//Retrieving appropriate DataSink according to service class 
							if(strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDRESULTSMANAGEMENTIOD))
								objIDataSink = objDataSinkCollection.get(EnumDataSink.DETACHED_RESULTS_MANAGEMENT);
							else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDINTERPRETATIONMANAGEMENTIOD.getValue()))
								objIDataSink = objDataSinkCollection.get(EnumDataSink.DETACHED_INTERPRETATION_MANAGEMENT);
							else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.PRINTJOBIOD.getValue()) || strAffectedSOPUid.equals(EnumSOPClassUIDs.PRINTERIOD.getValue()))
								objIDataSink = objDataSinkCollection.get(EnumDataSink.PRINT_MANAGEMENT);
							else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.STORAGECOMMITMENTPUSHMODELIOD.getValue()))
								objIDataSink = objDataSinkCollection.get(EnumDataSink.STORAGE_COMMITMENT_PUSH_MODEL);
							else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.PRINTQUEUEMANAGEMENTIOD.getValue()))
								objIDataSink = objDataSinkCollection.get(EnumDataSink.PRINT_QUEUE_MANAGEMENT);
							else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDPATIENTMANAGEMENTIOD.getValue()))
								objIDataSink = objDataSinkCollection.get(EnumDataSink.DETACHED_PATIENT_MANAGEMENT);
							else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDVISITMANAGEMENTIOD.getValue()))
								objIDataSink = objDataSinkCollection.get(EnumDataSink.DETACHED_VISIT_MANAGEMENT);
							else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDSTUDYMANAGEMENTIOD.getValue()))
								objIDataSink = objDataSinkCollection.get(EnumDataSink.DETACHED_STUDY_MANAGEMENT);
							else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.MPPSNOTIFICATION.getValue()))
								objIDataSink = objDataSinkCollection.get(EnumDataSink.MPPS);
							
							else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.RTCONVENTIONALMACHINEVERIFICATIONINFORMATIONOBJECTDEFINITION.getValue())
									||strAffectedSOPUid.equals(EnumSOPClassUIDs.RTIONMACHINEVERIFICATIONINFORMATIONOBJECTDEFINITION.getValue())
									)
							{
								objNEventReportRSP.setEventTypeID(EnumSCEventTypeID.RT_CONVENTIONAL_MACHINE_VERIFICATION_PENDING);
								objIDataSink = objDataSinkCollection.get(EnumDataSink.RTMACHINEVERIFICATION);
							}
							else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDINTERPRETATIONMANAGEMENTIOD.getValue())
									||strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDRESULTSMANAGEMENTIOD.getValue())
							)
								objIDataSink = objDataSinkCollection.get(EnumDataSink.RESULT_MANAGEMENT);

							else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDPATIENTMANAGEMENTIOD.getValue())
									||strAffectedSOPUid.equals(EnumSOPClassUIDs.DETACHEDVISITMANAGEMENTIOD.getValue())
							)
								objIDataSink = objDataSinkCollection.get(EnumDataSink.PATIENT_MANAGEMENT);

							if(objIDataSink != null)
							{
								short shStatus = objIDataSink.store(objDicomMessage,null);
								objNEventReportRSP.setStatus(shStatus);
								if(shStatus != EnumImplOpsStatus.SUCCESS.getValue())
								{
									if(DicomConfig.createInstance().isLoggingOn())
									{
										DicomLogger objDicomLogger = DicomLogger.getLogger();
										objDicomLogger.log(ResourceBundleWrapper.getValue("warning","ImplOperationFailure",new Locale(DicomConfig.createInstance().getLocale()))+ N_EVENT_REPORT);
									}
								}
							}
							else
							{
								objNEventReportRSP.setStatus(EnumNEventReportStatus.SUCCESS);
								if(DicomConfig.createInstance().isLoggingOn())
								{
									DicomLogger objDicomLogger = DicomLogger.getLogger();
									objDicomLogger.log(ResourceBundleWrapper.getValue("warning","ImplementationNotProvided",new Locale(DicomConfig.createInstance().getLocale()))+ N_EVENT_REPORT);
								}
							}
						}
						else
						{
							objNEventReportRSP.setStatus(EnumNEventReportStatus.PROCESSING_FAILURE);
							if(DicomConfig.createInstance().isLoggingOn())
							{
								DicomLogger objDicomLogger = DicomLogger.getLogger();
								objDicomLogger.log(ResourceBundleWrapper.getValue("warning","ImplementationNotProvided",new Locale(DicomConfig.createInstance().getLocale()))+ N_EVENT_REPORT);
							}
						}
					}
					else
					{
						if(objDataSourceCollection != null)
						{
							if(strAffectedSOPUid.equals("1.2.840.10008.5.1.4.34.8") || (strAffectedSOPUid.equals("1.2.840.10008.5.1.4.34.9")))
							objIDataSource = objDataSourceCollection.get(EnumDataSource.RTMACHINEVERIFICATION);
							else if(strAffectedSOPUid.equals("1.2.840.10008.5.1.4.34.6.4"))
								objIDataSource = objDataSourceCollection.get(EnumDataSource.UPSPULL);
							if(objIDataSource != null)
							{
																
								short shStatus = objIDataSource.retrieve(objDicomMessage,objResDatasetList);
								objNEventReportRSP.setStatus(shStatus);
								if(shStatus != EnumImplOpsStatus.SUCCESS.getValue())
								{
									if(DicomConfig.createInstance().isLoggingOn())
									{
										DicomLogger objDicomLogger = DicomLogger.getLogger();
										objDicomLogger.log(ResourceBundleWrapper.getValue("warning","ImplOperationFailure",new Locale(DicomConfig.createInstance().getLocale()))+ N_EVENT_REPORT);
									}
								}
								else if(shStatus == EnumImplOpsStatus.SUCCESS.getValue() || shStatus == EnumImplOpsStatus.FAILURE_NO_SUCH_OBJECT_INSTANCE.getValue())
								{
									if(objResDatasetList.size() != 0)
									{
										IDataSet objDataSet = objResDatasetList.elementAt(0);
										if(objDataSet.isEmpty())
										{
											objNEventReportRSP.setStatus(EnumNGetStatus.PROCESSING_FAILURE);
											objNEventReportRSP.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_EVENT_REPORT);
											}
										}
										else
										{
											objNEventReportRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_PRESENT);
											objNEventReportRSP.setDataSet(objResDatasetList.elementAt(0));
										}
									}
									else
									{
										objNEventReportRSP.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_EVENT_REPORT);
										}
									}
								}
							}
							else
							{
								objNEventReportRSP.setStatus(EnumNEventReportStatus.SUCCESS);
								if(DicomConfig.createInstance().isLoggingOn())
								{
									DicomLogger objDicomLogger = DicomLogger.getLogger();
									objDicomLogger.log(ResourceBundleWrapper.getValue("warning","ImplementationNotProvided",new Locale(DicomConfig.createInstance().getLocale()))+ N_EVENT_REPORT);
								}
							}
						}
						else
						{
							objNEventReportRSP.setStatus(EnumNEventReportStatus.PROCESSING_FAILURE);
							if(DicomConfig.createInstance().isLoggingOn())
							{
								DicomLogger objDicomLogger = DicomLogger.getLogger();
								objDicomLogger.log(ResourceBundleWrapper.getValue("warning","ImplementationNotProvided",new Locale(DicomConfig.createInstance().getLocale()))+ N_EVENT_REPORT);
							}
						}
					}

				}
				else
				{
					objNEventReportRSP.setStatus(EnumNEventReportStatus.SOP_CLASS_NOT_SUPPORTED);
					if(DicomConfig.createInstance().isLoggingOn())
					{
						DicomLogger objDicomLogger = DicomLogger.getLogger();
						objDicomLogger.log(ResourceBundleWrapper.getValue("warning","EarlyFailedResponse",new Locale(DicomConfig.createInstance().getLocale()))+ N_EVENT_REPORT);
					}
				}

				int iGroupLength = objNEventReportRSP.getCommandSet().getCommandSetLength();
				objNEventReportRSP.setCommandGroupLength(iGroupLength);
			}
			else
			{
				if(DicomConfig.createInstance().isLoggingOn())
				{
					DicomLogger objDicomLogger = DicomLogger.getLogger();
					objDicomLogger.log(ResourceBundleWrapper.getValue("status","AssociationDoesNotExists",new Locale(DicomConfig.createInstance().getLocale()))+ N_EVENT_REPORT);
				}
			}
		}
		catch(ParseException pe)
		{
			if(DicomConfig.createInstance().isLoggingOn() )
			{
				String strService = ResourceBundleWrapper.getValue("exceptions", "NEventReport", new Locale(DicomConfig.createInstance().getLocale()));
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log( strService + pe.getMessage());
			}
			objNEventReportRSP = getNEventReportResponse(objDicomMessage,EnumNEventReportStatus.UNABLE_TO_PROCESS_PARSING_FAILED);
		}
		catch(SinkException se)
		{
			if(DicomConfig.createInstance().isLoggingOn() )
			{
				String strService = ResourceBundleWrapper.getValue("exceptions", "NEventReport", new Locale(DicomConfig.createInstance().getLocale()));
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log( strService + se.getMessage());
			}
			objNEventReportRSP = getNEventReportResponse(objDicomMessage,EnumNEventReportStatus.PROCESSING_FAILURE);
		}
		catch(IOException ioe)
		{
			if(DicomConfig.createInstance().isLoggingOn() )
			{
				String strService = ResourceBundleWrapper.getValue("exceptions", "NEventReport", new Locale(DicomConfig.createInstance().getLocale()));
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log( strService + ioe.getMessage());
			}
			objNEventReportRSP = getNEventReportResponse(objDicomMessage,EnumNEventReportStatus.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(objNEventReportRSP.getStatus()));
		}
		return objNEventReportRSP;
	}

	/**
	 * 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 NEventReport Response.
	 * @param objDicomMessage Object of DicomMessage.
	 * @param enumStatus Enum for EnumNEventReportStatus which specifies status of response.
	 * @return objNEventReportRSP - Object of NEventReportRSP.
	 */
	private NEventReportRSP getNEventReportResponse(DicomMessage objDicomMessage, EnumNEventReportStatus enumStatus)
	{
		NEventReportRSP objNEventReportRSP = new NEventReportRSP();
		
		objNEventReportRSP.setAffectedSOPClassUID(objDicomMessage.getCommandSet().getValue(0x0000, 0x0002).toString());
		objNEventReportRSP.setCommandField(EnumCommandField.N_EVENT_REPORT_RSP);
		objNEventReportRSP.setMessageIDBeingRespondedTo(Short.parseShort(objDicomMessage.getCommandSet().getValue(0x0000,0x0110).toString()));
		
		objNEventReportRSP.setCommandDataSetType((short)0x0101);										
		objNEventReportRSP.setAffectedSOPInstanceUID(objDicomMessage.getCommandSet().getValue(0x0000, 0x1000).toString());
		objNEventReportRSP.setStatus(enumStatus);
		objNEventReportRSP.setCommandGroupLength(objNEventReportRSP.getCommandSet().getCommandSetLength());
		
		return objNEventReportRSP;
	}
	/*
	 * This function create the data element from the information provided.
	 */
	private IDataElement getDataElement(int GroupNo,int ElementNo,Object objVal,IDictionaryReader objDictionaryReader)
	{
		IDataElement objIDataElement = new ImplicitDE(GroupNo, ElementNo,objVal,objDictionaryReader);
		return objIDataElement;
	}
}
