kernel = {
  cache: {
    requests: 0,
    misses: 0,
    data: {}
  },
  errors: [],

  newClient: function() {
    try {
      return window.XMLHttpRequest ?
              new XMLHttpRequest() :
//              new ActiveXObject("MSXML2.XMLHTTP.5.0");
              new ActiveXObject("Microsoft.XMLHTTP");
    } catch ( e ) {
      this.errors[this.errors.length] = e;
      warn("Failed to create XMLHttpRequest: " + e);
    }
  },

  fetch: function(href, action, proc, opts) {
    action = action || SYNC;
    var async = action != SYNC;
    var cached = this.cache.data[href];
//    var cached = false;
    if ( action.cache ) {
      this.cache.requests++;
    } else {
      href += (href.indexOf('?') > 0 ? "&" : "?") + Math.floor(Math.random() * 100000);
    }
    if ( action.cache && cached ) {
      if ( async ) {
        invoke(action, cached);
      } else {
        return cached;
      }
    } else {
      if ( action.cache ) this.cache.misses++;
      var processor = proc || this.processText;
      if ( ! opts ) opts = {};
      var method = opts.method || "GET";
      var client = this.newClient();
      client.open(method || "GET", href, async);
      if ( async ) {
        client.onreadystatechange = function() {
          if (kernel.debugging) log("status of " + href + " is now " + client.readyState, 'kernel');
          if ( client.readyState == 4 ) {
            if ( invoke && action ) {
              if ( client.status > 299 ) {
                if ( client.status == 410 ) this.sessionTimeout();
                throw "Status of '" + href + "': " + parseInt(client.status);
              }
              invoke(action, processor.call(kernel, client, href, action), client);
            }
          }
        };
      }
      if ( top.fetchlog ) log("---------- fetching " + href);
      if ( opts.type ) client.setRequestHeader('Content-Type',opts.type);
      client.setRequestHeader('Accept','text/xml,*/*');
      client.send(opts.data);
      if ( ! async ) {
        if ( client.status > 299 ) {
          if ( client.status == 410 ) this.sessionTimeout();
          throw "Status of '" + href + "': " + parseInt(client.status);
        }
        return processor.call(this, client, href, action);
      }
    }
  },

  post: function(href, action, proc, data, type) {
    action = action || SYNC;
    var async = action != SYNC;
    var processor = proc || this.processText;
    var client = this.newClient();
    client.open("POST", href, async);
    client.setRequestHeader("Content-Type", type || "text/xml");
    if ( async ) {
      client.onreadystatechange = function() {
        if (kernel.debugging) log("status of " + href + " is now " + client.readyState, 'kernel');
        if ( client.readyState == 4 ) {
          if ( invoke && action ) {
            if ( client.status > 299 ) {
              if ( client.status == 410 ) this.sessionTimeout();
              throw "Status of '" + href + "': " + parseInt(client.status);
            }
            invoke(action, processor.call(kernel, client, href, action), client);
          }
        }
      };
    }
    client.send(data);
    if ( ! async ) {
      if ( client.status > 299 ) {
        if ( client.status == 410 ) this.sessionTimeout();
        throw "Status of '" + href + "': " + parseInt(client.status);
      }
      return processor.call(this, client, href, action);
    }
  },

  sessionTimeout: function() {
    if ( dispatcher.dispatch("session.timeout") ) {
      // dispatched event not handled:
      document.location.reload();
    }
  },

  loadScript: function(href, action) {
    action = action || {
      completionToken: href, // todo: toUpper the stub of the href
      run: function(result) {
        announce(this.completionToken, result);
      }
    };
    var result = this.fetch(href + "?" + (new Date()).getTime(), action, this.processScript);
//    var result = this.fetch(href, action, this.processScript);
    return action != SYNC ? action.completionToken : result;
  },

  processText: function(client, href) {
    var result = client.responseText;
    this.cache.data[href] = result;
    return result;
  },

  processScript: function(client, href) {
    var text = this.processText(client, href);
    try {
      return eval(text);
    } catch ( e ) {
      warn("Eval error #" + this.errors.length + " while processing " + href + ": " + e);
      this.errors[this.errors.length] = {
        error: e,
        href: href
      };
      throw e;
    }
  },

  clear: function(href) {
    delete this.cache.data[href];
  },

  loadRemote: function(name, type, action) {
    var msg = "<?xml version='1.0'?>\n\n";
    msg += "<invoke action='get' name='" + name + "' type='" + type + "' />";
    var client = this.newClient();
    var async = action != SYNC;
    var postProcess = this.processScript;
    client.open("POST", "invoke", async);
    if ( msg ) {
      client.setRequestHeader("Content-Type", "text/xml");
    }
    if ( async ) {
      client.onreadystatechange = function() {
        if ( client.readyState == 4 ) {
          var res = postProcess(client);
          if ( invoke && action ) {
            invoke(action, res);
          }
        }
      };
    }
    client.send(msg);
    if ( !async ) {
      log("about to process " + client);
      return this.processScript(client, "nothing");
//      return postProcess(client);
    }
  },

  call: function(obj, signature, args, action) {
     var msg = "<?xml version='1.0'?>\n\n";
     msg += "<invoke action='call' type='session' name='" + obj.name + "' method='" + signature.method + "'>\n";
     for ( var i = 0 ; i < signature.args.length ; ++i ) {
       var arg = signature.args[i];
       msg += "  <arg name='" + arg.name + "' type='" + arg.type + "'>" + args[i] + "</arg>\n";
     }
     msg += "</invoke>\n";
     var client = this.newClient();
//     var async = action != SYNC;
     var async = false;
//     var postProcess = signature.returnType == "java.lang.String" ? textResponse : detectResponse;
     client.open("POST", "invoke", async);
     if ( msg ) {
       client.setRequestHeader("Content-Type", "text/xml");
     }
/*
     if ( async ) {
       client.onreadystatechange = function() {
         if ( client.readyState == 4 ) {
           var res = postProcess(client);
           if ( invoke && action ) {
             invoke(action, res);
           }
         }
       };
     }
*/
     client.send(msg);
     if ( !async ) {
       call = client;
       log("about to process call " + client);
       return this.processScript(client, "whaddeva");

//       return postProcess(client);
     }
  }


};

SYNC = { run: function() {} };

if ( typeof warn != "function" ) warn = alert;
if ( typeof log != "function" ) log = alert;
if ( typeof doEval != "function" ) doEval = function doEval(s) { return eval(s); };
