/*******************************************************************************
 *  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.cservices.tools.cstorescu;


import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.net.InetAddress;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;

import org.apache.commons.codec.binary.Base64;


import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataSet;
import cdac.medinfo.sdk.dcm30_04.dicomparserserializers.DicomParser;
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.AgreedAssociation;
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.association.RQPresContext;
import cdac.medinfo.sdk.dcm30_04.services.association.RQUserInfo;
import cdac.medinfo.sdk.dcm30_04.services.association.SOPClassExtendedNegotiationSubItemFields;
import cdac.medinfo.sdk.dcm30_04.services.cstore.commons.CStoreRQ;
import cdac.medinfo.sdk.dcm30_04.services.cstore.commons.CStoreRSP;
import cdac.medinfo.sdk.dcm30_04.services.cstore.scu.CStoreSCU;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumLevelofDigitalSignatureSupport;
import cdac.medinfo.sdk.dcm30_04.services.dsp.DSPSigner;
import cdac.medinfo.sdk.dcm30_04.services.dsp.EnumDigitalSignatureProfile;
import cdac.medinfo.sdk.dcm30_04.services.dsp.EnumMACAlgorithm;
import cdac.medinfo.sdk.dcm30_04.services.scu.commons.SCUSession;
import cdac.medinfo.sdk.dcm30_04.services.tls.commons.TLSClientConfig;
import cdac.medinfo.sdk.dcm30_04.services.tlsscu.commons.TLSSCUSession;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConfig;
import cdac.medinfo.sdk.dcm30_04.utils.ICollectionIterator;
import cdac.medinfo.sdk.dcm30_04.utils.ISingleCollection;
import cdac.medinfo.sdk.dcm30_04.utils.SingleCollection;
import cdac.medinfo.sdk.dcm30_04.utils.StandardMethods;

/**
 * This class acts as main class for C-Store Utility tools of DICOM.
 * One the desired parameters are provided it performs C-Store Operation.
 */
public class CStore_SCU
{
	static int lengthOfOption = 18;
	static int lengthOfDetails = 79 - lengthOfOption;

