/*
 * 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
 */
/* jshint node:true */
'use strict';

var PlaintextEqualityProofTestData =
    require('../data/plaintext-equality-proof-data');
var ProgressMeterTestData = require('../data/progress-meter-data');
var zkProof = require('../../lib/index');
var elGamal = require('scytl-elgamal');

describe('The zero-knowledge proof module should be able to ...', function() {
  var proofService_;
  var progressMeterTestData_;
  var group_;
  var primarySecret_;
  var primaryCiphertext_;
  var secondarySecret_;
  var secondaryCiphertext_;
  var primaryPublicKey_;
  var secondaryPublicKey_;
  var proofHandler_;
  var proof_;
  var numProgressChecks_;
  var callback_;
  var defaultMinCheckInterval_;
  var customMinCheckInterval_;

  beforeAll(function() {
    proofService_ = zkProof.newService();

    progressMeterTestData_ = new ProgressMeterTestData();

    var proofTestData = new PlaintextEqualityProofTestData();
    var elGamalService = elGamal.newService();
    group_ = proofTestData.getGroup();
    var primaryEncryptedElements = proofTestData.getPrimaryEncryptedElements();
    primarySecret_ = primaryEncryptedElements.secret;
    primaryCiphertext_ = elGamalService.newEncryptedElements(
        primaryEncryptedElements.gamma, primaryEncryptedElements.phis);
    var secondaryEncryptedElements =
        proofTestData.getSecondaryEncryptedElements();
    secondarySecret_ = secondaryEncryptedElements.secret;
    secondaryCiphertext_ = elGamalService.newEncryptedElements(
        secondaryEncryptedElements.gamma, secondaryEncryptedElements.phis);
    primaryPublicKey_ = proofTestData.getPrimaryPublicKey();
    secondaryPublicKey_ = proofTestData.getSecondaryPublicKey();
    proofHandler_ = proofService_.newPlaintextEqualityProofHandler(group_).init(
        primaryPublicKey_, secondaryPublicKey_);
    proof_ = proofHandler_.generate(
        primarySecret_, secondarySecret_, primaryCiphertext_,
        secondaryCiphertext_);

    numProgressChecks_ = proofTestData.getNumProgressChecks();
    callback_ = progressMeterTestData_.progressCallback;
    defaultMinCheckInterval_ =
        progressMeterTestData_.getDefaultProgressPercentMinCheckInterval();
    customMinCheckInterval_ =
        progressMeterTestData_.getCustomProgressPercentMinCheckInterval();
  });

  beforeEach(function() {
    progressMeterTestData_.initializeProgressMeterData();
  });

  describe('create a zero-knowledge proof service that should be able to ..', function() {
    describe('create a plaintext equality proof handler that should be able to', function() {
      it('measure progress when generating a plaintext equality proof',
         function() {
           var proof = proofHandler_.measureProgress(callback_).generate(
               primarySecret_, secondarySecret_, primaryCiphertext_,
               secondaryCiphertext_);
           checkVerifiedAndProgressMeasured(proof);
         });

      it('measure progress when pre-computing a plaintext equality proof',
         function() {
           var preComputation =
               proofHandler_.measureProgress(callback_).preCompute();
           var proof = proofHandler_.generate(
               primarySecret_, secondarySecret_, primaryCiphertext_,
               secondaryCiphertext_, {preComputation: preComputation});
           checkVerifiedAndProgressMeasured(proof);
         });

      it('measure progress when verifying a plaintext equality proof',
         function() {
           var verified = proofHandler_.measureProgress(callback_).verify(
               proof_, primaryCiphertext_, secondaryCiphertext_);
           expect(verified).toBeTruthy();
           checkProgressMeasured();
         });

      it('not measure progress when generating a plaintext equality proof and the progress meter is not used',
         function() {
           var proof = proofHandler_.generate(
               primarySecret_, secondarySecret_, primaryCiphertext_,
               secondaryCiphertext_);
           checkVerifiedAndProgressNotMeasured(proof);
         });

      it('not measure progress when pre-computing a plaintext equality proof and the progress meter is not used',
         function() {
           var preComputation = proofHandler_.preCompute();
           var proof = proofHandler_.generate(
               primarySecret_, secondarySecret_, primaryCiphertext_,
               secondaryCiphertext_, {preComputation: preComputation});
           checkVerifiedAndProgressNotMeasured(proof);
         });

      it('not measure progress when verifying a plaintext equality proof and the progress meter is not used',
         function() {
           var verified = proofHandler_.verify(
               proof_, primaryCiphertext_, secondaryCiphertext_);
           expect(verified).toBeTruthy();
           checkProgressNotMeasured();
         });

      it('measure progress when generating a plaintext equality proof, using a custom minimum check interval',
         function() {
           var proof =
               proofHandler_.measureProgress(callback_, customMinCheckInterval_)
                   .generate(
                       primarySecret_, secondarySecret_, primaryCiphertext_,
                       secondaryCiphertext_);
           checkVerifiedAndProgressMeasured(proof, customMinCheckInterval_);
         });

      it('measure progress when pre-computing a plaintext equality proof, using a custom minimum check interval',
         function() {
           var preComputation =
               proofHandler_.measureProgress(callback_, customMinCheckInterval_)
                   .preCompute();
           var proof = proofHandler_.generate(
               primarySecret_, secondarySecret_, primaryCiphertext_,
               secondaryCiphertext_, {preComputation: preComputation});
           checkVerifiedAndProgressMeasured(proof, customMinCheckInterval_);
         });

      it('measure progress when verifying a plaintext equality proof, using a custom minimum check interval',
         function() {
           var verified =
               proofHandler_.measureProgress(callback_, customMinCheckInterval_)
                   .verify(proof_, primaryCiphertext_, secondaryCiphertext_);
           expect(verified).toBeTruthy();
           checkProgressMeasured(customMinCheckInterval_);
         });

      it('throw an error when measuring plaintext equality proof and not enough input arguments are provided',
         function() {
           expect(function() {
             proofHandler_.measureProgress();
           }).toThrow();
         });

      it('throw an error when measuring plaintext equality proof with null input arguments',
         function() {
           expect(function() {
             proofHandler_.measureProgress(null);
           }).toThrow();

           expect(function() {
             proofHandler_.measureProgress(callback_, null);
           }).toThrow();
         });

      it('throw an error when measuring plaintext equality proof with empty input arguments',
         function() {
           expect(function() {
             proofHandler_.measureProgress('');
           }).toThrow();

           expect(function() {
             proofHandler_.measureProgress(callback_, '');
           }).toThrow();
         });

      it('throw an error when measuring plaintext equality proof with a progress callback function that is not of type \'function\'',
         function() {
           expect(function() {
             proofHandler_.measureProgress(customMinCheckInterval_);
           }).toThrow();
         });

      it('throw an error when measuring plaintext equality proof with a progress percent minimum check interval that is not of type \'number\'',
         function() {
           var percentMeasuredLatest = 0;
           var progressCallback = function(progressPercent) {
             percentMeasuredLatest = progressPercent;
           };

           expect(function() {
             proofHandler_.measureProgress(progressCallback, progressCallback);
           }).toThrow();
         });
    });
  });

  function checkVerifiedAndProgressMeasured(proof, minCheckInterval) {
    checkVerified(proof);

    checkProgressMeasured(minCheckInterval);
  }

  function checkVerifiedAndProgressNotMeasured(proof) {
    checkVerified(proof);

    checkProgressNotMeasured();
  }

  function checkVerified(proof) {
    var verified =
        proofHandler_.verify(proof, primaryCiphertext_, secondaryCiphertext_);
    expect(verified).toBeTruthy();
  }

  function checkProgressMeasured(minCheckInterval) {
    if (typeof minCheckInterval === 'undefined') {
      minCheckInterval = defaultMinCheckInterval_;
    }

    expect(progressMeterTestData_.getProgressPercentLatest()).toBe(100);
    expect(progressMeterTestData_.getProgressPercentSum())
        .toBe(progressMeterTestData_.calculateProgressPercentSum(
            numProgressChecks_, minCheckInterval));
  }

  function checkProgressNotMeasured() {
    expect(progressMeterTestData_.getProgressPercentLatest()).toBe(0);
    expect(progressMeterTestData_.getProgressPercentSum()).toBe(0);
  }
});
