(function($){

var Queue = function(chain){this.q = chain||[]};
Queue.prototype = {
	q:[],
	flushed:false,
	add:function(method,args){
		console.log('adding to queue:',method,args);
		this.q.push({cb:method,args:args||[]});
	},
	flush:function(context){
		if(!this.flushed){
			this.flushed = context
			while(this.q.length){
				var callable = this.q.shift()||{};
				//run the callback but keep the original context
				(context[callable.cb]||$.noop).apply(context,callable.args);
			}
		}
	}
};


var FnProxy = function(q,reflect){
	var z = this,k;
	z._fp_set_queue(q);
	z._fp_reflect(reflect);

	return this;
};

FnProxy.prototype = {
	_fp_queue:[],
	_fp_set_queue:function(q){
		this._fp_queue = q;
	},
	_fp_reflect:function(reflect){
		var z = this;
		for( k in reflect){//yes this is an unfiltered for in
			(function(k){
				if(typeof(reflect[k]) == 'function'){
					z[k] = function(_reflected_name){
						if(_reflected_name == '_reflected_name'){
							return k;
						} else {
							z._fp_queue.add(k,arguments);
							
							return z;
						}
					};
				} else {
					if(Object.__defineGetter__){
						z.__defineGetter__(k,function(){
							return reflect[k];
						});
						z.__defineSetter__(k,function(v){
							reflect[k] = v;
						});
					} else {
						z[k] = reflect[k];//copy =( hopefully objects will be refs
					}
					
				}

			}(k));
		}
	}
}


$.asyncSelect = function(fn,jqueryResult){
	var q = new Queue();
	var fn_proxy = new FnProxy(q,jqueryResult||$());

	fn.call(this,function(data){
		q.flush($(data));
	});

	return fn_proxy;
};

$.fn.appendAndSelect = function(markup){
	var target = this;
	return $.asyncSelect(function(cb){
		setTimeout(function(){
			markup = $(markup).appendTo(target);
			cb(markup);
		},5000);
	},this);
}

$(function(){
	var tests = [
		{
			name:"reflect basic object",
			test:function(){
				var obj = {
					data:'',
					lala:function(){
						this.data +='lala';
						return this;
					},
					tata:function(){
						this.data +='tata';
						return this;
					}
				};
				
				var q = new Queue();
				var fnProxy = new FnProxy(q,obj);
				
				obj.lala().tata();
				fnProxy.lala().tata();
				q.flush(obj);
				return obj.data == "lalatatalalatata";
			}
		},
		{
			name:"reflect jquery",
			test:function(){
				var jq = $("#replace_me");
				
				var q = new Queue();
				var fnProxy = new FnProxy(q,jq);

				fnProxy.html("burrito!");
				q.flush(jq);
				
				return $("#replace_me").html() == 'burrito!';
			}
		}
		
	];
	
	if(!window.console){
		var logger = $("<div>").appendTo('body').css({
			height:"120px",
			backgroundColor:'#fff',
			padding:'4px',
			margin:'15px;',
			border:'2px inset #999',
			borderRadius:'5px',
			'-moz-border-radius':'5px',
			'overflow-y':'scroll'
		}).append('<div class="holder"></div>')[0];
		console = {
			log:function(){
				this._log_it(arguments,"<div></div>");
			},
			info:function(){
				this._log_it(arguments,"<div style='color:blue;'></div>");
			},
			warn:function(){
				this._log_it(arguments,"<div style='color:orange;'></div>");
			},
			_log_it:function(args,html){
				var msg = '';
				$.each(args,function(k,v){
					msg += v.toString();
				});
				var holder = $(logger).find('.holder');
				$(html).html(msg).appendTo(holder);
				logger.scrollTop = holder.innerHeight();
			}
		};
	}
	
	$.each(tests,function(i,test){
		console.log(test.name)
		if(test.test()){
			console.info('pass!');
		} else {
			console.warn('fail!');
		}
	});
});


}(jQuery));


