var q = require('q');
var util = require('util');
var EventEmitter = require('events').EventEmitter;

/**
 * Defines a record
 */
module.exports = function(manager, mapper, body) {

  // record constructor
  var record = function(doc) {
    // reserved field _id
    this._id = false;
    // sets values
    for(var i in doc) {
      if (mapper.options.properties.hasOwnProperty(i)) {
        this[i] = mapper.options.properties[i].unserialize(doc[i]);
      } else {
        this[i] = doc[i];
      }
    }
  };
  util.inherits(record, EventEmitter);

  // declare cached variables (not serialized)
  Object.defineProperty(record.prototype, "__buffer", {
    enumerable: false,
    configurable: false,
    writable: true,
    value: {}
  });

  // Hide the EventEmitter property, avoid its serialisation
  Object.defineProperty(record.prototype, "_events", {
    enumerable: false,
    configurable: false,
    writable: true,
    value: {}
  });

  // handles getters & setters
  for(var name in mapper.options.properties) {
    var property = mapper.options.properties[name];
    if (
      property.hasOwnProperty('get') 
      || property.hasOwnProperty('set') 
    ) {
      Object.defineProperty(record.prototype, name, {
          get: property.hasOwnProperty('get') ? property.get : function() {
              return this.__buffer[name];
          },
          set: property.hasOwnProperty('set') ? property.set : function(value) {
              this.__buffer[name] = value;
          },
          enumerable: true,
          configurable: false
      });
    }
  }


  // Gets the record ID
  record.prototype.getId = function() {
    return mapper.options.autoincrement ? 
      mapper.options.type + '.' + this._id : this._id
  };

  // saves the current record
  record.prototype.save = function(wait) {
    var self = this;
    if (!this._id) {
      return mapper.nextId().then(function(id) {
        self._id = id;
        return self.save(wait);
      });
    } else {
      var result = q.defer();
      try {
        for(var name in mapper.options.properties) {
          var property = mapper.options.properties[name];
          if (this.hasOwnProperty(name) && !property.checkType(this[name])) {
            throw new Error('Unable to validate "' + name + '" type as "' + property.type + '"');
          }
          if (!property.checkContents(this[name])) {
            throw new Error('Unable to validate "' + name + '" contents');
          }
        }
        this.emit('save', this);
        mapper.emit('save', this);
        // reserved field _type
        this._type = mapper.options.type;
        manager.cb.set(
          this.getId(), this, 
          wait ? { persist_to: 1 } : {},
          function(err, data) {
            if (err) {
              result.reject(err);
              self.emit('error', err);
              mapper.emit('error', err);
            } else {
              result.resolve(self);
              self.emit('saved', self);
              mapper.emit('saved', self);
            }
          }
        );
      } catch(err) {
        result.reject(err);
        this.emit('error', err);
        mapper.emit('error', err);
      }
      return result.promise;
    }
  };
  
  // removes the current entry
  record.prototype.remove = function() {
    var result = q.defer();
    var self = this;
    try {
      if (!this._id) throw new Error(
        "Can not remove, the entry is not saved yet"
      );
      this.emit('remove', this);
      mapper.emit('remove', this);
      manager.cb.remove(this.getId(), function(err) {
        if (err) {
          result.reject(err);
          self.emit('error', err);
          mapper.emit('error', err);
        } else {
          // reset the current field ID
          self._id = false;
          result.resolve(self);
          self.emit('removed', self);
          mapper.emit('removed', self);
        }
      });
    } catch(err) {
      result.reject(err);
      this.emit('error', err);
      mapper.emit('error', err);
    }
    return result.promise;
  };
  
  // extends with custom functions
  for(var name in body) {
    record.prototype[name] = body[name];
  }
  
  // expose the record class
  return record;
};
@@ -2700,16 +2700,49 @@
 st_to: 1
+, replicate_to: 0, spooled: false
  %7D : %7B%7D,
@@ -2742,16 +2742,16 @@
 %7D : %7B%7D,%0A
-
         
@@ -3315,32 +3315,36 @@
 move = function(
+wait
 ) %7B%0A    var resu
@@ -3596,16 +3596,97 @@
 etId(), 
+%0A        wait ? %7B persist_to: 1, replicate_to: 0, spooled: false %7D : %7B%7D,%0A        
 function
@@ -3693,32 +3693,34 @@
 (err) %7B%0A        
+  
 if (err) %7B%0A     
@@ -3706,32 +3706,34 @@
      if (err) %7B%0A
+  
           result
@@ -3748,32 +3748,34 @@
 err);%0A          
+  
 self.emit('error
@@ -3775,32 +3775,34 @@
 ('error', err);%0A
+  
           mapper
@@ -3822,32 +3822,34 @@
 , err);%0A        
+  
 %7D else %7B%0A       
@@ -3847,24 +3847,26 @@
 %7B%0A          
+ 
+ 
 // reset the
@@ -3885,32 +3885,34 @@
 ld ID%0A          
+  
 self._id = false
@@ -3915,32 +3915,34 @@
 alse;%0A          
+ 
+ 
 result.resolve(s
@@ -3939,32 +3939,34 @@
 .resolve(self);%0A
+  
           self.e
@@ -3989,32 +3989,34 @@
 elf);%0A          
+  
 mapper.emit('rem
@@ -4021,32 +4021,44 @@
 emoved', self);%0A
+          %7D%0A
         %7D%0A      
@@ -4051,23 +4051,22 @@
       %7D%0A
+
       
-%7D
 );%0A    %7D

implement wait on remove function