/*
 * 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 Tools = require('./tools');
var validator = require('./input-validator');
var codec = require('scytl-codec');
var forge = require('node-forge');

module.exports = Prng;

/**
 * @class Prng
 * @classdesc A pseudo-random number generator that generates data, based on a
 *            hash of the entropy that has been collected by various available
 *            sources. This object is created and managed internally by the
 *            secure random service.
 * @private
 * @param entropyHash
 *            {Uint8Array} The hash of the collected entropy.
 */
function Prng(entropyHash) {
  var tools_ = new Tools();
  var key_ = '';
  var counter_ = 0;

  /**
   * Generates some random bytes, using the gathered entropy data.
   *
   * @function nextBytes
   * @private
   * @param {number}
   *            numBytes The number of bytes to generate.
   * @return {Uint8Array} The random bytes.
   */
  this.nextBytes = function(numBytes) {
    validator.checkIsPositiveNumber(
        numBytes, 'Number of random bytes to generate');

    var keyAux;

    if (key_ === null || key_ === '') {
      reseed();
    }

    // Buffer in which to store the random bytes.
    var b = forge.util.createBuffer();
    var limitReach = 1;
    while (b.length() < numBytes) {
      b.putBytes(tools_.cipher(key_, counter_));
      counter_++;

      if (b.length >= (limitReach * Math.pow(2, 20))) {
        keyAux = key_;
        key_ = '';
        for (var i = 0; i < 2; i++) {
          key_ += tools_.cipher(keyAux, counter_);
          counter_++;
        }
        limitReach++;
      }
    }

    // Do it twice to ensure a key with 256 bits.
    keyAux = key_;
    key_ = '';
    for (var j = 0; j < 2; j++) {
      key_ += tools_.cipher(keyAux, counter_);
      counter_++;
    }

    return codec.binaryDecode(b.getBytes(numBytes));
  };

  function reseed() {
    var digester = tools_.getReseedDigester();
    digester.start();
    digester.update(codec.hexEncode(entropyHash));
    digester.update(key_);
    key_ = codec.hexEncode(codec.binaryDecode(digester.digest().getBytes()));
    counter_++;
  }
}
