/*******************************************************************************
 *  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.tools.basicappconfprofile;
import java.io.FileInputStream;

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

import cdac.medinfo.sdk.dcm30_04.services.basicapplicationlevelconfidentialityprofile.BasicAppLevelConfidentialityProfile;
import cdac.medinfo.sdk.dcm30_04.services.basicapplicationlevelconfidentialityprofile.EnumProfileAlgorithm;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConfig;

/**
 * Command line utility for Basic Application Confidentiality profile. 
 */

public class DicomBasicAppConfProfile
{
	static int lengthOfOption = 18;
	static int lengthOfDetails = 79 - lengthOfOption;

	public DicomBasicAppConfProfile()
	{
	}

	public static void main(String args[])
	{
		EnumLoggingLevel enumlogginglevel = EnumLoggingLevel.SEVERE;
		String s = null;
		
		DicomBasicAppConfProfile objDicomBasicAppConfProfile = new DicomBasicAppConfProfile();
		String strDCMFile = null;
		Character character = null;
		EnumProfileAlgorithm enumprofilealgorithm = EnumProfileAlgorithm.AES;
		String strSecretKeyFilepath = null;       
		String strDestFile = null;

		String strIniFile = null;
		String strKey = null;


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

		for(int i = 0; i < args.length; i++)
		{
			if(args[i].equalsIgnoreCase("-l") && args.length > i + 1)
			{
				String strLoggingMode= args[i + 1];
				if(strLoggingMode.equalsIgnoreCase("i"))
				{
					enumlogginglevel = EnumLoggingLevel.INFO;
				} else
					if(strLoggingMode.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)");
						objDicomBasicAppConfProfile.displayHelp();
						System.exit(0);
					}
				i++;
				continue;
			}
			if(args[i].equalsIgnoreCase("-d") && args.length > i + 1)
			{
				s = args[i + 1];
				i++;
				continue;
			}
			if(args[i].equalsIgnoreCase("-v") && args.length > i + 1)
			{
	
				i++;
				continue;
			}
			if(args[i].equalsIgnoreCase("-f") && args.length > i + 1)
			{
				strDCMFile = args[i + 1];
				i++;
				continue;
			}
			if(args[i].equalsIgnoreCase("-o") && args.length > i + 1)
			{
				if(args[i + 1].length() == 1)
				{
					character = Character.valueOf(args[i + 1].charAt(0));
				} else
				{
					System.out.println("Not a Valid Command.");
					objDicomBasicAppConfProfile.displayHelp();
					System.exit(0);
				}
				i++;
				continue;
			}
			if(args[i].equalsIgnoreCase("-k") && args.length > i + 1)
			{
				strSecretKeyFilepath = args[i + 1];
				i++;
				continue;
			}
			if(args[i].equalsIgnoreCase("-s") && args.length > i + 1)
			{
				strDestFile = args[i + 1];
				i++;
				continue;
			}
			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");
					objDicomBasicAppConfProfile.displayHelp();
					System.exit(0);
				}
				strIniFile = valueArray[0];
				strKey = valueArray[1];
				i++;
				continue;
			}
			if(args[i].equalsIgnoreCase("-a") && args.length > i + 1)
			{
				String strAlgo = args[i + 1];
				if(strAlgo.equalsIgnoreCase("AES"))
				{
					enumprofilealgorithm = EnumProfileAlgorithm.AES;
				} else
					if(strAlgo.equalsIgnoreCase("DES"))
					{
						enumprofilealgorithm = EnumProfileAlgorithm.DES;
					} else
						if(strAlgo.equalsIgnoreCase("TRIPPLE_DES"))
						{
							enumprofilealgorithm = EnumProfileAlgorithm.TRIPPLE_DES;
						} else
						{
							System.out.println("Not a Valid Command. The value of algorithm(-a) for encryption/decryption(AES(De" +
									"fault),DES,TRIPPLE_DES)"
							);
							objDicomBasicAppConfProfile.displayHelp();
							System.exit(0);
						}
				i++;
			} else
			{
				System.out.println("Not a valid command");
				objDicomBasicAppConfProfile.displayHelp();
				System.exit(0);
			}
		}
		switch(character.charValue())
		{
		case  'D':
		case 'E':
		case  'd':
		case  'e':
			if(strSecretKeyFilepath == null || strDCMFile == null || strDestFile == null)
			{
				System.out.println("Not a valid command,Source and Destination file/ key file path should be specified.");
				objDicomBasicAppConfProfile.displayHelp();
				System.exit(0);
			}
			break;     

		default:
			System.out.println("Operation specified is not valid.");
			objDicomBasicAppConfProfile.displayHelp();
			System.exit(0);
			break;
		}


		try
		{
			DicomConfig dicomconfig = DicomConfig.createInstance();
			dicomconfig.enableLogging(enumlogginglevel, s);
			
			switch(character.charValue())
			{
			case  'E':
			case  'e':
				FileInputStream fileinputstream = new FileInputStream(strSecretKeyFilepath);
				byte abyte0[] = new byte[fileinputstream.available()];
				fileinputstream.read(abyte0);
				fileinputstream.close();
				objDicomBasicAppConfProfile.encryptDicomFile(enumprofilealgorithm, abyte0, null, strDCMFile, strDestFile);
				break;

			case 'D':
			case 'd':
				FileInputStream fileinputstream1 = new FileInputStream(strSecretKeyFilepath);
				byte abyte1[] = new byte[fileinputstream1.available()];
				fileinputstream1.read(abyte1);
				fileinputstream1.close();
				objDicomBasicAppConfProfile.decryptDicomFile(enumprofilealgorithm, abyte1, null, strDCMFile, strDestFile);
				break;

			default:
				System.out.println("Operation specified is not valid.");
				objDicomBasicAppConfProfile.displayHelp();
				System.exit(0);
				break;
			}
		}
		catch(Exception exception)
		{
			System.out.println("Operation failed :");
			exception.printStackTrace();
		}
	}




	private void encryptDicomFile(EnumProfileAlgorithm enumprofilealgorithm, byte bSecretKey[], byte bIV[], String strSourceFile, String strDestFile)	throws Exception
	{
		DicomParser objDicomParser = new DicomParser();
		IDataSet objSRCDataSet = objDicomParser.parseAsPart10File(strSourceFile,true);
		BasicAppLevelConfidentialityProfile objBasicAppLevelConfidentialityProfile = new BasicAppLevelConfidentialityProfile();
		IDataSet objRSPDataSet = objBasicAppLevelConfidentialityProfile.encryptDataSet(objSRCDataSet, enumprofilealgorithm, bSecretKey,bIV);

		DicomSerializer objDicomSerializer = new DicomSerializer();
		objDicomSerializer.serializeAsPart10File(strDestFile,objRSPDataSet,null,true);

		System.out.println("File encrypted successfully and serialized to path : "+ strDestFile);
	}

	private void decryptDicomFile(EnumProfileAlgorithm enumprofilealgorithm, byte bSecretKey[], byte bIV[], String strSourceFile, String strDestFile)	throws Exception
	{
		DicomParser objDicomParser = new DicomParser();
		IDataSet objSRCDataSet = objDicomParser.parseAsPart10File(strSourceFile,true);
		BasicAppLevelConfidentialityProfile objBasicAppLevelConfidentialityProfile = new BasicAppLevelConfidentialityProfile();
		IDataSet objRSPDataSet = objBasicAppLevelConfidentialityProfile.decryptDataSet(objSRCDataSet, enumprofilealgorithm, bSecretKey,bIV);		
		DicomSerializer objDicomSerializer = new DicomSerializer();
		objDicomSerializer.serializeAsPart10File(strDestFile,objRSPDataSet,null,true);

		System.out.println("File decrypted successfully and serialized to path : "+ strDestFile);
	}

	private void displayHelp()
	{
		System.out.println("\nDICOMSECBALC ::DICOM Basic Application Level Confidentiality Profile, This tool secures DICOM file by encrypting confidential attributes.");
		
		System.out.println("\nDICOMSECBALC [-d LogDir]  [-l LogMode] -o Operation to perf" +
		"orm -f DICOMFile -k SecretKeyFilePath -s DestinationStoragePath [-a Algorithm]\n");
		
		printToConsole("-d", "Log directory");
		printToConsole("", "User's temp directory is taken as default.");
		printToConsole("-l", "Log mode");
		String as1[] = {
				"I INFO", "S SEVER"
		};
		printToConsole("LogMode", as1);
		printToConsole("", "Default Logging Mode is INFO");	

		printToConsole("-o", "Operation to perform");
		String[] arrOperation = { "E Encrypt", "D Decrypt"};
		printToConsole("Operation", arrOperation); 


		printToConsole("-f", "Source DICOM file for specified operation.");
		printToConsole("-k", "Secret key file path for encryption/decryption");		
		printToConsole("-s", "Destination file path to store encrypted/decrypted Dicom file.");	

		printToConsole("-a", "Algorithm for encryption/decryption");
		String[] arrAlgo = { "AES","DES","TRIPPLE_DES"};
		printToConsole("Algorithm", arrAlgo);
		printToConsole("", "Default algorithm is AES.");

		
		System.out.println("\nExample :");
		
		System.out.println("\n1. Secure DICOM file by using encryption/decryption" +
				"\n\n a.Secures the DICOM file by encrypting confidential attributes using secret key" +
				"\nDICOMSECBALC -o E -f 0003.dcm -k Secret.key  -s C:\\encryptedfile.dcm" +
				"\n\n b.Decrypts the DICOM file attributes which are secured using secret key" +
				"\n\nDICOMSECBALC -o D -f C:\\encryptedfile.dcm -k Secret.key  -s C:\\decrypted.dcm");
		
		
	}

	/**
	 * 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]);
	}
}
