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

import java.io.FileInputStream;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;

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


import cdac.medinfo.sdk.dcm30_04.baselibs.DataSet;
import cdac.medinfo.sdk.dcm30_04.baselibs.DicomV3Factory;
import cdac.medinfo.sdk.dcm30_04.baselibs.ImplicitDE;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataElement;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataSet;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataTypeSQ;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDictionaryReader;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IItem;
import cdac.medinfo.sdk.dcm30_04.commoninterface.ITransferSyntax;
import cdac.medinfo.sdk.dcm30_04.dicomparserserializers.DicomSerializer;
import cdac.medinfo.sdk.dcm30_04.enums.EnumLoggingLevel;
import cdac.medinfo.sdk.dcm30_04.enums.EnumModality;
import cdac.medinfo.sdk.dcm30_04.services.dsp.DSPSigner;
import cdac.medinfo.sdk.dcm30_04.services.dsp.DSPVerifier;
import cdac.medinfo.sdk.dcm30_04.services.dsp.EnumDigitalSignatureProfile;
import cdac.medinfo.sdk.dcm30_04.services.dsp.EnumMACAlgorithm;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConfig;

/**
 * This testcode illustrate's Digital Signature Profiles in DICOM, Digital Signatures 
 * to be included in a DICOM Data Set for the purpose of insuring the integrity of the 
 * Data Set, and to authenticate the sources of the Data Set.
 */
public class Digital_Signature 
{
	
	public static final String DIGITAL_SIGNATURE_UUID = "e9e5ce1c-8001-42cb-93d7-9e74210f0926";

	public static void main(String[] args) throws Exception
	{
		
		DicomConfig.createInstance().enableLogging(EnumLoggingLevel.INFO);

		Digital_Signature objTestDigitalSignature = new Digital_Signature();
		IDataSet objDataSet = objTestDigitalSignature.getSampleDataSet();

		DicomSerializer objDicomSerializer = new DicomSerializer();

		// Create instance of DataSet Signer.
		DSPSigner objSigner = new DSPSigner();

		//Input and Convert the Encoded Private Key Bytes
		FileInputStream keyfis = new FileInputStream(objTestDigitalSignature.getJarPath()+"private.key");
		byte[] encprivateKey = new byte[keyfis.available()];  
		keyfis.read(encprivateKey);
		keyfis.close();

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

		short MACID1 = 125;	

		// Add Digital Signature to DataSet
		objSigner.addDigitalSignature(objDataSet,				
				priv,
				objTestDigitalSignature.getJarPath()+"certificate.cer",
				null, 
				EnumDigitalSignatureProfile.BASERSA,
				EnumMACAlgorithm.HMACSHA1,
				MACID1, 
				DIGITAL_SIGNATURE_UUID, null,null);

		// Create instance of Digital Signature Verifier
		DSPVerifier objVerifier = new DSPVerifier();

		// Verify All the Digital Sigantures present in DataSet
		boolean verified = objVerifier.VerifyDigitalSignatures(objDataSet);

		// Serialize Digitally Signed DataSet
		objDicomSerializer.serializeAsPart10File("D:\\dcmbAfteraddingDSP.dcm", objDataSet, true);

		System.out.println("DataSet verified : " + verified);

	}

	/**
	 *  retrives local jar path.
	 */
	private String getJarPath()
	{
		String strPath = this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath()+ "cdac/medinfo/sdk/dcm30_04/dsp/testcases/";
		strPath = strPath.replaceAll("file:/", "");
		strPath = strPath.replaceAll("%20", " ");
		return strPath;
	}

	/*
	 * This function gets the sequence belonging to group no and element number specified.
	 */
	private IDataTypeSQ getSQ(int GroupNo,int ElementNo , ITransferSyntax objTS)
	{
		return DicomV3Factory.createInstance().createSequence(GroupNo, ElementNo, objTS);
	}

