/*jshint node:true */

"use strict";

var mongoose = require("mongoose"),
	resource = require("./resource"),
	utils = require("./utils"),

	DocumentArray = mongoose.Types.DocumentArray,
	Embedded = mongoose.Types.Embedded,
	DocumentArraySchema = mongoose.Schema.Types.DocumentArray,
	EmbeddedSchema = mongoose.Schema.Types.Embedded,

	rxAllDots = /\./g;


function getPath(obj, path) {
	var parts = path.split(".");

	while (parts.length) {
		if (!obj) {
			return;
		}

		obj = obj[parts.shift()];
	}

	return obj;
}


function getObject(req, doc, prefix) {
	var obj = doc.toObject({ virtuals: true });

	utils.addHref(req, obj, prefix, obj._id);
	prefix = prefix + "/" + obj._id;

	if (doc.schema) {
		doc.schema.eachPath(function(path, type) {
			var urlpath = path.replace(rxAllDots, "/");

			if (type instanceof DocumentArraySchema) {
				getPath(obj, path).forEach(function(subobj) {
					utils.addHref(req, subobj, prefix + "/" + urlpath, subobj._id);
				});
			}
		});
	}

	return obj;
}


/**
 * Mongoose document property resource helper
 */
function mongooseValueResource(prefix, doc, path) {
	return {
		get: function(req, cb) {
			process.nextTick(function() {
				cb(null, doc.get(path));
			});
		},

		put: function(req, patch, cb) {
			doc.set(path, req.body);
			doc.save(function(err) {
				cb(err, doc.get(path));
			});
		}
	};
}

/**
 * Mongoose document resource helper
 */
function mongooseDocResource(prefix, doc) {
	return {
		sub: function(id, cb) {
			var subitem = doc.get(id),
				subprefix = prefix + "/" + doc._id + "/" + id;

			if (subitem instanceof DocumentArray) {
				subitem = mongooseDocArrayResource(subprefix, doc, id);
			} else if (subitem instanceof Embedded) {
				subitem = mongooseDocResource(subprefix, subitem);
			} else {
				subitem = mongooseValueResource(subprefix, doc, id);
			}

			process.nextTick(function() {
				cb(null, subitem);
			});
		},

		get: function(req, cb) {
			var body = getObject(req, doc, prefix);

			process.nextTick(function() {
				cb(null, body);
			});
		},

		put: function(req, patch, cb) {
			var resource = this;

			doc.update(req.body, function(err) {
				if (err) {
					cb(err);
				} else {
					resource.get(req, cb);
				}
			});
		},

		del: function(req, cb) {
			doc.remove(cb);
		}
	};
}


/**
 * Mongoose document array resource helper
 */
function mongooseDocArrayResource(prefix, doc, path) {
	var docArray = doc.get(path);

	return {
		isCollection: true,

		sub: function(id, cb) {
			var subdoc = docArray.id(id);

			process.nextTick(function() {
				cb(null, subdoc ? mongooseDocResource(prefix, subdoc) : null);
			});
		},

		count: function(req, cb) {
			process.nextTick(function() {
				cb(null, docArray.length);
			});
		},

		list: function(req, offset, limit, cb) {
			var sdocs;

			if (limit > 0) {
				sdocs = docArray.slice(offset, offset+limit);
			} else {
				sdocs = docArray.slice(offset);
			}

			process.nextTick(function() {
				cb(null, sdocs.map(function(sdoc) {
					return getObject(req, sdoc, prefix);
				}));
			});
		},

		post: function(req, cb) {
			docArray.push(req.body);
			doc.save(cb);
		}
	};
}


/**
 * Define a REST resource that gives access to a Mongoose model collection
 *
 * @param name resource name
 * @param model Mongoose model
 */
function mongooseResource(name, model) {
	resource(name, {
		isCollection: true,

		sub: function(id, cb) {
			model.findById(id, function(err, item) {
				cb(err, item ? mongooseDocResource(name, item) : null);
			});
		},

		count: function(req, cb) {
			return model.count(cb);
		},

		list: function(req, offset, limit, cb) {
			return model.find({}).skip(offset).limit(limit).exec(function(err, items) {
				cb(err, items.map(function(item) {
					return getObject(req, item, name);
				}));
			});
		},

		post: function(req, cb) {
			model.create(req.body, cb);
		}
	});
}


module.exports = mongooseResource;

@@ -3321,19 +3321,55 @@
 e, model
-) %7B
+, options) %7B%0A%09options = options %7C%7C %7B%7D;%0A
 %0A%09resour
@@ -3649,30 +3649,35 @@
 t, cb) %7B%0A%09%09%09
-return
+var query =
  model.find(
@@ -3705,16 +3705,101 @@
 t(limit)
+;%0A%0A%09%09%09if (options.sort) %7B%0A%09%09%09%09query = query.sort(options.sort);%0A%09%09%09%7D%0A%0A%09%09%09return query
 .exec(fu

Add sort option to mongoose resource