// Generated by CoffeeScript 1.10.0
(function() {
  var Model, NotOnNewModel, _wrapCallback, castObject, deprecated, fs, util,
    indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
    hasProp = {}.hasOwnProperty;

  util = require('util');

  fs = require('fs');

  deprecated = function(what) {
    var lines, ref;
    if (deprecated.alreadySaid == null) {
      deprecated.alreadySaid = [];
    }
    if (((ref = process.env.NODE_ENV) !== 'test' && ref !== 'production') && indexOf.call(deprecated.alreadySaid, what) < 0) {
      deprecated.alreadySaid.push(what);
      lines = new Error().stack.split("\n").slice(2, 5).join('\n');
      return console.log("Deprecated " + what + "\n" + lines);
    }
  };

  _wrapCallback = function(that, changes, callback) {
    return function(err, data) {
      var key, value;
      if (err) {
        return callback(err);
      }
      if (data != null ? data.success : void 0) {
        for (key in changes) {
          value = changes[key];
          that[key] = value;
        }
      } else {
        for (key in data) {
          value = data[key];
          that[key] = value;
        }
      }
      return callback(null, that);
    };
  };

  NotOnNewModel = require('./utils/errors').NotOnNewModel;

  Model = (function() {
    Model.getDocType = function() {
      var ref;
      return ((ref = this.docType) != null ? ref.toLowerCase() : void 0) || this.name.toLowerCase();
    };

    Model.exists = function(id, callback) {
      return this.adapter.exists(id, callback);
    };

    Model.find = function(id, callback) {
      return this.adapter.find(id, (function(_this) {
        return function(err, attributes) {
          var ref;
          if (err) {
            return callback(err);
          } else if ((attributes != null ? (ref = attributes.docType) != null ? ref.toLowerCase() : void 0 : void 0) !== _this.getDocType()) {
            return callback(null, null);
          } else {
            return callback(null, new _this(attributes));
          }
        };
      })(this));
    };

    Model.create = function(data, callback) {
      data.docType = this.getDocType();
      data = this.cast(data);
      return this.adapter.create(data, (function(_this) {
        return function(err, attributes) {
          var k, v;
          if (err) {
            return callback(err);
          }
          for (k in attributes) {
            v = attributes[k];
            data[k] = v;
          }
          return callback(null, new _this(data));
        };
      })(this));
    };

    Model.save = function(id, data, callback) {
      return this.adapter.save(id, data, (function(_this) {
        return function(err, attributes) {
          if (err != null) {
            return callback(err);
          }
          return callback(null, new _this(attributes));
        };
      })(this));
    };

    Model.updateAttributes = function(id, data, callback) {
      return this.adapter.updateAttributes(id, data, (function(_this) {
        return function(err, updated) {
          if (err != null) {
            return callback(err);
          }
          return callback(null, new _this(updated));
        };
      })(this));
    };

    Model.destroy = function(id, callback) {
      return this.adapter.destroy(id, callback);
    };

    Model.search = function(query, callback) {
      return this.indexAdapter.search.call(this, query, (function(_this) {
        return function(err, objects) {
          var results;
          if (err) {
            return callback(err);
          }
          results = objects.map(function(row) {
            return new _this(row);
          });
          results.totalHits = objects.totalHits;
          results.facets = objects.facets;
          results.hits = objects.hits;
          return callback(null, results);
        };
      })(this));
    };

    Model.attachFile = function(id, path, data, callback) {
      return this.fileAdapter.attach(id, path, data, callback);
    };

    Model.getFile = function(id, path, callback) {
      return this.fileAdapter.get(id, path, callback);
    };

    Model.saveFile = function(id, path, filePath, callback) {
      deprecated("Model.saveFile, use streams instead");
      return this.fileAdapter.get(id, path, filePath, function(err, res) {
        var writeStream;
        if (err) {
          return callback(err);
        }
        res.pipe(writeStream = fs.createWriteStream(filePath));
        return writeStream.on('finish', function() {
          return callback(null, res);
        });
      });
    };

    Model.removeFile = function(id, path, callback) {
      return this.fileAdapter.remove(id, path, callback);
    };

    Model.attachBinary = function(id, path, data, callback) {
      var ref;
      if (typeof data === "function") {
        ref = [null, data], data = ref[0], callback = ref[1];
      }
      return this.binaryAdapter.attach(id, path, data, callback);
    };

    Model.getBinary = function(id, path, callback) {
      return this.binaryAdapter.get(id, path, callback);
    };

    Model.saveBinary = function(id, path, filePath, callback) {
      deprecated("Model.saveBinary, use streams instead");
      return this.binaryAdapter.get(id, path, filePath, function(err, res) {
        var writeStream;
        if (err) {
          return callback(err);
        }
        res.pipe(writeStream = fs.createWriteStream(filePath));
        return writeStream.on('finish', function() {
          return callback(null, res);
        });
      });
    };

    Model.removeBinary = function(id, path, callback) {
      return this.binaryAdapter.remove(id, path, callback);
    };

    Model.defineRequest = function(name, request, callback) {
      var map, reduce;
      if (typeof request === "function" || typeof request === 'string') {
        map = request;
      } else {
        map = request.map;
        reduce = request.reduce;
      }
      return this.requestsAdapter.define.call(this, name, {
        map: map,
        reduce: reduce
      }, callback);
    };

    Model.request = function(name, params, callback) {
      var ref;
      if (typeof params === "function") {
        ref = [{}, params], params = ref[0], callback = ref[1];
      }
      return this.rawRequest(name, params, (function(_this) {
        return function(err, results) {
          if (err) {
            return callback(err);
          }
          return callback(null, results.map(function(row) {
            return new _this(row.value);
          }));
        };
      })(this));
    };

    Model.rawRequest = function(name, params, callback) {
      var ref;
      if (typeof params === "function") {
        ref = [{}, params], params = ref[0], callback = ref[1];
      }
      return this.requestsAdapter.run.call(this, name, params, callback);
    };

    Model.removeRequest = function(name, callback) {
      return this.requestsAdapter.remove.call(this, name, callback);
    };

    Model.requestDestroy = function(name, params, callback) {
      var ref;
      if (typeof params === "function") {
        ref = [{}, params], params = ref[0], callback = ref[1];
      }
      if (params.limit == null) {
        params.limit = 100;
      }
      return this.requestsAdapter.requestDestroy.call(this, name, params, callback);
    };

    Model.all = function(params, callback) {
      var ref;
      if (typeof params === "function") {
        ref = [{}, params], params = ref[0], callback = ref[1];
      }
      return this.request('all', params, callback);
    };

    Model.first = function(callback) {
      return this.all(function(err, items) {
        return callback(err, (items != null ? items[0] : void 0) || null);
      });
    };

    Model.cast = function(attributes, target) {
      if (target == null) {
        target = {};
      }
      return castObject(attributes, this.schema, target, this.name);
    };

    Model.destroyAll = function(params, callback) {
      var ref;
      if (typeof params === "function") {
        ref = [{}, params], params = ref[0], callback = ref[1];
      }
      return this.requestDestroy('all', params, callback);
    };

    Model.registerIndexDefinition = function(callback) {
      if (this.fullTextIndex) {
        return this.indexAdapter.registerIndexDefinition.call(this, callback);
      } else {
        return setImmediate(callback);
      }
    };

    function Model(attributes) {
      if (attributes == null) {
        attributes = {};
      }
      this.constructor.cast(attributes, this);
      if (attributes._id) {
        if (this.id == null) {
          this.id = attributes._id;
        }
      }
    }

    Model.prototype.save = function(callback) {
      var cb;
      cb = _wrapCallback(this, {}, callback);
      if (this.id) {
        return this.constructor.adapter.save.call(this.constructor, this.id, this.getAttributes(), cb);
      } else {
        return this.constructor.adapter.create.call(this.constructor, this.getAttributes(), cb);
      }
    };

    Model.prototype.updateAttributes = function(attributes, callback) {
      var cb;
      if (!this.id) {
        return callback(NotOnNewModel());
      }
      cb = _wrapCallback(this, attributes, callback);
      return this.constructor.adapter.updateAttributes.call(this.constructor, this.id, attributes, cb);
    };

    Model.prototype.destroy = function(callback) {
      if (!this.id) {
        return callback(NotOnNewModel());
      }
      return this.constructor.destroy.call(this.constructor, this.id, callback);
    };

    Model.prototype.index = function(fields, callback) {
      deprecated("Model::index is not necessary with DS > v2.1.0");
      if (!this.id) {
        return callback(NotOnNewModel());
      }
      return this.constructor.indexAdapter.index.call(this.constructor, this.id, fields, callback);
    };

    Model.prototype.attachFile = function(path, data, callback) {
      if (!this.id) {
        return callback(NotOnNewModel());
      }
      return this.constructor.attachFile.call(this.constructor, this.id, path, data, callback);
    };

    Model.prototype.getFile = function(path, callback) {
      if (!this.id) {
        return callback(NotOnNewModel());
      }
      return this.constructor.getFile.call(this.constructor, this.id, path, callback);
    };

    Model.prototype.saveFile = function(path, filePath, callback) {
      if (!this.id) {
        return callback(NotOnNewModel());
      }
      return this.constructor.saveFile.call(this.constructor, this.id, path, filePath, callback);
    };

    Model.prototype.removeFile = function(path, callback) {
      if (!this.id) {
        return callback(NotOnNewModel());
      }
      return this.constructor.removeFile.call(this.constructor, this.id, path, callback);
    };

    Model.prototype.attachBinary = function(path, data, callback) {
      if (!this.id) {
        return callback(NotOnNewModel());
      }
      return this.constructor.attachBinary.call(this.constructor, this.id, path, data, callback);
    };

    Model.prototype.getBinary = function(path, callback) {
      if (!this.id) {
        return callback(NotOnNewModel());
      }
      return this.constructor.getBinary.call(this.constructor, this.id, path, callback);
    };

    Model.prototype.saveBinary = function(path, filePath, callback) {
      if (!this.id) {
        return callback(NotOnNewModel());
      }
      return this.constructor.saveBinary.call(this.constructor, this.id, path, filePath, callback);
    };

    Model.prototype.removeBinary = function(path, callback) {
      if (!this.id) {
        return callback(NotOnNewModel());
      }
      return this.constructor.removeBinary.call(this.constructor, this.id, path, callback);
    };

    Model.prototype.getAttributes = function() {
      var key, out, value;
      out = {};
      for (key in this) {
        if (!hasProp.call(this, key)) continue;
        value = this[key];
        out[key] = value;
      }
      return out;
    };

    Model.prototype.toJSON = function() {
      return this.getAttributes();
    };

    Model.prototype.toObject = function() {
      return this.getAttributes();
    };

    Model.prototype.toString = function() {
      return this.constructor.getDocType() + JSON.stringify(this.toJSON());
    };

    return Model;

  })();

  module.exports = Model;

  castObject = require('./utils/type_checking').castObject;

}).call(this);
