/*******************************************************************************
 *  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.testscp.securityprofiles.onlineelectronicstoragesecureuse;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConstants;

import java.io.IOException;
import java.net.InetSocketAddress;


import cdac.medinfo.sdk.dcm30_04.baselibs.DataSet;
import cdac.medinfo.sdk.dcm30_04.baselibs.DicomV3Factory;
import cdac.medinfo.sdk.dcm30_04.baselibs.FailedSOPSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.ImplicitDE;
import cdac.medinfo.sdk.dcm30_04.baselibs.ReferencedSOPSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.ReferencedSOPSequenceItem;
import cdac.medinfo.sdk.dcm30_04.baselibs.TransferSyntax;
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.commoninterface.ITag;
import cdac.medinfo.sdk.dcm30_04.enums.EnumCommandField;
import cdac.medinfo.sdk.dcm30_04.enums.EnumLoggingLevel;
import cdac.medinfo.sdk.dcm30_04.enums.EnumPDUType;
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.cstore.scp.CStoreSCP;
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.EnumSCEventTypeID;
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.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.IDataSink;
import cdac.medinfo.sdk.dcm30_04.services.dimsecommons.IDataSource;
import cdac.medinfo.sdk.dcm30_04.services.javadbhelper.DataSinkImpl;
import cdac.medinfo.sdk.dcm30_04.services.naction.scp.NActionSCP;
import cdac.medinfo.sdk.dcm30_04.services.neventreport.scu.NEventReportSCU;
import cdac.medinfo.sdk.dcm30_04.services.onlineelectronicstoragesecureuse.EnumSecureStorageStatus;
import cdac.medinfo.sdk.dcm30_04.services.onlineelectronicstoragesecureuse.OnlineElectronicStorageSecureUseProfile;
import cdac.medinfo.sdk.dcm30_04.services.scp.commons.ClientSession;
import cdac.medinfo.sdk.dcm30_04.services.scp.commons.ProcessHandler;
import cdac.medinfo.sdk.dcm30_04.services.scp.commons.SCPSession;
import cdac.medinfo.sdk.dcm30_04.services.storagecommitment.storagecommitmentsinkimpl.StorageCommitmentSinkImpl;
import cdac.medinfo.sdk.dcm30_04.services.storagecommitment.storagecommitmentsourceimpldb.StorageCommitmentSourceImplDB;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConfig;
import cdac.medinfo.sdk.dcm30_04.utils.DicomLogger;
import cdac.medinfo.sdk.dcm30_04.utils.IPairedCollection;
import cdac.medinfo.sdk.dcm30_04.utils.ISingleCollection;
import cdac.medinfo.sdk.dcm30_04.utils.PairedCollection;
import cdac.medinfo.sdk.dcm30_04.utils.SingleCollection;

/**
 * This is the customized implementation of the test code for Online Electronic Storage Secure Use Profile.
 * This is the testcode to demonstrate use of Online Electronic Secure Storage Use profile in DICOM Communication Scenario on Service Provider.
 * This sample test code demonstrates the verification and tracking of data sets in Secure Dicom Communication Scenario.
 * The testcode uses Storage Commitment Push Model and Storage Services like C-STORE.
 * For more information can be get from the tutorial of Online Electronic Storage Secure Use Profile.
 * This sample codes is to show how user can define its own implementation for handling connection, association and service request.
 * In this sample test code all the required methods of IProcessHandler are implemented (onConnect(), onAssociateReceive() and onServiceRequest()) 
 * and their definition is provided here. User can define these methods according to his need.
 * N-Event Report Server information should be set in Storage Commitment SCP to send N-Event Report of Storage
 * Commitment Operation.
 * The IP and port on which this server is started should also be provided to Storage Commitment SCP.
 */
