dojo.provide("dojox.lang.tests.observable");
dojo.require("dojox.lang.observable");

(function(){
	tests.register("dojox.lang.tests.observable", [
		function propertyAccessMakeObservable(t){
			console.log("start");
			var testStrs = [];
			var observable = new dojox.lang.makeObservable(
				function(obj,i){
					testStrs.push("onRead " + i);
					return obj[i];
				},
				function(obj,i,value){
					testStrs.push("onWrite " + i);
					obj[i] = value;
				},
				function(scope,obj,i,args){
					testStrs.push("onInvoke " + i);
					return obj[i].apply(scope,args);
				}
			);
			var obj = {foo:"bar",test:function(){
					return this.foo;
				}
			};
			var newObj = observable(obj);
			testStrs.push("returned " + newObj.test());
			newObj.foo = "new";
			testStrs.push(newObj.foo);
			t.assertEqual("onInvoke test,onRead foo,returned bar,onWrite foo,onRead foo,new",testStrs.join(','));
		},
		function propertyAccessObservable(t){
			var testStrs = [];
			var obj = {foo:"bar",test:function(){
				return this.foo;
			}};
			var newObj = dojox.lang.observable(obj,function(obj,i){
					testStrs.push("onRead " + i);
					return obj[i];
				},function(obj,i,value){
					testStrs.push("onWrite " + i);
					obj[i] = value;
				},function(scope,obj,i,args){
					testStrs.push("onInvoke " + i);
					return obj[i].apply(scope,args);
				}
			);
			testStrs.push("returned " + newObj.test());
			newObj.foo = "new";
			testStrs.push(newObj.foo);
			t.assertEqual("onInvoke test,onRead foo,returned bar,onWrite foo,onRead foo,new",testStrs.join(','));
		},
		function readonlyProxy(t){
			console.log("start");
			var testStrs = [];
			var obj = {foo:"bar"};
			var newObj = dojox.lang.ReadOnlyProxy(obj);
			testStrs.push(newObj.foo);
			newObj.foo = "illegal";

			testStrs.push(newObj.foo);
			obj.foo = "new";
			testStrs.push(newObj.foo);
			t.assertEqual("bar,bar,new",testStrs.join(','));
		},
		function perf(t){
			var getter = function(obj,i){
				return obj[i];
			};
			var observable = new dojox.lang.makeObservable(
				function(obj,i){
					return obj[i];
				},
				function(obj,i,value){
					obj[i] = value;
				},
				function(scope,obj,i,args){
					return obj[i].apply(scope,args);
				}
			);
			var obj = {foo:"bar",bar:'foo'};
			var newObj = observable(obj);
			var start = new Date().getTime();
			for(var i = 0; i < 100000;i++){ // normal access
				var a = obj.foo;
				a = obj.bar;
			}
			var store = {
				getValue:function(item, property,lazyCallback){
					// summary:
					//	Gets the value of an item's 'property'
					//
					// item: /* object */
					// property: /* string */
					//		property to look up value for
					// lazyCallback: /* function*/
					//		not part of the API, but if you are using lazy loading properties, you may provide a callback to resume, in order to have asynchronous loading
					var value = item[property];
					if(value instanceof dojo.Deferred){
						dojox.rpc._sync = !lazyCallback; // tell the service to operate synchronously (I have some concerns about the "thread" safety with FF3, as I think it does event stacking on sync calls)
						value.addCallback(function(returned){
								value = returned;
								if(lazyCallback){lazyCallback(value);}
								return value;
							}
						);
						delete dojox.rpc._sync; // revert to normal async behavior
					}else if(lazyCallback){lazyCallback(value);}
					return value;
				}
			};
			console.log(new Date().getTime() - start);
			start = new Date().getTime();
			for(i = 0; i < 100000;i++){// observed access
				a = store.getValue(obj,"foo");
				a = store.getValue(obj,"bar");
			}
			console.log(new Date().getTime() - start);
			start = new Date().getTime();
			for(i = 0; i < 1000000;i++){ // measure the loop time itself
			}
			console.log(new Date().getTime() - start);
		}
	]);
})();
