/*
 * 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.homomorphic = cryptolib.modules.homomorphic || {};

cryptolib.modules.homomorphic.keypair = function(box) {
  'use strict';

  box.homomorphic = box.homomorphic || {};
  box.homomorphic.keypair = {};

  /**
   * A submodule that holds ElGamal key pair functionalities.
   * @exports homomorphic/keypair/factory
   */
  box.homomorphic.keypair.factory = {};

  var converters;
  var mathematical;
  var f = function(box) {
    converters = new box.commons.utils.Converters();
    mathematical = box.commons.mathematical;
  };

  cryptolib('commons.exceptions', 'commons.utils', 'commons.mathematical', f);

  /** @class */
  box.homomorphic.keypair.factory.KeyFactory = function() {

  };

  box.homomorphic.keypair.factory.KeyFactory.prototype = {

    /**
     * @function
     * @returns ElGamalKeyPair
     */
    createKeyPair: function(elGamalPublicKey, elGamalPrivateKey) {
      return new ElGamalKeyPair(elGamalPublicKey, elGamalPrivateKey);
    },

    /**
     * @function
     * @returns ElGamalPublicKey
     */
    createPublicKey: function(
        serializedElGamalPublicKeyB64OrListOfGroupElements, keyGroup) {
      return new ElGamalPublicKey(
          serializedElGamalPublicKeyB64OrListOfGroupElements, keyGroup);
    },

    /**
     * @function
     * @returns ElGamalPrivateKey
     */
    createPrivateKey: function(
        serializedElGamalPrivateKeyB64OrListOfExponents, keyGroup) {
      return new ElGamalPrivateKey(
          serializedElGamalPrivateKeyB64OrListOfExponents, keyGroup);
    }
  };

  /**
   * Representation of an ElGamal key pair. This key pair is composed of a
   * private key and a public key.
   * @class ElGamalKeyPair
   * @param elGamalPublicKey
   *            ElGamal public key.
   * @param elGamalPrivateKey
   *            ElGamal private key.
   * @returns {ElGamalKeyPair}
   */
  function ElGamalKeyPair(elGamalPublicKey, elGamalPrivateKey) {
    var _elGamalPublicKey = elGamalPublicKey;
    var _elGamalPrivateKey = elGamalPrivateKey;

    this.getPublicKey = function() {
      return _elGamalPublicKey;
    };

    this.getPrivateKey = function() {
      return _elGamalPrivateKey;
    };
  }

  /**
   * Encapsulates an ElGamal public key.
   * <P>
   * This class includes the key itself (which is actually a list of
   * elements), as well as the mathematical group that this key belongs to.
   * @class ElGamalPublicKey
   * @param {String_or_Array} serializedElGamalPublicKeyB64OrListOfGroupElements
   *            this parameter can be either a string containing the
   *     serialized ElGamal public key to JSON string, or an array of
   * ZpGroupElement.
   * @param {ZpSubgroup} keyGroup
   *            the ZpSubgroup of the public key.
   * @returns {ElGamalPublicKey}
   */
  function ElGamalPublicKey(
      serializedElGamalPublicKeyB64OrListOfGroupElements, keyGroup) {
    var _group;
    var _groupElementsArray = [];

    var _fromJson = function(serializedElGamalPublicKey) {

      var parsedPublicKey = JSON.parse(serializedElGamalPublicKey).publicKey;

      var p = converters.base64ToBigInteger(
          parsedPublicKey.zpSubgroup.p.toString());
      var q = converters.base64ToBigInteger(
          parsedPublicKey.zpSubgroup.q.toString());
      var generator = converters.base64ToBigInteger(
          parsedPublicKey.zpSubgroup.g.toString());
      _group = new mathematical.ZpSubgroup(generator, p, q);

      for (var i = 0; i < parsedPublicKey.elements.length; i++) {
        var elementValue =
            converters.base64ToBigInteger(parsedPublicKey.elements[i]);
        var element = new mathematical.ZpGroupElement(
            elementValue, _group.getP(), _group.getQ());
        _groupElementsArray.push(element);
      }
    };

    if (typeof keyGroup === 'undefined') {
      var serializedElGamalPublicKey = converters.base64Decode(
          serializedElGamalPublicKeyB64OrListOfGroupElements);

      _fromJson(serializedElGamalPublicKey);
    } else {
      _groupElementsArray = serializedElGamalPublicKeyB64OrListOfGroupElements;
      _group = keyGroup;
    }


    this.getGroup = function() {
      return _group;
    };

    this.getGroupElementsArray = function() {
      return _groupElementsArray;
    };
  }

  /**
   * Encapsulates an ElGamal private key.
   * <P>
   * This class includes the key itself (which is actually a list of
   * exponents), as well as the mathematical group that this key belongs to.
   * @class ElGamalPrivateKey
   * @param {String_or_Array} serializedElGamalPrivateKeyB64OrListOfExponents
   *            this parameter can be either a string containing the
   *            serialized to JSON string ElGamal private key
   *            or an array of Exponents.
   * @param {ZpSubgroup} keyGroup
   *            the ZpSubgroup of the private key.
   * @returns {ElGamalPrivateKey}
   */
  function ElGamalPrivateKey(
      serializedElGamalPrivateKeyB64OrListOfExponents, keyGroup) {
    var _group;
    var _exponentsArray = [];

    var _fromJson = function(serializedElGamalPrivateKey) {

      var parsedPrivateKey = JSON.parse(serializedElGamalPrivateKey).privateKey;

      var p = converters.base64ToBigInteger(
          parsedPrivateKey.zpSubgroup.p.toString());
      var q = converters.base64ToBigInteger(
          parsedPrivateKey.zpSubgroup.q.toString());
      var generator = converters.base64ToBigInteger(
          parsedPrivateKey.zpSubgroup.g.toString());
      _group = new mathematical.ZpSubgroup(generator, p, q);

      for (var i = 0; i < parsedPrivateKey.exponents.length; i++) {
        var exponentValue =
            converters.base64ToBigInteger(parsedPrivateKey.exponents[i]);
        var exponent = new mathematical.Exponent(_group.getQ(), exponentValue);
        _exponentsArray.push(exponent);
      }
    };

    if (typeof keyGroup === 'undefined') {
      var serializedElGamalPrivateKey = converters.base64Decode(
          serializedElGamalPrivateKeyB64OrListOfExponents);
      _fromJson(serializedElGamalPrivateKey);
    } else {
      _exponentsArray = serializedElGamalPrivateKeyB64OrListOfExponents;
      _group = keyGroup;
    }

    this.getGroup = function() {
      return _group;
    };

    this.getExponentsArray = function() {
      return _exponentsArray;
    };
  }
};