public class Secure_Use_Profile_SCP 
{
	public static void main(String[] args) throws IOException
	{
		try
		{
			DicomConfig objDicomConfig = DicomConfig.createInstance();


			//enable logging,specify the path of the directory where log files are created.
			//User can specify any directory in place of 'c:\DCM_Logs'. It should have read and write permision
			//for that directory.
			objDicomConfig.enableLogging(EnumLoggingLevel.SEVERE,"C:\\DCM_Logs");


			/*
			 * Creates StorageCommitmentHandler.
			 */
			StorageCommitmentHandler scp = new StorageCommitmentHandler();

			//It is storage path where the DICOM files are stored.It should have read and write permision
			//for that directory.
			scp.setStoragePath("C:/StoragePath");

			/*
			 * DataBase path.
			 */
			String dataBasePath = "C:/DataBase/SDKdb";
			String ConnectionString = "jdbc:derby://localhost:5050/" + dataBasePath;
			String driverString = "org.apache.derby.jdbc.ClientDriver";

			/*
			 * Add DataSink for C-Store and Storage Commitment.
			 */
			IDataSink objSCDataSink = new StorageCommitmentSinkImpl();
			IDataSink objCServicesDataSink = new DataSinkImpl(ConnectionString,driverString);
			IDataSource objSCDataSource = new StorageCommitmentSourceImplDB(ConnectionString,driverString);

			scp.addDataSink(EnumDataSink.CSERVICES, objCServicesDataSink);
			scp.addDataSink(EnumDataSink.STORAGE_COMMITMENT_PUSH_MODEL, objSCDataSink);
			scp.addDataSource(EnumDataSource.STORAGE_COMMITMENT_PUSH_MODEL, objSCDataSource);

			/*
			 * Alternatively User can  use StorageCommitmentSourceImplDicomDir.
			 * instead of Db.
			 * IDataSource objDataSource =new StorageCommitmentSourceImplDICOMDIR("D:\\DICOMDIRPATH");
			 * addDataSource(EnumDataSource.STORAGE_COMMITMENT_PUSH_MODEL, objDataSource);
			 */

			/*
			 * Add N-Event Report SCP IP, Port information.
			 * The Storage Commitment SCP sends N-Event Report
			 * Request to the Event SCP.
			 */
			scp.addNEventReportSCPInformation("SDK", "192.168.8.101", 9999);
			scp.addNEventReportSCPInformation("DCMRCV", "192.168.8.101",9002);

			/*
			 * Configure the path to store DataSet for which Storage Commitment
			 * is to be provided. If this path is not set then the files are not
			 * moved from the server.
			 */
			scp.setStorageCommitmentPath("C:\\StorageCommitmentDir");

			//Create SCP Session 
			SCPSession session = new SCPSession();

			//Set Process Handler as the SCP TestCode
			session.setIProcessHandler(scp);
			
			/*
			 * Setting time out as 5000 milli second. It will wait 5000 mili seconds for read and write operation.
			 * If in the time specified (5000 ms) read or write operation is not performed then 
			 * session is time out.
			 */ 
			//session.setTimeOut(5000);

			/*
			 * Initializing selected services on SCPSession.
			 * This selection customises assocaition loading process where the association context related to
			 * selected services will only be loaded for negotiation purpose.
			 */
			EnumServiceType []arrenumServiceType = new EnumServiceType[2];
			arrenumServiceType[0] = EnumServiceType.NACTION_STORAGE_COMMITMENT;
			arrenumServiceType[1] = EnumServiceType.CSTORE_DEFAULT;
			session.selectServices(arrenumServiceType);

			/*
			 * Set EnumCommandFiled on ProcessHandler. The procesHandler will process the services whose
			 * command field is set in Processhandler. For the request of other services the process handler
			 * will generate Abort.
			 */
			ISingleCollection<EnumCommandField> objEnumCommandField = new SingleCollection<EnumCommandField>();
			objEnumCommandField.add(EnumCommandField.C_STORE_RQ);
			objEnumCommandField.add(EnumCommandField.N_ACTION_RQ);
			scp.setEnumCommandField(objEnumCommandField);

			//Start the Session
			session.start("SDK",9000);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
}


/**
 * Note ProcessHandler is the class which implements interface IProcessHandler or extends ProcessHandler.
 * This class is implemented to test the Storage Commitment Server Operation.
 * In order to run the following class , please refer to this link($Link) to create the environment.
 * To implement an SCP of a particular we need to do the following things :
 * To create SCP user should either has to implement class IProcessHandler (which is an interface ) or extends Process handler.
 * If test code implements process handler then user has to provide implementation of the methods declared in the interface.
 * Otherwise he can extend the process handler which containts the default implementation for processing of association and service request.
 * Create the SCPSession object which maintains the session of the SCP. 
 * Set the services for which SCP is started and Process handler(reference of test code) in SCPSession.
 * Its "start" method starts the SCP server.
 * When an SCU connects to the Storage Commitment Server then when the connection is accepted client session is created for that SCU.
 * When SCU connects to theStorage Commitment Server the call back on onConnect of ProcessHandler is made. Now user can get the connection parameters
 * from client session and can decide whether to accept the connection or not. To accpect the connection user has to
 * pass true to acceptConnection function in ClientSession class.
 * After the connection is established the next request expected to arrive at SCP is Association request.
 * When Assocaition request arrives, callback is made to onAssociateReceive method of ProcessHandler.
 * User now can negotioate Association request with the default association and creates AssociateAC or Reject.
 * The library provides AssociationNegotiator Class to negotiate and create appropriate response.
 * When N-Action request arrives the call back is made to onServiceRequest method of ProcessHandler.
 * The user can get the N-Action  Request from current ClientSession object passed in onServiceRequest method, processes it
 * and creates N-Action -RSP. SDK provides processRequest method in N-Action SCP to process the request and generates response.
 * The N-Action SCP for Storage COmmitment request search for the SOP Class UID and SOP Instance UID 
 * in the SourceImpl and move these to StorageCommitment Path. The it establishes connection with N-Event 
 * Report SCP and sends the storage Commitment report to N-Event Report SCP. 
 */
class StorageCommitmentHandler extends ProcessHandler
{
	private OnlineElectronicStorageSecureUseProfile objSecureUseProfile;

	public StorageCommitmentHandler()
	{
		objSecureUseProfile = OnlineElectronicStorageSecureUseProfile.getOnlineElectronicStorageSecureUseProfile();
		// Informing the Profile that Connection is a Secure Connection
		objSecureUseProfile.setSecureConnection(true);
		objSecureUseProfile.enable(true);
	}


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

	/**
	 * 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 AssociateRJ 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 :
		{
			System.out.println("Association Request Received");
			AssociateRQ objAssociateRQ = (AssociateRQ)objIDIMSEAssociation;

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

			//Negotiatie 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;
		}
		}
	}

	/**
	 * This method notifies when a service related request is received.
	 * For Example,
	 * If N-Action-RQ is received,
	 * 		Create NActionSCP using the ClientSession.
	 * 		Process the reqeust using the NActionSCP which actually searches for the files for which storage commitment is requested
	 * 		and moves then to storage commtiment path. Also sends N-Event Report of Storage commitment operation to 
	 * 		N-Event Report SCP.
	 * @see objClientSession Object of Client Session.
	 */
	@SuppressWarnings("unchecked")
	public void onServiceRequest(ClientSession objClientSession)
	{
		DIMSEResponse objDResponse = null;
		EnumCommandField enumCommandType = objClientSession.getDicomMessage().getCommandType();

		try
		{
			if(enumCommandType.equals(EnumCommandField.N_ACTION_RQ))
			{
				System.out.println("N-Action Request Received");
				/*
				 * create N-Action SCP.
				 */
				NActionSCP objNActionSCP = NActionSCP.createInstance(objClientSession); 

				IPairedCollection<EnumDataSink,IDataSink> objDataSinkCollection = new PairedCollection<EnumDataSink, IDataSink>();
				objDataSinkCollection.put(EnumDataSink.CSERVICES, getDataSink(EnumDataSink.CSERVICES));
				objDataSinkCollection.put(EnumDataSink.STORAGE_COMMITMENT_PUSH_MODEL, getDataSink(EnumDataSink.STORAGE_COMMITMENT_PUSH_MODEL));

				IPairedCollection<EnumDataSource,IDataSource> objDataSourceCollection = new PairedCollection<EnumDataSource, IDataSource>();
				objDataSourceCollection.put(EnumDataSource.STORAGE_COMMITMENT_PUSH_MODEL, getDataSource(EnumDataSource.STORAGE_COMMITMENT_PUSH_MODEL));

				/*
				 * Set Data sink and Data source on N-Action SCP.
				 */
				objNActionSCP.setDataSinkList(objDataSinkCollection);
				objNActionSCP.setDataSourceList(objDataSourceCollection);

				/*
				 * Call process Reqiest function to process N-ACtion Request and generates response
				 */
				objDResponse = objNActionSCP.processRequest(objClientSession.getDicomMessage());

				IDataSet objITempDataSet = (IDataSet)objClientSession.getDicomMessage().getDataSet().clone();
				if(objDResponse != null)
					objClientSession.sendServiceMessage(objDResponse);

				System.out.println("N-Action Response Sent");

				/*
				 * Move files for which storage commitment is requested to particular location.
				 */
				IDataSet objIDataSet = retrieveAndStore(objITempDataSet, getDataSource(EnumDataSource.STORAGE_COMMITMENT_PUSH_MODEL), getDataSink(EnumDataSink.STORAGE_COMMITMENT_PUSH_MODEL),getStorageCommitmentPath());

				// code to finalise waiting confirmations of StorageCommitment
				{
					ISingleCollection<IItem> objSQReferencedSOPSequenceItem = (ISingleCollection<IItem>)(objIDataSet.getValue(0x0008, 0x1199));
					int groupNumber=0x0008;
					int elementNumber= 0x1155;
					for(IItem objItem : objSQReferencedSOPSequenceItem)
					{
						for(IDataElement objDataElement : objItem)
						{
							ITag objTag = objDataElement.getTag();
							int iGroupNumber = objTag.getGroupNumber();
							int iElementNumber = objTag.getElementNumber();

							if(groupNumber == iGroupNumber && elementNumber==iElementNumber)
							{
								String strSOPInstanceUID = (String)objDataElement.getValue();
								objSecureUseProfile.finalise(strSOPInstanceUID);
							}
						}
					}
				}

				/*
				 * Generate NEvent Report Request and send it to N-Event Report Server.
				 */
				InetSocketAddress objInetSocketAddress = new InetSocketAddress("192.168.8.101",9999);
				NEventReportSCU objNEventReportSCU = new NEventReportSCU(objInetSocketAddress);

				objNEventReportSCU.send("SDK", "SDK", DicomConstants.DCM_STORAGE_COMMITMENT_PUSH_MODEL_SOP_CLASS, DicomConstants.DCM_STORAGE_COMMITMENT_PUSH_MODEL_SOP_INSTANCE,EnumSCEventTypeID.STORAGE_COMMITMENT_ACTION_TYPE_ID, objIDataSet);
				System.out.println("N-Event Report Operation performed");
			}
			else 
			{
				CStoreSCP objCStoreSCP = CStoreSCP.createInstance(objClientSession); 

				System.out.println("C-Store Request is received");
				objCStoreSCP.setStoragePath("C:/StoragePath");

				/*
				 * Process CStore Request. The DataSet is serialized at the Storage Path provided on C-Store SCP.
				 * If No Storage Path is provide on C-Store SCP then File is serialized at "%temp%/CDAC-SDK/DCM-J/DicomDump"
				 * in User Temp Folder.
				 */
				objCStoreSCP.setDataSinkList(getDataSinkList());

				DicomMessage objReceivedMessage = objClientSession.getDicomMessage();
				
				/* Securing the Dataset for verifying and keep tacking of Original and Other Copies of the same Dataset.
				 * In case of sending dataset with SOPInstanceStatus Original, the method informs whether to expect Storage Commitment or Not.
				 */
				
				if(EnumSecureStorageStatus.STORAGECOMMITMENTEXPECTED == objSecureUseProfile.secureDataSetForStorage(objReceivedMessage.getDataSet(), true))
					System.out.println("StorageCommitment Confirmation  expected..");
				else
					System.out.println("StorageCommitment Not Confirmation expected..");

				objDResponse = objCStoreSCP.processRequest(objReceivedMessage);

				/*
				 * Send the service message through Client Session.
				 */
				if(objDResponse != null)
					objClientSession.sendServiceMessage(objDResponse);

				System.out.println("C-Store Response sent");
			}
		}catch(Exception ioe)
		{
			ioe.printStackTrace();
			objClientSession.close();
		}
	}

	/*
	 * This method stores the Data Set and returns N-Event-Report Data Set.
	 */
	@SuppressWarnings("unchecked")
	private IDataSet retrieveAndStore(IDataSet objITempDataSet, IDataSource objIDataSource, IDataSink objIDataSink, String strStorageCommitmentPath) 
	{
		IDataSet objIDataSet = new DataSet();

		/*
		 * Create Instance of Reference SOP Sequence and Failed SOP Sequence,
		 * That are used in N-Event Report Request.
		 */
		ReferencedSOPSequence objRSPReferencedSOPSequence = (ReferencedSOPSequence)DicomV3Factory.createInstance().createSequence(0x0008,0x1199, new TransferSyntax(DicomConstants.TS_DEFAULT_IMPLICIT_VR_LITTLE_ENDIAN));
		FailedSOPSequence objRSPFailedSOPSequence = (FailedSOPSequence)DicomV3Factory.createInstance().createSequence(0x0008,0x1198, new TransferSyntax(DicomConstants.TS_DEFAULT_IMPLICIT_VR_LITTLE_ENDIAN));

		ISingleCollection<IDataSet> objDataSetCollection = new SingleCollection<IDataSet>();
		try
		{
			/*
			 * Retrive DataSet Collection, for which Storage Commitment has to be performed.
			 */
			DicomMessage objDicomMessage = new DicomMessage();
			objDicomMessage.setDataSet(objITempDataSet);
			objIDataSource.retrieve(objDicomMessage, objDataSetCollection);

			DicomMessage objTempDicomMessage = new DicomMessage();
			if(objDataSetCollection.size() != 0)
			{
				/*
				 * For each DataSet Collection Peform Storage Operation.
				 * If the Storage Operation is sucessfully performed , then from SOP Class UID and SOP Instance UID 
				 * of the DataSet , Referenced SOP Sequence is created. If the Storage Operation is failed
				 * then Failed SOP Sequence is provided.
				 */
				for(IDataSet objTempIDataSet : objDataSetCollection)
				{
					try
					{
						objTempDicomMessage.setDataSet(objTempIDataSet);
						if(strStorageCommitmentPath != null)
						{
							objIDataSink.store(objTempDicomMessage, getStorageCommitmentPath());
							IItem objRSPReferencedSOPSequenceItem = objRSPReferencedSOPSequence.getNewItem();
							IDataElement objIDataElement = new ImplicitDE(0x0008,0x1155,objTempIDataSet.getValue(0x0008, 0x0018),objDicomMessage.getDataSet().getDictionaryReader());
							objRSPReferencedSOPSequenceItem.addDataElement(objIDataElement);
							objIDataElement = new ImplicitDE(0x0008,0x1150,objTempIDataSet.getValue(0x0008,0x0016),objDicomMessage.getDataSet().getDictionaryReader());
							objRSPReferencedSOPSequenceItem.addDataElement(objIDataElement);
							objRSPReferencedSOPSequence.addItem(objRSPReferencedSOPSequenceItem);
						}
					}
					catch(SinkException se)
					{
						if(DicomConfig.createInstance().isLoggingOn())
						{
							DicomLogger.getLogger().log(se.getMessage());
						}
						IItem objRSPFailedSOPSequenceItem = objRSPFailedSOPSequence.getNewItem();
						IDataElement objIDataElement = new ImplicitDE(0x0008,0x1155,objTempIDataSet.getValue(0x0008, 0x0018),objDicomMessage.getDataSet().getDictionaryReader());
						objRSPFailedSOPSequenceItem.addDataElement(objIDataElement);
						objIDataElement = new ImplicitDE(0x0008,0x1150,objTempIDataSet.getValue(0x0008,0x0016),objDicomMessage.getDataSet().getDictionaryReader());
						objRSPFailedSOPSequenceItem.addDataElement(objIDataElement);
						objRSPFailedSOPSequence.addItem(objRSPFailedSOPSequenceItem);
					}
				}
			}

			/*
			 * Now Failed SOP Sequence is to be created for the DataSets which are not retrived.
			 */
			ISingleCollection<ReferencedSOPSequenceItem> objReferencedSOPSequenceItemCol = (ISingleCollection<ReferencedSOPSequenceItem>)objDicomMessage.getDataSet().getValue(0x0008,0x1199);
			for(ReferencedSOPSequenceItem objReferencedSOPSequenceItem : objReferencedSOPSequenceItemCol)
			{
				boolean isPresent = false;
				String strSOPClassUID = objReferencedSOPSequenceItem.getValue(0x0008,0x1150).toString();
				String strSOPInstanceUID = objReferencedSOPSequenceItem.getValue(0x0008,0x1155).toString();
				for(IDataSet objTempIDataSet : objDataSetCollection)
				{
					if(objTempIDataSet.getSOPClassUID().equals(strSOPClassUID) && objTempIDataSet.getValue(0x0008, 0x0018).toString().equals(strSOPInstanceUID))
					{
						isPresent = true;
						break;
					}
				}
				if(!isPresent)
				{
					IItem objRSPFailedSOPSequenceItem = objRSPFailedSOPSequence.getNewItem();
					IDataElement objIDataElement = new ImplicitDE(0x0008,0x1155,strSOPInstanceUID,objDicomMessage.getDataSet().getDictionaryReader());
					objRSPFailedSOPSequenceItem.addDataElement(objIDataElement);
					objIDataElement = new ImplicitDE(0x0008,0x1150,strSOPClassUID,objDicomMessage.getDataSet().getDictionaryReader());
					objRSPFailedSOPSequenceItem.addDataElement(objIDataElement);
					objRSPFailedSOPSequence.addItem(objRSPFailedSOPSequenceItem);
				}
			}
			IDataElement objIDataElement = new ImplicitDE(0x0008,0x1199,objRSPReferencedSOPSequence,objIDataSet.getTransferSyntax(),objIDataSet.getDictionaryReader());
			objIDataSet.addDataElement(objIDataElement);
			objIDataElement = new ImplicitDE(0x0008,0x1198,objRSPFailedSOPSequence,objIDataSet.getTransferSyntax(),objIDataSet.getDictionaryReader());
			objIDataSet.addDataElement(objIDataElement);

		}
		catch(SinkException de)
		{
			objIDataSet = null;
			if(DicomConfig.createInstance().isLoggingOn() ) 
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(de.getMessage());
			}
		}
		catch(SourceException se)
		{
			objIDataSet = null;
			if(DicomConfig.createInstance().isLoggingOn() ) 
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(se.getMessage());
			}
		}
		catch(IOException ioe)
		{
			if(DicomConfig.createInstance().isLoggingOn() ) 
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ioe.getMessage());
			}
		}
		return objIDataSet;
	}
}



