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

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

import cdac.medinfo.sdk.dcm30_04.baselibs.DataSet;
import cdac.medinfo.sdk.dcm30_04.baselibs.DicomV3Factory;
import cdac.medinfo.sdk.dcm30_04.baselibs.ExplicitDE;
import cdac.medinfo.sdk.dcm30_04.baselibs.ImplicitDE;
import cdac.medinfo.sdk.dcm30_04.baselibs.ReferencedBasicAnnotationBoxSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.ReferencedImageBoxSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.ReferencedSequenceDependentItem;
import cdac.medinfo.sdk.dcm30_04.baselibs.SOPInstanceReferenceMacroDependentItem;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataElement;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataSet;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IItem;
import cdac.medinfo.sdk.dcm30_04.enums.EnumCommandField;
import cdac.medinfo.sdk.dcm30_04.enums.EnumMetaSOPClassUID;
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.EnumEarlyFailedStatus;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumImplOpsStatus;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumNCreateStatus;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumServiceType;
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.ncreate.commons.NCreateRQ;
import cdac.medinfo.sdk.dcm30_04.services.ncreate.commons.NCreateRSP;
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.EnumFeatureList;
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.StandardMethods;

/**
 * NCreateSCP works as a processor for NCreate Service.
 * The N-CREATE service is used by a DIMSE-service-user to request a peer DIMSE-service-user to create
 * a new managed SOP Instance, complete with its identification and the values of its associated Attributes,
 * and simultaneously to register its identification.
 * It is a confirmed service.
 * It processes the arrived NCreate Request and generates the response.
 * It processes the request according to the Service Class SOP ClassUID specified in request.
 */
public class NCreateSCP extends SCP
{
	
	private final String N_CREATE = ResourceBundleWrapper.getValue("status", "NCreate", new Locale(DicomConfig.createInstance().getLocale()));

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

	/**
	 * Instantiates object of NCreate SCP.
	 */
	private NCreateSCP()
	{
	}

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

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

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

	/**
	 * 
	 * @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[14];
		arrenumServiceType[0] = EnumServiceType.NCREATE_GPPPS;
		arrenumServiceType[1] = EnumServiceType.NCREATE_IAN;
		arrenumServiceType[2] = EnumServiceType.NCREATE_MPPS;
		arrenumServiceType[3] = EnumServiceType.NCREATE_BASIC_COLOR_PRINT_MANAGEMENT;
		arrenumServiceType[4] = EnumServiceType.NCREATE_BASIC_GRAYSCALEPRINT_MANAGEMENT;
		arrenumServiceType[5] = EnumServiceType.NCREATE_BASIC_PRINT_IMAGE_OVERLAY;
		arrenumServiceType[6] = EnumServiceType.NCREATE_PRESENTATION_LUT;
		arrenumServiceType[7] = EnumServiceType.NCREATE_PULLPRINT;
		arrenumServiceType[8] = EnumServiceType.NCREATE_MEDIA_CREATION;
		arrenumServiceType[9] = EnumServiceType.NCREATE_STUDY_COMPONENT_MANAGEMENT;
		arrenumServiceType[10] = EnumServiceType.NCREATE_RT_CONVENTIONAL_MACHINE_VERIFICATION;
		arrenumServiceType[11] = EnumServiceType.NCREATE_RT_ION_MACHINE_VERIFICATION;
		arrenumServiceType[12] = EnumServiceType.NCREATE_RT_ION_MACHINE_VERIFICATION;
		arrenumServiceType[13] = EnumServiceType.NCREATE_UPS;
		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[14];
		arrenumServiceType[0] = EnumServiceType.NCREATE_GPPPS;
		arrenumServiceType[1] = EnumServiceType.NCREATE_IAN;
		arrenumServiceType[2] = EnumServiceType.NCREATE_MPPS;
		arrenumServiceType[3] = EnumServiceType.NCREATE_BASIC_COLOR_PRINT_MANAGEMENT;
		arrenumServiceType[4] = EnumServiceType.NCREATE_BASIC_GRAYSCALEPRINT_MANAGEMENT;
		arrenumServiceType[5] = EnumServiceType.NCREATE_BASIC_PRINT_IMAGE_OVERLAY;
		arrenumServiceType[6] = EnumServiceType.NCREATE_PRESENTATION_LUT;
		arrenumServiceType[7] = EnumServiceType.NCREATE_PULLPRINT;
		arrenumServiceType[8] = EnumServiceType.NCREATE_MEDIA_CREATION;
		arrenumServiceType[9] = EnumServiceType.NCREATE_STUDY_COMPONENT_MANAGEMENT;
		arrenumServiceType[10] = EnumServiceType.NCREATE_RT_CONVENTIONAL_MACHINE_VERIFICATION;
		arrenumServiceType[11] = EnumServiceType.NCREATE_RT_ION_MACHINE_VERIFICATION;
		arrenumServiceType[12] = EnumServiceType.NCREATE_RT_ION_MACHINE_VERIFICATION;
		arrenumServiceType[13] = EnumServiceType.NCREATE_UPS;
		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 DataSink List.
	 * @param objSinkCollection collection of Data Sink.
	 */
	public void setDataSinkList(IPairedCollection<EnumDataSink, IDataSink> objSinkCollection) 
	{
		this.objDataSinkCollection = objSinkCollection;
	}

