// vim: et:ts=4:sw=4:sts=4:ft=javascript
/*@
Ox.Request <o> Basic request handler object
FIXME: options is not a property, just documenting defaults
options <o> Options object
    timeout <n|60000> request timeout
    type    <s|POST>  request type, possible values POST, GET, PUT, DELETE
    url     <s>       request url
@*/

Ox.Request = function(options) {

    var cache = {},
        //dfd = $.Deferred(),
        pending = {},
        requests = {},
        self = {
            options: $.extend({
                timeout: 60000,
                type: 'POST',
                url: '/api/'
            }, options)
        };

    return {
        /*@
        cancel <f> cancel pending requests
            ()  -> <u>  cancel all requests
            (f)  -> <u>  cancel all requests where function returns true
            (n)  -> <u>  cancel request by id
        @*/
        cancel: function() {
            if (arguments.length == 0) {
                // cancel all requests
                requests = {};
            } else if (Ox.isFunction(arguments[0])) {
                // cancel with function
                Ox.forEach(requests, function(req, id) {
                    if (arguments[0](req)) {
                        delete requests[id];
                    }
                });
            } else {
                // cancel by id
                delete requests[arguments[0]];
            }
        },
        /*@
        clearCache <f> clear cached results
            () -> <u>
        @*/
        clearCache: function() {
            cache = {};
        },

        /*@
        options <f> get/set options
            ()        -> <o> get options
            (options) -> <o> set options
            options <o> Options Object
        @*/
        options: function(options) {
            return Ox.getset(self.options, options, $.noop(), this);
        },

        /*@
        requests <f> pending requests
            () -> <n> returns number of requests
        @*/
        requests: function() {
            return Ox.len(requests);
        },

        /*@
        send <f> send request
            (options) -> <n> returns request id
            options <o> Options Object
                age <n|-1> cache age
                id <n|Ox.uid()> request id
                timeout <n|self.options.timeout> overwrite default timeout
                type <n|self.options.timeout> overwrite default type
                url <n|self.options.timeout> overwrite default url
        @*/
        send: function(options) {

            var options = $.extend({
                    age: -1,
                    callback: null,
                    id: Ox.uid(),
                    timeout: self.options.timeout,
                    type: self.options.type,
                    url: self.options.url
                }, options),
                req = JSON.stringify({
                    url: options.url,
                    data: options.data
                });

            if (pending[options.id]) {
                setTimeout(function() {
                    Ox.Request.send(options);
                }, 0);
            } else {
                requests[options.id] = {
                    url: options.url,
                    data: options.data
                };
                if (cache[req] && (options.age == -1 || options.age > Ox.getTime() - cache[req].time)) {
                    setTimeout(function() {
                        callback && callback(cache[req].data);
                    }, 0);
                } else {
                    pending[options.id] = true;
                    $.ajax({
                        data: options.data,
                        dataType: 'json',
                        error: error,
                        success: success,
                        timeout: options.timeout,
                        type: options.type,
                        url: options.url
                    });
                }
            }

            function callback(data) {
                delete requests[options.id];
                //Ox.len(requests) == 0 && $body.trigger('requestStop');
                options.callback && options.callback(data);
            }

            function debug(request) {
                var $iframe = $('<iframe>')
                        .css({ // fixme: should go into a class
                            width: 768,
                            height: 384
                        }),
                    $dialog = Ox.Dialog({
                            title: 'Application Error',
                            buttons: [
                                Ox.Button({
                                    title: 'Close'
                                })
                                .bindEvent({
                                    click: function() {
                                        $dialog.close();
                                    }
                                })
                            ],
                            content: $iframe,
                            width: 800,
                            height: 400
                        })
                        .open(),
                    iframe = $iframe[0].contentDocument || $iframe[0].contentWindow.document;
                iframe.open();
                iframe.write(request.responseText);
                iframe.close();
            }

            function error(request, status, error) {
                var data;
                if (arguments.length == 1) {
                    data = arguments[0]
                } else {
                    try {
                        data = JSON.parse(request.responseText);
                    } catch (err) {
                        try {
                            data = {
                                status: {
                                    code: request.status,
                                    text: request.statusText
                                }
                            };
                        } catch (err) {
                            data = {
                                status: {
                                    code: '500',
                                    text: 'Unknown Error'
                                }
                            };
                        }
                    }
                }
                if (data.status.code < 500) {
                    callback(data);
                } else {
                    var $dialog = Ox.Dialog({
                            title: 'Application Error',
                            buttons: [
                                Ox.Button({
                                        id: 'details',
                                        title: 'Details'
                                    })
                                    .bindEvent({
                                        click: function() {
                                            $dialog.close(function() {
                                                debug(request);
                                            });
                                        }
                                    }),
                                Ox.Button({
                                        id: 'close',
                                        title: 'Close'
                                    })
                                    .bindEvent({
                                        click: function() {
                                            $dialog.close();
                                        }
                                    })
                            ],
                            content: 'Sorry, we have encountered an application error while handling your request. To help us find out what went wrong, you may want to report this error to an administrator. Otherwise, please try again later.',
                            keys: {enter: 'close', escape: 'close'},
                            width: 400,
                            height: 200
                        })
                        .open();
                        // fixme: change this to Send / Don't Send
                    /*Ox.print({
                        request: request,
                        status: status,
                        error: error
                    });*/
                }
                pending[options.id] = false;
            }

            function success(data) {
                pending[options.id] = false;
                cache[req] = {
                    data: data,
                    time: Ox.getTime()
                };
                callback(data);
            }

            // return dfd.promise();

            return options.id;

        }

    };

}();