text: var enforce = require("enforce");
  var util    = require("util");
  
  var validators = {
  	required       : enforce.required,
  	notEmptyString : enforce.notEmptyString,
  
  	rangeNumber    : enforce.ranges.number,
  	rangeLength    : enforce.ranges.length,
  
  	insideList     : enforce.lists.inside,
  	outsideList    : enforce.lists.outside,
  
  	password       : enforce.security.password,
  
  	patterns       : enforce.patterns
  };
  
  
  /**
   * Check if a value is the same as a value
   * of another property (useful for password
   * checking).
   **/
  validators.equalToProperty = function (name, msg) {
  	return function (v, next) {
  		if (v === this[name]) {
  			return next();
  		}
  		return next(msg || 'not-equal-to-property');
  	};
  };
  
  /**
   * Check if a property is unique in the collection.
   * This can take a while because a query has to be made against the Model.
   *
   * Due to the async nature of node, and concurrent web server environments,
   * an index on the database column is the only way to gurantee uniqueness.
   *
   * For sensibility's sake, undefined and null values are ignored for uniqueness
   * checks.
   *
   * Options:
   *   ignoreCase: for postgres; mysql ignores case by default.
   *   scope: (Array) scope uniqueness to listed properties
   **/
  validators.unique = function () {
  	var arg, k;
  	var msg = null, opts = {};
  
  	for (k in arguments) {
  		arg = arguments[k];
  		if (typeof arg === "string") {
  			msg = arg;
  		} else if (typeof arg === "object") {
  			opts = arg;
  		}
  	}
  
  	return function (v, next, ctx) {
  		var s, scopeProp;
  
  		if (typeof v === "undefined" || v === null) {
  			return next();
  		}
  
  		//Cannot process on database engines which don't support SQL syntax
  		if (!ctx.driver.isSql) {
  			return next('not-supported');
  		}
  
  		var chain = ctx.model.find();
  
  		var chainQuery = function (prop, value) {
  			var query = null;
  
  			if (opts.ignoreCase === true && ctx.model.properties[prop] && ctx.model.properties[prop].type === 'text') {
  				query = util.format('LOWER(%s.%s) LIKE LOWER(?)',
  					ctx.driver.query.escapeId(ctx.model.table), ctx.driver.query.escapeId(prop)
  				);
  				chain.where(query, [value]);
  			} else {
  				query = {};
  				query[prop] = value;
  				chain.where(query);
  			}
  		};
  
  		var handler = function (err, records) {
  			if (err) {
  				return next();
  			}
  			if (!records || records.length === 0) {
  				return next();
  			}
  			if (records.length === 1 && records[0][ctx.model.id] === this[ctx.model.id]) {
  				return next();
  			}
  			return next(msg || 'not-unique');
  		}.bind(this);
  
  		chainQuery(ctx.property, v);
  
  		if (opts.scope) {
  			for (s in opts.scope) {
  				scopeProp = opts.scope[s];
  
  				// In SQL unique index land, NULL values are not considered equal.
  				if (typeof ctx.instance[scopeProp] == 'undefined' || ctx.instance[scopeProp] === null) {
  					return next();
  				}
  
  				chainQuery(scopeProp, ctx.instance[scopeProp]);
  			}
  		}
  
  		chain.all(handler);
  	};
  };
  
  module.exports = validators;
  