	/*
	 * This function create the data element from the information provided.
	 */
	private IDataElement getDataElement(int GroupNo,int ElementNo,Object objVal,IDictionaryReader objDictionaryReader)
	{
		IDataElement objIDataElement = new ImplicitDE(GroupNo, ElementNo,objVal,objDictionaryReader);
		return objIDataElement;
	}

	public IDataSet getSampleDataSet()
	{
		IDataSet objIDataSet = new DataSet();
		IDictionaryReader objDictionaryReader = objIDataSet.getDictionaryReader();
		ITransferSyntax objTS = objIDataSet.getTransferSyntax();
		//SOP Common Module
		String []arrSpecificCharSet = new String[2]; 
		arrSpecificCharSet[0] = "ISO_IR 192";
		arrSpecificCharSet[1] = "ISO_IR 192";
		objIDataSet.addDataElement(getDataElement(0x0008,0x0005, arrSpecificCharSet, objDictionaryReader));

		//Scheduled Procedure Step
		{
			IDataTypeSQ objScheduledProcedureStepSQ = getSQ(0x0040,0x0100,objTS);
			IItem objScheduledProcedureStepItem = objScheduledProcedureStepSQ.getNewItem();
			String []strAETitle = new String[2];
			strAETitle[0] = "SDK";
			strAETitle[1] = "SDK";
			objScheduledProcedureStepItem.addDataElement(getDataElement(0x0040,0x0001,strAETitle, objDictionaryReader));
			objScheduledProcedureStepItem.addDataElement(getDataElement(0x0040,0x0002,"19930325", objDictionaryReader));
			objScheduledProcedureStepItem.addDataElement(getDataElement(0x0040,0x0003,"135731", objDictionaryReader));
			objScheduledProcedureStepItem.addDataElement(getDataElement(0x0008,0x0060,EnumModality.XRAY_ANGIOGRAPHY.getValue(), objDictionaryReader));
			objScheduledProcedureStepItem.addDataElement(getDataElement(0x0040,0x0006,"Personal Name", objDictionaryReader));
			objScheduledProcedureStepItem.addDataElement(getDataElement(0x0040,0x0007,"Code Meaning String", objDictionaryReader));
			String []strStationName = new String[2];
			strStationName[0] = "Station1";
			strStationName[1] = "Station2";
			objScheduledProcedureStepItem.addDataElement(getDataElement(0x0040,0x0010,strStationName, objDictionaryReader));
			objScheduledProcedureStepItem.addDataElement(getDataElement(0x0040,0x0011,"Scheduled", objDictionaryReader));
			{
				IDataTypeSQ objScheduledProtocolCodeSQ = getSQ(0x0040,0x0008, objTS);
				IItem objScheduledProtocolCodeItem = objScheduledProtocolCodeSQ.getNewItem();
				objScheduledProtocolCodeItem.addDataElement(getDataElement(0x0008,0x0100,"CodeValue", objDictionaryReader));
				objScheduledProtocolCodeItem.addDataElement(getDataElement(0x0008,0x0102,"Scheme", objDictionaryReader));
				objScheduledProtocolCodeItem.addDataElement(getDataElement(0x0008,0x0103,"Version", objDictionaryReader));
				objScheduledProtocolCodeItem.addDataElement(getDataElement(0x0008,0x0104,"imeglumine gadopentetate 469.01mg/mL", objDictionaryReader));
				{
					IDataTypeSQ objProtocolContextSQ = getSQ(0x0040,0x0440, objTS);
					IItem objProtocolContextItem = objProtocolContextSQ.getNewItem();
					objProtocolContextItem.addDataElement(getDataElement(0x0040,0xA040,"Value Type", objDictionaryReader));
					{
						IDataTypeSQ objConceptNameCodeSQ = getSQ(0x0040,0xA043, objTS);
						IItem objConceptNameCodeItem = objConceptNameCodeSQ.getNewItem();
						objConceptNameCodeItem.addDataElement(getDataElement(0x0008,0x0100,"CodeValue", objDictionaryReader));
						objConceptNameCodeItem.addDataElement(getDataElement(0x0008,0x0102,"Scheme", objDictionaryReader));
						objConceptNameCodeItem.addDataElement(getDataElement(0x0008,0x0103,"Version", objDictionaryReader));
						objConceptNameCodeItem.addDataElement(getDataElement(0x0008,0x0104,"Code Meaning", objDictionaryReader));
						objConceptNameCodeSQ.addItem(objConceptNameCodeItem);

						objProtocolContextItem.addDataElement(getDataElement(0x0040,0xA043,objConceptNameCodeSQ.getValue(), objDictionaryReader));
					}
					objProtocolContextItem.addDataElement(getDataElement(0x0040,0xA120,"19990827111300.0", objDictionaryReader));
					objProtocolContextItem.addDataElement(getDataElement(0x0040,0xA123,"Person Name", objDictionaryReader));
					objProtocolContextItem.addDataElement(getDataElement(0x0040,0xA160,"Text Value", objDictionaryReader));
					{
						IDataTypeSQ objConceptCodeSQ = getSQ(0x0040,0xA168, objTS);
						IItem objConceptCodeItem = objConceptCodeSQ.getNewItem();
						objConceptCodeItem.addDataElement(getDataElement(0x0008,0x0100,"CodeValue", objDictionaryReader));
						objConceptCodeItem.addDataElement(getDataElement(0x0008,0x0102,"Scheme", objDictionaryReader));
						objConceptCodeItem.addDataElement(getDataElement(0x0008,0x0103,"Version", objDictionaryReader));
						objConceptCodeItem.addDataElement(getDataElement(0x0008,0x0104,"Code Meaning", objDictionaryReader));
						objConceptCodeSQ.addItem(objConceptCodeItem);

						objProtocolContextItem.addDataElement(getDataElement(0x0040,0xA168,objConceptCodeSQ.getValue(), objDictionaryReader));
					}
					String []strNumericValue = new String[2];
					strNumericValue[0] = "123";
					strNumericValue[1] = "124";
					objProtocolContextItem.addDataElement(getDataElement(0x0040,0xA30A,strNumericValue, objDictionaryReader));
					{
						IDataTypeSQ objMeasurementUnitsCodeSQ = getSQ(0x0040,0x08EA, objTS);
						IItem objMeasurementUnitsCodeItem = objMeasurementUnitsCodeSQ.getNewItem();
						objMeasurementUnitsCodeItem.addDataElement(getDataElement(0x0008,0x0100,"CodeValue", objDictionaryReader));
						objMeasurementUnitsCodeItem.addDataElement(getDataElement(0x0008,0x0102,"Scheme", objDictionaryReader));
						objMeasurementUnitsCodeItem.addDataElement(getDataElement(0x0008,0x0103,"Version", objDictionaryReader));
						objMeasurementUnitsCodeItem.addDataElement(getDataElement(0x0008,0x0104,"Code Meaning", objDictionaryReader));
						objMeasurementUnitsCodeSQ.addItem(objMeasurementUnitsCodeItem);

						objProtocolContextItem.addDataElement(getDataElement(0x0040,0x08EA,objMeasurementUnitsCodeSQ.getValue(), objDictionaryReader));
					}
					objProtocolContextSQ.addItem(objProtocolContextItem);

					objScheduledProtocolCodeItem.addDataElement(getDataElement(0x0040,0x0440, objProtocolContextSQ.getValue(), objDictionaryReader));
				}
				objScheduledProtocolCodeSQ.addItem(objScheduledProtocolCodeItem);

				objScheduledProcedureStepItem.addDataElement(getDataElement(0x0040,0x0008, objScheduledProtocolCodeSQ.getValue(), objDictionaryReader));
			}
			objScheduledProcedureStepItem.addDataElement(getDataElement(0x0040,0x0012,"Pre Medication", objDictionaryReader));
			objScheduledProcedureStepItem.addDataElement(getDataElement(0x0040,0x0009,"2222", objDictionaryReader));
			objScheduledProcedureStepItem.addDataElement(getDataElement(0x0032,0x1070,"Requested Contrast Agent", objDictionaryReader));
			objScheduledProcedureStepItem.addDataElement(getDataElement(0x0040,0x0020,"Status", objDictionaryReader));

			objScheduledProcedureStepSQ.addItem(objScheduledProcedureStepItem);

			objIDataSet.addDataElement(getDataElement(0x0040,0x0100,objScheduledProcedureStepSQ.getValue(), objDictionaryReader));
		}

		//Requested Procedure
		objIDataSet.addDataElement(getDataElement(0x0040,0x1001, "1", objDictionaryReader));
		objIDataSet.addDataElement(getDataElement(0x0032,0x1060, "XRAY_ANGIOGRAPHY", objDictionaryReader));
		{
			IDataTypeSQ objRequestedProcedureCodeSQ = getSQ(0x0032,0x1064, objTS);
			IItem objRequestedProcedureCodeItem = objRequestedProcedureCodeSQ.getNewItem();
			objRequestedProcedureCodeItem.addDataElement(getDataElement(0x0008,0x0100,"Code Value", objDictionaryReader));
			objRequestedProcedureCodeItem.addDataElement(getDataElement(0x0008,0x0102,"Scheme", objDictionaryReader));
			objRequestedProcedureCodeItem.addDataElement(getDataElement(0x0008,0x0103,"Version", objDictionaryReader));
			objRequestedProcedureCodeItem.addDataElement(getDataElement(0x0008,0x0104,"Code Meaning", objDictionaryReader));
			objRequestedProcedureCodeSQ.addItem(objRequestedProcedureCodeItem);

			objIDataSet.addDataElement(getDataElement(0x0032,0x1064,objRequestedProcedureCodeSQ.getValue(), objDictionaryReader));
		}
		objIDataSet.addDataElement(getDataElement(0x0020,0x000D, "1.3.12.2.1107.5.4.3.4975316777216.19951114.94101.16", objDictionaryReader));
		{
			IDataTypeSQ objReferencedStudySQ = getSQ(0x0008,0x1110, objTS);
			IItem objReferencedStudyItem = objReferencedStudySQ.getNewItem();
			objReferencedStudyItem.addDataElement(getDataElement(0x0008,0x1150,"1.2.840.10008.5.1.4.1.1.12.1", objDictionaryReader));
			objReferencedStudyItem.addDataElement(getDataElement(0x0008,0x1155,"1.3.12.2.1107.5.4.3.284980.19951129.170916.11", objDictionaryReader));
			objReferencedStudySQ.addItem(objReferencedStudyItem);

			objIDataSet.addDataElement(getDataElement(0x0008,0x1110,objReferencedStudySQ.getValue(), objDictionaryReader));
		}
		objIDataSet.addDataElement(getDataElement(0x0040,0x1003,"1", objDictionaryReader));
		objIDataSet.addDataElement(getDataElement(0x0040,0x1004,"Patient Transport Arrangements", objDictionaryReader));

		//Imaging Service Request
		objIDataSet.addDataElement(getDataElement(0x0008,0x0050,"2", objDictionaryReader));
		objIDataSet.addDataElement(getDataElement(0x0032,0x1032,"Requesting Physician", objDictionaryReader));
		objIDataSet.addDataElement(getDataElement(0x0008,0x0090,"Referring Physicians Name", objDictionaryReader));

		//Visit Identification  
		objIDataSet.addDataElement(getDataElement(0x0038,0x0010,"322-292-73-6", objDictionaryReader));

		//Visit Status
		objIDataSet.addDataElement(getDataElement(0x0038,0x0300,"Pune", objDictionaryReader));

		//Visit Relationship
		{     
			IDataTypeSQ objReferencedPatientSQ = getSQ(0x0008,0x1120, objTS);
			IItem objReferencedPatientItem = objReferencedPatientSQ.getNewItem();
			objReferencedPatientItem.addDataElement(getDataElement(0x0008,0x1150,"1.2.840.10008.5.1.4.1.1.12.1", objDictionaryReader));
			objReferencedPatientItem.addDataElement(getDataElement(0x0008,0x1155,"1.3.12.2.1107.5.4.3.284980.19951129.170916.11", objDictionaryReader));
			objReferencedPatientSQ.addItem(objReferencedPatientItem);

			objIDataSet.addDataElement(getDataElement(0x0008,0x1120,objReferencedPatientSQ.getValue(), objDictionaryReader));
		}

		//Patient Identification
		objIDataSet.addDataElement(getDataElement(0x0010,0x0010,"(Rubo DEMO", objDictionaryReader));
		objIDataSet.addDataElement(getDataElement(0x0010,0x0020,"322-292-73-6", objDictionaryReader));

		//Patient Demographic  
		objIDataSet.addDataElement(getDataElement(0x0010,0x0030,"19580719", objDictionaryReader));
		objIDataSet.addDataElement(getDataElement(0x0010,0x0040,"F", objDictionaryReader));
		{
			IDataTypeSQ objPatientPrimaryLanguageSQ = getSQ(0x0010,0x0101, objTS);
			IItem objPatientPrimaryLanguageItem = objPatientPrimaryLanguageSQ.getNewItem();
			objPatientPrimaryLanguageItem.addDataElement(getDataElement(0x0008,0x0100,"CodeValue", objDictionaryReader));
			objPatientPrimaryLanguageItem.addDataElement(getDataElement(0x0008,0x0102,"Scheme", objDictionaryReader));
			objPatientPrimaryLanguageItem.addDataElement(getDataElement(0x0008,0x0104,"Code Meaning", objDictionaryReader));
			{
				IDataTypeSQ objPatientPrimaryLanguageCodeModifierSQ = getSQ(0x0010,0x0102, objTS);
				IItem objPatientPrimaryLanguageCodeModifierItem = objPatientPrimaryLanguageCodeModifierSQ.getNewItem();
				objPatientPrimaryLanguageCodeModifierItem.addDataElement(getDataElement(0x0008,0x0100,"Code Value", objDictionaryReader));
				objPatientPrimaryLanguageCodeModifierItem.addDataElement(getDataElement(0x0008,0x0102,"123", objDictionaryReader));
				objPatientPrimaryLanguageCodeModifierItem.addDataElement(getDataElement(0x0008,0x0104,"Code Meaning", objDictionaryReader));
				objPatientPrimaryLanguageCodeModifierSQ.addItem(objPatientPrimaryLanguageCodeModifierItem);

				objPatientPrimaryLanguageItem.addDataElement(getDataElement(0x0010,0x0102, objPatientPrimaryLanguageCodeModifierSQ.getValue(), objDictionaryReader));
			}
			objPatientPrimaryLanguageSQ.addItem(objPatientPrimaryLanguageItem);

			objIDataSet.addDataElement(getDataElement(0x0010,0x0101,objPatientPrimaryLanguageSQ.getValue(), objDictionaryReader));
		}
		objIDataSet.addDataElement(getDataElement(0x0010,0x1030,"52", objDictionaryReader));
		objIDataSet.addDataElement(getDataElement(0x0040,0x3001,"Confidentiality constraint on patient data", objDictionaryReader));

		//Patient Medical
		objIDataSet.addDataElement(getDataElement(0x0038,0x0500,"Patient State", objDictionaryReader));
		objIDataSet.addDataElement(getDataElement(0x0010,0x21C0,(short)26, objDictionaryReader));
		objIDataSet.addDataElement(getDataElement(0x0010,0x2000,"Medical Alerts", objDictionaryReader));
		objIDataSet.addDataElement(getDataElement(0x0010,0x2110,"Contrast Allergies", objDictionaryReader));
		objIDataSet.addDataElement(getDataElement(0x0038,0x0050,"Special Needs", objDictionaryReader));

		return objIDataSet;
	}

}