/*
 * 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
 */
describe('Proofs service ...', function() {
  'use strict';

  // Initialize variables common to all proof tests.
  beforeEach(initializeTestVars);

  // Exponentiation proof tests.
  describe('should be able to ..', function() {
    var num_base_elements = 3;
    var num_progress_checks = num_base_elements;

    var initialized = false;
    var witness;
    var baseElements;
    var exponentiatedElements;
    var proof;
    var preComputedValues;
    var anotherWitness;
    var anotherBaseElements;
    var anotherExponentiatedElements;
    var aShorterBaseElements;

    // Initialize variables common to exponentiation proof tests.
    beforeEach(function() {
      if (!initialized) {
        cryptolib('proofs.service', function(box) {
          witness = generateRandomExponent(_zpSubgroup);
          baseElements = generatePlaintext(num_base_elements);
          exponentiatedElements =
              generateExponentiatedElements(baseElements, witness);
          proof = box.proofs.service.createExponentiationProof(
              exponentiatedElements, baseElements, witness, _zpSubgroup);
          preComputedValues = box.proofs.service.preComputeExponentiationProof(
              baseElements, _zpSubgroup);

          do {
            anotherWitness = generateRandomExponent(_zpSubgroup);
          } while (anotherWitness.getValue().equals(witness.getValue()));
          do {
            anotherBaseElements = generatePlaintext(num_base_elements);
          } while (anotherBaseElements.equals(baseElements));
          anotherExponentiatedElements =
              generateExponentiatedElements(anotherBaseElements, witness);
          aShorterBaseElements = generatePlaintext(num_base_elements - 1);
        });

        initialized = true;
      }
    });

    it('generate and verify an exponentiation proof', function() {
      cryptolib('proofs.service', function(box) {
        var verification = box.proofs.service.verifyExponentiationProof(
            exponentiatedElements, baseElements, proof, _zpSubgroup);
        expect(verification).toBeTruthy();
      });
    });

    it('and generate and verify an exponentiation proof, using pre-computed values',
       function() {
         cryptolib('proofs.service', function(box) {
           expect(preComputedValues).toBeDefined();

           var anotherProof = box.proofs.service.createExponentiationProof(
               exponentiatedElements, baseElements, witness, _zpSubgroup,
               preComputedValues);

           var verification = box.proofs.service.verifyExponentiationProof(
               exponentiatedElements, baseElements, anotherProof, _zpSubgroup);
           expect(verification).toBeTruthy();
         });
       });

    it('and generate and verify an exponentiation proof, using null for the pre-computed values option',
       function() {
         cryptolib('proofs.service', function(box) {
           var anotherProof = box.proofs.service.createExponentiationProof(
               exponentiatedElements, baseElements, witness, _zpSubgroup, null);

           var verification = box.proofs.service.verifyExponentiationProof(
               exponentiatedElements, baseElements, anotherProof, _zpSubgroup);
           expect(verification).toBeTruthy();
         });
       });

    it('generate and verify an exponentiation proof, using serialization',
       function() {
         cryptolib('commons.mathematical', 'proofs', function(box) {
           var proofAsJson = proof.stringify();

           var proofFromJson = box.proofs.utils.deserializeProof(proofAsJson);

           var verification = box.proofs.service.verifyExponentiationProof(
               exponentiatedElements, baseElements, proofFromJson, _zpSubgroup);
           expect(verification).toBeTruthy();
         });
       });

    it('generate and verify an exponentiation proof, using pre-computed values and serialization',
       function() {
         cryptolib('commons.mathematical', 'proofs', function(box) {
           var preComputedValuesAsJson = preComputedValues.stringify();

           var preComputedValuesFromJson =
               box.proofs.utils.deserializeProofPreComputedValues(
                   preComputedValuesAsJson);

           var anotherProof = box.proofs.service.createExponentiationProof(
               exponentiatedElements, baseElements, witness, _zpSubgroup,
               preComputedValuesFromJson);

           var verification = box.proofs.service.verifyExponentiationProof(
               exponentiatedElements, baseElements, anotherProof, _zpSubgroup);
           expect(verification).toBeTruthy();
         });
       });

    it('and measure progress when generating an exponentiation proof',
       function() {
         cryptolib('proofs.service', function(box) {
           initializeProgressMeter();
           var anotherProof = box.proofs.service.createExponentiationProof(
               exponentiatedElements, baseElements, witness, _zpSubgroup,
               progressCallback, PROGRESS_PERCENT_MIN_CHECK_INTERVAL);

           expect(_progressPercentSum)
               .toBe(getProgressPercentSum(
                   num_progress_checks, PROGRESS_PERCENT_MIN_CHECK_INTERVAL));
           expect(_progressPercentLatest).toBe(100);

           var verification = box.proofs.service.verifyExponentiationProof(
               exponentiatedElements, baseElements, anotherProof, _zpSubgroup);
           expect(verification).toBeTruthy();
         });
       });

    it('and measure progress when pre-computing values for an exponentiation proof',
       function() {
         cryptolib('proofs.service', function(box) {
           initializeProgressMeter();
           box.proofs.service.preComputeExponentiationProof(
               baseElements, _zpSubgroup, progressCallback,
               PROGRESS_PERCENT_MIN_CHECK_INTERVAL);

           expect(_progressPercentSum)
               .toBe(getProgressPercentSum(
                   num_progress_checks, PROGRESS_PERCENT_MIN_CHECK_INTERVAL));
           expect(_progressPercentLatest).toBe(100);

           var anotherProof = box.proofs.service.createExponentiationProof(
               exponentiatedElements, baseElements, witness, _zpSubgroup,
               preComputedValues);
           var verification = box.proofs.service.verifyExponentiationProof(
               exponentiatedElements, baseElements, anotherProof, _zpSubgroup);
           expect(verification).toBeTruthy();
         });
       });

    it('and measure progress when verifying an exponentiation proof',
       function() {
         cryptolib('proofs.service', function(box) {
           initializeProgressMeter();
           var verification = box.proofs.service.verifyExponentiationProof(
               exponentiatedElements, baseElements, proof, _zpSubgroup,
               progressCallback, PROGRESS_PERCENT_MIN_CHECK_INTERVAL);
           expect(verification).toBeTruthy();

           expect(_progressPercentSum)
               .toBe(getProgressPercentSum(
                   num_progress_checks, PROGRESS_PERCENT_MIN_CHECK_INTERVAL));
           expect(_progressPercentLatest).toBe(100);
         });
       });

    it('and fail to verify an exponentiation proof that was generated with the wrong base elements',
       function() {
         cryptolib('proofs.service', function(box) {
           var anotherProof = box.proofs.service.createExponentiationProof(
               exponentiatedElements, anotherBaseElements, witness, _zpSubgroup,
               null);

           var verification = box.proofs.service.verifyExponentiationProof(
               exponentiatedElements, anotherBaseElements, anotherProof,
               _zpSubgroup);
           expect(verification).toBeFalsy();
         });
       });

    it('and fail to verify an exponentiation proof that was generated with the wrong witness',
       function() {
         cryptolib('proofs.service', function(box) {
           var anotherProof = box.proofs.service.createExponentiationProof(
               exponentiatedElements, baseElements, anotherWitness,
               _zpSubgroup);

           var verification = box.proofs.service.verifyExponentiationProof(
               exponentiatedElements, baseElements, anotherProof, _zpSubgroup);
           expect(verification).toBeFalsy();
         });
       });

    it('and fail to verify an exponentiation proof when using the wrong base elements',
       function() {
         cryptolib('proofs.service', function(box) {
           var verification = box.proofs.service.verifyExponentiationProof(
               exponentiatedElements, anotherBaseElements, proof, _zpSubgroup);
           expect(verification).toBeFalsy();
         });
       });

    it('and fail to verify an exponentiation proof when using the wrong exponentiated elements',
       function() {
         cryptolib('proofs.service', function(box) {
           var verification = box.proofs.service.verifyExponentiationProof(
               anotherExponentiatedElements, baseElements, proof, _zpSubgroup);
           expect(verification).toBeFalsy();
         });
       });

    it('and fail to verify an exponentiation proof when using the wrong mathematical group',
       function() {
         cryptolib('proofs.service', function(box) {
           var verification = box.proofs.service.verifyExponentiationProof(
               exponentiatedElements, baseElements, proof, _anotherZpSubgroup);
           expect(verification).toBeFalsy();
         });
       });

    it('and throw an exception when generating an exponentiation proof, using null input data',
       function() {
         cryptolib('proofs.service', function(box) {
           expect(function() {
             box.proofs.service.createExponentiationProof(
                 null, baseElements, witness, _zpSubgroup);
           }).toThrow();

           expect(function() {
             box.proofs.service.createExponentiationProof(
                 exponentiatedElements, null, witness, _zpSubgroup);
           }).toThrow();

           expect(function() {
             box.proofs.service.createExponentiationProof(
                 exponentiatedElements, baseElements, null, _zpSubgroup);
           }).toThrow();

           expect(function() {
             box.proofs.service.createExponentiationProof(
                 exponentiatedElements, baseElements, witness, null);
           }).toThrow();
         });
       });

    it('and throw an exception when generating an exponentiation proof, using the wrong mathematical group',
       function() {
         cryptolib('proofs.service', function(box) {
           expect(function() {
             box.proofs.service.createExponentiationProof(
                 exponentiatedElements, baseElements, witness,
                 _anotherZpSubgroup);
           }).toThrow();
         });
       });

    it('and throw an exception when generating an exponentiation proof, using base element and exponentiated element collections with different lengths',
       function() {
         cryptolib('proofs.service', function(box) {
           expect(function() {
             box.proofs.service.createExponentiationProof(
                 exponentiatedElements, aShorterBaseElements, witness,
                 _zpSubgroup);
           }).toThrow();
         });
       });

    it('and throw an exception when generating an exponentiation proof, using a progress callback function that is not a function',
       function() {
         cryptolib('proofs.service', function(box) {
           expect(function() {
             box.proofs.service.createExponentiationProof(
                 exponentiatedElements, baseElements, witness, _zpSubgroup,
                 PROGRESS_PERCENT_MIN_CHECK_INTERVAL);
           }).toThrow();
         });
       });

    it('and throw an exception when generating an exponentiation proof, using a progress percent check interval that is not a number',
       function() {
         cryptolib('proofs.service', function(box) {
           var percentMeasuredLatest = 0;
           var progressCallback = function(progressPercent) {
             percentMeasuredLatest = progressPercent;
           };

           expect(function() {
             box.proofs.service.createExponentiationProof(
                 exponentiatedElements, baseElements, witness, _zpSubgroup,
                 _zpSubgroup, progressCallback, progressCallback);
           }).toThrow();
         });
       });

    it('and throw an exception when pre-computing values for an exponentiation proof, using null input data',
       function() {
         cryptolib('proofs.service', function(box) {
           expect(function() {
             box.proofs.service.preComputeExponentiationProof(
                 null, _zpSubgroup);
           }).toThrow();

           expect(function() {
             box.proofs.service.preComputeExponentiationProof(
                 baseElements, null);
           }).toThrow();

         });
       });

    it('and throw an exception when verifying an exponentiation proof, using null input data',
       function() {
         cryptolib('proofs.service', function(box) {
           expect(function() {
             box.proofs.service.verifyExponentiationProof(
                 null, baseElements, proof, _zpSubgroup);
           }).toThrow();

           expect(function() {
             box.proofs.service.verifyExponentiationProof(
                 exponentiatedElements, null, proof, _zpSubgroup);
           }).toThrow();

           expect(function() {
             box.proofs.service.verifyExponentiationProof(
                 exponentiatedElements, baseElements, null, _zpSubgroup);
           }).toThrow();

           expect(function() {
             box.proofs.service.verifyExponentiationProof(
                 exponentiatedElements, baseElements, proof, null);
           }).toThrow();
         });
       });

    it('and throw an exception when verifying an exponentiation proof, using base element and exponentiated element collections with different lengths',
       function() {
         cryptolib('proofs.service', function(box) {
           expect(function() {
             box.proofs.service.verifyExponentiationProof(
                 exponentiatedElements, aShorterBaseElements, proof,
                 _zpSubgroup);
           }).toThrow();
         });
       });

    it('and throw an exception when verifying an exponentiation proof, using a progress callback function that is not a function',
       function() {
         cryptolib('proofs.service', function(box) {
           expect(function() {
             box.proofs.service.verifyExponentiationProof(
                 exponentiatedElements, baseElements, proof, _zpSubgroup,
                 _zpSubgroup, PROGRESS_PERCENT_MIN_CHECK_INTERVAL);
           }).toThrow();
         });
       });

    it('and throw an exception when verifying an exponentiation proof, using a progress percent check interval that is not a number',
       function() {
         cryptolib('proofs.service', function(box) {
           var percentMeasuredLatest = 0;
           var progressCallback = function(progressPercent) {
             percentMeasuredLatest = progressPercent;
           };

           expect(function() {
             box.proofs.service.verifyExponentiationProof(
                 exponentiatedElements, baseElements, proof, _zpSubgroup,
                 progressCallback, progressCallback);
           }).toThrow();
         });
       });
  });
});