	public static void main(String[] args)
	{
		EnumLoggingLevel enumLoggingLevel = EnumLoggingLevel.INFO;
		String strLoggingPath = null;
		
		String strCalledAETitle = null;
		String strCallingAETitle = "SDK";
		int iPort = 0;
		String strIP = null;
		CStore_SCU objCStore_SCU = new CStore_SCU();
		ISingleCollection<String> objCollectionOfFiles = new SingleCollection<String>();
		int iTimeout=0;
		String strpkeypath = null ,strcertpath = null;
		EnumDigitalSignatureProfile enumDigitalSignatureProfile = EnumDigitalSignatureProfile.BASERSA;
		EnumMACAlgorithm enumMACAlgorithm = EnumMACAlgorithm.HMACSHA1;

		// attribute required for TLS support
		String strKeyStorePath = null;
		String strKeyStorePassword = null;
		ISingleCollection<String> strTrustedCertPathCollection = new SingleCollection<String>();


		/*
		 * If no parameters are provided then display the help.
		 */
		String strIniFile = null;
		String strKey = null;


		if(args == null || args.length == 0)
		{
			objCStore_SCU.displayHelp();
			System.exit(0);
		}

		/*
		 * This loop initializes the service parameters from the arguments provided.
		 */
		for(int i=0; i<args.length; i++)
		{
			//logging level
			if(args[i].equalsIgnoreCase("-l") && args.length > i+1)
			{
				String strLoggingLevel = args[i+1];
				if(strLoggingLevel.equalsIgnoreCase("i"))
				{
					enumLoggingLevel = EnumLoggingLevel.INFO;
				}
				else if(strLoggingLevel.equalsIgnoreCase("s"))
				{
					enumLoggingLevel = EnumLoggingLevel.SEVERE;
				}
				else
				{
					System.out.println("Not a Valid Command. The value of logging mode can be INFO(I) or SEVER(S)");
					objCStore_SCU.displayHelp();
					System.exit(0);
				}
				i++;
			}

			//logging path.
			else if(args[i].equalsIgnoreCase("-d") && args.length > i+1)
			{
				strLoggingPath = args[i+1];
				i++;
			}

			//Validation Mode
			else if(args[i].equalsIgnoreCase("-v") && args.length > i+1)
			{
								i++;
			}

			//calling AE Title
			else if(args[i].equalsIgnoreCase("-h") && args.length > i+1)
			{
				strCallingAETitle = args[i+1] ;
				if(strCallingAETitle == null)
				{
					System.out.println("Not a Valid Command. AE Title name is not defined.");
					objCStore_SCU.displayHelp();
					System.exit(0);
				}
				i++;
			}

			//called AE Title
			else if(args[i].equalsIgnoreCase("-r") && args.length > i+1)
			{
				strCalledAETitle = args[i+1] ;
				if(strCalledAETitle == null)
				{
					System.out.println("Not a valid command");
					System.exit(0);
				}
				i++;
			}

			//Port
			else if(args[i].equalsIgnoreCase("-p") && args.length > i+1)
			{
				String strPort = args[i+1];
				iPort = Integer.parseInt(strPort);
				if(iPort == 0)
				{
					System.out.println("Not a valid command. Define proper port number");
					objCStore_SCU.displayHelp();
					System.exit(0);
				}
				i++;
			}

			//DICOM File
			else if(args[i].equalsIgnoreCase("-f") && args.length > i+1)
			{
				String strDCMFile = args[i+1] ;
				objCollectionOfFiles.add(strDCMFile);
				i++;
			}

			//IP
			else if(args[i].equalsIgnoreCase("-i") && args.length > i+1)
			{
				strIP = args[i+1] ;
				i++;
			}

			//			Timeout
			else if(args[i].equalsIgnoreCase("-t") && args.length > i+1)
			{
				String strTimeout = args[i+1];
				iTimeout = Integer.parseInt(strTimeout);
				i++;
			}
			// For Digital Signature Support
			else if(args[i].equalsIgnoreCase("-k") && args.length > i+1)
			{
				strpkeypath = args[i+1];
				i++;
			}

			else if(args[i].equalsIgnoreCase("-c") && args.length > i+1)
			{
				strcertpath = args[i+1];
				i++;
			}

			else if(args[i].equalsIgnoreCase("-s") && args.length > i+1)
			{
				//				BASERSA :BASE  
				//				CREATORRSA: CREATOR  
				//				AUTHORIZATIONRSA: AUTHORIZATION 
				String strdsprofile = args[i+1];
				if(strdsprofile.equalsIgnoreCase("BASE"))
				{
					enumDigitalSignatureProfile = EnumDigitalSignatureProfile.BASERSA;
				}
				else if(strdsprofile.equalsIgnoreCase("CREATOR"))
				{
					enumDigitalSignatureProfile = EnumDigitalSignatureProfile.CREATORRSA;
				}
				else if(strdsprofile.equalsIgnoreCase("AUTHORIZATION"))
				{
					enumDigitalSignatureProfile = EnumDigitalSignatureProfile.AUTHORIZATIONRSA;	
				}
				else
				{
					System.out.println("specified digital signature Profile is invalid.");
					objCStore_SCU.displayHelp();
					System.exit(0);

				}				
				i++;
			}

			else if(args[i].equalsIgnoreCase("-a") && args.length > i+1)
			{
				String strmacalgo = args[i+1];				
				//				HMACMD5("HmacMD5"),  MD5
				//				HMACSHA1("HmacSHA1"); SHA1			

				if(strmacalgo.equalsIgnoreCase("MD5"))
				{
					enumMACAlgorithm = EnumMACAlgorithm.HMACMD5;
				}
				else if(strmacalgo.equalsIgnoreCase("SHA1"))
				{
					enumMACAlgorithm = EnumMACAlgorithm.HMACSHA1;
				}
				else
				{
					System.out.println("specified MAC algorithm is invalid.");
					objCStore_SCU.displayHelp();
					System.exit(0);					
				}				
				i++;
			}

			//TLS
			//KeyStorePath
			else if(args[i].equalsIgnoreCase("-n") && args.length > i+1)
			{
				strKeyStorePath = args[i+1] ;
				i++;
			}
			//KeyStore Password
			else if(args[i].equalsIgnoreCase("-q") && args.length > i+1)
			{
				strKeyStorePassword = args[i+1] ;
				i++;
			}
			//trusted certificate path
			else if(args[i].equalsIgnoreCase("-u") && args.length > i+1)
			{					
				strTrustedCertPathCollection.add(args[i+1]);
				i++;
			}
			else if (args[i].trim().equalsIgnoreCase("-z") && args.length > i + 1)
			{
				String temp = args[i + 1].trim();
				String[] valueArray = temp.split("#");
				if (valueArray.length != 2)
				{
					System.out.println("Not a valid command");
					objCStore_SCU.displayHelp();
					System.exit(0);
				}
				strIniFile = valueArray[0];
				strKey = valueArray[1];
				i++;
			}
			else
			{
				System.out.println("Not a valid command");
				objCStore_SCU.displayHelp();
				System.exit(0);
			}
		}

		if(strCalledAETitle == null ||  iPort ==0 || objCollectionOfFiles.size() == 0)
		{
			System.out.println("Not a valid command");
			objCStore_SCU.displayHelp();
			System.exit(0);
		}

		if ((strpkeypath != null && strcertpath == null) || strpkeypath == null && strcertpath != null)
		{
			System.out.println("Not a valid command");
			objCStore_SCU.displayHelp();
			System.exit(0);
		}
		if(strKeyStorePath != null)
		{
			if(strTrustedCertPathCollection.size() == 0)
			{
				System.out.println("No trusted certificate provided.");
				objCStore_SCU.displayHelp();
				System.exit(0);
			}
		}
		if(strTrustedCertPathCollection.size()> 0)
		{
			if(strKeyStorePath == null)
			{
				System.out.println("Keystore path is not provided.");
				objCStore_SCU.displayHelp();
				System.exit(0);
			}
		}

		objCStore_SCU.doCStore(enumLoggingLevel, strLoggingPath,  objCollectionOfFiles, strIP, iPort,  strCallingAETitle,strCalledAETitle,
				iTimeout,strpkeypath,strcertpath,enumDigitalSignatureProfile,enumMACAlgorithm
				,strKeyStorePath,strKeyStorePassword,strTrustedCertPathCollection,strIniFile, strKey);
	}

