/*******************************************************************************
 *  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.mediacreation.testcases.scu;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConstants;


import cdac.medinfo.sdk.dcm30_04.baselibs.ReferencedSOPSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.ReferencedSOPSequenceItem;
import cdac.medinfo.sdk.dcm30_04.baselibs.SQ;
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.dicommodel.mediacreationmanagement.MediaCreationManagementIOD;
import cdac.medinfo.sdk.dcm30_04.dicommodel.modules.MediaCreationManagementModule;
import cdac.medinfo.sdk.dcm30_04.dicommodel.modules.SOPCommonModule;
import cdac.medinfo.sdk.dcm30_04.enums.EnumAllowLossyCompression;
import cdac.medinfo.sdk.dcm30_04.enums.EnumAllowMediaSplitting;
import cdac.medinfo.sdk.dcm30_04.enums.EnumIncludeDisplayApplication;
import cdac.medinfo.sdk.dcm30_04.enums.EnumIncludeNonDICOMObjects;
import cdac.medinfo.sdk.dcm30_04.enums.EnumLabelUsingInformationExtractedFromInstances;
import cdac.medinfo.sdk.dcm30_04.enums.EnumLoggingLevel;
import cdac.medinfo.sdk.dcm30_04.enums.EnumPDUType;
import cdac.medinfo.sdk.dcm30_04.enums.EnumPreserveCompositeInstancesAfterMediaCreation;
import cdac.medinfo.sdk.dcm30_04.enums.EnumSOPClassUIDs;

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.DefaultAssociation;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumActionTypeID;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumServiceType;
import cdac.medinfo.sdk.dcm30_04.services.dimsecommons.DicomMessage;
import cdac.medinfo.sdk.dcm30_04.services.naction.commons.NActionRQ;
import cdac.medinfo.sdk.dcm30_04.services.naction.scu.NActionSCU;
import cdac.medinfo.sdk.dcm30_04.services.ncreate.commons.NCreateRQ;
import cdac.medinfo.sdk.dcm30_04.services.ncreate.scu.NCreateSCU;
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.nget.scu.NGetSCU;
import cdac.medinfo.sdk.dcm30_04.services.scu.commons.SCUSession;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConfig;
import cdac.medinfo.sdk.dcm30_04.utils.ICollectionIterator;

/**
 * This sample code specifies the role of Media Creation service class user.
 * It uses respective SCU to send the request to SCP.
 * Media Creation service class supports four services :-
 * 1. NCreate
 * 2. NGet
 * 3. NAction
 * 
 * In order to run the following class , please refer to this link($Link) to create the environment.
 * To implement an SCU of a particular we need to do the following things:
 * Create the SCUSession object which maintains the current session of the SCU. Its "start" method accepts
 * the SCP's IP Address and Port and tries to establish a connection with the SCP.
 * 
 * Next step is to establish association .Some ways to create an Association Request: 
 * 1)Use Default Association class to Load the association for requested serive. User can load the association 
 * by specifying the Serivces Type in loadAssociation method of Default association.
 * 2) Manually create object of AssociationRQ class and set all presentation contexts colleciton on it. 
 * More details provided in the API DOCS of AssociationRQ class.
 * 
 * After successful association is established the SCUSession stores the status of the Association with it.  
 * Create respective SCU object to send the service Request to the SCP. Currently the "sendMessage" method 
 * of the respective SCU would wait after sending the service DIMSE-RQ until a suitable DIMSE-RSP is received. 
 * This operation is part of Synchrnous mode request handling.  
 * Once the DIMSE response is received by the SCU it stores it into its collection
 * which then can be retrieved.
 * 
 * After this, create a A-Release-RQ in order to request for association release and send it using the
 * SCUSession object. This again is a blocking call and the SCUSession waits until a A-Release-RP is received.
 * 
 * At the end of an operation calling "close" on SCUSession would disconnect the connection made with the 
 * SCP and release all the acquired resources.
 * Instead of closing the session after a service operation is compelte,we can create another association
 * and send mroe requests. 
 * Also it is possible to create multiple service requests and send them one by one on the same Association.
 */
public class Media_Creation_SCU 
{
	public static void main(String[] args)
	{
		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'.
		//If no directory is defined by user to create logs then it is created in User Temp DIR.
		objDicomConfig.enableLogging(EnumLoggingLevel.INFO, "C:\\DCM_Logs");

		
		
		

		

		/*
		 * Creates instance of Media_Creation_SCU
		 */
		Media_Creation_SCU objMediaCreation_SCUTest = new Media_Creation_SCU();

		/*
		 * Performs operation of MediaCreation Services 
		 */
		objMediaCreation_SCUTest.doMediaCreation();
	}

