// O3Ajax class
function O3Ajax()
{
  var oXML                = null;
  var fncInternalCallback = null;
  var fncCallback         = null;
  var sContainer          = null;
  var aPostData           = null;
  var sPostData           = '';
  var aHandlers           = Array();

  // Constructor
  function O3Ajax()
  {
    // Build the XML requester
    if(window.XMLHttpRequest) // Mozilla, Safari, etc.
      oXML = new XMLHttpRequest();
    else if(window.ActiveXObject) // IE
    {
      try
      {
        oXML = new ActiveXObject("Msxml2.XMLHTTP");
      }
      catch(e)
      {
        try
        {
          oXML = new ActiveXObject("Microsoft.XMLHTTP");
        }
        catch(e)
        {
        }
      }
    }
    else
      return false;

    if(oXML == null)
      return false;

    // Attach callback
    oXML.onreadystatechange = callback;

    // Build post data array
    aPostData = new Array();

    // All ok
    return true;
  }

  function addPostData(aAdd)
  {
    // Port them over (concat doesn't work for associative)
    for(sKey in aAdd)
      aPostData[sKey] = aAdd[sKey];
  }

  function addRawPostData(sPost)
  {
    // Add it to the end of the existing post data
    sPostData += '&' + sPost;
  }

  function addPostValue(sKey, sValue)
  {
    // Add a single post value
    aPostData[sKey] = sValue;
  }

  function clearPostData()
  {
    // Clear post data
    aPostData = new Array();
  }

  function buildPostData()
  {
    // Loop post array & build string
    var sPost = '';
    for(var sKey in aPostData)
    {
      // Skip prototypes
      if(typeof aPostData[sKey] == 'function')
        continue;

      // Handle arrays
      if(O3Global.Types.isArray(aPostData[sKey]))
      {
        for(var sSubKey in aPostData[sKey])
        {
          // Skip prototypes
          if(typeof aPostData[sKey][sSubKey] == 'function')
            continue;

          sPost += O3Global.Types.encodeURL(sKey) + '[' + O3Global.Types.encodeURL(sSubKey) + ']=' + O3Global.Types.encodeURL(aPostData[sKey][sSubKey]) + '&';
        }
      }
      else
        sPost += O3Global.Types.encodeURL(sKey) + '=' + O3Global.Types.encodeURL(aPostData[sKey]) + '&';
    }

    // Add raw string
    sPost += sPostData;

    return sPost;
  }

  function setContainer(sId)
  {
    sContainer = sId;
  }

  function setCallback(fncFinalCallback)
  {
    fncCallback = fncFinalCallback;
  }

  // First callback
  function callback()
  {
    // Check status
    if(oXML.readyState == 4 && (oXML.status == 200 || window.location.href.indexOf("http") == -1))
    {
      // Decrement loading (if available)
      if(window.O3AjaxHandler)
        O3AjaxHandler.decrementPending();

      // Pass on to actual callback
      if(fncInternalCallback)
        fncInternalCallback();
    }
  }

  // Page loader callback
  function callbackPage()
  {
    // Check for XML (login timeout)
    if(oXML.responseXML && oXML.responseXML.documentElement && oXML.responseXML.documentElement.attributes.getNamedItem('code'))
    {
      callbackXML();
      return;
    }

    // Check for a container ID
    if(sContainer)
    {
      var hContainer = O3Global.DOM.getById(sContainer);
      if(hContainer)
      {
        hContainer.innerHTML = oXML.responseText;
	      hContainer.disabled  = '';
	    }
    }

    // Check for callback
    if(fncCallback)
      fncCallback(oXML.responseText);
  }

  // XML loader callback
  function callbackXML()
  {
    // Extract XML data
    var oResponse = oXML.responseXML;
    if(oResponse)
      oResponse = oResponse.documentElement;

    var iCode = AjaxCodeConfig.OK;
    if(!oResponse)
    {
      iCode = AjaxCodeConfig.STRUCTURAL_NO_XML; // Structural XML error: no XML data
      alert(oXML.responseText);
    }
    else
    {
      // Extract code
      var oCode = oResponse.attributes.getNamedItem('code');
      if(!oCode)
        iCode = AjaxCodeConfig.STRUCTURAL_NO_CODE; // Structural XML error: no code
      else
      {
        iCode = oCode.value;

        // Get and update time if possible
        var oTime = oResponse.attributes.getNamedItem('time');
        if(oTime)
          Ozone.setServerTime(oTime.value);
      }
    }

    // Pre-process output for this code
    O3AjaxHandler.preHandle(iCode, oResponse);

    // Check for handler
    if(typeof aHandlers[iCode] == 'function')
    {
      if(!aHandlers[iCode](oResponse))
        O3AjaxHandler.handle(iCode, oResponse);
    }
    else
      O3AjaxHandler.handle(iCode, oResponse);
  }

  // Event loader callback
  function callbackEvent()
  {
    // Extract XML data
    var oResponse = oXML.responseXML;
    if(oResponse)
      oResponse = oResponse.documentElement;
    if(!oResponse)
    {
      alert(oXML.responseText);
      return;
    }

    // Check for occurrence
    var oOccurred = oResponse.attributes.getNamedItem('occurred');
    var bOccurred = false;
    if(!oOccurred || oOccurred.value == 'error')
    {
      alert('Error; could not start message pump. Live updates are disabled!');
      return;
    }

    bOccurred = oOccurred.value == 'yes';

    // Read command if an event occurred
    var sCommand = '';
    if(bOccurred)
    {
      bOccurred    = false;
      var oCommand = oResponse.getElementsByTagName('command');
      if(oCommand && oCommand[0] && oCommand[0].firstChild)
      {
        bOccurred = true;
        sCommand  = oCommand[0].firstChild.data;
      }
    }

    // Call callback with this info
    if(fncCallback)
      fncCallback(bOccurred, sCommand);
  }

  function processRequest(sModule, sAction)
  {
    // Build url with anti-cache
    var sUrl = Ozone.BASE_LINK + sModule + '&action=' + sAction + '&O3Request=' + (new Date).getTime();

    // Build post data
    var sPost = buildPostData();

    // Send request
    oXML.open("POST", sUrl, true);
    oXML.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    oXML.send(sPost);
	
    // Increment loading
    O3AjaxHandler.incrementPending();
  }

  function requestPage(sModule, sAction)
  {
    // Set the internal callback
    fncInternalCallback = callbackPage;

    // Process this request
    processRequest(sModule, sAction);
  }

  function requestXML(sModule, sAction)
  {
    // Set the internal callback
    fncInternalCallback = callbackXML;

    // Process this request
    processRequest(sModule, sAction);
  }

  function requestEvent(sServer)
  {
    // Set the internal callback
    fncInternalCallback = callbackEvent;

    // Send raw request
    oXML.open("POST", "http://" + sServer + "/event", true);
    oXML.send(null);
  }

  function setHandler(iCode, fncHandler)
  {
    // Add or override handler
    aHandlers[iCode] = fncHandler;
  }

  // Call constructor
  if(!O3Ajax())
    alert("Could not create O3Ajax. Your browser does not support XMLHttpRequest.");

  // Bind public functions
  this.addPostData     = addPostData;
  this.addRawPostData  = addRawPostData;
  this.addPostValue    = addPostValue;
  this.clearPostData   = clearPostData;
  this.setContainer    = setContainer;
  this.setCallback     = setCallback;
  this.requestPage     = requestPage;
  this.requestXML      = requestXML;
  this.requestEvent    = requestEvent;
  this.setHandler      = setHandler;
}