var getXMLHttpRequest = function() {
    try {
        req = new XMLHttpRequest();
    } catch(err1) {
        alert(err1);
    }
    return req;
};

var JSONCommunicatorManager = function(servletPath, key) {
  this.l = false;
  this.s = servletPath;
  this.cs = Array();
  this.key = key || '';
  this.cb = function(cb) {
    return function(response) {
      if (response.r.success && response.r.newKey) this.key = response.r.newKey;
      cb(response);
    };
  };
}; JSONCommunicatorManager.prototype = {
  'sendCommand' : function(c, pl, cb, eb) {
    if (this.l) {
      setTimeout(function(o, c, pl, cb, eb) {o.sendCommand(c, pl, cb, eb);}, 10, this, c, pl, cb, eb)
      return;
    }
    this.l = true;
    var fc = Array(); 
    for ( var i=0; i<this.cs.length; i++ ) if ( !this.cs[i].inUse )
      fc.push(i);
    if ( fc.length ) {
      this.cs[fc[0]].sendCommand(c, this.key, pl, this.cb(cb), eb);
      for (var i=1; i<fc.length; i++) {
        var dead = this.cs[i];
        this.cs.splice(i, 1);
        delete dead;
      }
    } else {
      this.cs[this.cs.length] = new Communicator(this.s);
      this.cs[this.cs.length-1].sendCommand(c, this.key, pl, this.cb(cb), eb);
    }
    this.l = false;
  }
}

var Communicator = function (servletPath) {
    this.myServlet = servletPath;
    this.rNew = false;
    this.r = "";
    this.rError = "";
    this.inUse = false;
}; Communicator.prototype = {

    "sendJSONHttpRequest" : function(data, cb) {
        var req = getXMLHttpRequest();
        req.open("POST", this.myServlet, true);
        req.setRequestHeader('Content-Type', 'text/x-json');
        var that = this;
        req.onreadystatechange = function() {
            that.handleCallbacks(req, cb);
        };
        req.send(JSON.stringify(data));
    },

    "handleCallbacks" : function (req, cb, eb) {
        if (req.readyState == 4) {
            if (req.status == 200) {
                //alert("Raw response = " + req['responseText']);
                this.r = JSON.parse(req['responseText']);
                this.rawResponse = req['responseText'];
                this.rError = null;
                this.rNew = true;
                cb(this);
                this.inUse = false;
                return;
            } else {
                eb(this);
            }
        }
        this.saveError(req['responseText']);
        this.inUse = false;
        return;
    },
    
    "saveError" : function (err) {
        this.r = null;
        this.rError = err;
        this.rNew = true;
    },

    "sendCommand" : function (command, auth, payload, callback) {
        this.inUse = true;
        this.rNew = false;
        this.r = null;
        this.rError = null;
        this.command = command
        var data = {'command': command, 'payload': payload, 'auth': auth};
        this.sendJSONHttpRequest(data, callback);
    },

    "onProcessCommand" : function (command, callback) {
        that = this;
        return function(response) {
            that.rError = null;
            that.r = JSON.parse(response);
            that.rNew = true;
            callback(that);
            return false;
        };
    },
    
    "retrieveCommandResult" : function () {
        if( !this.rError && this.rNew ) return this.r;
        else return false;
    },

    "retrieveError" : function () {
        if( !this.r && this.rNew ) return this.rError;
        else return false;
    }
};

