// TODO: add asynchronous loading


function HashedUrl(type, id, url, md5hash) {
   this.url = url;
   this.md5Hash = md5hash;
   this.type = type;
   if (type == "img") {
       this.isBinary = true;
   } else {
       this.isBinary = false;
   }
   this.remoteData = null;
   this.httpCode = "";
   this.status = 0;
   this.id = id;
   this.obj = null;
} 

HashedUrl.translateToBinaryString = function(text) {
   var out; 
   out='';
   for(i=0;i<text.length;i++){
       //*bugfix* by Marcus Granado 2006 [http://mgran.blogspot.com] adapted by Thomas Belot
       out+=String.fromCharCode(text.charCodeAt(i) & 0xff);
   }
   return out;
}

/**
 * Try to load the data using XHR and set the dataScheme
 * if the result is available.
 */
HashedUrl.prototype.fetchData = function () {
  var req = new XMLHttpRequest();
  req.open('GET', this.url, false);
  if (this.isBinary) {
     req.overrideMimeType('text/plain; charset=x-user-defined');
  }

  req.send(null); 
  this.httpCode = req.status;

  if (this.httpCode != 200) return;
  var dataUrl = 'data:'+req.getResponseHeader('content-type')+';base64,';
  this.remoteDataHash = hex_md5(req.responseText);
  this.hashMatched = (this.remoteDataHash == this.md5Hash);
  if (this.isBinary) {
     var stream = HashedUrl.translateToBinaryString(req.responseText);
     this.dataScheme = dataUrl + window.btoa(stream);
  } else {
     this.dataScheme = dataUrl + window.btoa(req.responseText);
  }
} 


HashedUrl.prototype.getDOMObject = function() {
   if (this.httpCode != 200) {
      return null;
   }
   this.obj = document.createElement(this.type);
   this.obj.src = this.dataScheme;
   this.obj.md5hash = this.remoteDataHash;
   this.obj.hashMatched = (this.remoteDataHash == this.md5Hash);
   this.obj.setAttribute('id', this.id);

   return this.obj;  
}

HashedUrl.prototype.copyAttributes = function(elem) {
   if (this.obj == null) {
      return;
   }
   var attrs = elem.attributes;
   for (var i=0; i<attrs.length; i++) {
      var name = attrs[i].nodeName;
      if (name.indexOf('hash::') == -1 
	  && name.charAt(0) != '_') {
         this.obj.setAttribute(attrs[i].nodeName, attrs[i].nodeValue);
      }
   }
}

HashedUrl.prototype.toString = function() {
   var msg = "Link type: " + this.type + " URL: " + this.url + '\n'
             + "expected hash: " + this.md5Hash;
   return msg;
}

function fixupHashedResources() {
   var links = document.getElementsByTagName("hashedElement");

   for (var i=0; i<links.length; i++) {
      var hashed_url = new HashedUrl(links[i].getAttribute('hash::type'),
                                     links[i].getAttribute('hash::id'),
                                     links[i].getAttribute('hash::src'),
                                     links[i].getAttribute('hash::md5'));


      hashed_url.fetchData();
      if (hashed_url.hashMatched) {
         var obj = hashed_url.getDOMObject(); 

         // TODO: copy all attributes (except id/src/md5/type) from hashedElement
         // to the DOM Object obj.
         hashed_url.copyAttributes(links[i]);
         links[i].appendChild(obj);
         
      } else {
         var msg = document.createElement('p');
         var text = "<p><font color='red'>Warning: resource not loaded. Hash mismatch for \n" + hashed_url.toString();
         msg.innerHTML = text;
         links[i].appendChild(msg);
      }
   }
}