	/**
	 * This will give the response of NCreate 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) 
	{
		/*
		 * NCreate Response to be send
		 */
		NCreateRSP objNCreateRSP = null;

		/*
		 * NCreate Request
		 */
		NCreateRQ objNCreateRQ  = 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 || objDicomMessage.getDataSet() == null)
			{
				if(DicomConfig.createInstance().isLoggingOn())
				{
					DicomLogger objDicomLogger = DicomLogger.getLogger();
					objDicomLogger.log(ResourceBundleWrapper.getValue("warning","InvalidDicomMessage",new Locale(DicomConfig.createInstance().getLocale())));
				}

				objNCreateRQ = new NCreateRQ(objDicomMessage);

				objNCreateRSP = new NCreateRSP();
				objNCreateRSP.setCommandField(EnumCommandField.N_CREATE_RSP);
				objNCreateRSP.setAffectedSOPClassUID(objNCreateRQ.getAffectedSOPClassUID());
				objNCreateRSP.setMessageIDBeingRespondedTo(objNCreateRQ.getMessageID());
				//constant should be used here.
				objNCreateRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_NOT_PRESENT);
				objNCreateRSP.setStatus(EnumNCreateStatus.PROCESSING_FAILURE);
				return objNCreateRSP;
			}
			
			/*
			 * Construct NCreate RQ from Dicom Message.
			 */
			objNCreateRQ = new NCreateRQ(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())) + objNCreateRQ.getAffectedSOPClassUID());
				if(objNCreateRQ.getDataSet() != null)
					objDicomLogger.log(ResourceBundleWrapper.getValue("status", "TransferSyntax", new Locale(DicomConfig.createInstance().getLocale())) + objNCreateRQ.getDataSet().getTransferSyntax().getTransferSyntaxUID());
				objDicomLogger.log(ResourceBundleWrapper.getValue("status", "NCreateRequest", new Locale(DicomConfig.createInstance().getLocale())) + objNCreateRQ.getAffectedSOPInstanceUID());
			}

			if(objClientSession.getAssociationStatus())
			{
				String strSOPInstanceUID = objNCreateRQ.getAffectedSOPInstanceUID();
				/*
				 * Generating SOP instance UID if not provided using IP and Date/Time stamp.
				 */
				if(strSOPInstanceUID == null)
				{
					strSOPInstanceUID = createSOPInstanceUID(objNCreateRQ.getAffectedSOPClassUID());
					objNCreateRQ.setAffectedSOPInstanceUID(strSOPInstanceUID);
					if(DicomConfig.createInstance().isLoggingOn())
					{
						DicomLogger objDicomLogger =DicomLogger.getLogger();
						objDicomLogger.log(ResourceBundleWrapper.getValue("warning","SOPInstanceUIDNotProvided",new Locale(DicomConfig.createInstance().getLocale()))+ strSOPInstanceUID);
					}
				}
				objNCreateRSP = new NCreateRSP();
				IDataSet objIRSPDataSet = new DataSet(objDicomMessage.getDataSet().getTransferSyntax());

				//Initializing RSP 
				objNCreateRSP.setAffectedSOPClassUID(objNCreateRQ.getAffectedSOPClassUID());
				objNCreateRSP.setCommandField(EnumCommandField.N_CREATE_RSP);
				objNCreateRSP.setMessageIDBeingRespondedTo(objNCreateRQ.getMessageID());
				//Constant should be used here.
		//		objNCreateRSP.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. 
				objNCreateRSP.setAffectedSOPInstanceUID(objNCreateRQ.getAffectedSOPInstanceUID());

				/*
				 * This if-else block checks for the status of arrived RQ and takes corresponding action.
				 */
				if(enumEarlyFailedStatus == EnumEarlyFailedStatus.NO_EARLY_FAILED)
				{
					if(objNCreateRQ.getAffectedSOPClassUID().equals(EnumSOPClassUIDs.BASICFILMBOXIOD.getValue()))
					{
						createImageBoxAndBasicAnnotationBoxSequence(objDicomMessage);
						IDataElement objIDataElement = objDicomMessage.getDataSet().getDataForTag(0x2010, 0x0510);
						objIRSPDataSet.addDataElement(objIDataElement);
						objIDataElement = objDicomMessage.getDataSet().getDataForTag(0x2010, 0x0520);
						if(objIDataElement!=null)
						{
							objIRSPDataSet.addDataElement(objIDataElement);
						}
						objNCreateRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_PRESENT); 
						objNCreateRSP.setDataSet(objIRSPDataSet);
					objNCreateRSP.setCommandDataSetType(DicomConstants.DCM_DATASET_PRESENT);
					}
					IDataSink objIDataSink = null;
					String strAffectedSOPUid = objNCreateRQ.getAffectedSOPClassUID();
					if(objDataSinkCollection != null)
					{
						//Retrieving appropriate DataSink according to service class 
						if(strAffectedSOPUid.equals(EnumSOPClassUIDs.MPPS.getValue()))
							objIDataSink = objDataSinkCollection.get(EnumDataSink.MPPS);
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.GPPPS.getValue()))
							objIDataSink = objDataSinkCollection.get(EnumDataSink.GPPPS);
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.INSTANCEAVAILABILITYNOTIFICATION.getValue()))
							objIDataSink = objDataSinkCollection.get(EnumDataSink.IAN);
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.BASICFILMSESSIONIOD.getValue()) || strAffectedSOPUid.equals(EnumSOPClassUIDs.BASICFILMBOXIOD.getValue()) || 
								strAffectedSOPUid.equals(EnumSOPClassUIDs.PRESENTATIONLUTIOD.getValue()) || strAffectedSOPUid.equals(EnumSOPClassUIDs.PULLPRINTREQUESTIOD.getValue()) ||
								strAffectedSOPUid.equals(EnumSOPClassUIDs.BASICPRINTIMAGEOVERLAYBOXIOD.getValue()))
							objIDataSink = objDataSinkCollection.get(EnumDataSink.PRINT_MANAGEMENT);
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.MEDIACREATIONMANAGEMENT.getValue()))
							objIDataSink = objDataSinkCollection.get(EnumDataSink.MEDIA_CREATION);
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.STUDYCOMPONENTMANAGEMENT.getValue()))
							objIDataSink = objDataSinkCollection.get(EnumDataSink.STUDY_MANAGEMENT_META);
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.DCM_UPS_PUSH.getValue()))
							objIDataSink = objDataSinkCollection.get(EnumDataSink.UPSPUSH);
						
						else if(strAffectedSOPUid.equals(EnumSOPClassUIDs.RTCONVENTIONALMACHINEVERIFICATIONINFORMATIONOBJECTDEFINITION.getValue())
								||strAffectedSOPUid.equals(EnumSOPClassUIDs.DCM_RT_ION_MACHINE_VERIFICATION.getValue())
								)
							objIDataSink = objDataSinkCollection.get(EnumDataSink.RTMACHINEVERIFICATION);


						if(objIDataSink != null)
						{
							short shStatus = objIDataSink.store(objDicomMessage,null);
							objNCreateRSP.setStatus(shStatus);
							if(shStatus == EnumImplOpsStatus.FAILURE_STEP_STATUS_CONFLICT.getValue())
							{
								if(DicomConfig.createInstance().isLoggingOn())
								{
									DicomLogger objDicomLogger = DicomLogger.getLogger();
									objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","ProcedureStepObjectCreationFailure",new Locale(DicomConfig.createInstance().getLocale()))+ N_CREATE);
								}
							}
							else 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_CREATE);
								}
							}
						}
						else
						{
							objNCreateRSP.setStatus(EnumNCreateStatus.PROCESSING_FAILURE);
							if(DicomConfig.createInstance().isLoggingOn())
							{
								DicomLogger objDicomLogger = DicomLogger.getLogger();
								objDicomLogger.log(ResourceBundleWrapper.getValue("warning","ImplementationNotProvided",new Locale(DicomConfig.createInstance().getLocale()))+ N_CREATE);
							}
						}
					}
					else
					{
						objNCreateRSP.setStatus(EnumNCreateStatus.PROCESSING_FAILURE);
						if(DicomConfig.createInstance().isLoggingOn())
						{
							DicomLogger objDicomLogger = DicomLogger.getLogger();
							objDicomLogger.log(ResourceBundleWrapper.getValue("warning","ImplementationNotProvided",new Locale(DicomConfig.createInstance().getLocale()))+ N_CREATE);
						}
					}
				}
				else
				{
					objNCreateRSP.setStatus(EnumNCreateStatus.SOP_CLASS_NOT_SUPPORTED);
					if(DicomConfig.createInstance().isLoggingOn())
					{
						DicomLogger objDicomLogger = DicomLogger.getLogger();
						objDicomLogger.log(ResourceBundleWrapper.getValue("warning","EarlyFailedResponse",new Locale(DicomConfig.createInstance().getLocale()))+ N_CREATE);
					}
				}

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

			objNCreateRSP = getNCreateResponse(objDicomMessage,EnumNCreateStatus.UNABLE_TO_PROCESS_PARSING_FAILED);
		}
		catch(SerializeException pe)
		{
			if(DicomConfig.createInstance().isLoggingOn() )
			{
				String strService = ResourceBundleWrapper.getValue("exceptions", "NCreate", new Locale(DicomConfig.createInstance().getLocale()));
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log( strService + pe.getMessage());
			}
			
			objNCreateRSP = getNCreateResponse(objDicomMessage,EnumNCreateStatus.INVALID_ATTRIBUTE_VALUE);
		}
		catch(SinkException se)
		{
			if(DicomConfig.createInstance().isLoggingOn() )
			{
				String strService = ResourceBundleWrapper.getValue("exceptions", "NCreate", new Locale(DicomConfig.createInstance().getLocale()));
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log( strService + se.getMessage());
			}
			objNCreateRSP = getNCreateResponse(objDicomMessage,EnumNCreateStatus.PROCESSING_FAILURE);
		}
		catch(IOException ioe)
		{
			if(DicomConfig.createInstance().isLoggingOn() )
			{
				String strService = ResourceBundleWrapper.getValue("exceptions", "NCreate", new Locale(DicomConfig.createInstance().getLocale()));
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log( strService + ioe.getMessage());
			}
			objNCreateRSP = getNCreateResponse(objDicomMessage,EnumNCreateStatus.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(objNCreateRSP.getStatus()));
		}
		return objNCreateRSP;
	}

	/**
	 * This method generates the SOP Instance UID.
	 */
	private String createSOPInstanceUID(String strAffectedSOPClassUID)
	{
		String strIP = objClientSession.getClientAddress().toString();
		strIP = strIP.substring(1,strIP.lastIndexOf(":"));
		String strSOPInstanceUID = strAffectedSOPClassUID+ "." + strIP + "." + ((new Date()).getTime());
		return strSOPInstanceUID;
	}

	/**
	 * This method creates the Image Box Sequence and basic Annotation Box Sequence.
	 */
	@SuppressWarnings("unchecked")
	private void createImageBoxAndBasicAnnotationBoxSequence(DicomMessage objDicomMessage) throws IOException
	{
		NCreateRQ objNCreateRQ = new NCreateRQ(objDicomMessage);
		IDataSet objIDataSet = objDicomMessage.getDataSet();
		IDataElement objIDataElement  = null;
		if(objIDataSet.isPresent(0x2010, 0x0510))
		{
			ISingleCollection<IItem> objImageBoxSequenceItemCollection = (ISingleCollection<IItem>)objIDataSet.getValue(0x2010, 0x0500);
			if(objImageBoxSequenceItemCollection != null && objImageBoxSequenceItemCollection.size() !=0)
			{

			}
		}
		else
		{
			String strSOPInstanceUID = createSOPInstanceUID(objNCreateRQ.getAffectedSOPClassUID());
			ReferencedImageBoxSequence objReferencedImageBoxSequence= (ReferencedImageBoxSequence)DicomV3Factory.createInstance().createSequence(0x2010, 0x0510, objIDataSet.getTransferSyntax());
			SOPInstanceReferenceMacroDependentItem objSOPInstanceReferenceMacroDependentItem = objReferencedImageBoxSequence.getNewItem();
			if(objClientSession.getAgreedAssociation().isSOPClassUIDPresent(EnumMetaSOPClassUID.BASIC_GRAYSCALE_PRINT_MANAGEMENT_META_SOP_CLASS.getValue()))
			{
				objSOPInstanceReferenceMacroDependentItem.getSOPInstanceReferenceMacro().setReferencedSOPClassUID(EnumSOPClassUIDs.BASICGRAYSCALEIMAGEBOX.getValue());
			}
			else
			{
				objSOPInstanceReferenceMacroDependentItem.getSOPInstanceReferenceMacro().setReferencedSOPClassUID(EnumSOPClassUIDs.BASICCOLORIMAGEBOX.getValue());
			}
			objSOPInstanceReferenceMacroDependentItem.getSOPInstanceReferenceMacro().setReferencedSOPInstanceUID(EnumSOPClassUIDs.BASICGRAYSCALEIMAGEBOX.getValue()); //strSOPInstanceUID
			objReferencedImageBoxSequence.addItem(objSOPInstanceReferenceMacroDependentItem);
			if(objIDataSet.iterator().next() instanceof ImplicitDE)
			{
				objIDataElement = new ImplicitDE(0x2010, 0x0510,objReferencedImageBoxSequence,objIDataSet.getTransferSyntax(),objIDataSet.getDictionaryReader());
				objIDataSet.addDataElement(objIDataElement);
			}
			else
			{
				String strVR = objIDataSet.getDictionaryReader().getVR(0x2010, 0x0510);
				objIDataElement = new ExplicitDE(0x2010, 0x0510,StandardMethods.getiVRtype(strVR),objReferencedImageBoxSequence,objIDataSet.getTransferSyntax(),objIDataSet.getDictionaryReader());
				objIDataSet.addDataElement(objIDataElement);
			}
		}
		if(objClientSession.getAgreedAssociation().isSOPClassUIDPresent(EnumSOPClassUIDs.BASICANNOTATIONBOX.getValue()))
		{
			if(objIDataSet.isPresent(0x2010, 0x0520))
			{
				ISingleCollection<IItem> objAnnotationBoxSequenceItemCollection = (ISingleCollection<IItem>)objIDataSet.getValue(0x2010, 0x0500);
				if(objAnnotationBoxSequenceItemCollection != null && objAnnotationBoxSequenceItemCollection.size() !=0)
				{
				}

			}
			else
			{
				String strSOPInstanceUID = createSOPInstanceUID(objNCreateRQ.getAffectedSOPClassUID());
				ReferencedBasicAnnotationBoxSequence objReferencedBasicAnnotationBoxSequence = (ReferencedBasicAnnotationBoxSequence)DicomV3Factory.createInstance().createSequence(0x2010, 0x0520, objIDataSet.getTransferSyntax());
				SOPInstanceReferenceMacroDependentItem objSOPInstanceReferenceMacroDependentItem = objReferencedBasicAnnotationBoxSequence.getNewItem();
				objSOPInstanceReferenceMacroDependentItem.getSOPInstanceReferenceMacro().setReferencedSOPClassUID(EnumSOPClassUIDs.BASICANNOTATIONBOX.getValue());
				objSOPInstanceReferenceMacroDependentItem.getSOPInstanceReferenceMacro().setReferencedSOPInstanceUID(EnumSOPClassUIDs.BASICANNOTATIONBOX.getValue());
				objReferencedBasicAnnotationBoxSequence.addItem(objSOPInstanceReferenceMacroDependentItem);
				if(objIDataSet.iterator().next() instanceof ImplicitDE)
				{
					objIDataElement = new ImplicitDE(0x2010, 0x0520,objReferencedBasicAnnotationBoxSequence,objIDataSet.getTransferSyntax(),objIDataSet.getDictionaryReader());
					objIDataSet.addDataElement(objIDataElement);
				}
				else
				{
					String strVR = objIDataSet.getDictionaryReader().getVR(0x2010, 0x0520);
					objIDataElement = new ExplicitDE(0x2010, 0x0520,StandardMethods.getiVRtype(strVR),objReferencedBasicAnnotationBoxSequence,objIDataSet.getTransferSyntax(),objIDataSet.getDictionaryReader());
					objIDataSet.addDataElement(objIDataElement);
				}
			}
		}
	}

	/**
	 * 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 NCreate Response.
	 * @param objDicomMessage Object of DicomMessage.
	 * @param enumStatus Enum for EnumNCreateStatus which specifies status of response.
	 * @return objNCreateRSP - Object of NCreateRSP.
	 */
	private NCreateRSP getNCreateResponse(DicomMessage objDicomMessage, EnumNCreateStatus enumStatus)
	{
		NCreateRSP objNCreateRSP = new NCreateRSP();
		
		objNCreateRSP.setAffectedSOPClassUID(objDicomMessage.getCommandSet().getValue(0x0000, 0x0002).toString());
		objNCreateRSP.setCommandField(EnumCommandField.N_CREATE_RSP);
		objNCreateRSP.setMessageIDBeingRespondedTo(Short.parseShort(objDicomMessage.getCommandSet().getValue(0x0000,0x0110).toString()));
		
		objNCreateRSP.setCommandDataSetType((short)0x0101);										
		objNCreateRSP.setAffectedSOPInstanceUID(objDicomMessage.getCommandSet().getValue(0x0000, 0x1000).toString());
		objNCreateRSP.setStatus(enumStatus);
		objNCreateRSP.setCommandGroupLength(objNCreateRSP.getCommandSet().getCommandSetLength());
		
		return objNCreateRSP;
	}
}
