(function() {
  var $, Ajax, Base, Collection, Extend, Include, Model, Singleton,
    __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
    __hasProp = Object.prototype.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; },
    __slice = Array.prototype.slice;

  if (typeof Spine === "undefined" || Spine === null) Spine = require('spine');

  $ = Spine.$;

  Model = Spine.Model;

  Ajax = {
    getURL: function(object) {
      return object && (typeof object.url === "function" ? object.url() : void 0) || object.url;
    },
    enabled: true,
    pending: false,
    requests: [],
    disable: function(callback) {
      if (this.enabled) {
        this.enabled = false;
        callback();
        return this.enabled = true;
      } else {
        return callback();
      }
    },
    requestNext: function() {
      var next;
      next = this.requests.shift();
      if (next) {
        return this.request(next);
      } else {
        return this.pending = false;
      }
    },
    request: function(callback) {
      var _this = this;
      return (callback()).complete(function() {
        return _this.requestNext();
      });
    },
    queue: function(callback) {
      if (!this.enabled) return;
      if (this.pending) {
        this.requests.push(callback);
      } else {
        this.pending = true;
        this.request(callback);
      }
      return callback;
    }
  };

  Base = (function() {

    function Base() {}

    Base.prototype.defaults = {
      contentType: 'application/json',
      dataType: 'json',
      processData: false,
      headers: {
        'X-Requested-With': 'XMLHttpRequest'
      }
    };

    Base.prototype.ajax = function(params, defaults) {
      return $.ajax($.extend({}, this.defaults, defaults, params));
    };

    Base.prototype.queue = function(callback) {
      return Ajax.queue(callback);
    };

    return Base;

  })();

  Collection = (function(_super) {

    __extends(Collection, _super);

    function Collection(model) {
      this.model = model;
      this.errorResponse = __bind(this.errorResponse, this);
      this.recordsResponse = __bind(this.recordsResponse, this);
    }

    Collection.prototype.find = function(id, params) {
      var record;
      record = new this.model({
        id: id
      });
      return this.ajax(params, {
        type: 'GET',
        url: Ajax.getURL(record)
      }).success(this.recordsResponse).error(this.errorResponse);
    };

    Collection.prototype.all = function(params) {
      return this.ajax(params, {
        type: 'GET',
        url: Ajax.getURL(this.model)
      }).success(this.recordsResponse).error(this.errorResponse);
    };

    Collection.prototype.fetch = function(params, options) {
      var id,
        _this = this;
      if (params == null) params = {};
      if (options == null) options = {};
      if (id = params.id) {
        delete params.id;
        return this.find(id, params).success(function(record) {
          return _this.model.refresh(record, options);
        });
      } else {
        return this.all(params).success(function(records) {
          return _this.model.refresh(records, options);
        });
      }
    };

    Collection.prototype.recordsResponse = function(data, status, xhr) {
      return this.model.trigger('ajaxSuccess', null, status, xhr);
    };

    Collection.prototype.errorResponse = function(xhr, statusText, error) {
      return this.model.trigger('ajaxError', null, xhr, statusText, error);
    };

    return Collection;

  })(Base);

  Singleton = (function(_super) {

    __extends(Singleton, _super);

    function Singleton(record) {
      this.record = record;
      this.errorResponse = __bind(this.errorResponse, this);
      this.recordResponse = __bind(this.recordResponse, this);
      this.model = this.record.constructor;
    }

    Singleton.prototype.reload = function(params, options) {
      var _this = this;
      return this.queue(function() {
        return _this.ajax(params, {
          type: 'GET',
          url: Ajax.getURL(_this.record)
        }).success(_this.recordResponse(options)).error(_this.errorResponse(options));
      });
    };

    Singleton.prototype.create = function(params, options) {
      var _this = this;
      return this.queue(function() {
        return _this.ajax(params, {
          type: 'POST',
          data: JSON.stringify(_this.record),
          url: Ajax.getURL(_this.model)
        }).success(_this.recordResponse(options)).error(_this.errorResponse(options));
      });
    };

    Singleton.prototype.update = function(params, options) {
      var _this = this;
      return this.queue(function() {
        return _this.ajax(params, {
          type: 'PUT',
          data: JSON.stringify(_this.record),
          url: Ajax.getURL(_this.record)
        }).success(_this.recordResponse(options)).error(_this.errorResponse(options));
      });
    };

    Singleton.prototype.destroy = function(params, options) {
      var _this = this;
      return this.queue(function() {
        return _this.ajax(params, {
          type: 'DELETE',
          url: Ajax.getURL(_this.record)
        }).success(_this.recordResponse(options)).error(_this.errorResponse(options));
      });
    };

    Singleton.prototype.recordResponse = function(options) {
      var _this = this;
      if (options == null) options = {};
      return function(data, status, xhr) {
        var _ref;
        if (Spine.isBlank(data)) {
          data = false;
        } else {
          data = _this.model.fromJSON(data);
        }
        Ajax.disable(function() {
          if (data) {
            if (data.id && _this.record.id !== data.id) {
              _this.record.changeID(data.id);
            }
            return _this.record.updateAttributes(data.attributes());
          }
        });
        _this.record.trigger('ajaxSuccess', data, status, xhr);
        return (_ref = options.success) != null ? _ref.apply(_this.record) : void 0;
      };
    };

    Singleton.prototype.errorResponse = function(options) {
      var _this = this;
      if (options == null) options = {};
      return function(xhr, statusText, error) {
        var _ref;
        _this.record.trigger('ajaxError', xhr, statusText, error);
        return (_ref = options.error) != null ? _ref.apply(_this.record) : void 0;
      };
    };

    return Singleton;

  })(Base);

  Model.host = '';

  Include = {
    ajax: function() {
      return new Singleton(this);
    },
    url: function() {
      var args, url;
      args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      url = Ajax.getURL(this.constructor);
      if (url.charAt(url.length - 1) !== '/') url += '/';
      url += encodeURIComponent(this.id);
      args.unshift(url);
      return args.join('/');
    }
  };

  Extend = {
    ajax: function() {
      return new Collection(this);
    },
    url: function() {
      var args;
      args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      args.unshift(this.className.toLowerCase() + 's');
      args.unshift(Model.host);
      return args.join('/');
    }
  };

  Model.Ajax = {
    extended: function() {
      this.fetch(this.ajaxFetch);
      this.change(this.ajaxChange);
      this.extend(Extend);
      return this.include(Include);
    },
    ajaxFetch: function() {
      var _ref;
      return (_ref = this.ajax()).fetch.apply(_ref, arguments);
    },
    ajaxChange: function(record, type, options) {
      if (options == null) options = {};
      if (options.ajax === false) return;
      return record.ajax()[type](options.ajax, options);
    }
  };

  Model.Ajax.Methods = {
    extended: function() {
      this.extend(Extend);
      return this.include(Include);
    }
  };

  Ajax.defaults = Base.prototype.defaults;

  Spine.Ajax = Ajax;

  if (typeof module !== "undefined" && module !== null) module.exports = Ajax;

}).call(this);