	/**
	 * This function performs the operation for all supported services of MediaCreation
	 * MediaCreation operation involves following steps :-
	 * 1. Establish the connection with SCP
	 * 2. Establish the association with SCP
	 * 3. Initializes the SCU for corresponding N-Service and performs the operation using service request
	 * 4. Releases the association after successful processing of service request
	 * 5. Closes the connection with SCP. 
	 */
	public void doMediaCreation()
	{
		try
		{
			/*
			 * *************************************************************************
			 * This block of code performs the connection process with SCP.
			 * It creates the SCUSession and connects with SCP using this session.
			 * *************************************************************************
			 */
			//Create SCUSession object
			SCUSession objSession = new SCUSession();
			
			/*
			 * 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.
			 */ 
		//	objSession.setTimeOut(5000);

			//Start SCU Session by providing the IP address and Port of SCP.
			objSession.start("192.168.8.94", 9000);

			/*
			 * *************************************************************************
			 * This block of code initializes the DefaultAssociation information
			 * Using the DefaultAssociation it generates the Association Request and 
			 * creates association  with SCP.
			 * *************************************************************************
			 */

			// Initializes the service types and loads the Default Association
			EnumServiceType[] arrenumServiceType = new EnumServiceType[3];
			arrenumServiceType[0] = EnumServiceType.NCREATE_MEDIA_CREATION;
			arrenumServiceType[1] = EnumServiceType.NACTION_MEDIA_CREATION;
			arrenumServiceType[2] = EnumServiceType.NGET_MEDIA_CREATION;
			DefaultAssociation objDefaultAssociation = new DefaultAssociation("SDK");
			objDefaultAssociation.loadAssociation(arrenumServiceType);

			//Create Association RQ
			AssociateRQ objAssociateRQ = objDefaultAssociation.getAssociationRQ("SDK");

			//Send the Association Request
			if(objSession.sendAssociationMessage(objAssociateRQ))
			{	
				//When associaton is successfully established the "objSession.sendAssociationMessage()" method returns true.
				System.out.println("Association established");

				/*
				 * *************************************************************************
				 * This block of code creates the NCreate SCU to perform the process of 
				 * SOP Instance creation operation for MediaCreation.
				 * It initializes the NCreate RQ and sends it to SCP.
				 * *************************************************************************
				 */
				//Create NCreate SCU object using the current SCU Session.
				NCreateSCU objNCreateSCU = new NCreateSCU(objSession);

				//Genarating Dataset for MediaCreation
				IDataSet objMediaCreationDataSet = getDatasetForCreate();

				//Initializing NCreate RQ 
				NCreateRQ objNCreateRQ = NCreateRQ.getDefaultRQ(EnumSOPClassUIDs.MEDIACREATIONMANAGEMENT.getValue(), "1.3.12.2.1107.5.1.4.36085.4.0.22", objMediaCreationDataSet);
				System.out.println("N-Create Request for Media Creation initialized");

				//sending RQ to SCP
				if(objNCreateSCU.sendMessage(objNCreateRQ)) //sending request
					System.out.println("Media Creation Instance Creation Operation performed");
				else
					System.out.println("Media Creation Instance Creation Operation failed");

				/*
				 * *************************************************************************
				 * This block of code creates the NGet SCU to perform the process of 
				 * SOP Instance retrieval operation for Media Creation.
				 * It initializes the NGet RQ and sends it to SCP.
				 * After successful completion of process it displays the retrieved Dataset
				 * using NGet RSP
				 * *************************************************************************
				 */
				//Create NGet SCU object using the current SCU Session.
				NGetSCU objNGetSCU = new NGetSCU(objSession);

				//Initializing NGet RQ
				NGetRQ objNGetRQ = NGetRQ.getDefaultRQ(EnumSOPClassUIDs.MEDIACREATIONMANAGEMENT.getValue(), "1.3.12.2.1107.5.1.4.36085.4.0.22", null);
				System.out.println("N-Get Request for Media Creation initialized");

				//sending RQ to SCP
				if(objNGetSCU.sendMessage(objNGetRQ)) //sending request
					System.out.println("Media Creation Scheduled Instance Retrieval Operation performed");
				else
					System.out.println("Media Creation Scheduled Instance Retrieval Operation failed");

				//Retrieves Dataset from NGet RSP and displays it. 
				DicomMessage objDicomMessage = objNGetSCU.getDIMSEResponse().elementAt(0);
				NGetRSP objNGetRSP = new NGetRSP(objDicomMessage);
				IDataSet objRstMediaCreationDataSet = objNGetRSP.getDataSet();
				if(objRstMediaCreationDataSet != null)
				{
					System.out.println("Retrieved Dataset information ---- ");
					display(objRstMediaCreationDataSet);
				}

				/*
				 * *************************************************************************
				 * This block of code creates the NAction SCU to perform the process of Media Creation.
				 * It initializes the NAction RQ and sends it to SCP.
				 * *************************************************************************
				 */
				//Create NAction SCU object using the current SCU Session.
				NActionSCU objNActionSCU = new NActionSCU(objSession);
				//Generates Modified Dataset for Media Creation 

				//Initializing NAction RQ for same SOP Instance to initiate the operation
				NActionRQ objNActionInitiateRQ = NActionRQ.getDefaultRQ(EnumSOPClassUIDs.MEDIACREATIONMANAGEMENT.getValue(), "1.3.12.2.1107.5.1.4.36085.4.0.22",EnumActionTypeID.MEDIA_CREATION_INITIATE,objMediaCreationDataSet);
				System.out.println("N-Action Request for Media Creation initialized");

				//sending RQ to SCP
				if(objNActionSCU.sendMessage(objNActionInitiateRQ)) //sending request
					System.out.println("Initiate Operation performed for Media Creation");
				else
					System.out.println("Initiate Operation failed for Media Creation");

				/*
				 * ************************************************************************************
				 * This block of code uses the already created NGet SCU to perform the process of 
				 * SOP Instance retrieval operation for Media Creation.
				 * It initializes the NGet RQ and sends it to SCP.
				 * After successful completion of process it displays the retrieved Dataset
				 * using NGet RSP which is supposed to be modified through earlier performed operation
				 * ************************************************************************************
				 */

				//Initializing NGet RQ for same SOP Instance
				objNGetRQ = NGetRQ.getDefaultRQ(EnumSOPClassUIDs.MEDIACREATIONMANAGEMENT.getValue(), "1.3.12.2.1107.5.1.4.36085.4.0.22", null);
				System.out.println("N-Get Request for Media Creation initialized");

				//sending RQ to SCP
				if(objNGetSCU.sendMessage(objNGetRQ)) //sending request
					System.out.println("Media Creation Instance Retrieval Operation performed");
				else
					System.out.println("Media Creation Instance Retrieval Operation failed");

				//Retrieves Dataset from NGet RSP and displays it. 
				objDicomMessage = objNGetSCU.getDIMSEResponse().elementAt(0);
				objNGetRSP = new NGetRSP(objDicomMessage);
				objRstMediaCreationDataSet = objNGetRSP.getDataSet();
				if(objRstMediaCreationDataSet != null)
				{
					//Displaying modified Media Creation Dataset
					System.out.println("Retrieved Dataset information ---- ");
					display(objRstMediaCreationDataSet);
				}

				/*
				 * *************************************************************************
				 * This block of code creates the NAction RQ to stop the process of Media Creation.
				 * It initializes the NAction RQ and sends it to SCP.
				 * *************************************************************************
				 */
				NActionRQ objNActionCancelRQ = NActionRQ.getDefaultRQ(EnumSOPClassUIDs.MEDIACREATIONMANAGEMENT.getValue(), "1.3.12.2.1107.5.1.4.36085.4.0.22",EnumActionTypeID.MEDIA_CREATION_CANCEL,objMediaCreationDataSet);
				System.out.println("N-Action Request for Media Creation initialized");

				//sending RQ to SCP
				if(objNActionSCU.sendMessage(objNActionCancelRQ)) //sending request
					System.out.println("Cancellation Operation performed for Media Creation");
				else
					System.out.println("Cancellation Operation failed for Media Creation");

				synchronized (this) 
				{
					wait(100000);	
				}
				
				/*
				 * ************************************************************************************
				 * This block of code uses the already created NGet SCU to perform the process of 
				 * SOP Instance retrieval operation for Media Creation.
				 * It initializes the NGet RQ and sends it to SCP.
				 * After successful completion of process it displays the retrieved Dataset
				 * using NGet RSP which is supposed to be modified through earlier performed operation
				 * ************************************************************************************
				 */
				objNGetRQ = NGetRQ.getDefaultRQ(EnumSOPClassUIDs.MEDIACREATIONMANAGEMENT.getValue(), "1.3.12.2.1107.5.1.4.36085.4.0.22", null);
				System.out.println("N-Get Request for Media Creation initialized");

				//sending RQ to SCP
				if(objNGetSCU.sendMessage(objNGetRQ)) //sending request
					System.out.println("Media Creation Instance Retrieval Operation performed");
				else
					System.out.println("Media Creation Instance Retrieval Operation failed");

				//Retrieves Dataset from NGet RSP and displays it. 
				objDicomMessage = objNGetSCU.getDIMSEResponse().elementAt(0);
				objNGetRSP = new NGetRSP(objDicomMessage);
				objRstMediaCreationDataSet = objNGetRSP.getDataSet();
				if(objRstMediaCreationDataSet != null)
				{
					//Displaying modified Media Creation Dataset
					System.out.println("Retrieved Dataset information ---- ");
					display(objRstMediaCreationDataSet);
				}

				/*
				 * *************************************************************************
				 * This block of code releases the association and 
				 * removes the connection with SCP.  
				 * *************************************************************************
				 */
				//Create A-Release-RQ to release the association
				AReleaseRQorRP objAReleaseRQorRP = new AReleaseRQorRP(EnumPDUType.RELEASE_RQ);

				//Send the A-Release-RQ
				if(objSession.sendAssociationMessage(objAReleaseRQorRP))
				{
					//When successful A-Release-RP is received, close the Session.
					//Please note that instead of closing the session we can create 
					//another association and send more requests. Also it is possible
					//to create multiple N-Services-RQs and send them one by one.
					objSession.close();
				}

				//In case an ABORT is received from SCP during the Media Creation operation
				if(objSession.getDIMSEAssociation() instanceof AAbort)
				{
					System.out.println("Abort arrives. Closing session.");
					objSession.close();
				}
			}
			else
			{
				System.out.println("Association cannot to extablished");
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

	//Populates the Dataset with default values for Media Creation Management SOP Class.
	private IDataSet getDatasetForCreate()
	{
		MediaCreationManagementIOD objMediaCreationManagementIOD = new MediaCreationManagementIOD();

		//SOP Common Module
		SOPCommonModule objSOPCommonModule = objMediaCreationManagementIOD.getSOPCommonModule();
		String []arrStrSpecificCharacterSet = new String[2];
		arrStrSpecificCharacterSet[0] = "ISO-IR 100";
		arrStrSpecificCharacterSet[1] = "ISO-IR 100";
		objSOPCommonModule.setSpecificCharacterSet(arrStrSpecificCharacterSet);

		//Media Creation Management
		MediaCreationManagementModule objMediaCreationManagementModule = objMediaCreationManagementIOD.getMediaCreationManagementModule();
		objMediaCreationManagementModule.setStorageMediaFileSetID("3");
		objMediaCreationManagementModule.setStorageMediaFileSetUID("3");
		objMediaCreationManagementModule.setLabelUsingInformationExtractedFromInstances(EnumLabelUsingInformationExtractedFromInstances.YES);
		objMediaCreationManagementModule.setLabelText("LabelText");
		objMediaCreationManagementModule.setLabelStyleSelection("2");
		objMediaCreationManagementModule.setBarcodeValue("BarcodeValue");
		objMediaCreationManagementModule.setBarcodeSymbology("BarcodeSymbology");
		objMediaCreationManagementModule.setMediaDisposition("MediaDisposition");
		objMediaCreationManagementModule.setAllowMediaSplitting(EnumAllowMediaSplitting.YES);
		objMediaCreationManagementModule.setAllowLossyCompression(EnumAllowLossyCompression.YES);
		objMediaCreationManagementModule.setIncludeNonDICOMObjects(EnumIncludeNonDICOMObjects.FOR_PATIENT);
		objMediaCreationManagementModule.setIncludeDisplayApplication(EnumIncludeDisplayApplication.YES);
		objMediaCreationManagementModule.setPreserveCompositeInstancesAfterMediaCreation(EnumPreserveCompositeInstancesAfterMediaCreation.YES);

		{
			//adding our items containing values of referenced sop instance uids
			ReferencedSOPSequence objReferencedSOPSequence = objMediaCreationManagementModule.getReferencedSOPSequence();
			{
				ReferencedSOPSequenceItem  objReferencedSOPSequenceItem1 = objReferencedSOPSequence.getNewItem();

				objReferencedSOPSequenceItem1.getSOPInstanceReferenceMacro().setReferencedSOPClassUID("1.2.840.10008.5.1.4.1.1.12.1");
				objReferencedSOPSequenceItem1.getSOPInstanceReferenceMacro().setReferencedSOPInstanceUID("1.3.12.2.1107.5.4.3.284980.19951129.170916.9");
 
				objReferencedSOPSequence.addItem(objReferencedSOPSequenceItem1);
			}
			{
				ReferencedSOPSequenceItem  objReferencedSOPSequenceItem2 = objReferencedSOPSequence.getNewItem();

				objReferencedSOPSequenceItem2.getSOPInstanceReferenceMacro().setReferencedSOPClassUID("1.2.840.10008.5.1.4.1.1.12.1");
				objReferencedSOPSequenceItem2.getSOPInstanceReferenceMacro().setReferencedSOPInstanceUID("1.3.12.2.1107.5.4.3.284980.19951129.170916.9");

				objReferencedSOPSequence.addItem(objReferencedSOPSequenceItem2);
			}
			{
				ReferencedSOPSequenceItem  objReferencedSOPSequenceItem3 = objReferencedSOPSequence.getNewItem();

				objReferencedSOPSequenceItem3.getSOPInstanceReferenceMacro().setReferencedSOPClassUID("1.2.840.10008.5.1.4.1.1.12.1");
				objReferencedSOPSequenceItem3.getSOPInstanceReferenceMacro().setReferencedSOPInstanceUID("1.3.12.2.1107.5.4.3.284980.19951129.170916.9");

				objReferencedSOPSequence.addItem(objReferencedSOPSequenceItem3);
			}
			{
				ReferencedSOPSequenceItem  objReferencedSOPSequenceItem4 = objReferencedSOPSequence.getNewItem();

				objReferencedSOPSequenceItem4.getSOPInstanceReferenceMacro().setReferencedSOPClassUID("1.2.840.10008.5.1.4.1.1.12.1");
				objReferencedSOPSequenceItem4.getSOPInstanceReferenceMacro().setReferencedSOPInstanceUID("1.3.12.2.1107.5.4.3.284980.19951129.170916.9");

				objReferencedSOPSequence.addItem(objReferencedSOPSequenceItem4);
			}
		}

		return objMediaCreationManagementIOD.getDataSet();
	}

	/*
	 * Displays the DataElements of Dataset
	 */
	private void display(IDataSet objIDataSet)
	{
		ICollectionIterator<IDataElement> objIDataSetIterator = objIDataSet.iterator();

		System.out.println("Tag     Value");
		while(objIDataSetIterator.hasNext())
		{
			IDataElement objDataElement = objIDataSetIterator.next();				

			if(!(objDataElement.getDicomValue() instanceof SQ))
			{

				System.out.println(objDataElement.getTag().getGroupNumber()+"    "+
						objDataElement.getTag().getElementNumber()+"    "+
						objDataElement.getDicomValue().toString());
			}
			else  
			{
				displaySequence((SQ)objDataElement.getDicomValue(),"> ");					
			}
		}

	}

	/*
	 * Displays the DataElements of Sequence
	 */
	private void displaySequence(SQ objSQDataElement,String ss)
	{
		ICollectionIterator<IItem> objSQItemIterator = objSQDataElement.iterator();
		int i = 1;
		while(objSQItemIterator.hasNext())
		{
			System.out.println("ITEM :"+ i++ );
			IItem objItem = objSQItemIterator.next();
			ICollectionIterator<IDataElement> objDataElementIterator = objItem.iterator();
			while(objDataElementIterator.hasNext())
			{
				IDataElement objDataElement = objDataElementIterator.next();

				if(!(objDataElement.getDicomValue() instanceof SQ))
				{
					System.out.println(ss +objDataElement.getTag().getGroupNumber()+"    "+
							objDataElement.getTag().getElementNumber()+"    "+
							objDataElement.getDicomValue().toString());						 

				}
				else
				{
					displaySequence((SQ)objDataElement.getDicomValue(),ss+ss);
				}	 
			}
		}	 
	}
}