/**
 * XMLHttpRequest Wrapper
 * @category    HTML
 * @package     AJAX
 * @author      Joshua Eichorn <josh@bluga.net>
 * @copyright   2005 Joshua Eichorn
 * @license     http://www.opensource.org/licenses/lgpl-license.php  LGPL
 */
function HTML_AJAX_HttpClient() { }
HTML_AJAX_HttpClient.prototype = {
    // request object
    request: null,

    // timeout id
    _timeoutId: null,

    callbackComplete: true,

    // has this request been aborted
    aborted: false,
    
    // method to initialize an xmlhttpclient
    init:function() 
    {
        try {
            // Mozilla / Safari
            //this.xmlhttp = new HTML_AJAX_IframeXHR(); //uncomment these two lines to test iframe
            //return;
            this.xmlhttp = new XMLHttpRequest();
        } catch (e) {
            // IE
            var XMLHTTP_IDS = new Array(
            'MSXML2.XMLHTTP.5.0',
            'MSXML2.XMLHTTP.4.0',
            'MSXML2.XMLHTTP.3.0',
            'MSXML2.XMLHTTP',
            'Microsoft.XMLHTTP' );
            var success = false;
            for (var i=0;i < XMLHTTP_IDS.length && !success; i++) {
                try {
                    this.xmlhttp = new ActiveXObject(XMLHTTP_IDS[i]);
                    success = true;
                } catch (e) {}
            }
            if (!success) {
                try{
                    this.xmlhttp = new HTML_AJAX_IframeXHR();
                    this.request.iframe = true;
                } catch(e) {
                    throw new Error('Unable to create XMLHttpRequest.');
                }
            }
        }
    },

    // check if there is a call in progress
    callInProgress: function() 
    {
        switch ( this.xmlhttp.readyState ) {
            case 1:
            case 2:
            case 3:
                return true;
            break;
            default:
                return false;
            break;
        }
    },

    // make the request defined in the request object
    makeRequest: function() 
    {
        if (!this.xmlhttp) {
            this.init();
        }

        try {
            if (this.request.Open) {
                this.request.Open();
            }
            else if (HTML_AJAX.Open) {
                HTML_AJAX.Open(this.request);
            }

            if (this.request.multipart) {
                if (document.all) {
                    this.iframe = true;
                } else {
                    this.xmlhttp.multipart = true;
                }
            }
    
            // set onreadystatechange here since it will be reset after a completed call in Mozilla
            var self = this;
            this.xmlhttp.open(this.request.requestType,this.request.completeUrl(),this.request.isAsync);
            if (this.request.customHeaders) {
                for (var i in this.request.customHeaders) {
                    this.xmlhttp.setRequestHeader(i, this.request.customHeaders[i]);
                }
            }
            if (this.request.customHeaders && !this.request.customHeaders['Content-Type']) {
                var content = this.request.getContentType();
                //opera is stupid for anything but plain text or xml!!
                if(window.opera && content != 'application/xml')
                {
                    this.xmlhttp.setRequestHeader('Content-Type','text/plain; charset=utf-8');
                    this.xmlhttp.setRequestHeader('x-Content-Type', content + '; charset=utf-8');
                }
                else
                {
                    this.xmlhttp.setRequestHeader('Content-Type', content +  '; charset=utf-8');
                }
            }

            if (this.request.isAsync) {
                if (this.request.callback) {
                    this.callbackComplete = false;
                }
                this.xmlhttp.onreadystatechange = function() { self._readyStateChangeCallback(); }
            } else {
                this.xmlhttp.onreadystatechange = function() {}
            }
            var payload = this.request.getSerializedPayload();
            if (payload) {
                this.xmlhttp.setRequestHeader('Content-Length', payload.length);
            }
            this.xmlhttp.send(payload);

            if (!this.request.isAsync) {
                if ( this.xmlhttp.status == 200 ) {
                    HTML_AJAX.requestComplete(this.request);
                    if (this.request.Load) {
                        this.request.Load();
                    } else if (HTML_AJAX.Load) {
                        HTML_AJAX.Load(this.request);
                    }
                        
                    return this._decodeResponse();
                } else {
                    var e = new Error('['+this.xmlhttp.status +'] '+this.xmlhttp.statusText);
                    e.headers = this.xmlhttp.getAllResponseHeaders();
                    this._handleError(e);
                }
            }
            else {
                // setup timeout
                var self = this;
                this._timeoutId = window.setTimeout(function() { self.abort(true); },this.request.timeout);
            }
        } catch (e) {
            this._handleError(e);
        }
    },
    
    // abort an inprogress request
    abort: function (automatic) 
    {
        if (this.callInProgress()) {
            this.aborted = true;
            this.xmlhttp.abort();

            if (automatic) {
                HTML_AJAX.requestComplete(this.request);
                this._handleError(new Error('Request Timed Out: time out was '+this.request.timeout+'ms'));
            }
        }
    },

    // internal method used to handle ready state changes
    _readyStateChangeCallback:function() 
    {
        try {
            switch(this.xmlhttp.readyState) {
                // XMLHTTPRequest.open() has just been called
                case 1:
                    break;
                // XMLHTTPRequest.send() has just been called
                case 2:
                    if (this.request.Send) {
                        this.request.Send();
                    } else if (HTML_AJAX.Send) {
                        HTML_AJAX.Send(this.request);
                    }
                    break;
                // Fetching response from server in progress
                case 3:
                    if (this.request.Progress) {
                        this.request.Progress();
                    } else if (HTML_AJAX.Progress ) {
                        HTML_AJAX.Progress(this.request);
                    }
                break;
                // Download complete
                case 4:
                    window.clearTimeout(this._timeoutId);
                    if (this.aborted) {
                        if (this.request.Load) {
                            this.request.Load();
                        } else if (HTML_AJAX.Load) {
                            HTML_AJAX.Load(this.request);
                        }
                    }
                    else if (this.xmlhttp.status == 200) {
                        if (this.request.Load) {
                            this.request.Load();
                        } else if (HTML_AJAX.Load ) {
                            HTML_AJAX.Load(this.request);
                        }

                        var response = this._decodeResponse();

                        if (this.request.callback) {
                            this.request.callback(response);
                            this.callbackComplete = true;
                        }
                    }
                    else {
                        var e = new Error('HTTP Error Making Request: ['+this.xmlhttp.status+'] '+this.xmlhttp.statusText);
                        this._handleError(e);
                    }
                    HTML_AJAX.requestComplete(this.request);
                break;
            }
        } catch (e) {
                this._handleError(e);
        }
    },

    // decode response as needed
    _decodeResponse: function() {
        //try for x-Content-Type first
        var content = null;
        try {
            content = this.xmlhttp.getResponseHeader('X-Content-Type');
        } catch(e) {}
        if(!content || content == null)
        {
            content = this.xmlhttp.getResponseHeader('Content-Type');
        }
        //strip anything after ;
        if(content.indexOf(';') != -1)
        {
            content = content.substring(0, content.indexOf(';'));
        }
        // hook for xml, it doesn't need to be unserialized
        if(content == 'application/xml')
        {
            return this.xmlhttp.responseXML;
        }
        var unserializer = HTML_AJAX.serializerForEncoding(content);
        //alert(this.xmlhttp.getAllResponseHeaders()); // some sort of debug hook is needed here
        return unserializer.unserialize(this.xmlhttp.responseText);
    },

    // handle sending an error where it needs to go
    _handleError: function(e) 
    {
        HTML_AJAX.requestComplete(this.request,e);
        if (this.request.onError) {
            this.request.onError(e);
        } else if (HTML_AJAX.onError) {
            HTML_AJAX.onError(e,this.request);
        }
        else {
            throw e;
        }
    }
}
