/*
 * 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
 */
/* zlobal OV */
/* zlobal TD */
/* zlobal CL */
/* zlobal forge */

/* jshint ignore: start */

/* jshint maxlen: 6666 */

describe('Precomputations api', function() {

    'use strict';

    var TD = require('./mocks/testdata.json');

    var eventId,
        encParms,
        messages,
        startVotingKey,
        verificationCard,
        verificationCardSecret,
        verificationCardPublicKey;

    beforeEach(function() {

        encParms = OV.parseEncryptionParams(TD.authResponse);
        messages = [new CL.commons.mathematical.ZpGroupElement(new forge.jsbn.BigInteger('1'), encParms.p, encParms.q)];
        eventId = TD.eventId;
        startVotingKey = OV.parseStartVotingKey(TD.startVotingKey, eventId);
        encParms = OV.parseEncryptionParams(TD.authResponse);
        verificationCard = TD.authResponse.verificationCard;
        verificationCardPublicKey = JSON.parse(forge.util.decode64(verificationCard.signedVerificationPublicKey));
        verificationCardSecret = OV.parseVerificationCard(verificationCard.verificationCardKeystore, startVotingKey.pin);

    });


    var encryptAndProve = function(precomputedEncrypterValues, precomputedProofValues) {

        var start = Date.now(); // Date.now();

        // generate pcc and encrypt

        var encryptedOptions = OV.encryptOptions(messages, encParms, precomputedEncrypterValues);
        var partialChoiceCodes = OV.generatePartialChoiceCodes(messages, encParms, verificationCardSecret);
        var encryptedPCC = OV.encryptPartialChoiceCodes(partialChoiceCodes, encParms, precomputedEncrypterValues);

        // generate proofs

        var schnorr = OV.generateSchnorrProof('votingCardId', 'electionId', encParms, encryptedOptions, precomputedProofValues);
        var cipherTextExponentiations = OV.generateCipherTextExponentiations(encParms, encryptedOptions, verificationCardSecret);
        var exponentiationProof = OV.generateExponentiationProof(encParms, cipherTextExponentiations, verificationCardPublicKey.publicKey, precomputedProofValues);
        var plaintextEqualityProof = OV.generatePlaintextEqualityProof(encParms, encryptedOptions, encryptedPCC, verificationCardSecret, cipherTextExponentiations, precomputedProofValues);

        var elapsed = Date.now() - start;

        // prove them

        var primaryCiphertext = (function() {

            var exponentiatedVoteCiphertext = CL.commons.mathematical.groupUtils.exponentiateArrays(
                [encryptedOptions.getGamma()].concat(encryptedOptions.getPhis()), // base elements
                new CL.commons.mathematical.Exponent(encParms.q, verificationCardSecret), // exponent
                encParms.group);

            return {
                getGamma: function() {
                    return exponentiatedVoteCiphertext[0];
                },
                getPhis: function() {
                    return exponentiatedVoteCiphertext.slice(1);
                },
                getComputationalValues: function() {
                    return {
                        gamma: exponentiatedVoteCiphertext[0],
                        phis: exponentiatedVoteCiphertext.slice(1)
                    };
                }
            };
        })();

        var secondaryCipherText = (function() {

            var compressedPRCphis = CL.commons.mathematical.groupUtils.compressGroupElements(
                encParms.group,
                encryptedPCC.getPhis());

            var gamma = encryptedPCC.getGamma();

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

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

        var primaryPublicKey = ebPubKey;
        var secondaryPublicKey = (function() {

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

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

        // verify schnorr proof

        expect((new CL.proofs.ProofsFactory()).createSchnorrProofVerifier(
                encryptedOptions.getElGamalComputationValues().getGamma(),
                'votingCardId',
                'electionId',
                schnorr,
                encParms.group)
            .verify()).toBe(true);

        // verify plain text eq proof

        expect((new CL.proofs.ProofsFactory()).createPlaintextEqualityProofVerifier(
                primaryCiphertext,
                primaryPublicKey,
                secondaryCipherText,
                secondaryPublicKey,
                plaintextEqualityProof,
                encParms.group)
            .verify()).toBe(true);

        // verify exponentiation proof

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

        expect((new CL.proofs.ProofsFactory()).createExponentiationProofVerifier(
                [publicKey].concat(cipherTextExponentiations.cipherTextExponentiation), //
                [generator].concat(cipherTextExponentiations.cipherTextBases),
                exponentiationProof,
                encParms.group)
            .verify()).toBe(true);

        return elapsed;
    };

    it('should create a vote without precomputations', function() {
        var elapsed = encryptAndProve();
        // console.log('Encryption and proofs without precomputations:', elapsed);
    });

    it('should create a vote with precomputations', function() {
        var serializedEncrypterValues = OV.precomputeEncrypterValues(encParms);
        var precomputedEncrypterValues = OV.deserializeEncrypterValues(serializedEncrypterValues);
        var precomputedProofValues = OV.precomputeProofs('electionId', 'votingCardId', encParms, serializedEncrypterValues);
        var elapsed = encryptAndProve(precomputedEncrypterValues, precomputedProofValues);
        // console.log('Encryption and proofs with precomputations:', elapsed);
    });


});
