// # ModelClass
// Base Model where the 'VNLS.createModel" method Inherits from

VNLS.addClass("Model",function(){
    // ----------------------------
	   // ## Constructor
	   // Arguments are never passed in the constructor
	   // However, we may set some props and
	   // may do some vital checkings.
	   // 
	   //
	   function Model() {
	       if(typeof(Lawnchair)!="function"){
	           throw "Lawnchair needed:(http://westcoastlogic.com/lawnchair/)";
	       }     
	       this.name = false;
	       this.structure = {};
	       this.required_fields = [];
	       this.storage = false;
  	   };
  	   // Extend Model with the EventEmitter
  	   // easy :) isn't it?
  	   Model.prototype = VNLS.getObject("EventEmitter");
       Model.prototype.constructor = Model;
       
  	   Model.prototype.init = function(structure){
  	       
  	       this._checkModelStructure(structure);
           this.name = structure.name;
	       this.structure = {};
	       this.required_fields = [];
	       
	       this._setStructure(structure.bucket_structure);
	       this._setRequiredFields(structure.required_fields);
	       
	       // Not entirely convinced to hard coded LawnChair this way. 
	       this.storage = new Lawnchair({name:this.name},function(){});
  	   }
  	   
	   // -----------------------------
	   // ## Public methods
	   // ### checkDoc(document,error_callback)
	   // Check an entire document and throw or call the callback
	   Model.prototype.checkDoc = function(doc,err){
         var cb = (typeof(err) == "function");
         for(var i=0, len=this.required_fields.length; i< len ; i++){
            if(!doc.hasOwnProperty(this.required_fields[i])){
                if(cb){
                    err(this.required_fields[i],"required");
                    return false;
                };
                throw "Required field missing:"+this.required_fields[i];
            };
            if(!this.checkKey(this.required_fields[i],doc[this.required_fields[i]])){
                if(cb){
                    err(this.required_fields[i],"invalid_value");
                    return false;
                };
                throw "Invalid field value:"+this.required_fields[i];
            };
         }
         return true;
  	   };
  	   // ### checkKey(prop,value)
  	   // Check a key against a regex
  	   Model.prototype.checkKey = function (field,val){
  	     if(this.structure.hasOwnProperty(field)){
  	         if(this.structure[field] instanceof RegExp){
  	             return this.structure[field].test(val);
  	         }else if(typeof(this.structure[field])=="function"){
  	             return this.structure[field](val);
  	         }
  	     }
  	     throw "No such field:"+field;
  	   }
  	   
  	   // ----------------------
  	   // ## PUBLIC DATABASE METHODS
        Model.prototype.save = function(doc,fn,err) {
           var self = this;
           if(this.checkDoc(doc,err)){
             this.storage.save(doc,function(r){
                self.emit('save',r);
                if(typeof(fn)=="function"){
                    fn(r);
                }
             });
             return this;
           }
        };
        // Get a record by 'key' or obj.key string
        Model.prototype.get = function(obj,cb) {
           this.storage.get(obj,cb);
           return this;
        };
        
        // Find records (or,and)
        // We should we be able to use operators  <>,== etc. ?
        // if and == true all props should match
        // if !and one of the props should match
        // needs some optimizing on large datasets
        Model.prototype.find = function(ob,cb,and) {
           this.storage.each(function(r){
             var index = 0;
             var found_index = 0;
             for(var prp in ob){
                 index ++;
                 if(r.hasOwnProperty(prp) && r[prp] == ob[prp]){
                    found_index ++;
                    if(!and){
                        break;
                    }
                 }                 
             }
             if(found_index > 0){
                if(and && index==found_index){
                    cb(r);
                }else if(!and){
                    // OR
                    cb(r);
                }
             }
           });
           return this;
        };
        
        // Get all
        // This could be a function to combine
        // with sort and 'paging'
        // It will just fetch 'all' the records
        // and return it in an array
        Model.prototype.all= function(fn) {
           this.storage.all(fn);
           return this;
        };
        
        // remove a record on
        // 'key' or obj(.key)
        Model.prototype.remove= function(obj,fn) {
           var self = this;
           this.storage.remove(obj,function(){
                if(typeof(fn)=='function'){
                    fn(obj);
                }
                self.emit('remove',obj);
           });
           return this;
        };
  	   
	   // ----------------------------------------------------
	   // ## Private methods
	   //
	   // ### _setStructure(object)
	   // We copy the object so we don't use a references
	   Model.prototype._setStructure = function(structure){
	       for(var prop in structure){
                this.structure[prop] = structure[prop];
           }
	   };
	   // We rather copy the array to prevent references
	   Model.prototype._setRequiredFields = function(fields){
	       for(var i = 0; i < fields.length; i++){
                this.required_fields.push(fields[i]);
           }
	   };
	   
	   Model.prototype._checkModelStructure = function (structure){
        if(typeof(structure)!="object"){
	        throw "Missing structure";
	    }
	    if(typeof(structure.name)!="string"){
	        throw "Missing modelname";
	    }
	    if(typeof(structure.bucket_structure)!="object"){
	        throw "Missing bucket_structure";
	    }
	    if(typeof(structure.required_fields)!="object"){
	        throw "Missing required_fields";
	    }
	    return true;
        };

	   
	   return Model;
});
