/*
 * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
 * 
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Logging;

namespace SafeExamBrowser.Configuration.Cryptography
{
	internal class PublicKeyHashWithSymmetricKeyEncryption : PublicKeyHashEncryption
	{
		private const int KEY_LENGTH_SIZE = 4;

		private PasswordEncryption passwordEncryption;

		internal PublicKeyHashWithSymmetricKeyEncryption(ILogger logger, PasswordEncryption passwordEncryption) : base(logger)
		{
			this.passwordEncryption = passwordEncryption;
		}

		internal override LoadStatus Decrypt(Stream data, out Stream decrypted, out X509Certificate2 certificate)
		{
			var keyHash = ParsePublicKeyHash(data);
			var found = TryGetCertificateWith(keyHash, out certificate);

			decrypted = default(Stream);

			if (!found)
			{
				return FailForMissingCertificate();
			}

			var symmetricKey = ParseSymmetricKey(data, certificate);
			var stream = new SubStream(data, data.Position, data.Length - data.Position);
			var status = passwordEncryption.Decrypt(stream, symmetricKey, out decrypted);

			return status;
		}

		internal override SaveStatus Encrypt(Stream data, X509Certificate2 certificate, out Stream encrypted)
		{
			// TODO: Don't forget to write encryption parameters!

			throw new NotImplementedException();
		}

		private string ParseSymmetricKey(Stream data, X509Certificate2 certificate)
		{
			var keyLengthData = new byte[KEY_LENGTH_SIZE];

			logger.Debug("Parsing symmetric key...");

			data.Seek(PUBLIC_KEY_HASH_SIZE, SeekOrigin.Begin);
			data.Read(keyLengthData, 0, keyLengthData.Length);

			var keyLength = BitConverter.ToInt32(keyLengthData, 0);
			var encryptedKey = new byte[keyLength];

			data.Read(encryptedKey, 0, encryptedKey.Length);

			var stream = new SubStream(data, PUBLIC_KEY_HASH_SIZE + KEY_LENGTH_SIZE, keyLength);
			var decryptedKey = Decrypt(stream, 0, certificate);
			var symmetricKey = Convert.ToBase64String(decryptedKey.ToArray());

			return symmetricKey;
		}
	}
}