/*
 * 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 PlaintextExponentEqualityProofTestData =
    require('../data/plaintext-exponent-equality-proof-data');
var ProgressMeterTestData = require('../data/progress-meter-data');
var zkProof = require('../../lib/index');

describe('The zero-knowledge proof module should be able to ...', function() {
  var proofService_;
  var progressMeterTestData_;
  var group_;
  var firstSecret_;
  var secondSecret_;
  var baseElements_;
  var ciphertext_;
  var proofHandler_;
  var proof_;
  var numProgressChecks_;
  var callback_;
  var defaultMinCheckInterval_;
  var customMinCheckInterval_;

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

    progressMeterTestData_ = new ProgressMeterTestData();

    var proofTestData = new PlaintextExponentEqualityProofTestData();
    group_ = proofTestData.getGroup();
    firstSecret_ = proofTestData.getFirstSecret();
    secondSecret_ = proofTestData.getSecondSecret();
    baseElements_ = proofTestData.getBaseElements();
    ciphertext_ = proofTestData.getCiphertext();

    proofHandler_ =
        proofService_.newPlaintextExponentEqualityProofHandler(group_).init(
            baseElements_);
    proof_ = proofHandler_.generate(firstSecret_, secondSecret_, ciphertext_);

    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 exponent equality proof handler that should be able to', function() {
      it('measure progress when generating a plaintext exponent equality proof',
         function() {
           var proof = proofHandler_.measureProgress(callback_).generate(
               firstSecret_, secondSecret_, ciphertext_);
           checkVerifiedAndProgressMeasured(proof);
         });

      it('measure progress when pre-computing a plaintext exponent equality proof',
         function() {
           var preComputation =
               proofHandler_.measureProgress(callback_).preCompute();
           var proof = proofHandler_.generate(
               firstSecret_, secondSecret_, ciphertext_,
               {preComputation: preComputation});
           checkVerifiedAndProgressMeasured(proof);
         });

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

      it('not measure progress when generating a plaintext exponent equality proof and the progress meter is not used',
         function() {
           var proof =
               proofHandler_.generate(firstSecret_, secondSecret_, ciphertext_);
           checkVerifiedAndProgressNotMeasured(proof);
         });

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

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

      it('measure progress when generating a plaintext exponent equality proof, using a custom minimum check interval',
         function() {
           var proof =
               proofHandler_.measureProgress(callback_, customMinCheckInterval_)
                   .generate(firstSecret_, secondSecret_, ciphertext_);
           checkVerifiedAndProgressMeasured(proof, customMinCheckInterval_);
         });

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

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

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

      it('throw an error when measuring plaintext exponent 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 exponent 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 exponent 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 exponent 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, ciphertext_);
    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);
  }
});
