/*******************************************************************************
 *  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.basicapplicationlevelconfidentialityprofile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import cdac.medinfo.sdk.dcm30_04.dicomstreams.DicomBufferStream;
import cdac.medinfo.sdk.dcm30_04.dicomstreams.DicomStream;

class EncryptAgent
{
	int iBlockSize = 128;

	/*
	 * This method encrypts the Data Elements present in the Dicom Stream with the enumProfileAlgorithm and the objKey Speciifed.
	 */
	byte[] encrypt(DicomStream objDicomBufferStream, EnumProfileAlgorithm enumProfileAlgorithm, byte[] objKey, byte[] objIV) throws IOException,NoSuchAlgorithmException, NoSuchPaddingException,InvalidKeyException,IllegalBlockSizeException,BadPaddingException, InvalidAlgorithmParameterException
	{
		ByteArrayOutputStream objByteArrayOutputStream = new ByteArrayOutputStream();

		/*
		 * Created Block Size of 128 bytes to read the Data.
		 */
		byte[] blockData = new byte[iBlockSize];
		Cipher rsaCipher = null;
		SecretKeySpec objSecretKeySpec = null;
		IvParameterSpec  objIVSpec = null;

		if(objIV != null && objIV.length !=0)
			objIVSpec = new IvParameterSpec(objIV); 

		/*
		 * Initializes the Cipher bases on the Profile Algorithm specified
		 */
		if(enumProfileAlgorithm == EnumProfileAlgorithm.DES)
		{
			objSecretKeySpec = new SecretKeySpec(objKey, EnumProfileAlgorithm.DES.getValue());
			rsaCipher = Cipher.getInstance(EnumProfileAlgorithm.DES.getValue());
		}
		else if(enumProfileAlgorithm == EnumProfileAlgorithm.AES)
		{
			objSecretKeySpec = new SecretKeySpec(objKey, EnumProfileAlgorithm.AES.getValue());
			rsaCipher = Cipher.getInstance(EnumProfileAlgorithm.AES.getValue());
		}
		else if(enumProfileAlgorithm == EnumProfileAlgorithm.TRIPPLE_DES)
		{
			objSecretKeySpec = new SecretKeySpec(objKey, EnumProfileAlgorithm.TRIPPLE_DES.getValue());
			rsaCipher = Cipher.getInstance(EnumProfileAlgorithm.TRIPPLE_DES.getValue());
		}

		rsaCipher.init(Cipher.ENCRYPT_MODE, objSecretKeySpec, objIVSpec);

		DicomBufferStream objDicomStreamDecrypted = new DicomBufferStream();
		objDicomStreamDecrypted.setEndianNess(objDicomBufferStream.getEndianNess());
		objDicomStreamDecrypted.setOutputStream(objByteArrayOutputStream);

		while(objDicomBufferStream.available() != 0 && (objDicomBufferStream.available() >=128))
		{
			byte[] arrOriginalData = new byte[blockData.length];
			objDicomBufferStream.readBuffer(arrOriginalData);
			byte[] arrEncData = rsaCipher.update(arrOriginalData);
			objDicomStreamDecrypted.writeBuffer(arrEncData, 0, arrEncData.length);
		}

		/*
		 * if Data Available on the stream is less than the Block size (128 is used here)
		 * then read only the data available on the stream and then encrypt and then
		 * serialize it to the byte output stream.
		 * If this case is not taken then garbage data is also written to the file.
		 */
		if(objDicomBufferStream.available() <128)
		{
			byte[] arrOriginalData = new byte[objDicomBufferStream.available()];
			objDicomBufferStream.readBuffer(arrOriginalData);
			byte[] arrEncData = rsaCipher.doFinal(arrOriginalData);
			objDicomStreamDecrypted.writeBuffer(arrEncData, 0, arrEncData.length);
		}
		return objByteArrayOutputStream.toByteArray();
	}
}