	void doCStore(EnumLoggingLevel enumLoggingLevel, String strLogPath,  ISingleCollection<String> objCollectionOfFiles, String strIP, int iPort, String strCallingAETitle, String strCalledAString,int iTimeout,
			String	strpkeypath,String strcertpath,EnumDigitalSignatureProfile enumDigitalSignatureProfile,EnumMACAlgorithm enumMACAlgorithm
			,String strKeyStorePath,String strKeyStorePassword,ISingleCollection<String> strTrustedCertPathCollection,String strIniFile, String strKey)
	{
		try
		{
			if(strIP == null)
			{				
				strIP = InetAddress.getLocalHost().getHostAddress();
			}
			DicomConfig objDicomConfig = DicomConfig.createInstance();
			IDataSet objIDataSet = null;

			//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,strLogPath);

			ISingleCollection<IDataSet> objDataSetCollection = new SingleCollection<IDataSet>();
			for(int i=0; i<objCollectionOfFiles.size();i++)
			{
				DicomParser objDicomParser = new DicomParser();
				objIDataSet = objDicomParser.parseAsPart10File(objCollectionOfFiles.get(i), true);
				objDataSetCollection.add(objIDataSet);
			}

			SCUSession objSession = null;

			if(strTrustedCertPathCollection.size() >0 && strKeyStorePath != null)
			{
				objSession = new TLSSCUSession();

				//	set up key manager
				KeyManagerFactory   mgrFact     =   KeyManagerFactory.getInstance("SunX509");
				KeyStore            serverStore =   KeyStore.getInstance("JKS");		
				serverStore.load(new FileInputStream(strKeyStorePath),"private".toCharArray());
				mgrFact.init(serverStore,"private".toCharArray());

				//	set up a trustmanager so Server(SCP) can recognize the client(SCU)
				TrustManagerFactory trustFact   =   TrustManagerFactory.getInstance("SunX509");
				KeyStore            trustStore  =   KeyStore.getInstance("JKS");			
				trustStore.load(null, null);

				// Add multiple certificates in trustStore
				for (String strCertPath : strTrustedCertPathCollection)
				{
					CertificateFactory cf = CertificateFactory.getInstance("X.509");
					FileInputStream fis = new FileInputStream(strCertPath);
					BufferedInputStream bis = new BufferedInputStream(fis);
					X509Certificate objX509Certificate = (X509Certificate)cf.generateCertificate(bis);
					bis.close();

					// Add client certificate to trust store.
					trustStore.setCertificateEntry(strCertPath, objX509Certificate);
				}					

				// initialize trust Factory by trust store.
				trustFact.init(trustStore);	

				//initialize TLSClientConfig
				TLSClientConfig objClientConfig = new TLSClientConfig();
				objClientConfig.setKeyManagers(mgrFact.getKeyManagers());
				objClientConfig.setTrustManagers(trustFact.getTrustManagers());			
				objClientConfig.setSecureRandom(SecureRandom.getInstance("SHA1PRNG"));
				if(iTimeout>0)
					objSession.setTimeOut(iTimeout);
				((TLSSCUSession)objSession).start(strIP,iPort,objClientConfig);
			}
			else
			{
				objSession = new SCUSession();
				if(iTimeout>0)
					objSession.setTimeOut(iTimeout);
				objSession.start(strIP,iPort);
			}




			//Create CStore SCU object using the current SCU Session.
			CStoreSCU objCStoreSCU = new CStoreSCU(objSession);

			//AssociateRQ objAssociateRQ = new AssociateRQ(objDataSetCollection,strCalledAString,strCallingAETitle);
			/*
			 * Get RQ Presentation Context Collection.
			 */
			ISingleCollection<RQPresContext> objPressContextCol = getRQPressContextColl(objDataSetCollection);

			/*
			 * Load Default Association for RQPresContextProvided of all data sets.
			 */
			DefaultAssociation objDefaultAssociation = new DefaultAssociation(strCallingAETitle);
			objDefaultAssociation.loadAssociation(objPressContextCol);

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

			// Add extended negotiation information if private key and certificate path is provided
			if(null!=strpkeypath && null != strcertpath)
			{
				RQUserInfo objRQUserInfo = objAssociateRQ.getRQUserInfo();
				SOPClassExtendedNegotiationSubItemFields objSubITemField = new SOPClassExtendedNegotiationSubItemFields();
				objSubITemField.setSOPClassExtNegotiationSOPclassuid(objIDataSet.getSOPClassUID());			
				objSubITemField.setLevelofDigitalSignatureSupport(EnumLevelofDigitalSignatureSupport.LEVEL2);
				objRQUserInfo.addSOPClassExtendedNegotiationSubItemField(objSubITemField);
			}


			System.out.println("Sending Association Request");

			//Send the Association Request
			if(objSession.sendAssociationMessage(objAssociateRQ))
			{	
				//When association is successfully established the "objSession.sendAssociationMessage()"
				//method returns true.

				System.out.println("Association established");


				//Create Default CStore 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.
				ISingleCollection<CStoreRQ> objCStoreRQCollection = CStoreRQ.getDefaultRQ(objDataSetCollection);

				System.out.println("CStore Request initialized");


				// Sign all DataSet user want to sent to SCP


				AgreedAssociation objAgreedAssociation = objSession.getAgreedAssociation();				
				// retrieve Agreed Extended Negotiation Sub Item Fields
				SOPClassExtendedNegotiationSubItemFields objExtField =	objAgreedAssociation.getSOPClassExtendedNegotiationSubItemFields(objIDataSet.getSOPClassUID());			
				// Digitally Sign DataSet if Supported for this SOP Class UID

				if(objExtField != null && null!=strpkeypath && null != strcertpath)
				{
					short shmacid = 125;
					for (IDataSet objDataSet : objDataSetCollection) 
					{
						if(objExtField.getLevelofDigitalSignatureSupport() == EnumLevelofDigitalSignatureSupport.LEVEL2)
						{				
							DSPSigner objSigner = new DSPSigner();	

							FileInputStream keyfis = new FileInputStream(strpkeypath);
							byte[] encprivateKey = new byte[keyfis.available()];  
							keyfis.read(encprivateKey);
							keyfis.close();

							PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encprivateKey);
							KeyFactory keyFactory = KeyFactory.getInstance("RSA");
							RSAPrivateKey priv =(RSAPrivateKey)keyFactory.generatePrivate(privKeySpec);    


							// Digitally Sign the DataSet using private key
							objSigner.addDigitalSignature(objDataSet,								
									priv,
									strcertpath,
									null, 
									enumDigitalSignatureProfile,
									enumMACAlgorithm,
									shmacid++, 
									StandardMethods.generateUUID() , null,null);				


						}
					}

					System.out.println("Sending Digitally signed DataSet...");
				}



				//Send the C-Store-RQ and wait for a C-Store-RSP.
				//This method exits once the response is received. This response is 
				//added in the response collection maintained by the CStoreSCU which
				//can be retrieved.
				for(CStoreRQ obCStoreRQ : objCStoreRQCollection)
				{
					if(objCStoreSCU.sendMessage(obCStoreRQ))
						System.out.println("CStore Response received");
					else
						System.out.println("CStore Operation not performed");
				}

				//Get the Response Size collection from C-Store SCU.
				System.out.println("Response Collection Size ==== " + objCStoreSCU.getDIMSEResponse().size());

				//Printing Status of C-Store Request.
				if(objCStoreSCU.getDIMSEResponse().size() >0)
				{
					CStoreRSP objCStoreRSP = new CStoreRSP(objCStoreSCU.getDIMSEResponse().get(0));
					System.out.println("C-Store Response Status is :" + Integer.toHexString(objCStoreRSP.getStatus()));
				}

				//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 C-Store-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 SCU session.");
					objSession.close();
				}
			}
			else
			{
				System.out.println("Association cannot be established");
				objSession.close();
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * Returns RQ Presentation Context Collection from Collection of Datasets.
	 * @param objIDataSetColl Collection of DataSets.
	 * @return objPressContextCol Object of RQ Presentation Context Collection.
	 */
	private ISingleCollection<RQPresContext> getRQPressContextColl(ISingleCollection<IDataSet> objIDataSetColl)
	{
		ISingleCollection<RQPresContext> objPressContextCol = new SingleCollection<RQPresContext>();

		ICollectionIterator<IDataSet> objColl = objIDataSetColl.iterator();

		while(objColl.hasNext())
		{
			IDataSet objDataSet = objColl.next();
			String strSOPClassUID = objDataSet.getSOPClassUID();
			String strTS = objDataSet.getTransferSyntax().getTransferSyntaxUID();

			RQPresContext objRQPresContext = new RQPresContext();
			objRQPresContext.setAbstractSyntaxName(strSOPClassUID);
			objRQPresContext.addTransferSyntax(strTS);
			objPressContextCol.add(objRQPresContext);
		}
		return objPressContextCol;
	}

	/*
	 * This function displays the help.
	 */
	private void displayHelp()
	{
		//Description of command
		System.out.println("\nCSTORESCU :: Sends Storage request to DICOM SCP.");

		//Signature
		System.out.println("\n" + "CSTORESCU [-i IP] -p Port [-d LogDir] " +
				"[-l LogMode] \n -h AETitle -r AETitle -f DICOMFile " +
				"[-t TimeOut]   [-k PrivateKey]  \n[-c Certificate]" +
		"[-s SignProfile] [-a MACalg] [-n KeyStorePath] \n[-u TrustedCert] [-q Password] \n");

		//IP Address
		printToConsole("-i", "IP Address, default IP address is localhost.");

		//Port
		printToConsole("-p", "Port");

		//Log directory
		printToConsole("-d", "Log directory");
		printToConsole("", "User's temp directory is taken as default.");

		// Log Mode
		printToConsole("-l","Log mode");
		String [] arrLogmode = {"I INFO","S SEVER"};
		printToConsole("LogMode", arrLogmode);
		printToConsole("","Default Logging Mode is INFO.");

		//Calling(host) AE Title
		printToConsole("-h","Calling(host) AE Title");

		//Called AE Title
		printToConsole("-r","Called AE Title");

		//DICOM Files
		printToConsole("-f", "DICOM File which is to be stored at the server");

		// path to private key(base64 encoded) file path
		printToConsole("-k","private key(base64 encoded) file path");

		// path to certificate file		
		printToConsole("-c","certificate file path");

		// digital signature profile	
		printToConsole("-s", "Digital Signature Profile to be used for MAC calculation");
		String[] arrProfile = {"BASE","CREATOR","AUTHORIZATION"};
		printToConsole("Profiles", arrProfile);
		printToConsole("", "Default profile is BASE.");


		// Message Authentication code algorithm		
		printToConsole("-a", "Message Authentication Code algorithm");
		String[] arrAlgo = { "MD5", "SHA1"};
		printToConsole("Algorithms", arrAlgo);
		printToConsole("", "Default algorithm is SHA1.");


		//Session time out
		printToConsole("-t","Session time out in milliseconds");		

		printToConsole("-n","path to Keystore for SCU(JKS)");

		printToConsole("-q","password to open Keystore");

		printToConsole("-u","path to trusted certificate (multiple trusted certificates can be added using -r option multiple times)");
		

		//1st Example with Description
        System.out.println("\nExample :\n");

		System.out.println("This example is demonstration of simple C-Store Operation." +
				"\n1. CSTORESCU -p 9000 -h ABC -r SDK -f image.dcm" +
				"\nClient ABC, sends DICOM file image.dcm to store at AE SDK listening on port 9000\n");

		//2nd Example with Description
		System.out.println("This example is demonstration of C-Store operation with Digital Signature \nsupport." +
				"\n2. CSTORESCU -p 9000 -h ABC -r SDK -f image.dcm -k private.key -c certificate.cer -s BASE -a SHA1" +
				"\nClient ABC, sends DICOM file image.dcm to store at AE SDK listening on port 9000" +
				" by digitally signing dataset with private key using BASE digital signature \nprofile and SHA1" +
				" MAC algorithm.\n");

		System.out.println("This example is demonstration of C-Store operation with TLS support." +
				"\n3. CSTORESCU -p 4546 -h ABC -r SDK -f image.dcm -n Clientkeystore -q private -u ServerCert.cer" +
				"\nClient ABC, sends DICOM file image.dcm to store at AE SDK listening on port 4546" +
				" over secure socket connection with SCP.\n");

	}

	/**
	 * Display Help of command on console in proper manner.
	 * @param strOption Option for command.
	 * @param strDetails Description of option.
	 */
	public static void printToConsole(String strOption, String strDetails)
	{
		String strBuffer = "";
		if (strOption != null)
			strBuffer = strBuffer + strOption ;
		while (lengthOfOption > strBuffer.length())
		{
			strBuffer = strBuffer + " ";
		}
		if (strDetails != null)
		{
			if (strDetails.length() < lengthOfDetails || strDetails.length() == lengthOfDetails)
			{
				strBuffer = strBuffer + strDetails;
				strDetails = null;
			}
			else
			{
				strBuffer = strBuffer + strDetails.substring(0, lengthOfDetails);
				strDetails = strDetails.substring(lengthOfDetails, (strDetails.length() ));
			}
		}

		System.out.println(strBuffer);
		if (strDetails == null)
			return;
		else
			printToConsole(null, strDetails);
	}

	/**
	 * Display Help of command on console in proper manner.
	 * @param strAttribute Attribute for option.
	 * @param strDesc Description of attributes.
	 */
	public static void printToConsole(String strAttribute, String[] strDesc)
	{
		if (strDesc.length > 1)
			printToConsole(strAttribute, strDesc[0]);
		for(int i = 1;i<strDesc.length;i++)
			printToConsole(null, strDesc[i]);
	}
}



