/*
 * 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
 */
/* global CL */
/* jshint maxlen: 6666 */

module.exports = (function() {

    'use strict';

    var generateSchnorrProof = function(votingCardId, electionId, encryptionParms, voteEncryption, precomputeProofValues) {

        var precomputed = null ;
        if ( precomputeProofValues && precomputeProofValues.schnorr ) {
            precomputed = CL.proofs.utils.deserializeProofPreComputedValues(precomputeProofValues.schnorr);
        }

        var schnorrProof = CL.proofs.service.createSchnorrProof(
            votingCardId,
            electionId,
            voteEncryption.getElGamalComputationValues().getGamma(),
            voteEncryption.getR(),
            encryptionParms.group,
            precomputed);

        return schnorrProof;

    };

    var generateCipherTextExponentiations = function(encryptionParms, voteEncryption, verificationCardSecret) {

        var exponent = new CL.commons.mathematical.Exponent(encryptionParms.q, verificationCardSecret);
        var cipherTextBases = [voteEncryption.getGamma()].concat(voteEncryption.getPhis()[0]);
        var cipherTextExponentiation = CL.commons.mathematical.groupUtils.exponentiateArrays(cipherTextBases, exponent, encryptionParms.group);

        return {
            cipherTextBases: cipherTextBases,
            cipherTextExponentiation: cipherTextExponentiation,
            exponent: exponent
        };
    };


    var generateExponentiationProof = function(encryptionParms, cipherTextExponentiations, verificationCardPublicKey) { //, precomputeProofValues) {

        var keyfactory = new CL.homomorphic.keypair.factory.KeyFactory();
        var publicKey = keyfactory.createPublicKey(verificationCardPublicKey).getGroupElementsArray()[0];
        var generator = new CL.commons.mathematical.ZpGroupElement(
            encryptionParms.g,
            encryptionParms.p,
            encryptionParms.q);

        var precomputed = null ;
        
        var exponentiationProof = CL.proofs.service.createExponentiationProof(
            [publicKey].concat(cipherTextExponentiations.cipherTextExponentiation),
            [generator].concat(cipherTextExponentiations.cipherTextBases),
            cipherTextExponentiations.exponent,
            encryptionParms.group,
            precomputed);
        
        return exponentiationProof;

    };


    var generatePlaintextEqualityProof = function(encryptionParms, voteEncryption, prcEncryption, verificationCardSecret, cipherTextExponentiations, precomputeProofValues) {

        var precomputed = null ;
        if ( precomputeProofValues && precomputeProofValues.plaintextEquality ) {
            precomputed = CL.proofs.utils.deserializeProofPreComputedValues(precomputeProofValues.plaintextEquality);
        }

        var ebPubKey = encryptionParms.optionsEncryptionKey;
        var prcPubKey = encryptionParms.choiceCodesEncryptionKey;

        var primaryCiphertext = (function() {
            
            // use cipherTextExponentiations if supplied, else compute them
            var exponentiatedVoteCiphertext = cipherTextExponentiations ?
                cipherTextExponentiations.cipherTextExponentiation :
                CL.commons.mathematical.groupUtils.exponentiateArrays(
                    [voteEncryption.getGamma()].concat(voteEncryption.getPhis()[0]), // base elements
                    new CL.commons.mathematical.Exponent(encryptionParms.q, verificationCardSecret), // exponent
                    encryptionParms.group);
            
            return {
                getGamma: function() {
                    return exponentiatedVoteCiphertext[0];
                },
                getPhis: function() {
                    return exponentiatedVoteCiphertext.slice(1);
                },
                getComputationalValues: function() {
                    return {
                        gamma: exponentiatedVoteCiphertext[0],
                        phis: exponentiatedVoteCiphertext.slice(1)
                    };
                }
            };
        })();

        var primaryPublicKey = (function() {

            return {
                getGroup: function() {
                    return encryptionParms.group;
                },
                getGroupElementsArray: function() {

                    var firstSubKey = [];
                    firstSubKey.push(ebPubKey.getGroupElementsArray()[0]);
                    return firstSubKey;
                }
            };
        })();

        var primaryWitness = voteEncryption.getR().multiply(
            new CL.commons.mathematical.Exponent(encryptionParms.q,
                verificationCardSecret));

        var secondaryCipherText = (function() {

            var compressedPRCphis = CL.commons.mathematical.groupUtils.compressGroupElements(
                encryptionParms.group,
                prcEncryption.getPhis());

            var gamma = prcEncryption.getGamma();

            return {
                getGamma: function() {
                    return gamma;
                },
                getPhis: function() {
                    return [compressedPRCphis];
                },
                getComputationalValues: function() {
                    return {
                        gamma: gamma,
                        phis: [compressedPRCphis]
                    };
                }
            };
        })();

        var secondaryPublicKey = (function() {

            var compressedSecondaryPublicKey = CL.commons.mathematical.groupUtils.compressGroupElements(
                encryptionParms.group,
                prcPubKey.getGroupElementsArray());

            return {
                getGroup: function() {
                    return encryptionParms.group;
                },
                getGroupElementsArray: function() {
                    return [compressedSecondaryPublicKey];
                }
            };
        })();

        var secondaryWitness = prcEncryption.getR();

        var plaintextEqualityProof = CL.proofs.service.createPlaintextEqualityProof(
            primaryCiphertext,
            primaryPublicKey,
            primaryWitness,
            secondaryCipherText,
            secondaryPublicKey,
            secondaryWitness,
            encryptionParms.group,
            precomputed
        );
        return plaintextEqualityProof;
    };

    return {
        generateSchnorrProof: generateSchnorrProof,
        generateCipherTextExponentiations: generateCipherTextExponentiations,
        generateExponentiationProof: generateExponentiationProof,
        generatePlaintextEqualityProof: generatePlaintextEqualityProof
    };

})();
