using System;
using System.IO;

using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO;

namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples
{
    /**
    * A simple utility class that encrypts/decrypts password based
    * encryption files.
    * <p>
    * To encrypt a file: PBEFileProcessor -e [-ai] fileName passPhrase.<br/>
    * If -a is specified the output file will be "ascii-armored".<br/>
    * If -i is specified the output file will be "integrity protected".</p>
    * <p>
    * To decrypt: PBEFileProcessor -d fileName passPhrase.</p>
    * <p>
    * Note: this example will silently overwrite files, nor does it pay any attention to
    * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase
    * will have been used.</p>
    */
    public sealed class PbeFileProcessor
    {
        private PbeFileProcessor() {}

		private static void DecryptFile(string inputFileName, char[] passPhrase)
		{
			using (Stream input = File.OpenRead(inputFileName))
			{
				DecryptFile(input, passPhrase);
			}
		}

        /**
        * decrypt the passed in message stream
        */
        private static void DecryptFile(
            Stream	inputStream,
            char[]	passPhrase)
        {
            inputStream = PgpUtilities.GetDecoderStream(inputStream);

			PgpObjectFactory pgpF = new PgpObjectFactory(inputStream);
            PgpObject o = pgpF.NextPgpObject();

            //
            // the first object might be a PGP marker packet.
            //
			PgpEncryptedDataList enc = o as PgpEncryptedDataList;
            if (enc == null)
            {
                enc = (PgpEncryptedDataList)pgpF.NextPgpObject();
            }

            PgpPbeEncryptedData pbe = (PgpPbeEncryptedData)enc[0];

            Stream clear = pbe.GetDataStream(passPhrase);

            PgpObjectFactory pgpFact = new PgpObjectFactory(clear);

			//
			// if we're trying to read a file generated by someone other than us
			// the data might not be compressed, so we check the return type from
			// the factory and behave accordingly.
			//
			o = pgpFact.NextPgpObject();
			if (o is PgpCompressedData)
			{
				PgpCompressedData cData = (PgpCompressedData) o;
				pgpFact = new PgpObjectFactory(cData.GetDataStream());
				o = pgpFact.NextPgpObject();
			}

			PgpLiteralData ld = (PgpLiteralData) o;
			Stream unc = ld.GetInputStream();
            Stream fOut = File.Create(ld.FileName);
			Streams.PipeAll(unc, fOut);
			fOut.Close();

			if (pbe.IsIntegrityProtected())
            {
                if (!pbe.Verify())
                {
                    Console.Error.WriteLine("message failed integrity check");
                }
                else
                {
                    Console.Error.WriteLine("message integrity check passed");
                }
            }
            else
            {
                Console.Error.WriteLine("no message integrity check");
            }
        }

		private static void EncryptFile(
			string	outputFileName,
			string	inputFileName,
			char[]	passPhrase,
			bool	armor,
			bool	withIntegrityCheck)
		{
			using (Stream output = File.Create(outputFileName))
			{
				EncryptFile(output, inputFileName, passPhrase, armor, withIntegrityCheck);
			}
		}

        private static void EncryptFile(
            Stream	outputStream,
            string	fileName,
            char[]	passPhrase,
            bool	armor,
            bool	withIntegrityCheck)
        {
            if (armor)
            {
                outputStream = new ArmoredOutputStream(outputStream);
            }

			try
            {
				byte[] compressedData = PgpExampleUtilities.CompressFile(fileName, CompressionAlgorithmTag.Zip);

				PgpEncryptedDataGenerator encGen = new PgpEncryptedDataGenerator(
					SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom());
                encGen.AddMethod(passPhrase, HashAlgorithmTag.Sha1);

				Stream encOut = encGen.Open(outputStream, compressedData.Length);

	            encOut.Write(compressedData, 0, compressedData.Length);
				encOut.Close();

				if (armor)
				{
					outputStream.Close();
				}
            }
            catch (PgpException e)
            {
                Console.Error.WriteLine(e);

                Exception underlyingException = e.InnerException;
                if (underlyingException != null)
                {
                    Console.Error.WriteLine(underlyingException.Message);
                    Console.Error.WriteLine(underlyingException.StackTrace);
                }
            }
        }

		public static void Main(
			string[] args)
        {
            if (args[0].Equals("-e"))
            {
                if (args[1].Equals("-a") || args[1].Equals("-ai") || args[1].Equals("-ia"))
                {
					EncryptFile(args[2] + ".asc", args[2], args[3].ToCharArray(), true, (args[1].IndexOf('i') > 0));
                }
                else if (args[1].Equals("-i"))
                {
					EncryptFile(args[2] + ".bpg", args[2], args[3].ToCharArray(), false, true);
                }
                else
                {
					EncryptFile(args[1] + ".bpg", args[1], args[2].ToCharArray(), false, false);
                }
            }
            else if (args[0].Equals("-d"))
            {
				DecryptFile(args[1], args[2].ToCharArray());
            }
            else
            {
                Console.Error.WriteLine("usage: PbeFileProcessor -e [-ai]|-d file passPhrase");
            }
        }
    }
}
