var util = {
  
  debug: false,
  browserisms: '',
  
  inherits: function(ctor, superCtor) {
    ctor.super_ = superCtor;
    ctor.prototype = Object.create(superCtor.prototype, {
      constructor: {
        value: ctor,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
  },
  extend: function(dest, source) {
    for(var key in source) {
      if(source.hasOwnProperty(key)) {
        dest[key] = source[key];
      }
    }
    return dest;
  },
  pack: BinaryPack.pack,
  unpack: BinaryPack.unpack,
  randomPort: function() {
    return Math.round(Math.random() * 60535) + 5000;
  },
  
  log: function () {
    if (util.debug) {
      var copy = Array.prototype.slice.call(arguments);
      copy.unshift('PeerKit: ');
      console.log.apply(console, copy);
      if (window.log) window.log(copy);
    }
  },

  setZeroTimeout: (function(global) {
    var timeouts = [];
    var messageName = 'zero-timeout-message';

    // Like setTimeout, but only takes a function argument.	 There's
    // no time argument (always zero) and no arguments (you have to
    // use a closure).
    function setZeroTimeoutPostMessage(fn) {
      timeouts.push(fn);
      global.postMessage(messageName, '*');
    }		

    function handleMessage(event) {
      if (event.source == global && event.data == messageName) {
        if (event.stopPropagation) {
          event.stopPropagation();
        }
        if (timeouts.length) {
          timeouts.shift()();
        }
      }
    }
    if (global.addEventListener) {
      global.addEventListener('message', handleMessage, true);
    } else if (global.attachEvent) {
      global.attachEvent('onmessage', handleMessage);
    }
    return setZeroTimeoutPostMessage;
  }(this)),
  
  blobToArrayBuffer: function(blob, cb){
    var fr = new FileReader();
    fr.onload = function(evt) {
      cb(evt.target.result);
    };
    fr.readAsArrayBuffer(blob);
  },
  blobToBinaryString: function(blob, cb){
    var fr = new FileReader();
    fr.onload = function(evt) {
      cb(evt.target.result);
    };
    fr.readAsBinaryString(blob);
  },
  binaryStringToArrayBuffer: function(binary) {
    var byteArray = new Uint8Array(binary.length);
    for (var i = 0; i < binary.length; i++) {
      byteArray[i] = binary.charCodeAt(i) & 0xff;
    }
    return byteArray.buffer;
  },
  hash: function (uint8Arr) {
    //return buffer.byteLength;
    return murmurhash3_32_gc(uint8Arr);
  },
  randomToken: function () {
    return Math.random().toString(36).substr(2);
  },
  compareString: function(urlA, urlB) {
    return sanitizeUrl(urlA) == sanitizeUrl(urlB);
  },
  sanitizeUrl: function(url){
    return url.toLowerCase().replace(/^https?:\/\//,'');
  },
  xhrFile: function(url, cb) {
    var http = new XMLHttpRequest();
    http.open('get', url, true);
    http.responseType = "blob"; 
    http.onload = function(e) {
      var d = new Date(http.getResponseHeader('expires'));
      cb(http.status, http.response, d.getTime(), url);
    }
    http.send(null);
  },
  isSupportedBrowser: function() {
    var N = navigator.appName, ua= navigator.userAgent, tem;
    var M = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
    if (M && (tem= ua.match(/version\/([\.\d]+)/i))!= null) M[2]= tem[1];
    M = M? [M[1], M[2]]: [N, navigator.appVersion, '-?'];
    var versions = M[1].split('.');
    var browserInfo = {name: M[0] , versions: versions};
    if (browserInfo.name === 'Chrome') {
      var majorVersion = parseInt(browserInfo.versions[0], 10);
      if (majorVersion > 26) {
        return true;
      } else if ((majorVersion === 26) && (parseInt(browserInfo.versions[2], 10) > 1403)) {
        return true;
      } else{
        return false;
      }

    } else{
      return false;
    }
  }
};

//takes a UInt8Array and hashes it
function murmurhash3_32_gc(key, seed) {

  var remainder, bytes, h1, h1b, c1, c1b, c2, c2b, k1, i;

  remainder = key.length & 3; // key.length % 4
  bytes = key.length - remainder;
  h1 = seed;
  c1 = 0xcc9e2d51;
  c2 = 0x1b873593;
  i = 0;

  while (i < bytes) {
      k1 = 
        (key[i]) |
        (key[++i] << 8) |
        (key[++i] << 16) |
        (key[++i] << 24);
    ++i;

    k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff;
    k1 = (k1 << 15) | (k1 >>> 17);
    k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff;

    h1 ^= k1;
        h1 = (h1 << 13) | (h1 >>> 19);
    h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff;
    h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16));
  }

  k1 = 0;

  switch (remainder) {
    case 3: k1 ^= key[i + 2] << 16;
    case 2: k1 ^= key[i + 1] << 8;
    case 1: k1 ^= key[i];

    k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
    k1 = (k1 << 15) | (k1 >>> 17);
    k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
    h1 ^= k1;
  }

  h1 ^= key.length;

  h1 ^= h1 >>> 16;
  h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
  h1 ^= h1 >>> 13;
  h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff;
  h1 ^= h1 >>> 16;

  return h1 >>> 0;
}
