/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
cryptolib.modules.symmetric = cryptolib.modules.symmetric || {};

/**
 * Defines a symmetric secret key
 *
 * @namespace symmetric/secretkey
 */
cryptolib.modules.symmetric.secretkey = function(box) {
  'use strict';

  box.symmetric = box.symmetric || {};
  box.symmetric.secretkey = {};

  /**
   * Allows a symmetric secret key factory to be obtained.
   *
   * @exports symmetric/secretkey/factory
   */
  box.symmetric.secretkey.factory = {};

  var policies = {
    secretkey: {
      encryption: {
        lengthBytes: box.policies.symmetric.secretkey.encryption.lengthBytes
      },
      mac: {lengthBytes: box.policies.symmetric.secretkey.mac.lengthBytes}
    }
  };

  var utils, randomFactory, exceptions, secureRandom, lengthBytes;

  var f = function(box) {
    utils = box.commons.utils;
    exceptions = box.commons.exceptions;
    randomFactory =
        new box.primitives.securerandom.factory.SecureRandomFactory();
  };

  f.policies = {
    primitives: {
      secureRandom:
          {provider: box.policies.symmetric.secretkey.secureRandom.provider}
    }
  };

  cryptolib('commons', 'primitives.securerandom', f);

  /**
   * Validates the received length argument, in bytes.
   *
   * Confirms that:
   * <ul>
   * <li>the type of <code>lengthBytes</code> is number.</li>
   * <li><code>lengthBytes</code> is an integer with a value not less than
   * 1.</li>
   * </ul>
   *
   * @function validateParameter
   * @param lengthBytes
   *            {number} the length, in bytes, to be validated.
   */
  var validateParameter = function(lengthBytes) {
    if (typeof lengthBytes !== 'number' || lengthBytes % 1 !== 0 ||
        lengthBytes < 1) {
      throw new exceptions.CryptoLibException(
          'The given key length is not valid.');
    }
  };

  /**
   * Represents a secret key factory.
   *
   * @class
   */
  box.symmetric.secretkey.factory.SecretKeyFactory = function() {
    secureRandom = randomFactory.getCryptoRandomBytes();
  };

  box.symmetric.secretkey.factory.SecretKeyFactory.prototype = {

    /**
     * Get a CryptoSecretKeyGenerator for encryption.
     *
     * @function
     * @returns {symmetric/secretkey.CryptoSecretKeyGenerator} a
     *          CryptoSecretKeyGenerator for encryption.
     */
    getCryptoSecretKeyGeneratorForEncryption: function() {
      lengthBytes = policies.secretkey.encryption.lengthBytes;
      return this.getCryptoSecretKeyGenerator();
    },

    /**
     * Get a CryptoSecretKeyGenerator for MAC.
     *
     * @function
     * @returns {symmetric/secretkey.CryptoSecretKeyGenerator} a
     *          CryptoSecretKeyGenerator for MAC.
     */
    getCryptoSecretKeyGeneratorForMac: function() {
      lengthBytes = policies.secretkey.mac.lengthBytes;
      return this.getCryptoSecretKeyGenerator();
    },

    /**
     * Create a new CryptoSecretKeyGenerator.
     *
     * @function
     * @returns {symmetric/secretkey.CryptoSecretKeyGenerator} a
     *          CryptoSecretKeyGenerator.
     */
    getCryptoSecretKeyGenerator: function() {
      return new CryptoSecretKeyGenerator(secureRandom);
    }
  };

  /**
   * Defines a Secret Key generator.
   *
   * @class
   * @memberof symmetric/secretkey
   * @param randomGenerator
   *            {CryptoScytlRandomBytes} a secure random bytes generator.
   */
  function CryptoSecretKeyGenerator(randomGenerator) {
    this.converters = new utils.Converters();
    this.randomGenerator = randomGenerator;
  }

  CryptoSecretKeyGenerator.prototype = {

    /**
     * Generates a secret key.
     *
     * @function
     * @return A secret key, as string in Base64 encoded format.
     */
    generate: function() {

      try {
        validateParameter(lengthBytes);

        var key = this.randomGenerator.nextRandom(lengthBytes);
        var keyBase64 = this.converters.base64Encode(key);

        return keyBase64;

      } catch (error) {
        throw new exceptions.CryptoLibException(
            'CryptoSecretKey could not be generated.', error);
      }
    }
  };
};
