// where to hook this in

(function ($) {
    // attach an eventlistener to datasource
    var uniqid = (function () {
        var id = 0; return function () { return id++; };
    })();


    function observe(what, data, handler) {
        if (!handler) {
            // data is the handler
            $(this).bind(what, data);
            return this;
        }
        $(this).bind(what, data, handler);
    }
    function unobserve(what) {
        $(this).unbind(what);
        return this;
    }
    function trigger(what, data) {
        if (data) {
            $(this).triggerHandler(what, data);
        } else {
            $(this).triggerHandler(what);
        }
        return this;
    }
    // send have the same options as 
    // $.ajax
    // except 
    //      data    : if set will be used as data to events
    //      
    //
    function send(options) {
        
        if (this.requestQueue.length == 0) {
            return this;
        }
        var settings = {};
        var queueSize = this.requestQueue.length;
        var numRequested = queueSize;
        switch (typeof (options)) {
            case "object":
                $.extend(settings, options);
                if (settings.max) {
                    numRequested = parseInt(settings.max);
                    if (numRequested == 0 || isNaN(numRequested))
                        return this;
                    delete settings["max"];
                }
                break;
            case "number":
                numRequested = parseInt(options);
                if (numRequested == 0 || isNaN(numRequested))
                    return this;
                break;
            case "string":
                numRequested = parseInt(options);
                if (numRequested == 0 || isNaN(numRequested))
                    return this;
                break;
            default:
                // ignoring anything else.
        }
        var req = [];
        var info = [];

        // forcing type,url and dataType.
        settings.type = 'post';
        settings.url = this.url ;//+"?hackme=";
        settings.dataType = 'json'
        //settings.contentType = 'application/json'

        settings.success = function (responce, textStatus, xhr) {
            $(this).triggerHandler("receiving", [textStatus]);
            if ($.isFunction(settings.validate)) {
                $(this).triggerHandler("validating");
                var state;
                if (settings.context) {
                    state = settings.validate.call(settings.context, info, responce)
                } else {
                    state = settings.validate(info, responce)
                }

                if (!state) {
                    $(this).triggerHandler("invalid", ["settings.validate says invalid", info, responce]);
                    return this;
                }
                $(this).triggerHandler("validated");
            }
            if ($.isArray(responce)) {
                $(this).triggerHandler("recieved", [responce.length, info.length]);
                var handled = 0;
                var requed = 0;
                var abandoned = 0;
                for (var i = 0; i < info.length; i++) {
                    var reqInfo = info[i];
                    var resInfo = responce[i];
                    if (resInfo) {
                        if ($.isFunction(reqInfo.dataFilter)) {
                            if (reqInfo.context) {
                                resInfo = reqInfo.dataFilter.call(reqInfo.context, resInfo);
                            } else {
                                resInfo = reqInfo.dataFilter(resInfo);

                            }
                        }
                        if (reqInfo.context) {
                            reqInfo.success.call(reqInfo.context, resInfo, reqInfo.data.actions);
                        } else {
                            reqInfo.success(resInfo, reqInfo.data.actions);
                        }
                        handled++;
                    } else {
                        if (reqInfo.noResponce) {
                            if ($.isFunction(reqInfo.noResponce)) {
                                var action;
                                if (reqInfo.context) {
                                    action = reqInfo.noResponce.call(reqInfo.context);
                                } else {
                                    action = reqInfo.noResponce(reqInfo);
                                }
                                if (action) {
                                    requed++;
                                    this.queue(reqInfo);
                                } else {
                                    abandoned++;
                                }
                            } else {
                                requed++;
                                this.queue(reqInfo);
                            }
                        } else {
                            abandoned++;
                        }
                    }
                }
                $(this).triggerHandler("handled", [handled, requed, abandoned]);
            } else {
                $(this).triggerHandler("invalid", ["expecting array responce", info, responce]);
            }
        }
        settings.error = function (xhr, textStatus, errorThrown) {
            $(this).triggerHandler("receiving", [textStatus]);
            for (var i = 0; i < info.length; i++) {
                var reqInfo = info[i];
                if ($.isFunction(reqInfo.error)) {
                    if (reqInfo.context) {
                        reqInfo.error.call(reqInfo.context, xhr, textStatus, errorThrown, reqInfo);
                    } else {
                        reqInfo.error(xhr, textStatus, errorThrown, reqInfo);
                    }

                }
            }
            $(this).triggerHandler("error", [textStatus]);
            return this;
        }
        var data = [];
        do {
            var preparing = Math.min(numRequested, queueSize)
            $(this).triggerHandler("prepare", [preparing, numRequested, queueSize]);
            var reqInfo = this.requestQueue.shift();
            do {
                if ($.isFunction(reqInfo.prepare)) {
                    if (reqInfo.context) {
                        reqInfo.prepare.call(reqInfo.context, reqInfo);
                    } else {
                        reqInfo.prepare(reqInfo)
                    }
                }
                if (!reqInfo.success) obj.success = $.noop;
                if (!reqInfo.data) reqInfo.data = {};
                req.push(reqInfo.data);
                info.push(reqInfo);
                preparing--;
            } while ((reqInfo = this.requestQueue.shift()) && preparing > 0);
        } while (false);
        $(this).triggerHandler("prepared", [req.length, numRequested, this.requestQueue.length, queueSize]);

        //settings.data=$.toJSON(data);
        //settings.data = $.toJSON({ "request": req });

          // NHA EDIT - 26-10-2010 - removed submit option (submit is defined in the form of an action) 
//        if (options != undefined && options.dataSubmit)
//            settings.data = { submit: $.toJSON(req) };
//        else
            settings.data = { request: $.toJSON(req) };



        $(this).triggerHandler("queue", [this.requestQueue.length]);
        $(this).triggerHandler("send", [req.length]);
        //settings.processData = false;
        $.ajax(settings);
    }
    function queue(options) {
        // private. cant override
        // add the request to the
        // list of requests
        // add the fn to the list of callbacks
        if (arguments.length > 1) {
            for (i = 0; i < argumnts.length; i++)
                queue.apply(this, arguments[1]);
            return this;
        }
        if ($.isArray(options)) {
            for (var i = 0; i < options.length; i++)
                queue.apply(this, options[1]);
            return this
        }
        if (!typeof (options) == "object") return this;
        this.requestQueue.push(options)
        $(this).triggerHandler("queue", [this.requestQueue.length]);
        return this;
    }

    function DataSource(uri) {
        // defining private methods
        var url = uri
        if (!url) {
            var i = window.location.href.indexOf("?");
            url = (i == -1) ? window.location.href + "?json=1" : window.location.href + "&json=1";
        }
        var requestQueue = [];
        var callbacks = [];
        this.queue = function () {
            this.requestQueue = requestQueue;
            queue.apply(this, arguments);
            delete this["requestsQueue"];
        }
        this.send = function (options) {
            this.requestQueue = requestQueue;
            this.url = url;
            send.apply(this, arguments);
            delete this["requestsQueue"];
            delete this["url"];
        }
        this.clear = function () {
            $(this).triggerHandler("clear", [requestQueue]);
            requestQueue.length = 0;
        }
        this.observe = observe;
        this.unobserve = unobserve;
    }

    $.extend($, {
        JSONDataSource: function (uri) {
            uri = uri || window.location.href;
            var idx = uri.indexOf("?") == -1

            return new DataSource(uri);
        }
    })
})(jQuery)
