//{import path="mohawk.kernel.Forms" type="js" /}
//{import path="mohawk.kernel.XML" type="js" /}

var Ajax = new Class({
    XHR: null,
    caller: null,
    url: '',
    method: '',
    overwrite_method: true,
    data: '',
    charset: 'UTF-8',
    sync: true,
    xml: '',
    text: '',
    type: '',
    data: {},

    __construct: function (url, method) {
        self.url = url;
        self.method = method || Ajax.METHOD_GET;
        self.type = Ajax.TYPE_XML;
        
        if (window.XMLHttpRequest) {
            self.XHR = new XMLHttpRequest();
        } else if (window.ActiveXObject) {
            self.XHR = new ActiveXObject('Microsoft.XMLHTTP');
        } else {
            self.error('XMLHttpRequest is disabled in your browser');
            return false;
        }
    },

    responseHandler: function () {

    },

    errorHandler: function () {

    },

    send: function (data) {
        if (typeof self.XHR == 'object') {
            self.XHR.onreadystatechange = function () {
                Ajax.procedure(self);
            };
            
            self.method = self.method.toUpperCase()
            
            // initialize
            switch (self.method) {
            default:
            case Ajax.METHOD_GET:
                var url_with_data = self.url + (self.url.match(new RegExp('\\?')) ? '&' : '?') + Ajax.prepare(data);
                self.XHR.open(Ajax.METHOD_GET, url_with_data, self.sync);
                var get = true;
                break;

            case Ajax.METHOD_POST:
            case Ajax.METHOD_PUT:
            case Ajax.METHOD_DELETE:
            case Ajax.METHOD_OPTIONS:
            case Ajax.METHOD_HEAD:
                self.XHR.open(self.overwrite_method ? Ajax.METHOD_POST : self.method, self.url, self.sync);
                var get = false;
                break;
            }
            
            // set headers
            self.XHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=' + self.charset);
            self.XHR.setRequestHeader('X-Request-Method', self.method);
            self.XHR.setRequestHeader('Accept', self.type);
            
            // send query
            if (get) {
                if (window.XMLHttpRequest) {
                    self.XHR.send(null);
                } else {
                    self.XHR.send();
                }
            } else {
                self.XHR.send(Ajax.prepare(data));
            }
        } else {
            self.error('XMLHttpRequest is not initialized');
        }
    },

    error: function (message) {
        debug(message);
    }
});

Ajax.HTTP_OK = 200;
Ajax.HTTP_NOT_FOUND = 404;

Ajax.STATE_UNINITIALIZED = 0;
Ajax.STATE_OPEN          = 1;
Ajax.STATE_SENT          = 2;
Ajax.STATE_RECEIVING     = 3;
Ajax.STATE_LOADED        = 4;

Ajax.METHOD_GET     = 'GET';
Ajax.METHOD_POST    = 'POST';
Ajax.METHOD_PUT     = 'PUT';
Ajax.METHOD_DELETE  = 'DELETE';
Ajax.METHOD_HEAD    = 'HEAD';
Ajax.METHOD_OPTIONS = 'OPTIONS';

Ajax.TYPE_XML  = 'text/xml';
Ajax.TYPE_JSON = 'text/json';
Ajax.TYPE_TEXT = 'text/text';

Ajax.escape = function (str) {
    if (typeof str == 'string') {
        if (OPERA) {
            // to avoid spare linebreaks in opera
            str = str.replace(new RegExp('\r', 'g'), '');
        }
        str = str.replace(new RegExp('\n', 'g'), '%' + '0D');
        str = str.replace(new RegExp('\r', 'g'), '%' + '0A');
        str = str.replace(new RegExp('&', 'g'), '%' + '26');
        str = str.replace(new RegExp('#', 'g'), '%' + '23');
        str = str.replace(new RegExp('\\+', 'g'), '%' + '2B');
    }
    return str;
}

Ajax.prepare = function (data, prefix) {
    var query = '';
    switch (typeof data) {
    case 'object':
        if (data instanceof Array) {
            for (var i = 0; i < data.length; i ++) {
                if (typeof Array.prototype[i] != 'undefined') {
                    continue;
                }
                query += (query.length ? '&' : '');
                switch (typeof data[i]) {
                case 'object':
                    query += Ajax.prepare(data[i], prefix ? prefix + '[' + i + ']' : i);
                    break;
    
                case 'string':
                case 'number':
                case 'boolean':
                    query += (prefix ? prefix + (!prefix.match(new RegExp('\\[\\]$')) ? '[' + i + ']' : '') + '=' : i + '=') + Ajax.prepare(data[i], prefix ? prefix + '[' + i + ']' : i);
                    break;
                }
            }
        } else {
            for (var i in data) {
                if (typeof Object.prototype[i] != 'undefined') {
                    continue;
                }
                query += (query.length ? '&' : '');
                switch (typeof data[i]) {
                case 'object':
                    query += Ajax.prepare(data[i], prefix ? prefix + (!prefix.match(new RegExp('\\[\\]$')) ? '[' + i + ']' : '') : i);
                    break;
    
                case 'string':
                case 'number':
                case 'boolean':
                    query += (prefix ? prefix + (!prefix.match(new RegExp('\\[\\]$')) ? '[' + i + ']' : '') + '=' : i + '=') + Ajax.prepare(data[i], prefix ? prefix + '[' + i + ']' : i);
                    break;
                }
            }
        }
        break;

    case 'string':
    case 'number':
    case 'boolean':
        query = prefix ? Ajax.escape(data) : data;
        break;
    }
    return query;
}

Ajax.procedure = function (Request) {
    switch (Request.XHR.readyState) {

    case Ajax.STATE_UNINITIALIZED:
        break;

    case Ajax.STATE_LOADED:
        if (Request.XHR.status == Ajax.HTTP_OK) {
            if (Request.XHR.responseXML) {
                Request.xml = Request.XHR.responseXML;
            } else {
                // may be some error here
            }
        } else {
            // some error here
        }
        Request.text = Request.XHR.responseText;
        switch (Request.type) {
        case Ajax.TYPE_JSON:
            eval('Request.data = ' + Request.text); 
            break;
        case Ajax.TYPE_XML:
            Request.data = XML.toObject(Request.xml);
            break;
        }
        Request.responseHandler(Request);
        break;
        
    default:
        // something is wrong here
    }
}

