text: angular.module('webui.services.utils', ['webui.services.configuration'])
  .factory('$utils', ['$filter', '$name', '$titlePattern', function(filter, $name, $titlePattern) {
    var rnd16 = (function() {
      "use strict";
      var rndBuffer = new Uint8Array(16);
      var rnd16Weak = function() {
        for (var i = 0, r; i < 16; i++) {
          if (!(i % 0x3)) r = Math.random() * 0x100000000 | 0;
          rndBuffer[i] = r >>> ((i & 0x3) << 0x3) & 0xff;
        }
        return rndBuffer;
      };
  
      if (!window.crypto || !crypto.getRandomValues) {
        return rnd16Weak;
      }
      return function() {
        try {
          crypto.getRandomValues(rndBuffer);
          return rndBuffer;
        }
        catch (ex) {
          // Entropy might be exhausted
          return rnd16Weak();
        }
      };
    })();
  
    var utils = {
  
      fmtsize: function(len) {
        len = +len; // coerce to number
        if (len <= 1024) {
          return len.toFixed(0)  + " B";
        }
        len /= 1024;
        if (len <= 1024) {
          return len.toFixed(1) + " KB"
        }
        len /= 1024;
        if (len <= 1024) {
          return len.toFixed(2) + " MB";
        }
        len /= 1024;
        return len.toFixed(3) + " GB";
      },
  
      fmtspeed: function(speed) {
        return utils.fmtsize(speed) + "/s";
      },
      // saves the key value pair in cookies
      setCookie: function(key, value) {
        var exdate = new Date();
        exdate.setDate(exdate.getDate() + 30 * 12);
        var cvalue = escape(JSON.stringify(value)) + "; expires=" + exdate.toUTCString();
        document.cookie = key + "=" + cvalue;
      },
      // gets a value for a key stored in cookies
      getCookie: function(key) {
        var chunks = document.cookie.split(";");
        for (var i = 0; i < chunks.length; i++) {
          var ckey = chunks[i].substr(0, chunks[i].indexOf("=")).replace(/^\s+|\s+$/g,"");
          var cvalue = chunks[i].substr(chunks[i].indexOf("=") + 1);
          if (key == ckey) {
            return JSON.parse(unescape(cvalue));
          }
        }
  
        return null;
      },
      getFileName: function(path) {
        var seed = path.split(/[/\\]/);
        return seed[seed.length - 1];
      },
      uuid: (function() {
        var bt = [];
        for (var i = 0; i <  0x100; ++i) {
          bt.push((i + 0x100).toString(16).substr(1));
        }
        Object.freeze(bt);
  
        return function() {
          var r = rnd16();
          r[6] = (r[6] & 0xf) | 0x40; // Version 4
          r[8] = (r[8] & 0x3f) | 0x80; // Version 4y
          return bt[r[0]] + bt[r[1]] + bt[r[2]] + bt[r[3]] + "-" +
            bt[r[4]] + bt[r[5]] + "-" +
            bt[r[6]] + bt[r[7]] + "-" +
            bt[r[8]] + bt[r[9]] + "-" +
            bt[r[10]] + bt[r[11]] + bt[r[12]] + bt[r[13]] + bt[r[14]] + bt[r[15]];
        };
      })(),
      randStr: function() {
        return utils.uuid();
      },
  
      // maps the array in place to the destination
      // arr, dest (optional): array
      // func: a merge mapping  func, see ctrls/download.js
      mergeMap: function(arr, dest, func) {
        if (!dest) {
          dest = [];
        }
  
        for (var i = 0, e = Math.min(arr.length, dest.length); i < e; ++i) {
          func(arr[i], dest[i]);
        }
  
        // Insert newly created downloads
        while (i < arr.length) {
          dest.push(func(arr[i++]));
        }
  
        // Truncate if necessary.
        dest.length = arr.length;
  
        return dest;
      },
      // get info title from global statistics
      getTitle: function(stats) {
          if(!stats) {
              stats = {};
          }
          return $titlePattern
              .replace('{active}', stats.numActive || '⌛')
              .replace('{waiting}', stats.numWaiting || '⌛')
              .replace('{download_speed}', utils.fmtspeed(stats.downloadSpeed) || '⌛')
              .replace('{upload_speed}', utils.fmtspeed(stats.uploadSpeed) || '⌛')
              .replace('{stopped}', stats.numStopped || '⌛')
              .replace('{name}', $name);
      },
  
      // get download chunks from aria2 bitfield
      getChunksFromHex: function(bitfield, numOfPieces) {
        var chunks =  [], len = 0, numPieces = parseInt(numOfPieces);
        if (!bitfield) return [];
  
        var totalDownloaded = 0;
        if (numPieces > 1) {
          var chunk_ratio = 1 / numPieces;
          var piecesProcessed = 0;
          for (var i = 0; i < bitfield.length; i++) {
            var hex = parseInt(bitfield[i], 16);
            for (var j = 1; j <= 4; j++) {
              var bit = hex & (1 << (4 - j));
              if (bit) totalDownloaded++;
              var prog = !!bit;
              if (len >= 1 && chunks[len - 1].show == prog) {
                chunks[len - 1].ratio += chunk_ratio;
              }
              else {
                chunks.push({
                  ratio: chunk_ratio,
                  show: prog
                });
                len++;
              }
              piecesProcessed++;
              if (piecesProcessed == numPieces)
                return chunks;
            }
          }
        }
        return chunks;
      }
    };
    return utils;
  }]);
  