/*
 * 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('Utils', function() {
  'use strict';

  it('should exist as a module', function() {
    cryptolib('commons.utils', function(box) {
      expect(box.commons.utils).toBeDefined();
    });
  });

  it('should contain a Converters object', function() {
    cryptolib('commons.utils', function(box) {
      var converters = new box.commons.utils.Converters();
      expect(converters).toBeDefined();
    });
  });

  describe('Converters', function() {
    it('should encode a string to base64', function() {
      cryptolib('commons.utils', function(box) {
        var converters = new box.commons.utils.Converters();
        expect(converters.base64Encode('scytl')).toBe('c2N5dGw=');
      });
    });
    it('should decode a string in base64 encoded format to a string',
       function() {
         cryptolib('commons.utils', function(box) {
           var converters = new box.commons.utils.Converters();
           expect(converters.base64Decode('c2N5dGw=')).toBe('scytl');
         });
       });
    it('should convert a string to in its byte array presentation', function() {
      cryptolib('commons.utils', function(box) {
        var converters = new box.commons.utils.Converters();
        expect(converters.bytesFromString('scytl')).toEqual([
          115, 99, 121, 116, 108
        ]);
      });
    });
    it('should convert a byte array in its string representation', function() {
      cryptolib('commons.utils', function(box) {
        var converters = new box.commons.utils.Converters();
        expect(converters.bytesToString([
          115, 99, 121, 116, 108
        ])).toEqual('scytl');
      });
    });
    it('should convert a string to in its hexadecimal representation',
       function() {
         cryptolib('commons.utils', function(box) {
           var converters = new box.commons.utils.Converters();
           expect(converters.hexFromString('scytl')).toEqual('736379746c');
         });
       });
    it('should convert an hexadecimal in its string representation',
       function() {
         cryptolib('commons.utils', function(box) {
           var converters = new box.commons.utils.Converters();
           expect(converters.hexToString('736379746c')).toEqual('scytl');
         });
       });

    it('should be able to encode and decode BigIntegers as Base64', function() {
      cryptolib('commons.utils', function(box) {
        var converters = new box.commons.utils.Converters();
        var number =
            new forge.jsbn.BigInteger('123456789012345678901234567890');
        var base64Number = converters.base64FromBigInteger(number);
        expect(base64Number).toBeTruthy();
        expect(converters.base64ToBigInteger(base64Number)).toEqual(number);
      });
    });

    it('should correctly encode a big integer to Base64', function() {
      cryptolib('commons.utils', function(box) {
        var converters = new box.commons.utils.Converters();
        expect(converters.base64ToBigInteger('AQ=='))
            .toEqual(forge.jsbn.BigInteger.ONE);
        expect(converters.base64ToBigInteger('BNI='))
            .toEqual(new forge.jsbn.BigInteger('1234'));
      });
    });

    it('should correctly decode a big integer from its Base64 representation',
       function() {
         cryptolib('commons.utils', function(box) {
           var converters = new box.commons.utils.Converters();
           expect(converters.base64FromBigInteger(forge.jsbn.BigInteger.ONE))
               .toEqual('AQ==');
           expect(converters.base64FromBigInteger(
                      new forge.jsbn.BigInteger('1234')))
               .toEqual('BNI=');
         });
       });
  });

  it('should contain a BitOperators object', function() {
    cryptolib('commons.utils', function(box) {
      var bitOperators = new box.commons.utils.BitOperators();
      expect(bitOperators).toBeDefined();
    });
  });

  describe('BitOperators', function() {
    it('should make a  bit-wise concat of two given strings', function() {
      cryptolib('commons.utils', function(box) {
        var bitOperators = new box.commons.utils.BitOperators();
        var string1 = '101';
        var string2 = '010';

        expect(bitOperators.concatenate(string1, string2)).toBe('101010');
      });
    });
    it('should extract the latest part of a given string', function() {
      cryptolib('commons.utils', function(box) {
        var bitOperators = new box.commons.utils.BitOperators();
        var string = 'ScytlSecureElectronicVoting';

        expect(bitOperators.extract(string, 5, string.length))
            .toBe('SecureElectronicVoting');
      });
    });
  });

  describe('XPath', function() {
    var inStream = [
      '<?xml version="1.0" encoding="UTF-8"?>', '<root>',
      '    <name attr=\'attr value\'>node value</name>', '</root>'
    ].join('\n');

    it('should find the proper node and show the contents', function() {
      cryptolib('commons.utils', function(box) {
        var parsers = new box.commons.utils.Parsers();
        var xml = parsers.xmlFromString(inStream);
        var node = new box.commons.utils.XPath(xml, '/root/name');

        expect(node.getValue()).toBe('node value');
        expect(node.getAttribute('attr')).toBe('attr value');
      });
    });

    it('should preserve the original xml object', function() {
      cryptolib('commons.utils', function(box) {
        var parsers = new box.commons.utils.Parsers();
        var xml = parsers.xmlFromString(inStream);
        var node2 = new box.commons.utils.XPath(xml, '/root/name');

        expect(node2.getValue()).toBe('node value');
        expect(node2.getAttribute('attr')).toBe('attr value');
      });
    });
  });

  describe('StringUtils', function() {
    it('should find the substring in the string', function() {
      cryptolib('commons.utils', function(box) {
        var stringUtils = new box.commons.utils.StringUtils();
        expect(stringUtils.containsSubString(
                   'main text with a lot of words', 'text with'))
            .toBeTruthy();
      });
    });

    it('should not find the substring in the string', function() {
      cryptolib('commons.utils', function(box) {
        var stringUtils = new box.commons.utils.StringUtils();
        expect(stringUtils.containsSubString(
                   'main text with a lot of words', 'text a'))
            .toBeFalsy();
      });
    });

    it('should find the substring in the string with sensitive case',
       function() {
         cryptolib('commons.utils', function(box) {
           var stringUtils = new box.commons.utils.StringUtils();
           expect(stringUtils.containsSubString(
                      'main text with a lot of words', 'text with', true))
               .toBeTruthy();
         });
       });

    it('should find the substring in the string with sensitive case and capital letters',
       function() {
         cryptolib('commons.utils', function(box) {
           var stringUtils = new box.commons.utils.StringUtils();
           expect(stringUtils.containsSubString(
                      'main tExT with a lot of words', 'tExT with', true))
               .toBeTruthy();
         });
       });

    it('should find the substring in the string without sensitive case and capital letters',
       function() {
         cryptolib('commons.utils', function(box) {
           var stringUtils = new box.commons.utils.StringUtils();
           expect(stringUtils.containsSubString(
                      'main tExT with a lot of words', 'text with'))
               .toBeTruthy();
         });
       });

    it('should not find the substring in the string with sensitive case and capital letters',
       function() {
         cryptolib('commons.utils', function(box) {
           var stringUtils = new box.commons.utils.StringUtils();
           expect(stringUtils.containsSubString(
                      'main tExT with a lot of words', 'text with', true))
               .toBeFalsy();
         });
       });
  });

  describe('ProgressMeter ..', function() {
    var _progressMeter;
    var _percentMeasuredSum;
    var _percentMeasuredLatest;

    beforeEach(function() {
      var progressMax = 100;
      var progressCallback = function(progressPercent) {
        _percentMeasuredSum += progressPercent;
        _percentMeasuredLatest = progressPercent;
      };
      var progressPercentMinCheckInterval = 10;

      cryptolib('commons.utils', function(box) {
        _progressMeter = new box.commons.utils.ProgressMeter(
            progressMax, progressCallback, progressPercentMinCheckInterval);
      });

      _percentMeasuredSum = 0;
      _percentMeasuredLatest = 0;
    });

    it('should measure progress for variable increases in progress',
       function() {
         cryptolib('commons.utils', function() {
           _progressMeter.update(9);
           expect(_percentMeasuredSum).toBe(0);
           expect(_percentMeasuredLatest).toBe(0);

           _progressMeter.update(10);
           expect(_percentMeasuredSum).toBe(10);
           expect(_percentMeasuredLatest).toBe(10);

           _progressMeter.update(11);
           expect(_percentMeasuredSum).toBe(10);
           expect(_percentMeasuredLatest).toBe(10);

           _progressMeter.update(39);
           expect(_percentMeasuredSum).toBe(49);
           expect(_percentMeasuredLatest).toBe(39);

           _progressMeter.update(75);
           expect(_percentMeasuredSum).toBe(124);
           expect(_percentMeasuredLatest).toBe(75);

           _progressMeter.update(100);
           expect(_percentMeasuredSum).toBe(224);
           expect(_percentMeasuredLatest).toBe(100);
         });
       });

    it('should measure 100 percent if progress provided as input exceeds maximum',
       function() {
         cryptolib('commons.utils', function() {
           _progressMeter.update(15);
           expect(_percentMeasuredSum).toBe(15);
           expect(_percentMeasuredLatest).toBe(15);

           _progressMeter.update(100);
           expect(_percentMeasuredSum).toBe(115);
           expect(_percentMeasuredLatest).toBe(100);

           _progressMeter.update(110);
           expect(_percentMeasuredSum).toBe(115);
           expect(_percentMeasuredLatest).toBe(100);
         });
       });
  });
});
