/**
 * THttp allow remote HTTP requests and
 * server response handling
 */
var THttp = function () {
  this.id = null;
  this.xhr = null;
  this.open = false;
  this.aborted = false;
  this.error = false;
  this.params = [];
  this.lastError = '';
  //this.timeout = 1500;    // 15 seconds
};

THttp.inherits(TEventDispatcher);
THttp.MSXML = ["MSXML2.XMLHTTP.6.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
THttp.NULLFunction = null;

THttp.prototype.free = function () {
  this.abort();
  TEventDispatcher.prototype.free.call(this);
};

/**
 * Clear parameters
 */
THttp.prototype.clear = function() {
  this.params = [];
}

/**
 * Return formatted param string
 */
THttp.prototype.getParams = function() {
  return this.params.join('&');
}

/**
 * Add parameters for the next request
 * @param {String} name: param name
 * @param {String} value: param value
 */
THttp.prototype.addParam = function(name,value) {
  this.params.push(name+'='+value);
}

/**
 * Add multiple params at once
 * @param {String} params: a=1&b=2&...
 */
THttp.prototype.addParams = function(params) {
  if (params) {
    params = params.split('&');
    for (var i = 0; i < params.length; i++) {
      this.params.push(params[i]);
    }
  }
}

/**
 * Return an string representation of the
 * last xhr response
 */
THttp.prototype.toString = function() {
  return this.xhr ? this.xhr.responseText : '';
}

/**
 * Return an xml document representation of the
 * last xhr response
 */
THttp.prototype.toXML = function() {
  return this.xhr ? this.xhr.responseXML : null;
}

/**
 * Treats responseText as javascript and evaluate it
 * @todo: prevent unsafe scripts execution
 */
THttp.prototype.toScript = function() {
  //try {
  //  return this.xhr ? eval("(" + this.xhr.responseXML + ")") : null;
  //} catch (e) { }
}

/**
 * Return a response header value
 * @param {String} hdr: header
 */
THttp.prototype.getResponseHeader = function(hdr) {
  if (this.xhr && this.xhr.readyState == 4) {
    return this.xhr.getResponseHeader(hdr);
  }
}

/**
 * Create an XHR object
 * @return {Object}
 * @private
 */
THttp.prototype.getXhr = function() {
  if (this.xhr) { return this.xhr; }
  if (typeof XMLHttpRequest != 'undefined') {    // XHR
    return new XMLHttpRequest;
  } else {                                       // MSXML
    if (typeof THttp.MSXML == "string") {
      return new ActiveXObject(THttp.MSXML);
    } else {
      for(var i=0; i < THttp.MSXML.length; i++){
        try {
          var xhr = new ActiveXObject(THttp.MSXML[i]);
          THttp.MSXML = THttp.MSXML[i];
          THttp.NULLFunction = function() { };
          return xhr;
        } catch(e){}
      }
    }
  }
  // No XHR available
  throw new Error("Could not create XML HTTP Request Object.\nUpdate your browser");
}

/**
 * Handle request ready state and call the correct
 * callback function when finish
 * @todo add progress callback
 */
THttp.prototype.readyStateChange = function() {
  if (!this.open || !this.xhr) { return; }
  this.dispatch("readystatechange");
  if (this.xhr.readyState == 4) {
    this.open = false;
    var code = this.xhr.status;
    if (code == 200) {
      this.dispatch("complete");
      this.dispatch("success");
    } else {
      this.error = true;
      this.lastError = "[" + code + "] " + this.xhr.statusText;
      this.dispatch("complete");
      this.dispatch("error", this.xhr.status, this.xhr.statusText);
    }
    this.setReady();
  }
}

/**
 * Abort the current http connection
 */
THttp.prototype.abort = function() {
  if (this.open) {
    this.open = false;
    this.aborted = true;
    this.xhr.abort();
    this.dispatch("complete");
    this.dispatch("abort");
    this.setReady();
  }
}

THttp.prototype.setReady = function() {
  this.xhr.onreadystatechange = THttp.NULLFunction;
  this.xhr = null;
  this.dispatch("ready");
}

/**
 * Static method to quickly send a request to the server
 * @param {String} url: url to request
 * @param {Function} callback: (Optional) callback function
 * @param {String} method: (Optional) reuest method
 */
THttp.send = function (url, callback, method, params) {
  var http = new THttp;
  http.addEventListener("complete", callback);
  http.addEventListener("ready", http.free, http);
  http.send(url, method, params);
}

/**
 * Send a POST or GET request to the server
 * @param {String} url: url to request
 * @param {String} method: (Optional) reuest method
 */
THttp.prototype.send = function (url, method, params) {
  this.abort();
  var This = this;
  this.xhr = this.getXhr();
  this.open = true;
  this.error = false;
  this.aborted = false;
  this.addParams(params);

  params = this.getParams();
  method = method && method.toUpperCase() == 'POST' ? 'POST' : 'GET';
  if (method == 'GET' && params != '') {
    url += url.indexOf('?') ? '&'+params : '?'+params;
    params = '';
  }
  this.dispatch("open");
  try {
    this.xhr.onreadystatechange = function() { This.readyStateChange.call(This); }
    this.xhr.open(method,url,true);
    this.xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded;charset=iso-8859-1");
    if (method == 'POST') {
      this.xhr.setRequestHeader("Content-Length", params.length);
    }
    this.xhr.send(params);
  } catch (e) {
    this.open = false;
    this.error = true;
    this.lastError = "Error while opening connection";
    this.dispatch("complete");
    this.dispatch("error", 0, "Error while opening connection");
  }
}

/**
 * Do a GET request
 */
THttp.prototype.get = function (url) {
  this.send(url, 'GET');
}

/**
 * Do a POST request
 */
THttp.prototype.post = function (url, params) {
  this.send(url, 'POST', params);
}
