/*******************************************************************************
 *  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.DisplaySystemManagement.testcases.scu;

import cdac.medinfo.sdk.dcm30_04.utils.DicomConstants;
import java.io.IOException;

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.enums.EnumLoggingLevel;
import cdac.medinfo.sdk.dcm30_04.enums.EnumPDUType;

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.EnumServiceType;
import cdac.medinfo.sdk.dcm30_04.services.dimsecommons.DicomMessage;
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 class is implemented to test the Display System Management SCU using NGetSCU class of SDK for DICOM.
 * 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. Some ways to 
 * create an Association Request: 
 * 1)Use Default Association class to Load the association for requested service. User can load the association 
 * by specifying the Service Type in loadAssociation method of Default association. For e.g. here EnumServiceType.CFind
 * should be specified to load Association for N-Get  services. The association request for N-Get 
 * is made according to  jars in the class path.
 * 2) Manually create object of AssociationRQ class and set all presentation contexts collection 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 N-Get  object to send the N-Get  Request to the respective SCP. Currently the "sendMessage" method 
 * of the DisplaySystemSCU would wait after sending the N-Get -RQ until a suitable DIMSE response is received. 
 * When N-Get request is send to the server with key attribute to be searched in the data set
 * then for the matches find (for key attribute at SCP side) it sends the Pending response which contains 
 * matched data set. Then success response is sent by SCP.
 * This operation is part of Synchronous mode request handling.  
 * Once the DIMSE response is received by the DisplaySystemSCU 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 N-Get  operation is complete,we can create another association
 * and send more requests. 
 * Also it is possible to create multiple N-Get -RQs and send them one by one on the same Association.
 */
public class Display_System_SCU 
{
	public static void main(String[] args) throws IOException
	{
		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 object of TestCStoreSCU and Start CStore SCU.
		 */
		Display_System_SCU objDisplay_System_SCUTest = new Display_System_SCU();

		objDisplay_System_SCUTest.start();
	}

	/**
	 * This method starts N-Get SCU .
	 */
	public void start()
	{
		//Current SCU Session
		SCUSession objSession = null;

		try
		{
			//create SCU session.
			objSession = new SCUSession();
			
			/*
			 * Initializes the service types and populates the default association
			 */
			EnumServiceType[] arrenumServiceType = new EnumServiceType[1];
			arrenumServiceType[0] = EnumServiceType.NGET_DISPLAY_SYSTEM_MANAGEMENT;
			DefaultAssociation objDefaultAssociation = new DefaultAssociation("SDK");
			objDefaultAssociation.loadAssociation(arrenumServiceType);

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

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

			//Create CFind SCU object using the current SCU Session.
			NGetSCU objNGetSCU = new NGetSCU(objSession);

			//Send the Association Request
			if(objSession.sendAssociationMessage(objAssociateRQ))
			{	
				System.out.println("Association established");

				//Create Default NGet RQ by giving the parsed DataSet. This method uses default values for the attributes in the request. These attributes can be modified once the request object is created, before sending.
				NGetRQ objNGetRQ = NGetRQ.getDefaultRQ(DicomConstants.DCM_DISPLAY_SYSTEM_SOP_CLASS, "1.2.840.10008.5.1.1.40.1",null);

				System.out.println("N-Get Request for Display System initialized");

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

				//Retrieves Dataset from NGet RSP and displays it. 
				DicomMessage objDicomMessage = objNGetSCU.getDIMSEResponse().elementAt(0);
				NGetRSP objNGetRSP = new NGetRSP(objDicomMessage);
				IDataSet objRstDisplaySystemDataSet = objNGetRSP.getDataSet();
				if(objRstDisplaySystemDataSet != null)
				{
					//Displaying modified Display System Dataset
					System.out.println("*****Retrieved Dataset Information***** ");
					display(objRstDisplaySystemDataSet);
				}
				else
					System.out.println("N-Get operation failed.");

				//Create A-Release-RQ to release the association
				AReleaseRQorRP objAReleaseRQorRP = new AReleaseRQorRP(EnumPDUType.RELEASE_RQ);

				//Send the A-Release-RQ
				if(objSession.sendAssociationMessage(objAReleaseRQorRP))
				{
					System.out.println("Association is released");
					//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-Get-RQs and send them one by one.
					objSession.close();
				}

				//In case an ABORT is received from SCP during the C-Store 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();
			objSession.close();
		}
	}

	static int i = 0;

	/*
	 * 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 Dicom Data SQ.
	 */
	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);
				}	 
			}
		}
	}
}
