Ext.data.CSGenerator = Ext.extend(Ext.data.Config, {

	r: undefined, // replica_number
	t: undefined, // time, in seconds since epoch
	s: undefined, // sequence number
	
	clock: undefined,
	local_offset: undefined,
	global_offset: undefined,
	
	constructor: function(config,callback,scope) {
		Ext.apply(this, config);
		config.config_id= 'generator';
		Ext.data.CSGenerator.superclass.constructor.call(this, config);
		this.clock= this.clock || new Ext.data.Clock();
	  this.t= this.t || this.clock.now();
	  this.s= this.s || -1; // so that the next tick gets us to 0
	  this.local_offset= this.local_offset || 0;
	  this.global_offset= this.global_offset || 0;
		this.writeAndCallback(true,callback,scope);
	},
	
  get: function(callback,scope) { // the next change stamp
    var current_time= this.clock.now();
    this.update_local_offset(current_time);
    this.s+= 1;
    if (this.s>255) { // JCM This is totally arbitrary, and it's hard coded too....
      this.t= current_time;
      this.local_offset+= 1;
      this.s= 0;
    }
		this.writeAndCallback(true,callback,scope);
		// JCM it seems wrong to use the CS until it has been committed to disk...
    return new Ext.data.CS({r:this.r,t:this.global_time(),s:this.s}); // JCM return in the callback 
  },

  seen: function(cs,callback,scope) { // a change stamp we just received
		var changed= false;
    var current_time= this.clock.now();
		if (current_time>this.t) {
	    changed= this.update_local_offset(current_time);
		}
    changed= changed||this.update_global_offset(cs);
		this.writeAndCallback(changed,callback,scope);
  },
  
  setReplicaNumber: function(replica_number,callback,scope) {
		var changed= this.r!==replica_number;
		this.r= replica_number;
		this.writeAndCallback(changed,callback,scope);
  },

	// @private
  
  update_local_offset: function(current_time) {
		var changed= false;
    var delta= current_time-this.t;
    if (delta>0) { // local clock moved forwards
      var local_time= this.global_time();
      this.t= current_time;
      if (delta>this.local_offset) {
        this.local_offset= 0;
      } else {
        this.local_offset-= delta;
      }
      var local_time_after= this.global_time();
			if (local_time_after>local_time) {
      	this.s= -1;
			}
			changed= true;
    } else if (delta<0) { // local clock moved backwards
      // JCM if delta is too big, then complain
      this.t= current_time;
      this.local_offset+= -delta;
			changed= true;
    }
		return changed;
	},
	
	update_global_offset: function(remote_cs) {
		var changed= false;
    var local_cs= new Ext.data.CS({r:this.r,t:this.global_time(),s:this.s+1});
    var local_t= local_cs.t;
    var local_s= local_cs.s;
    var remote_t= remote_cs.t;
    var remote_s= remote_cs.s;
    if (remote_t==local_t && remote_s>=local_s) {
		  this.s= remote_s;
			changed= true;
    } else if (remote_t>local_t) {
      var delta= remote_t-local_t;
  		if (delta>this.global_offset) { // remote clock moved forwards
  		  // JCM guard against moving too far forward
      	this.global_offset+= delta;
    		this.s= remote_s;
				changed= true;
      }
  	}
		return changed; 
  },

  global_time: function() {
    return this.t+this.local_offset+this.global_offset;
	},
	
	as_data: function() {
		var data= {
			r: this.r,
			t: this.t,
			s: this.s,
			local_offset: this.local_offset,
			global_offset: this.global_offset
		};
		data[Ext.data.SyncModel.MODEL]= 'Ext.data.CSGenerator';
		return Ext.data.CSGenerator.superclass.as_data.call(this, data);
	}
});